如何在react-fiber/drei的<Text>组件中实现透明度过渡动画
问题描述
想用react-fiber/drei的<Text>组件实现从0到1的透明度过渡动画,试了react-spring/three但没效果,代码如下:
import { useEffect, useRef, useState} from "react"; import { Text } from "@react-three/drei"; import { animated, useSpring } from "@react-spring/three"; function Cube() { const meshRef = useRef<Mesh>(null); const styles = useSpring({ from: { opacity: 0 }, to: { opacity: 1 }, delay: 1000, }) return ( <group> <Text position={[0, 0, 0]} fontSize={1} color="white" anchorX="center" anchorY="middle" fillOpacity={styles.opacity as any} > test </Text> </group> ); }
请问react-spring/three失效时,还有其他框架可用吗?或者必须手动写动画?
解决方案
一、先修复react-spring/three的用法问题
你的代码没给<Text>套animated包装器,react-spring需要通过animated组件劫持目标组件的属性才能驱动动画,而且属性绑定要用到正确的插值语法。修改后代码如下:
import { useRef } from "react"; import { Text } from "@react-three/drei"; import { animated, useSpring } from "@react-spring/three"; // 用animated包装Text组件 const AnimatedText = animated(Text); function Cube() { const styles = useSpring({ from: { fillOpacity: 0 }, to: { fillOpacity: 1 }, delay: 1000, }) return ( <group> <AnimatedText position={[0, 0, 0]} fontSize={1} color="white" anchorX="center" anchorY="middle" {...styles} // 直接展开spring样式 > test </AnimatedText> </group> ); }
这里把fillOpacity直接放进spring配置,再展开到AnimatedText上,react-spring就能正确驱动这个属性的动画了。
二、替代框架:framer-motion/three
如果还是觉得react-spring不好用,可以试试framer-motion的three适配版本,用法更直观:
import { Text } from "@react-three/drei"; import { motion } from "framer-motion/three"; function Cube() { return ( <group> <motion(Text) position={[0, 0, 0]} fontSize={1} color="white" anchorX="center" anchorY="middle" initial={{ fillOpacity: 0 }} animate={{ fillOpacity: 1 }} transition={{ delay: 1 }} > test </motion(Text)> </group> ); }
三、手动实现过渡动画
不想用第三方库的话,也可以结合useState和requestAnimationFrame手动写,或者用gsap这类动画库直接操作组件:
方式1:原生requestAnimationFrame
import { useEffect, useState } from "react"; import { Text } from "@react-three/drei"; function Cube() { const [fillOpacity, setFillOpacity] = useState(0); useEffect(() => { let startTime: number | null = null; const duration = 1000; // 动画时长1秒 const delay = 1000; // 延迟1秒启动 function animate(timestamp: number) { if (!startTime) startTime = timestamp; // 计算延迟后的时间进度 const elapsed = timestamp - startTime - delay; const progress = Math.min(elapsed / duration, 1); if (progress >= 0) { setFillOpacity(progress); } if (progress < 1) { requestAnimationFrame(animate); } } const frameId = requestAnimationFrame(animate); return () => cancelAnimationFrame(frameId); }, []); return ( <group> <Text position={[0, 0, 0]} fontSize={1} color="white" anchorX="center" anchorY="middle" fillOpacity={fillOpacity} > test </Text> </group> ); }
方式2:用gsap直接控制
import { useEffect, useRef } from "react"; import { Text } from "@react-three/drei"; import gsap from "gsap"; function Cube() { const textRef = useRef<any>(null); useEffect(() => { gsap.fromTo(textRef.current, { fillOpacity: 0 }, { fillOpacity: 1, delay: 1, duration: 1 } ); }, []); return ( <group> <Text ref={textRef} position={[0, 0, 0]} fontSize={1} color="white" anchorX="center" anchorY="middle" > test </Text> </group> ); }
内容的提问来源于stack exchange,提问作者NiceBro
相关产品推荐
相关产品推荐

