我想創建一個CSS動畫,其中一個箭頭在滾動時沿著SVG蛇形路徑移動,并在每個新部分改變圖標。
下面是我的頁面設計,供參考。
我看了幾個例子:
codepen.io/yesvin/pen/XymwvX codepen.io/gkando/pen/LYEvjOv 但是我無法用我的頁面設計重現預期的行為。
以下是我目前為止的代碼:
window.addEventListener('scroll', function() {
let l = Path_440.getTotalLength();
let dasharray = l;
let dashoffset = l;
e = document.documentElement;
theFill.setAttributeNS(null, "stroke-dasharray", l);
theFill.setAttributeNS(null, "stroke-dashoffset", l);
dashoffset = l - window.scrollY * l / (e.scrollHeight - e.clientHeight);
//console.log('window.scrollY', window.scrollY, 'scrollTop', e.scrollTop, 'scrollHeight', e.scrollHeight, 'clientHeight', e.clientHeight, 'dash-offset', dashoffset);
theFill.setAttributeNS(null, "stroke-dashoffset", dashoffset);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<svg width="246" height="2990" viewBox="0 0 246 2990" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<path id="Path_440" d="M210.001 1.5C210.001 1.5 41.0015 324.5 6.50082 617.5C-27.004 902.042 182.501 1032.5 240.001 1313C275.095 1484.2 29.8527 1661 41.0008 1914.5C50.4751 2129.94 230.431 2237.5 235.001 2431.5C240.42 2661.59 41.0008 2988 41.0008 2988" stroke="#F39029" stroke-width="4" stroke-dasharray="20 10"/>
</defs>
<use xlink:href="#Path_440" stroke="#000" stroke-width="4" stroke-dasharray="1"/>
<use id="theFill" xlink:href="#Path_440" stroke="#000" stroke-width="1"/>
</svg>
# # #現代策略是使用CSS運動路徑(在CSS中也稱為offset-path和path())。通過計算SVG路徑的scrollProgress,你可以用它來計算圖標的偏移量。只要確保圖標絕對位于您的SVG,0)上。
在SVG路徑跨越整個頁面的簡單情況下,scrollProgress只是window . scrolly/(doc ELT . scroll height-doc ELT . client height)。但是,如果SVG路徑沒有跨越整個scroll容器,那么您需要使用getBoundingClientRect()來計算scrollProgress并將其固定在0和1之間。下面的代碼演示涵蓋了這種更復雜的情況。
要在用戶滾動時填充路徑,請將path元素的getTotalLength()方法與scrollProgress結合起來,計算用戶沿著路徑走了多遠(以像素為單位,drawLength)以及他們還剩多少(以像素為單位,rest)。然后,您可以對stroke-dasharray使用一個常見的技巧,即將其設置為${drawLength}px ${rest}px。
最后,您還可以使用scrollProgress來確定哪個圖標與CSS背景圖像一起顯示。使用偏移-旋轉:0rad以防止圖標沿路徑方向旋轉。
pathIcon.style.offsetPath = `path('${Path_440.getAttribute("d")}')`;
const pathLength = Path_440.getTotalLength();
function clamp(min, val, max) {
return Math.min(Math.max(min, val), max);
}
function updatePath() {
const docElt = document.documentElement;
const pathBox = theFill.getBoundingClientRect();
// calculates scroll progress based on viewport progress
const scrollProgress =
clamp(0, -pathBox.y / (pathBox.height - docElt.clientHeight), 1);
pathIcon.style.offsetDistance = `${scrollProgress * 100}%`;
// These lines fill in the dashes as you scroll down.
const drawLength = pathLength * scrollProgress;
const rest = pathLength - drawLength;
theFill.style.strokeDasharray = `${drawLength}px ${rest}px`;
// You can update the icon/SVG here using your own logic.
// For the example, I'm changing the CSS background-image.
pathIcon.style.backgroundImage = `url(${getIconSrc(scrollProgress)})`;
}
function getIconSrc(scrollPercent) {
if (scrollPercent < 0.2) {
return 'https://via.placeholder.com/25x25/FF0000?text=red';
} else if (scrollPercent < 0.4) {
return 'https://via.placeholder.com/25x25/FFA500?text=orange';
} else if (scrollPercent < 0.6) {
return 'https://via.placeholder.com/25x25/FFFF00?text=yellow';
} else if (scrollPercent < 0.8) {
return 'https://via.placeholder.com/25x25/00FF00?text=green';
} else if (scrollPercent < 1) {
return 'https://via.placeholder.com/25x25/0000FF?text=blue';
} else if (scrollPercent === 1) {
// A scrollPercent of 1 indicates that we have reached the end of the path.
return 'https://via.placeholder.com/25x25/A020F0?text=purple';
}
}
updatePath();
window.addEventListener("scroll", () => updatePath());
#pathIcon {
position: absolute;
inset: 0;
width: 25px;
height: 25px;
background-size: 25px;
offset-rotate: 0rad;
}
<div style="height: 175px;"></div>
<div style="position: relative;">
<svg width="246" height="2990" viewBox="0 0 246 2990" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<path id="Path_440"
d="M210.001 1.5C210.001 1.5 41.0015 324.5 6.50082 617.5C-27.004 902.042 182.501 1032.5 240.001 1313C275.095 1484.2 29.8527 1661 41.0008 1914.5C50.4751 2129.94 230.431 2237.5 235.001 2431.5C240.42 2661.59 41.0008 2988 41.0008 2988"
stroke-width="4" stroke="#F39029" />
</defs>
<use href="#Path_440" stroke-dasharray="20 10" />
<use id="theFill" href="#Path_440" />
</svg>
<div id="pathIcon"></div>
</div>
# # #這不完全是CSS動畫,但產生了類似的效果。如果你愿意這樣做,這可以進一步完善。
我只是使用HTML畫布來繪制正弦曲線。現在,你必須知道一些高中水平的三角學...
<!DOCTYPE html>
<html lang="en">
<head>
<title>Path</title>
<style>
body,
html,
canvas {
height: 99.6%;
width: 99.6%;
}
canvas {
background-color: black;
}
</style>
</head>
<body>
<canvas id="c"></canvas>
</body>
<script src="canvas.js"></script>
</html>
canvas.js有,
let canvas = document.getElementById('c');
canvas.width = document.body.clientWidth;
canvas.height = document.body.clientHeight;
let ctx = canvas.getContext('2d');
ctx.fillStyle = '#FF0000';
let delta = 100;
let n = 100;
addEventListener('wheel', (e) => {
let c = 150;
for (let i = n; i <= n; i++) {
let x = (i * c * 2 * Math.PI) / (300*delta);
let y = -c * Math.sin(x / c) + 600;
ctx.fillRect(y, x, 3, 3);
}
n += delta;
});
這是它看起來的樣子,