精彩余生 發表於 2025-5-11 10:18:00

Web前端入门第 49 问:CSS offset 路径动画演示

<p>什么是路径动画?</p>
<p>随手画一条不规则的线,让元素按照这条不规则的线运动起来,这就是所谓的路径动画。</p>
<p>前面说过的动画都只能针对某一个 CSS 属性,要想实现路径动画可没办法,路径动画必须借助 CSS3 的 <code>offset</code> 相关能力。</p>
<h2 id="offset-相关属性">offset 相关属性</h2>
<p>要实现路径动画,必须用到 offset 的相关属性:</p>
<p><code>offset-anchor</code> 指定元素的运动中心在哪个点上,默认是元素中心点。<br>
<code>offset-distance</code> 指定元素沿路径的运动距离,100% 表示路径总长度。<br>
<code>offset-rotate</code> 设置元素沿路径方向的旋转角度。<br>
<code>offset-position</code> 定义元素沿路径的初始位置。<br>
<code>offset-path</code> 指定元素运动路径。<br>
<code>offset</code> 简写属性,包括以上所有属性。</p>
<h3 id="offset-anchor">offset-anchor</h3>
<p>设置中心的位置:</p>
<p><img src="https://img2024.cnblogs.com/blog/596097/202505/596097-20250511101611229-793958741.png"></p>
<h3 id="offset-distance">offset-distance</h3>
<p>设置元素在这个路径中的运动距离:</p>
<p><img src="https://img2024.cnblogs.com/blog/596097/202505/596097-20250511101622433-710215173.png"></p>
<h3 id="offset-rotate">offset-rotate</h3>
<p>设置元素在路径上的旋转角度:</p>
<p><img src="https://img2024.cnblogs.com/blog/596097/202505/596097-20250511101629098-1019170097.png"></p>
<p>其中第一个 <code>auto</code> 表示让浏览器自己计算角度,第二个角度值表示在计算出来的角度基础上再旋转多少度。</p>
<h3 id="offset-position">offset-position</h3>
<p>说实话,没搞懂这属性的用法,虽然案例看起来属性值是有效的,但没理解到它的实际生效规则。</p>
<p>按照 w3c 的规范说法:如果 offset-path 属性使用的函数未指定起始位置时,则使用 offset-position 属性指定的起始位置。</p>
<p>看下图,虽然元素的位置变化了,但是路径动画的运动距离也跟着变化了!!真是没搞懂...</p>
<p><img src="https://img2024.cnblogs.com/blog/596097/202505/596097-20250511101634578-1350232336.gif"></p>
<p>以上图片的代码:</p>
<pre><code class="language-html">&lt;div class="container"&gt;
&lt;div class="box"&gt;
    &lt;div class="item"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="box"&gt;
    &lt;div class="item"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="box"&gt;
    &lt;div class="item"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="box"&gt;
    &lt;div class="item"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;style&gt;
.box {
margin: 20px 0;
width: 400px;
height: 280px;
border: 2px solid rgba(255, 71, 87,0.3);
position: relative;
}

@keyframes offset-ani {
0% {
    offset-distance: 0%;
}
100% {
    offset-distance: 100%;
}
}

