如何为滚动绘制的带遮罩SVG虚线添加随动箭头?
如何为随滚动绘制的SVG虚线添加同步移动的箭头?
你当前的实现是通过mask来显示随滚动逐步绘制的虚线,但原本的markerEnd绑定在完整路径上,导致箭头固定在路径终点,无法跟随绘制的末端移动。下面是具体的解决方法:
核心思路
- 移除路径上的
markerEnd,改用独立的箭头图形元素 - 在滚动事件中,计算当前绘制到的路径位置,获取该点的坐标和切线方向
- 动态更新箭头的位置和旋转角度,让它始终贴合绘制的末端
修改后的完整代码
import '../css/theArrow.css' import React, { useEffect, useRef } from 'react'; export default function TheArrow() { const thePathRef = useRef(null); const maskRef = useRef(null); const wrapRef = useRef(null); const arrowContainerRef = useRef(null); useEffect(() => { const thePath = thePathRef.current; const mask = maskRef.current; const wrap = wrapRef.current; const arrowContainer = arrowContainerRef.current; const l = thePath.getTotalLength(); const dasharray = l; mask.style.strokeDasharray = dasharray; let dashoffset = l; mask.style.strokeDashoffset = dashoffset; const updateArrow = () => { // 计算当前已绘制的路径长度 const currentLength = l - dashoffset; if (currentLength <= 0 || currentLength >= l) { arrowContainer.style.display = 'none'; return; } arrowContainer.style.display = 'block'; // 获取当前绘制末端的坐标 const currentPoint = thePath.getPointAtLength(currentLength); // 获取下一个相邻点,用于计算切线角度 const nextPoint = thePath.getPointAtLength(Math.min(currentLength + 1, l)); // 计算旋转角度(弧度转角度) const angle = Math.atan2(nextPoint.y - currentPoint.y, nextPoint.x - currentPoint.x) * 180 / Math.PI; // 更新箭头的位置和旋转 arrowContainer.setAttribute( 'transform', `translate(${currentPoint.x}, ${currentPoint.y}) rotate(${angle})` ); }; const handleScroll = () => { dashoffset = l - window.scrollY * l / (wrap.scrollHeight - wrap.clientHeight) - 300; // 限制dashoffset的范围,避免超出路径 dashoffset = Math.max(0, Math.min(l, dashoffset)); mask.style.strokeDashoffset = dashoffset; updateArrow(); }; window.addEventListener("scroll", handleScroll); // 初始化时更新一次箭头位置 updateArrow(); return () => window.removeEventListener("scroll", handleScroll); }, []) return ( <div id="wrap" ref={wrapRef}> <svg id="svg" viewBox="0 0 370 1856" fill="none" preserveAspectRatio="xMidYMax meet" > <defs> <path id="thePath" ref={thePathRef} d="M164.876 1C171.043 44.1667 143.139 77.3269 70.6388 108C-33.3612 152 9.13878 264 18.6388 353.5C24.4115 407.886 47.2145 492.854 132.814 534.054C239.814 585.554 319.217 442.842 244.639 343C200.901 284.446 45.9382 328.5 59.4382 474.5C70.2382 591.3 163.472 640.833 204.639 655C220.139 662 249.676 668.1 252.876 716.5C256.876 777 266.713 891.554 192.876 932.5C172.139 944 38.8763 992 38.8763 1141.5C38.8763 1261.1 38.8763 1455 38.8763 1537C38.8763 1540 43.4388 1544 50.6388 1544C57.8388 1544 178.876 1544 238.876 1544C254.376 1543.5 285.376 1547.5 285.376 1559.5C285.376 1574.5 290.876 1596 336.876 1596C382.876 1596 338.133 1641.82 353.876 1709C383.639 1836 371.639 1853.26 230.139 1853.26" stroke="#FFCC00" strokeWidth="6" /> <mask id="mask1"> <use id="mask" ref={maskRef} xlinkHref="#thePath" /> </mask> </defs> {/* 随绘制移动的箭头 */} <g ref={arrowContainerRef} style={{ display: 'none' }}> <path className="cool arrowHead" d="M0,-5L10,0L0,5" fill="#FFCC00" /> </g> <use xlinkHref="#thePath" strokeDasharray="8 6" stroke="black" mask="url(#mask1)"/> </svg> </div> ) }
关键改动说明
- 使用
useRef获取DOM元素,替代直接的id选择,符合React最佳实践 - 添加
updateArrow函数,负责计算箭头的位置和角度:- 通过
getPointAtLength获取当前绘制末端的坐标 - 取相邻点计算切线方向,用
Math.atan2转换为旋转角度 - 用
transform属性更新箭头的位置和旋转状态
- 通过
- 在滚动事件中同步调用
updateArrow,确保箭头和绘制进度同步 - 增加边界判断,当路径未开始绘制或已完成时隐藏箭头
内容的提问来源于stack exchange,提问作者vadim-nest
相关产品推荐
相关产品推荐

