自訂 CSS 動畫路徑 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 動畫需要實作,建議可以開始使用自訂動畫路徑的方式,創造出更加吸引眼球的動畫效果。
意見回饋
如果有任何建議或問題,可傳送「意見表單」給我,謝謝~