英文:
How to animate an element along an SVG path using only CSS and Scroll-driven Animations?
问题
我正在尝试使用Chrome Canary,并有以下代码,其中定义了一个SVG路径和一个图像元素。我想要在滚动时通过SVG路径来使图像动画化。如何仅使用CSS和滚动驱动的动画(不使用JavaScript)来实现这一目标?
<img id="car" width="256" height="256" src="data:image/png;base64,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
<details>
<summary>英文:</summary>
I am experimenting with Chrome Canary, and I have the following code which defines an SVG path and an image element. I would like to animate the image as I scroll, along the SVG path. How do I achieve this using only CSS and scroll-driven animations (without JavaScript)?
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-css -->
#car {
width: 25px;
height: 25px;
}
<!-- language: lang-html -->
<img id="car" width="256" height="256" src="data:image/png;base64,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">
<svg width="175" height="1300" preserveAspectRatio="none" viewBox="0 0 175 1300" fill="none" xmlns="http://www.w3.org/2000/svg">
<path id="my_path"
d="M 28 0 C -72 200 188 300 88 500 C -32 700 88 1100 148 1000 C 208 900 148 600 68 1000 C 28 1200 128 1300 128 1300"
stroke-width="4" stroke="#000" />
</svg>
<!-- end snippet -->
</details>
# 答案1
**得分**: -2
以下是您提供的文本的翻译:
**注意:这仅在启用了`experimental-web-platform-features`的Chrome Canary 115+中有效。**
总体而言,策略是使用`offset-path`样式属性和`url()` CSS函数引用元素应该遵循的路径。在这种情况下,您将其设置为`url(#my_path)`。然后在您的`@keyframes <name>`中,从`offset-distance: 0%;`过渡到`offset-distance: 100%;`。
然而,将滚动驱动的时间轴附加到您的动画的方法在很大程度上取决于您的页面结构以及路径在页面上的确切位置。具体来说,有两种情况。
**情况1:路径跨越整个滚动容器**
这是简单的情况。我们可以使用`animation-timeline: scroll();`将滚动时间轴与动画关联起来,该时间轴引用了最近祖先滚动容器的块轴。然后我们使用`animation: <name> linear;`将动画设置在元素上。
确保还通过`position: absolute`和`inset: 0`将元素的初始位置设置为偏移父元素的`(0,0)`。所有这些,代码应该看起来像这样:
```css
#car {
width: 25px;
height: 25px;
position: absolute;
inset: 0;
offset-path: url(#my_path);
animation: offsetDistance linear;
animation-timeline: scroll();
}
@keyframes offsetDistance {
from { offset-distance: 0%; }
to { offset-distance: 100%; }
}
情况2:路径是滚动容器的一部分
在这种情况下,我们必须使用View Progress Timeline,这需要更多的设置才能正确设置,因此更容易逐步解释。
- 在元素和路径的最近共同祖先上声明一个范围。这使得可以将视图时间轴的附加推迟到后代,后代将是路径元素,并由其他后代引用,后者将是图像元素。
#container {
position: relative;
timeline-scope: --container;
}
- 使用相同的名称将路径元素附加到视图时间轴,如下所示:
#my_path {
view-timeline-name: --container;
}
- 将命名的时间轴与元素的动画关联。
#car {
animation-timeline: --container; /* ... */
}
- 确定您的动画的时间轴范围。这也很大程度上取决于您页面的结构,可能需要一些手动测试。但是,这个工具在了解各种范围的行为方面非常有帮助。在我的示例中,我选择了以下范围:
#car {
animation-timeline: --container;
animation-range: exit-crossing -5% entry-crossing 105%;
/* ... */
}
- 最后,为了防止动画结束时出现视觉故障,请将
animation-fill-mode
更改为forwards
。
#car {
animation: <name> linear forwards;
animation-timeline: --container;
animation-range: exit-crossing -5% entry-crossing 105%;
/* ... */
}
最后,将所有内容放在一起,我们得到以下动画:
#container {
position: relative;
timeline-scope: --container;
}
#my_path {
view-timeline-name: --container;
}
#car {
width: 25px;
height: 25px;
position: absolute;
inset: 0;
offset-path: url(#my_path);
animation: offsetDistance linear forwards;
animation-timeline: --container;
animation-range: exit-crossing -5% entry-crossing 105%;
}
@keyframes offsetDistance {
from { offset-distance: 0%; }
to { offset-distance: 100%; }
}
希望这有助于您理解文本的内容和意义。
英文:
Note: This currently only works in Chrome Canary 115+ with experimental-web-platform-features
enabled.
In general, the strategy is to use the offset-path
style property with the url()
CSS function to reference the path the element should follow. In this case specifically, you would set it to url(#my_path)
. And then in your @keyframes <name>
, simply transition from offset-distance: 0%;
to offset-distance: 100%;
.
However, the method of attaching a scroll-driven timeline to your animation depends largely on the structure of your page and precisely where the path is located on the page. Namely, there are two cases.
Case 1: The path spans the entire scroll container
This is the simple case. We can associate a scroll timeline with the animation using animation-timeline: scroll();
, which references the block axis of the nearest ancestor scroll container. And then we set the animation on the element using animation: <name> linear;
.
Make sure to also set the initial position of the element to (0,0)
of the offset parent via position: absolute
and inset: 0
. All together, the code should look something like this:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-css -->
#car {
width: 25px;
height: 25px;
position: absolute;
inset: 0;
offset-path: url(#my_path);
animation: offsetDistance linear;
animation-timeline: scroll();
}
@keyframes offsetDistance {
from { offset-distance: 0%; }
to { offset-distance: 100%; }
}
<!-- language: lang-html -->
<img id="car" width="256" height="256" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAMAAAAp4XiDAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAFZaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA2LjAuMCI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnRpZmY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8xLjAvIj4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+Chle4QcAAAAJcEhZcwAACxMAAAsTAQCanBgAAAL6UExURUdwTLwoJQAEB8w9OuJBPuZWU9BCP7hPTOceHq4tKNktJdpsarwpJeRPSq8ICHEnJrAPC4cxL8lAPdBTULUbG6lCQs1FQbcpJ9hlYsAuK885NsIzMeM3Me07N4NQUr8hHq8yMNFHRMY8Obk4NcI3NMo1MrwpJ7spJrctK7QXEtdjX6MAAMo6OMhCPy4MDrxBP6osK8dAPOlAO9x5d8Q8OaYAALVXVrdLScZqaZBmZpAAAIwzM8xPTKw7Oa80NJ8nJ7wqJ78qKMY2NJcdG95LR0pLUMMrKbkzL8s5N6coJsQ6NkJDQzlMTGQAAOFPTGgAAB9QV510dOdSTjxaYs1YVdJkYv+apddqFdhlY9dhX9pqaBUUFdZjYdNWVNZaV9BEQdRYVdBGQ9VUUdNPS9pvbNx4dhsZGtJRTtxxb9ZfXdloZdI3M89QTcdOTNx0ctZdWjAtLttaWME7ON6AftlnZNpsaiUdHsxAPNlbWM8/PNQ9OgA3Os9KRtdXVNdQTSQkJdVbWCEWFzk5PM06N9NUUdNIRUlbYqBRUbJAPt57eSopK4o/PgAPEsYsKeJwbtA7OLg1M9pXVIosKy5ISwALDQAaHVBPUdJNStpRTshTUaI8Oqg3NrMzMahDQalSUtxfXAMGCHc5OsQzMAAsMNxLR9dEQeN3dbUjIdxjYAAUFk1FRU45OwAeIQEoK9NLR75VVLgtK20yM3ssLJIyMpk2Nd5pZq1CQa4SEJVKSRweH8o0MoI7Ot9lYTYzNa0wLbtDQH1LSqEuLEhNU9FUUURGSok2NQIxM0BAQ1tZXdVoZmdiZUxKSwAlKJI2NmIvLj85OisjJHZCRLMbGbYEAEtmbpkwLrQ7OYxMTZuFhScvMC8xNRxBQ9BfXTQbHCcUFgAWGFEnJ0BQUCoYGUc/QHZVW89NSjBUWAtGSXh7fIdDQl5sb5SYmISIiJdeXw0fIEoiIrxHRRYfIRM7QMdfXSw/QL8pJlFfX5hvb5MqKXh2dmlWVgBAQYFhYaBzc6yuSJ0AAADfdFJOUwD3/hQ5OTgCAQQg+g45Tf5v/vz4Gpos1vv89/ouMf79/fFu2q3s5anDkPsk1cr+YP3ILfu+PP3898yd9PmNdE7Kk8D7hPzukuXH+Pz8a4px/v5B/vn5kv////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////7////////////////////////////////////+//////////////6TEl4rAAAESUlEQVRIx2NgGAWjgHqAgxOHODsuwAmW5AABVA14bGHnQijj5OSEKAVq52AWE2NjY+MN4mVFAF5eXllZLr9+PmYRZh5OmCVQTUrKitOT6+qS0mfOzAWBDx/27+8AgW1z5879YSAlpeqho+Ok4ijApw3SBMT6vZdbk6enz5iRLNcWHPC8evGaHj09JqaeNWurV5/beHTL7KKl2dlVS5csWRLvpeHPCdTDqbxIOLYl6Wn7W7PD6+e1X794ceFGQUF+wbOzOq61B974tcrH2yxLWEiSUVoovXDJX0seBgbTz3kJCQmHNz57dWV9YVxFXFwckBvb0lJXt/Ttm9evpx2Z9nDVyYzSmJlFyy9Jx1vcsGJgEEg1bmkonLv49Im2eStiYmIiI2NAZH5CbM2fF29eHF91etrk9q0nS1Oi7uRu2C4/z1qEwX0Zd0JD4e4zTdOete+JKCmJKIkCgqyIyPxkuVc/T3U1HV/15UBESUREVlTUyZlzLs+xZ1ABaslPODe16/htkBagTEpGRkZKVERk0oFvp06nFRTsvbn6zoqYiAiQrizzbXYMJsu4G/IPv79ftrd6xx6gBFBHXmpqXkZURO2BhycKouvru7rerZ+fCXHw1tB2WwbDPomEipl3e9IurO1eERkJ1JKX2tnZmZeSVfFy8uPo6LS0srKV5vMb8jMzgdpWhOwQZdAFacl91BPdOLV7fmZmDEhLcXFxKlBL9+2CBdFATY092ybGJjQAQUKF8HWQFq1YoJaVaY1TjgLNiswqBdoC1VJdFg0CjQtmzchJB4Na4ZeiQIdpHY7LvXs/rXHB0cKGhnyIy1LzSiMm7l5ctjMNrOXg0vL4xJrpQC02QIeZ9Emk1+Y8WFNWNuVsYWxCXGYEOMhKSyKTFh4rqK/fuTMtrWdfFUjLuunJE2122DKoL+NOjk0/d+bChZVbJh5uiY3LjCkBBmZEZkPywk9Nt2521dd/TJsA1AKyJrlGYa4dg2Yn97rDdbuPFRSs2TIjvaWlJSEfGDLA2E9qvTj5yPGmvbcK1j7Y1V9eGZ+YKLRh+3YFe2CCKY7PmTH39+Pzi2fV5YDA4djYhNjDOU/Ln7RNOwTUdL56UnZzZWX5ouXbL4ETjOl643XJyTUXn0+uvtaa2NqaCALx8fGVldnXr1xpazt0aPJqyeZdizYsv7S8SAqcLDnFvyrW1CSWz+qeHV80++qkTfz8/JMmbdq8Zfa1CbPknpw60eZqsEiSkRGY+OPrsr9bMoOymPzl7P7myqqqyk1r7vdMYQKCe4+mrjmz+Fj16o2bgFlsQ29v767sqqr+RYyevpzgbKkkrirV3N9f1TyhCAgmTJijMGfOvn0HDx68uvns2UnOjG5qauIOGuqa8IwMKgiYuUDFBRsLEjAykpGRkeVy6efjEeHhgZdmkOKCA6oPf6EELM7ghRKIi7vsA6nlwCj68Bew7KOVzCigEAAAop3jpH2nqD8AAAAASUVORK5CYII=">
<svg width="175" height="1300" viewBox="0 0 175 1300" fill="none" xmlns="http://www.w3.org/2000/svg">
<path id="my_path" d="M 28 0 C -72 200 188 300 88 500 C -32 700 88 1100 148 1000 C 208 900 148 600 68 1000 C 28 1200 128 1300 128 1300"
stroke-width="4" stroke="#000" />
</svg>
<!-- end snippet -->
Case 2: The path is a portion of the scroll container
In this case, we must use a View Progress Timeline which requires a little more effort to setup correctly, and so it's easier to explain step-by-step.
- Declare a scope on the nearest common ancestor of the element and the path. This enables the attachment of a view timeline to be deferred to a descendent, which will be the path element, and referenced by other decendents, which will be the image element.
#container {
timeline-scope: --container;
}
- Attach the path element to the view timeline with the same name, like so:
#my_path {
view-timeline-name: --container;
}
- Associate the named timeline with the element's animation.
#car {
animation-timeline: --container; /* ... */
}
- Determine your animation's timeline range. This also depends greatly on your page's structure and likely requires some manual testing. However, this tool is extremely helpful in understanding the behavior of various ranges. In my example, I choose the following:
#car {
animation-timeline: --container;
animation-range: exit-crossing -5% entry-crossing 105%;
/* ... */
}
- Lastly, to prevent visual glitches at the end of the animation, change the
animation-fill-mode
toforwards
.
#car {
animation: <name> linear forwards;
animation-timeline: --container;
animation-range: exit-crossing -5% entry-crossing 105%;
/* ... */
}
Finally, putting everything together, we get the following animation:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-css -->
#container {
position: relative;
timeline-scope: --container;
}
#my_path {
view-timeline-name: --container;
}
#car {
width: 25px;
height: 25px;
position: absolute;
inset: 0;
offset-path: url(#my_path);
animation: offsetDistance linear forwards;
animation-timeline: --container;
animation-range: exit-crossing -5% entry-crossing 105%;
}
@keyframes offsetDistance {
from { offset-distance: 0%; }
to { offset-distance: 100%; }
}
<!-- language: lang-html -->
<div style="height: 150px;"></div>
<div id="container">
<img id="car" width="256" height="256"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAMAAAAp4XiDAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAFZaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA2LjAuMCI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnRpZmY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8xLjAvIj4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+Chle4QcAAAAJcEhZcwAACxMAAAsTAQCanBgAAAL6UExURUdwTLwoJQAEB8w9OuJBPuZWU9BCP7hPTOceHq4tKNktJdpsarwpJeRPSq8ICHEnJrAPC4cxL8lAPdBTULUbG6lCQs1FQbcpJ9hlYsAuK885NsIzMeM3Me07N4NQUr8hHq8yMNFHRMY8Obk4NcI3NMo1MrwpJ7spJrctK7QXEtdjX6MAAMo6OMhCPy4MDrxBP6osK8dAPOlAO9x5d8Q8OaYAALVXVrdLScZqaZBmZpAAAIwzM8xPTKw7Oa80NJ8nJ7wqJ78qKMY2NJcdG95LR0pLUMMrKbkzL8s5N6coJsQ6NkJDQzlMTGQAAOFPTGgAAB9QV510dOdSTjxaYs1YVdJkYv+apddqFdhlY9dhX9pqaBUUFdZjYdNWVNZaV9BEQdRYVdBGQ9VUUdNPS9pvbNx4dhsZGtJRTtxxb9ZfXdloZdI3M89QTcdOTNx0ctZdWjAtLttaWME7ON6AftlnZNpsaiUdHsxAPNlbWM8/PNQ9OgA3Os9KRtdXVNdQTSQkJdVbWCEWFzk5PM06N9NUUdNIRUlbYqBRUbJAPt57eSopK4o/PgAPEsYsKeJwbtA7OLg1M9pXVIosKy5ISwALDQAaHVBPUdJNStpRTshTUaI8Oqg3NrMzMahDQalSUtxfXAMGCHc5OsQzMAAsMNxLR9dEQeN3dbUjIdxjYAAUFk1FRU45OwAeIQEoK9NLR75VVLgtK20yM3ssLJIyMpk2Nd5pZq1CQa4SEJVKSRweH8o0MoI7Ot9lYTYzNa0wLbtDQH1LSqEuLEhNU9FUUURGSok2NQIxM0BAQ1tZXdVoZmdiZUxKSwAlKJI2NmIvLj85OisjJHZCRLMbGbYEAEtmbpkwLrQ7OYxMTZuFhScvMC8xNRxBQ9BfXTQbHCcUFgAWGFEnJ0BQUCoYGUc/QHZVW89NSjBUWAtGSXh7fIdDQl5sb5SYmISIiJdeXw0fIEoiIrxHRRYfIRM7QMdfXSw/QL8pJlFfX5hvb5MqKXh2dmlWVgBAQYFhYaBzc6yuSJ0AAADfdFJOUwD3/hQ5OTgCAQQg+g45Tf5v/vz4Gpos1vv89/ouMf79/fFu2q3s5anDkPsk1cr+YP3ILfu+PP3898yd9PmNdE7Kk8D7hPzukuXH+Pz8a4px/v5B/vn5kv////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////7////////////////////////////////////+//////////////6TEl4rAAAESUlEQVRIx2NgGAWjgHqAgxOHODsuwAmW5AABVA14bGHnQijj5OSEKAVq52AWE2NjY+MN4mVFAF5eXllZLr9+PmYRZh5OmCVQTUrKitOT6+qS0mfOzAWBDx/27+8AgW1z5879YSAlpeqho+Ok4ijApw3SBMT6vZdbk6enz5iRLNcWHPC8evGaHj09JqaeNWurV5/beHTL7KKl2dlVS5csWRLvpeHPCdTDqbxIOLYl6Wn7W7PD6+e1X794ceFGQUF+wbOzOq61B974tcrH2yxLWEiSUVoovXDJX0seBgbTz3kJCQmHNz57dWV9YVxFXFwckBvb0lJXt/Ttm9evpx2Z9nDVyYzSmJlFyy9Jx1vcsGJgEEg1bmkonLv49Im2eStiYmIiI2NAZH5CbM2fF29eHF91etrk9q0nS1Oi7uRu2C4/z1qEwX0Zd0JD4e4zTdOete+JKCmJKIkCgqyIyPxkuVc/T3U1HV/15UBESUREVlTUyZlzLs+xZ1ABaslPODe16/htkBagTEpGRkZKVERk0oFvp06nFRTsvbn6zoqYiAiQrizzbXYMJsu4G/IPv79ftrd6xx6gBFBHXmpqXkZURO2BhycKouvru7rerZ+fCXHw1tB2WwbDPomEipl3e9IurO1eERkJ1JKX2tnZmZeSVfFy8uPo6LS0srKV5vMb8jMzgdpWhOwQZdAFacl91BPdOLV7fmZmDEhLcXFxKlBL9+2CBdFATY092ybGJjQAQUKF8HWQFq1YoJaVaY1TjgLNiswqBdoC1VJdFg0CjQtmzchJB4Na4ZeiQIdpHY7LvXs/rXHB0cKGhnyIy1LzSiMm7l5ctjMNrOXg0vL4xJrpQC02QIeZ9Emk1+Y8WFNWNuVsYWxCXGYEOMhKSyKTFh4rqK/fuTMtrWdfFUjLuunJE2122DKoL+NOjk0/d+bChZVbJh5uiY3LjCkBBmZEZkPywk9Nt2521dd/TJsA1AKyJrlGYa4dg2Yn97rDdbuPFRSs2TIjvaWlJSEfGDLA2E9qvTj5yPGmvbcK1j7Y1V9eGZ+YKLRh+3YFe2CCKY7PmTH39+Pzi2fV5YDA4djYhNjDOU/Ln7RNOwTUdL56UnZzZWX5ouXbL4ETjOl643XJyTUXn0+uvtaa2NqaCALx8fGVldnXr1xpazt0aPJqyeZdizYsv7S8SAqcLDnFvyrW1CSWz+qeHV80++qkTfz8/JMmbdq8Zfa1CbPknpw60eZqsEiSkRGY+OPrsr9bMoOymPzl7P7myqqqyk1r7vdMYQKCe4+mrjmz+Fj16o2bgFlsQ29v767sqqr+RYyevpzgbKkkrirV3N9f1TyhCAgmTJijMGfOvn0HDx68uvns2UnOjG5qauIOGuqa8IwMKgiYuUDFBRsLEjAykpGRkeVy6efjEeHhgZdmkOKCA6oPf6EELM7ghRKIi7vsA6nlwCj68Bew7KOVzCigEAAAop3jpH2nqD8AAAAASUVORK5CYII=">
<svg width="175" height="1300" preserveAspectRatio="none" viewBox="0 0 175 1300" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path id="my_path" d="M 28 0 C -72 200 188 300 88 500 C -32 700 88 1100 148 1000 C 208 900 148 600 68 1000 C 28 1200 128 1300 128 1300"
stroke-width="4" stroke="#000" />
</svg>
</div>
<div style="height: 150px;"></div>
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论