lanxin 1 주 전
부모
커밋
74997b70ed

+ 28 - 0
src/components/FlipContainner/index.module.scss

@@ -0,0 +1,28 @@
+.FlipContainner {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+  :global {
+    .flipWrapper {
+      position: relative;
+      width: 100%;
+      height: 100%;
+      transform-style: 'preserve-3d';
+    }
+    .frontFace,
+    .backFace {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      backface-visibility: hidden;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
+  }
+}

+ 50 - 0
src/components/FlipContainner/index.tsx

@@ -0,0 +1,50 @@
+import React, { CSSProperties } from "react";
+import styles from "./index.module.scss";
+
+type FlipContainerProps = {
+  /** 正面内容 */
+  frontContent: React.ReactNode;
+  /** 背面内容 */
+  backContent: React.ReactNode;
+  style?: CSSProperties
+  axis?: 'y' | 'x'
+  duration?: number
+  isFlipped?: boolean
+  perspective?: number;
+  onClick?: () => void
+}
+function FlipContainer({ frontContent, backContent, style, axis = 'y', duration = 0.5, isFlipped, onClick, perspective = 1000 }: FlipContainerProps) {
+
+  const containerStyle: CSSProperties = {
+    perspective: `${perspective}px`, // 3D透视核心
+    ...style,
+  };
+
+  const flipWrapperStyle: CSSProperties = {
+    transition: `transform ${duration}s ease`,
+    transform: isFlipped ? `rotate${axis.toUpperCase()}(180deg)` : "none",
+  };
+
+  const backFaceStyle: CSSProperties = {
+    transform: `rotate${axis.toUpperCase()}(180deg)`,
+  };
+
+  return (
+    <div className={styles.FlipContainner} style={containerStyle} onClick={onClick}>
+      <div style={flipWrapperStyle} className="flipWrapper">
+        {/* 正面元素 */}
+        <div className="frontFace">
+          {frontContent}
+        </div>
+        {/* 背面元素 */}
+        <div style={backFaceStyle} className="backFace">
+          {backContent}
+        </div>
+      </div>
+    </div>
+  )
+}
+
+const MemoFlipContainer = React.memo(FlipContainer);
+
+export default MemoFlipContainer;

+ 57 - 29
src/pages/A2yblm/components/ModalTxt/index.tsx

@@ -9,6 +9,7 @@ import { useSelector } from 'react-redux'
 import { RootState } from '@/store'
 import Shufa from '../Shufa'
 import { forwardRef, useImperativeHandle } from 'react'
+import FlipContainer from '@/components/FlipContainner'
 
 type Props = {
   setIsShowTabBar: (isShowTabBar: boolean) => void
@@ -20,6 +21,7 @@ type Props = {
 function ModalTxt({ setIsShowTabBar, setIsShowMzmTitle, setBottomTxt }: Props, ref: any) {
   const { myData, myLangue } = useSelector((state: RootState) => state.A0Layout)
 
+  const [isFlipped, setIsFlipped] = useState(false)
   const [selectedTab, setSelectedTab] = useState(0)
   const [isOpenTrans, setIsOpenTrans] = useState(false)
   const [isShowShufa, setIsShowShufa] = useState(false)
@@ -85,8 +87,7 @@ function ModalTxt({ setIsShowTabBar, setIsShowMzmTitle, setBottomTxt }: Props, r
           // tooltip.style.setProperty('inset', inset, 'important');
           tooltip.style.inset = inset + '!important'
         }
-      }, 100);
-
+      }, 100)
     }, [index, inset])
 
     return (
@@ -225,10 +226,6 @@ function ModalTxt({ setIsShowTabBar, setIsShowMzmTitle, setBottomTxt }: Props, r
     [setBottomTxt, setIsShowMzmTitle]
   )
 
-  // 原文 | 译文 的背景
-  useEffect(() => {
-    callIframeFu('setStepBgActive', isOpenTrans ? 1 : 0)
-  }, [isOpenTrans])
 
   const gaiShuEn =
     'The inscription on the Cheng Zhe Stele consists of 31 lines of regular script,<br/>with 45 characters per line at maximum.<br/>The characters are approximately 2 cm in size, set within square grids, totaling 1,404 characters.<br/>No formal title of the stele is engraved. <br/>The entire text praises the historical achievements of the Cheng family.'
@@ -242,24 +239,48 @@ function ModalTxt({ setIsShowTabBar, setIsShowMzmTitle, setBottomTxt }: Props, r
         id='modalTxt'
       >
         <div className='modalTxtContainner'>
+          {/* <FlipContainer
+            style={{ position: 'absolute', right: '50px', top: '50%', transform: 'translateY(-50%)' }}
+            isFlipped={isFlipped}
+            onClick={() => setIsFlipped(!isFlipped)}
+            frontContent={
+              <div
+                className='aaaa'
+                onClick={() => setIsFlipped(true)}
+                style={{ width: '300px', height: '300px', background: '#ccc' }}
+              ></div>
+            }
+            backContent={
+              <div
+                className='aaaa'
+                onClick={() => setIsFlipped(false)}
+                style={{ width: '300px', height: '300px', background: '#6d1e1eff' }}
+              ></div>
+            }
+          /> */}
+
+          {!isOpenTrans && (
+            <div
+              className='intro'
+              id='introContent'
+              style={{
+                opacity: selectedTab !== 0 ? '1' : '0',
+                height: selectedTab !== 0 ? '78%' : '0%',
+                width: selectedTab !== 0 ? '100%' : '0%'
+              }}
+            >
+              <div className='intro_title songFont'>
+                {selectedTab !== 0 && modalTxtTab[selectedTab - 1].name}
+              </div>
+              <div className='intro_txt'>
+                {selectedTab !== 0 && myData.readDetail[selectedTab - 1].intro}
+              </div>
 
-
-          {!isOpenTrans && <div
-            className='intro'
-            id='introContent'
-            style={{
-              opacity: selectedTab !== 0 ? '1' : '0',
-              height: selectedTab !== 0 ? '78%' : '0%',
-              width: selectedTab !== 0 ? '100%' : '0%'
-            }}
-          >
-            <div className="intro_title songFont">
-              {selectedTab !== 0 && modalTxtTab[selectedTab - 1].name}
+              <div className='intro_btn' onClick={() => setIsOpenTrans(true)}>
+                原文 | 译文
+              </div>
             </div>
-            <div className="intro_txt"> {selectedTab !== 0 && myData.readDetail[selectedTab - 1].intro}</div>
-
-            <div className="intro_btn" onClick={() => setIsOpenTrans(true)}>原文 | 译文</div>
-          </div>}
+          )}
 
           {selectedTab === 0 && (
             <div className='content'>
@@ -270,7 +291,9 @@ function ModalTxt({ setIsShowTabBar, setIsShowMzmTitle, setBottomTxt }: Props, r
                 className='text'
                 dangerouslySetInnerHTML={{ __html: myLangue === 'EN' ? gaiShuEn : gaiShu }}
               ></div>
-              <div className="shufaBtn" onClick={() => setIsShowShufa(true)}>书法赏析</div>
+              <div className='shufaBtn' onClick={() => setIsShowShufa(true)}>
+                书法赏析
+              </div>
             </div>
           )}
 
@@ -306,12 +329,17 @@ function ModalTxt({ setIsShowTabBar, setIsShowMzmTitle, setBottomTxt }: Props, r
             ))}
           </div>
 
-          {(isOpenTrans && selectedTab !== 0) && <div className="translateModal">
-            <div className="txtWithTrans" onTouchMove={() => setShowTooltip(-1)}> {CommentText({
-              str: myData.readDetail[selectedTab - 1].translate_v2,
-              index: selectedTab - 1
-            })}</div>
-          </div>}
+          {isOpenTrans && selectedTab !== 0 && (
+            <div className='translateModal'>
+              <div className='txtWithTrans' onTouchMove={() => setShowTooltip(-1)}>
+                {' '}
+                {CommentText({
+                  str: myData.readDetail[selectedTab - 1].translate_v2,
+                  index: selectedTab - 1
+                })}
+              </div>
+            </div>
+          )}
         </div>
       </div>
       {/* 书法赏析 */}

+ 1 - 1
src/pages/A2yblm/components/Shufa/index.module.scss

@@ -220,7 +220,7 @@
             width: auto;
             height: 167px;
             object-fit: fill;
-            animation: bei_shang 5s steps(35) forwards;
+            animation: bei_shang 3s steps(35) forwards;
           }
           .bei2 {
             max-width: none;

+ 28 - 1
src/pages/A2yblm/components/Shufa/index.tsx

@@ -1,4 +1,4 @@
-import React, { useState } from 'react'
+import React, { useEffect, useState, useRef } from 'react'
 import styles from './index.module.scss'
 import Zback from '@/components/Zback'
 import { useSelector } from 'react-redux'
@@ -13,6 +13,7 @@ type ActiveItem = 'lian1' | 'lian2' | 'bei1' | 'bei2' | ''
 function Shufa({ setIsShowShufa }: { setIsShowShufa: (isShowShufa: boolean) => void }) {
   const [showName, setName] = useState('shufa1')
   const [activeItem, setActiveItem] = useState<ActiveItem>('')
+  const timerRef = useRef<NodeJS.Timeout | null>(null);
   const { myData, myLangue } = useSelector((state: RootState) => state.A0Layout)
   const Line = ({ index }: { index: number }) => {
     return (
@@ -27,6 +28,32 @@ function Shufa({ setIsShowShufa }: { setIsShowShufa: (isShowShufa: boolean) => v
     )
   }
 
+  useEffect(() => {
+    if (timerRef.current) {
+      clearTimeout(timerRef.current);
+    }
+    const delayMap = {
+      lian1: 5000,
+      lian2: 4000,
+      bei1: 4000,
+      bei2: 4000,
+    };
+
+    if (activeItem && delayMap.hasOwnProperty(activeItem)) {
+      timerRef.current = setTimeout(() => {
+        setActiveItem('');
+        // 定时器执行后清空 ref
+        timerRef.current = null;
+      }, delayMap[activeItem]);
+    }
+
+    return () => {
+      if (timerRef.current) {
+        clearTimeout(timerRef.current);
+      }
+    };
+  }, [activeItem])
+
   const backClick = () => {
     if (showName === 'shufa2') {
       setActiveItem('')