.item {
offset-path: ray(50deg);
animation: offset-ani 2s infinite alternate ease-in-out;
width: 40px;
height: 40px;
background-color: rgba(255, 71, 87,0.6);
/* animation-play-state: paused; */ /* 暂停动画 */
clip-path: polygon(0% 0%, 70% 0%, 100% 50%, 70% 100%, 0% 100%, 30% 50%);
}
.box:nth-child(2) .item {
offset-position: left 10px top 10px;
}
.box:nth-child(3) .item {
offset-position: right bottom;
}
.box:nth-child(4) .item {
offset-position: right 30px bottom 100px;
}
&lt;/style&gt;
</code></pre>
<h3 id="offset-path-指定元素运动路径">offset-path 指定元素运动路径</h3>
<p><code>url()</code> 指定 SVG 形状元素的 ID,用法 <code>offset-path: url(#myCircle)</code>。<br>
<code>ray()</code> 线段。用这东东还不如使用 transform 位移。<br>
<code>circle()</code> 圆形,用法:circle(6rem at 12rem 8rem),表示:圆形,半径为 6rem,圆心为 12rem 8rem。<br>
<code>ellipse()</code> 椭圆,用法 ellipse(40% 50% at left)。表示:椭圆,长轴为 40%,短轴为 50%,圆心为左边。<br>
<code>polygon()</code> 多边形,语法 <code>polygon(x1 y1, x2 y2, x3 y3, ...)</code><br>
<code>inset()</code> 内嵌矩形,用法 inset(20px 50px 10px 0 round 50px)。表示:20px 50px 10px 0 分别表示上右下左的距离,圆角为 50px。<br>
<code>rect()</code> 矩形,用法 rect(50px 150px 200px 50px round 20%)。表示:矩形上边在 50px,右边在 150px,下边在 200px,左边在 50px,圆角为 20%。<br>
<code>xywh()</code> 矩形,用法 xywh(20px 20px 100% 100% round 10%)。表示:矩形从 20px 20px 开始,宽高为 100%,圆角为 10%。<br>
<code>path()</code> 路径,最为强大的一个东东,复杂的路径动画都需要使用它。</p>
<p>各种路径动画:</p>
<p><img src="https://img2024.cnblogs.com/blog/596097/202505/596097-20250511101642943-766503008.gif"></p>
<p>上图源码:</p>
<pre><code class="language-html">&lt;svg class="defs" style="width: 0;height: 0;position: absolute;"&gt;
&lt;defs&gt;
    &lt;clipPath id="myClip"&gt;
      &lt;path id="myClipAni" d="M10,30 A20,20,0,0,1,50,30 A20,20,0,0,1,90,30 Q90,60,50,90 Q10,60,10,30 Z" /&gt;
    &lt;/clipPath&gt;
&lt;/defs&gt;
&lt;/svg&gt;
&lt;div class="container"&gt;
&lt;div class="box"&gt;
    &lt;div class="item"&gt;&lt;/div&gt;
    &lt;div class="bg"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="box"&gt;
    &lt;div class="item"&gt;&lt;/div&gt;
    &lt;div class="bg"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="box"&gt;
    &lt;div class="item"&gt;&lt;/div&gt;
    &lt;div class="bg"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="box"&gt;
    &lt;div class="item"&gt;&lt;/div&gt;
    &lt;div class="bg"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="box"&gt;
    &lt;div class="item"&gt;&lt;/div&gt;
    &lt;div class="bg"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="box"&gt;
    &lt;div class="item"&gt;&lt;/div&gt;
    &lt;div class="bg"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="box"&gt;
    &lt;div class="item"&gt;&lt;/div&gt;
    &lt;div class="bg"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="box"&gt;
    &lt;div class="item"&gt;&lt;/div&gt;
    &lt;div class="bg"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="box"&gt;
    &lt;div class="item"&gt;&lt;/div&gt;
    &lt;div class="bg"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;style&gt;
.box {
width: 300px;
height: 220px;
border: 2px solid rgba(255, 71, 87, 0.3);
position: relative;
}

.bg,
.svg {
position: absolute;
left: 0;
top: 0;
}
.bg {
width: 100%;
height: 100%;
background: linear-gradient(to bottom right, #ff56227e, #ff475650);
}

@keyframes offset-ani {
0% {
    offset-distance: 0%;
}
100% {
    offset-distance: 100%;
}
}

.item {
animation: offset-ani 2s infinite alternate ease-in-out;
width: 40px;
height: 40px;
background-color: rgba(255, 71, 87,0.6);
/* animation-play-state: paused; */ /* 暂停动画 */
clip-path: polygon(0% 0%, 70% 0%, 100% 50%, 70% 100%, 0% 100%, 30% 50%);
}
.box:nth-child(1) .bg {
clip-path: url("#myClip");
}
.box:nth-child(2) .bg {
clip-path: ray(45deg);
}
.box:nth-child(3) .bg {
clip-path: ellipse(40% 50% at left);
}
.box:nth-child(4) .bg {
clip-path: circle(6rem at 12rem 8rem);
}
.box:nth-child(5) .bg {
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
}
.box:nth-child(6) .bg {
clip-path: inset(20px 50px 10px 0 round 50px);
}
.box:nth-child(7) .bg {
clip-path: rect(50px 150px 200px 50px round 20%);
}
.box:nth-child(8) .bg {
clip-path: xywh(20px 20px 100px 100px round 10%);
}
.box:nth-child(9) .bg {
clip-path: path("M15,45 A30,30,0,0,1,75,45 A30,30,0,0,1,135,45 Q135,90,75,130 Q15,90,15,45 Z");
}
.box:nth-child(1) .item {
offset-path: url("#myClipAni");
}
.box:nth-child(2) .item {
offset-path: ray(45deg);
}
.box:nth-child(3) .item {
offset-path: ellipse(40% 50% at left);
}
.box:nth-child(4) .item {
offset-path: circle(6rem at 12rem 8rem);
}
.box:nth-child(5) .item {
offset-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
}
.box:nth-child(6) .item {
offset-path: inset(20px 50px 10px 0 round 50px);
}
.box:nth-child(7) .item {
offset-path: rect(50px 150px 200px 50px round 20%);
}
.box:nth-child(8) .item {
offset-path: xywh(20px 20px 100px 100px round 10%);
}
.box:nth-child(9) .item {
offset-path: path("M15,45 A30,30,0,0,1,75,45 A30,30,0,0,1,135,45 Q135,90,75,130 Q15,90,15,45 Z");
}
&lt;/style&gt;
</code></pre>
<h2 id="路径">路径</h2>
<p>这里说到的路径,使用到了 SVG 里面的相关知识,SVG 用于绘制矢量图形。</p>
<p>什么是矢量图形?矢量图形是由线条组成的图形,而不是由像素组成的图形,所以矢量图形在放大时,不会出现锯齿,也不会失真。</p>
<h3 id="绘制一条复杂的路径">绘制一条复杂的路径</h3>
<p>这里推荐一个网站:https://www.photopea.com/</p>
<p>此网站完全就是 PS 的在线版本,直接浏览器打开即可使用。</p>
<p>1、进入网站之后点击 <code>Start using Photopea</code> 开始使用。</p>
<p>2、有一个隐私选择,直接 <code>同意并继续</code> 即可,如果担心隐私问题,可以使用浏览器的 <code>无痕模式</code> 打开。</p>
<p>3、新建一个画布。</p>
<p><img src="https://img2024.cnblogs.com/blog/596097/202505/596097-20250511101653772-529198666.png"></p>
<p>4、使用钢笔工具勾勒出想要的路径形状。</p>
<p>提示:钢笔工具在画布上按住拖动可以绘制出曲线。</p>
<p><img src="https://img2024.cnblogs.com/blog/596097/202505/596097-20250511101657430-172312462.png"></p>
<p>5、导出 SVG 文件。</p>
<p><img src="https://img2024.cnblogs.com/blog/596097/202505/596097-20250511101703442-148831045.png"></p>
<p>6、找到导出的 SVG ,用记事本打开,path 标签中的 <code>d</code> 属性就是路径的坐标值。</p>
<p><img src="https://img2024.cnblogs.com/blog/596097/202505/596097-20250511101709238-130632684.png"></p>
<h3 id="使用路径做路径动画">使用路径做路径动画</h3>
<p>注意:本示例中为了看到运动路径,将 SVG 的代码也放在了代码中,实际无需 SVG 代码,仅需要 path 中的路径参数即可!</p>
<pre><code class="language-html">&lt;div class="box"&gt;
&lt;div class="item"&gt;&lt;/div&gt;
&lt;svg class="svg" version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 280" width="400" height="280"&gt;
    &lt;path stroke="#000000" fill="none" d="m52 52c0 0 54-43.9 104-17 50 26.9 61 27.1 88 1 27-26.1 72-28.9 95 16 23 44.9 41 92.1-16 94-57 1.9-28 91.1 10 50 38-41.1 38-104.9-17-108-55-3.1-176 41.1-142 93 34 51.9 92 80.1 145 67 53-13.1 33-95.9-97-61-130 34.9-65 72.1-22 70 43-2.1-38-65-65-91-27-26-72 113.1-42 96 30-17.1 6-63.9-33-89-39-25.1-83-52.9-29-56 54-3.1-47 114.1-2 112 45-2.1 67-86.9 71-101 4-14.1-124-47.9-48-54 76-6.1-34-4.9 0-22z"/&gt;
&lt;/svg&gt;
&lt;/div&gt;
&lt;style&gt;
.box {
width: 400px;
height: 280px;
border: 2px solid rgba(255, 71, 87, 0.3);
position: relative;
}

.svg {
position: absolute;
left: 0;
top: 0;
}
@keyframes offset-ani {
0% {
    offset-distance: 0%;
}
100% {
    offset-distance: 100%;
}
}

.item {
animation: offset-ani 10s infinite linear;
width: 20px;
height: 20px;
background-color: rgba(255, 71, 87,0.6);
offset-path: path("m52 52c0 0 54-43.9 104-17 50 26.9 61 27.1 88 1 27-26.1 72-28.9 95 16 23 44.9 41 92.1-16 94-57 1.9-28 91.1 10 50 38-41.1 38-104.9-17-108-55-3.1-176 41.1-142 93 34 51.9 92 80.1 145 67 53-13.1 33-95.9-97-61-130 34.9-65 72.1-22 70 43-2.1-38-65-65-91-27-26-72 113.1-42 96 30-17.1 6-63.9-33-89-39-25.1-83-52.9-29-56 54-3.1-47 114.1-2 112 45-2.1 67-86.9 71-101 4-14.1-124-47.9-48-54 76-6.1-34-4.9 0-22z");
}
&lt;/style&gt;
</code></pre>
<p>效果:</p>
<p><img src="https://img2024.cnblogs.com/blog/596097/202505/596097-20250511101714125-1617281888.gif"></p>
<h2 id="写在最后">写在最后</h2>
<p>路径动画让 CSS 在动画领域拥有了更多创造性,虽然还有一些新的特性在浏览器中的兼容不太理想,但未来可期~~</p>


</div>
<div id="MySignature" role="contentinfo">
    <p>&nbsp;</p>
<p style="font-size: 18px;font-weight: bold;">文章首发于微信公众号【<span style="color:rgb(255, 71, 87)">前端路引</span>】,欢迎 <span style="color:#4ec259">微信扫一扫</span> 查看更多文章。</p>
<p>
<img style="max-width: 320px;" src="https://images.cnblogs.com/cnblogs_com/linx/2447020/o_250228035031_%E5%85%AC%E4%BC%97%E5%8F%B7%E4%BA%8C%E7%BB%B4%E7%A0%81.png"/>
</p>
<p>本文来自博客园,作者:前端路引,转载请注明原文链接:https://www.cnblogs.com/linx/p/18870562</p>
<p>&nbsp;</p><br><br>
来源:https://www.cnblogs.com/linx/p/18870562
頁: [1]
查看完整版本: Web前端入门第 49 问:CSS offset 路径动画演示