近期執行的專案有一個動畫需要讓一個紙飛機的圖案進行不規則的移動,塑造出「正在飛行」的感覺,原本以為這會是一個比較困難的需求,在前端同事提供範例程式碼後才發覺「這東西好像沒有這麼難!」
offset-path
offset-path
是一個 CSS 屬性,它指定了元素要遵循的路徑,並確定該元素在路徑的父容器或 SVG 座標系統中的定位。這條路徑可以是一條直線、一個曲線或一個幾何形狀,沿著這條路徑元素將被定位或移動。它可以與其他 CSS 屬性如 offset-distance
、offset-rotate
和 offset-anchor
屬性一起使用,以控制元素沿著路徑的位置及方向。
如果曾經將一個設計師提供或繪製的 SVG 檔案開啟,可能會看到像是這樣的內容:
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
<path
fill="none"
stroke="red"
d="M 10,30
A 20,20 0,0,1 50,30
A 20,20 0,0,1 90,30
Q 90,60 50,90
Q 10,60 10,30 z"
/>
</svg>
這個由 mdn web docs 提供的範例最終會繪製出一張愛心圖:
其中 <path>
元素的 fill
代表了形狀中間的填色、stroke
代表了描繪的邊框的顏色,而 d
則代表 “drawn”,表示路徑如何被繪製。d
也是一個「表現屬性」(Presentation Attribute),可以被使用在 CSS 中。
以下是一個讓一個圓圈依照心型路徑進行移動的範例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
body {
padding: 60px;
}
.box {
width: 100px;
height: 100px;
border: 1px solid #000;
}
.circle {
width: 20px;
height: 20px;
border-radius: 50%;
background-color: red;
}
.moving {
offset-path: path(
"M 10,30 A 20,20 0,0,1 50,30 A 20,20 0,0,1 90,30 Q 90,60 50,90 Q 10,60 10,30 z"
);
animation: heart-moving 5s linear infinite;
}
@keyframes heart-moving {
0% {
offset-distance: 0%;
}
100% {
offset-distance: 100%;
}
}
</style>
</head>
<body>
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
<path
fill="none"
stroke="red"
d="M 10,30
A 20,20 0,0,1 50,30
A 20,20 0,0,1 90,30
Q 90,60 50,90
Q 10,60 10,30 z"
/>
</svg>
<div class="box">
<div class="circle moving"></div>
</div>
</body>
</html>
實際呈現如下:
這個範例我們使用了一個長寬各是 20px
,底色為紅色的圓形 div
。針對它設定一個 offset-path
並給予與愛心圖 d
屬性完全相同的值,並透過 animation
讓其的 offset-distance
從 0%
變成 100%
。這裡的 offset-distance
的概念是:定義元素在 offset-path
路徑的哪個位置,以上面的愛心圖為例,它的起始點會是:
我們可以調整一下頁面,新增三個按鈕:
<button onclick="moveTo(0)">移動至 0%</button>
<button onclick="moveTo(50)">移動至 50%</button>
<button onclick="moveTo(80)">移動至 80%</button>
一個 function
:
<script>
function moveTo(num) {
const circle = document.querySelector(".circle");
circle.style.offsetDistance = num + "%";
}
</script>
和異動 CSS:
.circle {
width: 20px;
height: 20px;
border-radius: 50%;
background-color: red;
transition: offset-distance 2s linear;
}
.moving {
offset-path: path(
"M 10,30 A 20,20 0,0,1 50,30 A 20,20 0,0,1 90,30 Q 90,60 50,90 Q 10,60 10,30 z"
);
/* animation: heart-moving 5s linear infinite; */
}
當我們在點擊移動到 50% 時,它就會依照繪製的愛心路徑移動至 50% 處:
看過範例後,我們就可以看看offset-distance
、offset-rotate
和 offset-anchor
分別代表的意思。
offset-distance
offset-distance
表示元素的 offset-path
移動到的位置,預設為 0,也因此
@keyframes heart-moving {
0% {
offset-distance: 0%;
}
100% {
offset-distance: 100%;
}
}
這個 keyframes
才能讓元素依循繪製的路徑去移動。
offset-rotate
offset-rotate
代表了元素在移動時的「方向」。例如以 MDN 的範例來看:
這是 offset-rotate
下了 90deg
時:
它也可以分別給予水平、垂直方向不同的值,這是 offset-rotate
下了 auto 90deg
時:
offset-anchor
offset-anchor
可以給予一個值,這個值代表了元素在路徑中所處的位置,有一點點類似 transform-origin
的用法,以 MDN 提供的範例,例如當 offset-anchor
的值為 left bottom
時,會像這樣:
當值為 right-top
時則會是:
實際應用
實際應用所需的要素其實就是兩個重點:
- 要進行移動的圖片
- 要進行移動的路徑
這兩項應該都會由專門的設計師進行繪製並提供,我們只需要將其帶入網頁中即可。如果真的需要自己進行路徑的繪製,也能使用像是 Vectr 這樣的工具。
以下是一個飛機飛行的範例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.plane {
offset-path: path(
"m111 483.5c0 0 172-283 432-215 260 68 213.5 78.2 420 225 206.5 146.9 443.5-44.8 475-180"
);
position: absolute;
top: 0;
left: 0;
}
.fly {
animation: plane-fly 5s infinite ease-in-out;
}
@keyframes plane-fly {
100% {
offset-distance: 100%;
}
}
</style>
</head>
<body>
<div>
<svg
version="1.2"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1640 664"
width="1640"
height="664"
fill="none"
stroke="#000"
stroke-width="5"
>
<path
d="m111 483.5c0 0 172-283 432-215 260 68 213.5 78.2 420 225 206.5 146.9 443.5-44.8 475-180"
/>
</svg>
<img
class="plane fly"
src="./plane-23.png"
alt=""
width="200"
height="200"
/>
</div>
</body>
</html>
實際呈現會長這個樣子:
以上就是關於 offset-path
的筆記,我自己覺得使用上是非常直觀好用的,本篇文如有任何錯誤也歡迎提出!
References: