簡體   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