自订动画路径 ( offset )
如果已经熟悉了 CSS 动画的基本语法,接下来还能透过一系列 CSS offset 样式属性,使用类似 SVG 的 path 路径绘制独一无二的动画路线,这篇教学除会介绍与“自订动画路径”相关的 offset、offset-anchor、offset-distance、offset-path、offset-position 和 offset-rotate 样式属性。
快速导览:
offset-path 动画路径
offset-path 表示“设定动画路径”的样式属性,没有继承特性,需要搭配 offset-distance 样式属性共同操作,如果没有使用 offset-distance,位置会一直保持在路径的起点,offset-path 所使用的属性值为“形状、线段、路径”相关的 CSS 函数 ( 点击函数名称可以查看详细教学 ):
| CSS 函数 | 说明 |
|---|---|
| circle() | 圆形 |
| ellipse() | 椭圆形 |
| rect() | 四边形 |
| polygon() | 多边形 |
| inset() | 内缩边距和圆角 |
| xywh() | 虚拟矩形 |
| ray() | 放射线形状 |
| path() | 路径 |
下方的范例会呈现不同路径的元素位置 ( 使用同样路径的 SVG 放在下方 )。
注意,因为 CSS 的
path()没办法像 SVG 可以透过viewpoint缩放,因此在定义路径时就必须要设定好长宽尺寸。
<!-- HTML 程式碼-->
<div class="a">
<svg viewBox="0 0 300 300"><path fill="none" stroke="#000" stroke-miterlimit="10" d="m35 11c-30.1 10.9-41.8 95.7-7 131 34.8 35.3 79 31.6 46 64-33 32.4 101.2 114.2 118 32 16.8-82.2 47.8-158.9-28-136-75.8 22.9-128.6 28.1-82-17 46.6-45.1 14.5-96.3-47-74z"/></svg>
<div></div>
</div>
<div class="b">
<svg viewBox="0 0 300 300"><path fill="none" stroke="#000" stroke-miterlimit="10" d="m148.9 281.8c-64.5-11.6-95.1-36.8-119.4-88.4-24.3-51.6 5-110.8 53.9-144.8 48.9-34 105.6-37.6 154 8 32 30.1 33.6 80.3 28.9 100-10.8 46-33.7 57.4-62 70-36.7 16.3-76.4 15.6-111.6-21.2-16.5-17.3-23.7-63.6-6.6-90.9 14.2-22.8 56-58.8 87.3-47.9 31.4 11 39.3 29.6 50.6 57.1 9.3 22.7-10.3 48.4-44.2 63.2-34 14.7-58.7-13.3-57-29.3 4.5-41.6 46.4-33.7 46.4-33.7"/></svg>
<div></div>
</div>
<div class="c">
<svg viewBox="0 0 300 300"><circle fill="none" stroke="#000" stroke-miterlimit="10" cx="150" cy="150" r="100"/></svg>
<div></div>
</div>
<div class="d">
<svg viewBox="0 0 300 300"><rect fill="none" stroke="#000" stroke-miterlimit="10" x="50" y="50" rx="20" ry="20" width="200" height="150"/></svg>
<div></div>
</div>
<!-- CSS 程式碼 -->
<style>
div {
position: relative;
width: 300px;
height: 300px;
float: left;
}
div {margin:0 -50px -20px 0;}
div div {
top: 0;
left: 0;
position: absolute;
width: 20px;
height: 20px;
background: red;
float: none;
offset-distance: 50%; /* 調整後可以觀察位置 */
}
/* 自訂路徑 */
.a div {
offset-path: path("m35 11c-30.1 10.9-41.8 95.7-7 131 34.8 35.3 79 31.6 46 64-33 32.4 101.2 114.2 118 32 16.8-82.2 47.8-158.9-28-136-75.8 22.9-128.6 28.1-82-17 46.6-45.1 14.5-96.3-47-74z");
}
/* 自訂路徑 */
.b div {
offset-path: path("m148.9 281.8c-64.5-11.6-95.1-36.8-119.4-88.4-24.3-51.6 5-110.8 53.9-144.8 48.9-34 105.6-37.6 154 8 32 30.1 33.6 80.3 28.9 100-10.8 46-33.7 57.4-62 70-36.7 16.3-76.4 15.6-111.6-21.2-16.5-17.3-23.7-63.6-6.6-90.9 14.2-22.8 56-58.8 87.3-47.9 31.4 11 39.3 29.6 50.6 57.1 9.3 22.7-10.3 48.4-44.2 63.2-34 14.7-58.7-13.3-57-29.3 4.5-41.6 46.4-33.7 46.4-33.7");
}
.c div {offset-path: circle(100px at 150px 150px);} /* 圓形 */
.d div {offset-path: xywh(50px 50px 200px 150px round 20px); } /* 矩形 */
</style>
offset-distance 移动距离
offset-distance 表示“元素在路径中移动的距离”,没有继承特性,只能使用“百分比”作为单位,0% 表示起点,100% 表示末端点,执行顺序“必须位于 offset-path 后方”,下方范例会搭配 animation 动画,让红色 div 在不同线段中移动。
<!-- HTML 程式碼-->
<div class="a">
<svg viewBox="0 0 300 300"><path fill="none" stroke="#000" stroke-miterlimit="10" d="m35 11c-30.1 10.9-41.8 95.7-7 131 34.8 35.3 79 31.6 46 64-33 32.4 101.2 114.2 118 32 16.8-82.2 47.8-158.9-28-136-75.8 22.9-128.6 28.1-82-17 46.6-45.1 14.5-96.3-47-74z"/></svg>
<div></div>
</div>
<div class="b">
<svg viewBox="0 0 300 300"><path fill="none" stroke="#000" stroke-miterlimit="10" d="m148.9 281.8c-64.5-11.6-95.1-36.8-119.4-88.4-24.3-51.6 5-110.8 53.9-144.8 48.9-34 105.6-37.6 154 8 32 30.1 33.6 80.3 28.9 100-10.8 46-33.7 57.4-62 70-36.7 16.3-76.4 15.6-111.6-21.2-16.5-17.3-23.7-63.6-6.6-90.9 14.2-22.8 56-58.8 87.3-47.9 31.4 11 39.3 29.6 50.6 57.1 9.3 22.7-10.3 48.4-44.2 63.2-34 14.7-58.7-13.3-57-29.3 4.5-41.6 46.4-33.7 46.4-33.7"/></svg>
<div></div>
</div>
<div class="c">
<svg viewBox="0 0 300 300"><circle fill="none" stroke="#000" stroke-miterlimit="10" cx="150" cy="150" r="100"/></svg>
<div></div>
</div>
<div class="d">
<svg viewBox="0 0 300 300"><rect fill="none" stroke="#000" stroke-miterlimit="10" x="50" y="50" rx="20" ry="20" width="200" height="150"/></svg>
<div></div>
</div>
<!-- CSS 程式碼 -->
<style>
div {
position: relative;
width: 300px;
height: 300px;
float: left;
}
div {margin:0 -50px -20px 0;}
div div {
top: 0;
left: 0;
position: absolute;
width: 20px;
height: 20px;
background: red;
float: none;
animation: oxxo 2s infinite alternate; /* 執行動畫 */
}
.a div {
offset-path: path("m35 11c-30.1 10.9-41.8 95.7-7 131 34.8 35.3 79 31.6 46 64-33 32.4 101.2 114.2 118 32 16.8-82.2 47.8-158.9-28-136-75.8 22.9-128.6 28.1-82-17 46.6-45.1 14.5-96.3-47-74z");
}
.b div {
offset-path: path("m148.9 281.8c-64.5-11.6-95.1-36.8-119.4-88.4-24.3-51.6 5-110.8 53.9-144.8 48.9-34 105.6-37.6 154 8 32 30.1 33.6 80.3 28.9 100-10.8 46-33.7 57.4-62 70-36.7 16.3-76.4 15.6-111.6-21.2-16.5-17.3-23.7-63.6-6.6-90.9 14.2-22.8 56-58.8 87.3-47.9 31.4 11 39.3 29.6 50.6 57.1 9.3 22.7-10.3 48.4-44.2 63.2-34 14.7-58.7-13.3-57-29.3 4.5-41.6 46.4-33.7 46.4-33.7");
}
.c div {offset-path: circle(100px at 150px 150px); }
.d div {offset-path: xywh(50px 50px 200px 150px round 20px);}
@keyframes oxxo {
0% {offset-distance: 0%;}
100% {offset-distance: 100%;}
}
</style>
offset-anchor 对齐路径
offset-anchor 表示“元素如何对齐路径”,没有继承特性,会使用 1~2 个属性值,定义元素的“对齐点”和“路径”之间的关系,属性值支援三种写法:
使用关键字“center、left、right、top、bottom”
div { offset-anchor: center; /* 等同 center center,表示元素中心 */ offset-anchor: left; /* 等同 left center,表示元素左邊中心 */ offset-anchor: left top; /* 表示元素左上角 */ offset-anchor: right bottom; /* 表示元素右下角 */ }使用长度单位数值
div { offset-anchor: 10px; /* 左往右移 10px,上往下移 10px,等同 left 10px top 10px */ offset-anchor: 10px 20px; /* 左往右移 10px,上往下移 20px,等同 left 10px top 20px */ }使用关键字搭配长度单位
div { offset-anchor: left 10px; /* 左往右移 10px,等同 left 10px center */ offset-anchor: left 20px bottom 50px; /* 左往右移 20px,下往上移 50px */ offset-anchor: right bottom 50px; /* 右邊對齊,下往上移 50px */ }
下方范例会使用比较粗的线条,呈现这些写法以及对齐路径的差异。
<!-- HTML 程式碼-->
<div class="a">
<svg viewBox="0 0 300 300"><path fill="none" stroke="#000" stroke-width="100" d="m22.4 88h244.2z"/></svg><div></div>
</div>
<div class="b">
<svg viewBox="0 0 300 300"><path fill="none" stroke="#000" stroke-width="100" d="m22.4 88h244.2z"/></svg><div></div>
</div>
<div class="c">
<svg viewBox="0 0 300 300"><path fill="none" stroke="#000" stroke-width="100" d="m22.4 88h244.2z"/></svg><div></div>
</div>
<div class="d">
<svg viewBox="0 0 300 300"><path fill="none" stroke="#000" stroke-width="100" d="m22.4 88h244.2z"/></svg><div></div>
</div>
<!-- CSS 程式碼 -->
<style>
div {
position: relative;
width: 300px;
height: 200px;
float: left;
}
div div {
top: 0;
left: 0;
position: absolute;
width: 50px;
height: 50px;
background: #f00a;
float: none;
offset-path: path("m22.4 88h244.2z"); /* 使用直線路徑 */
}
.a div {offset-anchor: center;} /* 中心點對齊路徑起始位置與中線 */
.b div {offset-anchor: left bottom;} /* 左下點對齊路徑起始位置與中線 */
.c div {offset-anchor: 10px 10px;} /* 左上內縮 10px 10px 對齊路徑起始位置與中線 */
.d div {offset-anchor: right 20px bottom 50px;} /* 右下內縮 20px 50px 對齊路徑起始位置與中線 */
</style>
offset-rotate 旋转方向
offset-rotate 表示“元素在路径上的旋转方向”,没有继承特性,有下列几种属性值写法
| 属性值 | 说明 |
|---|---|
| auto | 默认值,表示旋转方向和路径方向相同。 |
| auto angle | 在路径的方向上额外加上角度 ( 使用角度单位 ),顺时针为正。 |
| angle | 沿着 X 轴的水平方向为 0deg,完全按照指定角度旋转 ( 使用角度单位 ),顺时针为正。 |
| reverse | auto 反转 180deg,表示旋转方向和路径方向相反。 |
| reverse angle | 在路径相反方向上额外加上角度 ( 使用角度单位 ),顺时针为正。 |
下方范例会展示这五种旋转方向的差异。
<!-- HTML 程式碼-->
<div class="a">
<svg viewBox="0 0 300 300"><path fill="none" stroke="#000" stroke-width="2" d="m22 21l116.5 116.5z"/></svg>
<div></div>
</div>
<div class="b">
<svg viewBox="0 0 300 300"><path fill="none" stroke="#000" stroke-width="2" d="m22 21l116.5 116.5z"/></svg>
<div></div>
</div>
<div class="c">
<svg viewBox="0 0 300 300"><path fill="none" stroke="#000" stroke-width="2" d="m22 21l116.5 116.5z"/></svg>
<div></div>
</div>
<div class="d">
<svg viewBox="0 0 300 300"><path fill="none" stroke="#000" stroke-width="2" d="m22 21l116.5 116.5z"/></svg>
<div></div>
</div>
<div class="e">
<svg viewBox="0 0 300 300"><path fill="none" stroke="#000" stroke-width="2" d="m22 21l116.5 116.5z"/></svg>
<div></div>
</div>
<!-- CSS 程式碼 -->
<style>
div {
position: relative;
width: 300px;
height: 200px;
float: left;
margin: 10px -100px 10px 10px;
}
div div {
top: 0;
left: 0;
position: absolute;
width: 0;
height: 0;
float: none;
offset-path: path("m22 21l116.5 116.5z");
border-width: 10px 0 10px 50px;
border-color: #0000 #0000 #0000 #f00b;
border-style: solid;
}
.a div {offset-rotate: auto}
.b div {offset-rotate: 45deg;}
.c div {offset-rotate: auto 45deg;}
.d div {offset-rotate: reverse;}
.d div {offset-rotate: reverse 45deg;}
</style>
下方范例使用动画的方式,呈现 auto 和固定角度的差异。
<!-- HTML 程式碼-->
<div class="a">
<svg viewBox="0 0 300 300"><circle fill="none" stroke="#000" stroke-miterlimit="10" cx="150" cy="150" r="100"/></svg>
<div></div>
</div>
<div class="b">
<svg viewBox="0 0 300 300"><circle fill="none" stroke="#000" stroke-miterlimit="10" cx="150" cy="150" r="100"/></svg>
<div></div>
</div>
<!-- CSS 程式碼 -->
<style>
div {
position: relative;
width: 300px;
height: 200px;
float: left;
margin: 20px -20px;
}
div div {
top: 0;
left: 0;
position: absolute;
width: 0;
height: 0;
float: none;
offset-path: circle(100px at 150px 150px);
border-width: 20px 0 20px 100px;
border-color: #0000 #0000 #0000 #f00b;
border-style: solid;
animation: oxxo 2s infinite linear;
}
.a div {offset-rotate: auto;}
.b div {offset-rotate: 45deg;}
@keyframes oxxo {
0% {offset-distance: 0%;}
100% {offset-distance: 100%;}
}
</style>
offset-position 路径起点位置
offset-position 表示“路径起点位置”,没有继承特性,会使用 1~2 个属性值定义元素的“起点位置”,目前主要支援 ray() 函数产生的路径,定义方式如下:
参考:ray() 放射线
使用关键字“normal、center、left、right、top、bottom”
div { offset-position: normal; /* 等同 center center,表示元素中心 */ offset-position: center; /* 等同 center center,表示元素中心 */ offset-position: left; /* 等同 left center,表示元素左邊中心 */ offset-position: left top; /* 表示元素左上角 */ offset-position: right bottom; /* 表示元素右下角 */ }使用长度单位数值
div { offset-position: 10px; /* 左往右移 10px,上往下移 10px,等同 left 10px top 10px */ offset-position: 10px 20px; /* 左往右移 10px,上往下移 20px,等同 left 10px top 20px */ }使用关键字搭配长度单位
div { offset-position: left 10px; /* 左往右移 10px,等同 left 10px center */ offset-position: left 20px bottom 50px; /* 左往右移 20px,下往上移 50px */ offset-position: right bottom 50px; /* 右邊對齊,下往上移 50px */ }
下方范例会使用四个 div,呈现这些写法以及不同起点的差异。
<!-- HTML 程式碼-->
<div class="a"><div></div></div>
<div class="b"><div></div></div>
<div class="c"><div></div></div>
<div class="d"><div></div></div>
<!-- CSS 程式碼 -->
<style>
div {
position: relative;
width: 200px;
height: 200px;
float: left;
margin: 50px;
border: 1px solid #000;
}
div div {
top: 0;
left: 0;
position: absolute;
width: 50px;
height: 50px;
background: #f00a;
float: none;
offset-path: ray(30deg);
}
.a div {offset-position: normal;}
.b div {offset-position: right;}
.c div {offset-position: bottom left;}
.d div {offset-position: 30% 30%;}
</style>
offset 动画路径缩写格式
offset 表示“动画路径的缩写格式”,没有继承特性,是由上述所有的样式属性缩写而成,相关写法如下:
div {
offset: offset-path;
offset: offset-path offset-distance;
offset: offset-path offset-distance offset-rotate;
offset: offset-path offset-distance offset-rotate / offset-anchor; /* offset-anchor 前方要用斜線分隔 */
}
下方范例会使用四个 div 呈现不同的写法。
<!-- HTML 程式碼-->
<div class="a">
<svg viewBox="0 0 300 300"><circle fill="none" stroke="#000" stroke-miterlimit="10" cx="150" cy="150" r="100"/></svg>
<div></div>
</div>
<div class="b">
<svg viewBox="0 0 300 300"><circle fill="none" stroke="#000" stroke-miterlimit="10" cx="150" cy="150" r="100"/></svg>
<div></div>
</div>
<div class="c">
<svg viewBox="0 0 300 300"><circle fill="none" stroke="#000" stroke-miterlimit="10" cx="150" cy="150" r="100"/></svg>
<div></div>
</div>
<div class="d">
<svg id="s" viewBox="0 0 300 300"><circle fill="none" stroke="#000" stroke-miterlimit="10" cx="150" cy="150" r="100"/></svg>
<div></div>
</div>
<!-- CSS 程式碼 -->
<style>
div {
position: relative;
width: 300px;
height: 200px;
float: left;
margin: 20px -20px;
}
div div {
top: 0;
left: 0;
position: absolute;
width: 0;
height: 0;
float: none;
border-width: 20px 0 20px 100px;
border-color: #0000 #0000 #0000 #f00b;
border-style: solid;
animation: oxxo 2s infinite linear;
}
.a div {offset: circle(100px at 150px 150px);}
.b div {offset: circle(100px at 150px 150px) 45deg;}
.c div {offset: circle(100px at 150px 150px) 45deg / left;}
.d div {offset: circle(100px at 150px 150px) auto 90deg / right;}
@keyframes oxxo {
0% {offset-distance: 0%;}
100% {offset-distance: 100%;}
}
</style>
小结
透过动画路径的做法,可以让原本不容易控制位置的动画效果,突然变成可以很简单的操控移动的方向和位置,如果手边有些 CSS 动画需要实作,建议可以开始使用自订动画路径的方式,创造出更加吸引眼球的动画效果。
微信扫码关注
抖音扫码关注