繁体   English   中英

在 React 中滚动时沿 SVG 路径为元素设置动画

[英]Animate element along SVG path on scroll in React

在 vanilla Javascript 中有相对简单的方法来实现这一点(请参阅this for a这样的方法),但我正在努力让这样的东西在 React 中工作,特别是使用像 Framer Motion 这样的 animation 库。

Framer Motion 的useViewPortScroll返回一个方便的 scrollYProgress object ,其“当前”属性告诉您用户当前向下滚动页面的百分比。

我想用这个属性来做这样的事情: const pts = path.getPointAtLength(scrollPercentage * pathLength); ,然后使用pts.xpts.y作为圆 SVG 的 x 和 y 属性 - 所以每次我向下(或向上)滚动页面时,圆的 position 将沿着 ZCD15A75C260F089436B 更新/动画小路。

我正在努力让它与 React 更具声明性的风格一起使用,因为我必须对 circle 和 path 元素使用 refs,这意味着我必须将前面提到的pts = path.getPointAtLength...代码放在里面一个 useEffect 调用,两个 refs 作为依赖项(否则 refs 将是未定义的,在这种情况下,我的圈子 SVG 的 x 和 y 上的pts.xpts.y属性在第一次渲染时将无法访问。

有没有人解决过类似的问题或可能提供指导?

对于这样一个简单的 animation (点围绕圆圈旋转),您可以使用简单的transform: rotate()

 const { useState } = React, { render } = ReactDOM, rootNode = document.getElementById('root') const ScrollMeter = ({progress=0}) => ( <svg className="meter" style={{transform:`rotate(${360*progress}deg)`}} viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" > <circle cx="50" cy="50" r="40" fill="none" stroke="gray" /> <circle cx="90" cy="50" r="10" fill="gray" stroke="white" /> </svg> ) const App = () => { const [scrollProgress, setScrollProgress] = useState(0), onScroll = ({ target: { scrollTop, scrollHeight } }) => setScrollProgress(scrollTop / scrollHeight) return ( <div> <ScrollMeter progress={scrollProgress} /> <div className="scrollArea" onScroll={onScroll} > { 'there should be some random content here '.repeat(1e3)} </div> </div> ) } render(<App />, rootNode)
 .scrollArea { width: 100%; height: 200px; overflow: auto; }.meter { width: 100px; display: block; position: -webkit-sticky; /* Safari */ position: sticky; top: 0; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>

但是,如果您需要更通用的东西(例如任意形状的轨迹),您可以使用与帖子中相同的方法,您指的是:

 const { useState, useRef } = React, { render } = ReactDOM, rootNode = document.getElementById('root') const ScrollMeter = ({progress=0}) => { const route = useRef(), routeLength = (route.current && route.current.getTotalLength()), {x,y} = (route.current? route.current.getPointAtLength(routeLength*progress): {x: 90, y:50}) return ( <svg className="meter" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"> <circle ref={route} cx="50" cy="50" r="40" fill="none" stroke="gray" /> <circle cx={x} cy={y} r="10" fill="gray" stroke="white" /> </svg> ) } const App = () => { const [scrollProgress, setScrollProgress] = useState(0), onScroll = ({ target: { scrollTop, scrollHeight } }) => setScrollProgress(scrollTop / scrollHeight) return ( <div> <ScrollMeter progress={scrollProgress} /> <div className="scrollArea" onScroll={onScroll} > { 'there should be some random content here '.repeat(1e3)} </div> </div> ) } render(<App />, rootNode)
 .scrollArea { width: 100%; height: 200px; overflow: auto; }.meter { width: 100px; display: block; position: -webkit-sticky; /* Safari */ position: sticky; top: 0; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM