CSS 动画将页面上的图标滚动到路径

Kun*_*jan 4 html css jquery svg css-animations

我想创建一个 CSS 动画,其中箭头在滚动时沿着 SVG 蛇形路径移动,并在每个新部分上更改图标。

这是我的页面设计供参考

我看了几个例子:

  1. codepen.io/yesvin/pen/XymwvX
  2. 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);
})
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

Den*_*ats 6

现代策略是使用CSS 运动路径(又名CSS 中的offset-pathand path())。通过计算scrollProgressSVG 路径的 ,您可以使用它来计算offset-distance图标的百分比。只需确保图标绝对位于 SVG 的位置即可(0,0)

在 SVG 路径跨越整个页面的简单情况下,scrollProgress只是window.scrollY / (docElt.scrollHeight - docElt.clientHeight). 但是,如果 SVG 路径不跨越整个滚动容器,则需要使用getBoundingClientRect()来计算scrollProgress并将其限制在 0 和 1 之间。下面的代码演示涵盖了这种更复杂的情况。

要在用户滚动时填充路径,请结合getTotalLength()使用路径元素的方法来scrollProgress计算用户沿路径的距离(以像素为单位) ( drawLength) 以及他们还剩多少像素 ( 以像素为单位) ( rest)。然后,您可以使用一个常见技巧将stroke-dasharray其设置为${drawLength}px ${rest}px

最后,您还可以使用scrollProgressCSS 来确定要显示的图标background-image。用于offset-rotate: 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());
Run Code Online (Sandbox Code Playgroud)
#pathIcon {
  position: absolute;
  inset: 0;
  width: 25px;
  height: 25px;
  background-size: 25px;
  offset-rotate: 0rad;
}
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)


希望在不久的将来,我们将获得滚动驱动的动画,甚至更好地支持浏览器中的 CSS 运动路径,这将使仅使用 CSS 沿路径制作元素动画变得更加简单。例如,如果您使用的是启用了该experimental-web-platform-features标志的 Chrome Canary 115+,则以下 CSS 的效果即使不是更好,也同样有效。无需 JS:

#arrow {
  offset-path: url(#Path_440);
  offset-anchor: left;
  animation: offsetDistance linear;
  animation-timeline: scroll();
}

@keyframes offsetDistance {
  from {
    offset-distance: 0%;
  }
  to {
    offset-distance: 100%;
  }
}
Run Code Online (Sandbox Code Playgroud)
<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 xlink:href="#Path_440" stroke-dasharray="20 10" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" id="arrow" style="position: absolute; left: 0; top: 0;" fill-rule="evenodd" clip-rule="evenodd">
    <path d="M21.883 12l-7.527 6.235.644.765 9-7.521-9-7.479-.645.764 7.529 6.236h-21.884v1h21.883z" />
</svg>
Run Code Online (Sandbox Code Playgroud)

详细了解如何使用滚动驱动的动画。