offset-path

Baseline 廣泛可用 *

此特性已經十分成熟,可在許多裝置和瀏覽器版本上使用。自 2022 年 3 月起,它已在各瀏覽器中可用。

* 此特性的某些部分可能存在不同級別的支援。

offset-path CSS 屬性指定一個供元素跟隨的路徑,並決定該元素在其父容器或 SVG 座標系中的位置。這個路徑可以是直線、曲線或幾何圖形,元素將沿著它定位或移動。

offset-path 屬性與 offset-distanceoffset-rotateoffset-anchor 屬性結合使用,以控制元素沿路徑的位置和方向。

試一試

offset-path: path("M-70,-40 C-70,70 70,70 70,-40");
offset-path: path("M0,0 L60,70 L-60,30z");
<section class="default-example" id="default-example">
  <div class="transition-all" id="example-element"></div>
  <button id="playback" type="button">Play</button>
</section>
#example-element {
  width: 24px;
  height: 24px;
  background: #2bc4a2;
  animation: distance 8000ms infinite linear;
  animation-play-state: paused;
  clip-path: polygon(0% 0%, 70% 0%, 100% 50%, 70% 100%, 0% 100%, 30% 50%);
}

#example-element.running {
  animation-play-state: running;
}

#playback {
  position: absolute;
  top: 0;
  left: 0;
  font-size: 1em;
}

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

#default-example {
  position: relative;
}
const example = document.getElementById("example-element");
const button = document.getElementById("playback");

button.addEventListener("click", () => {
  if (example.classList.contains("running")) {
    example.classList.remove("running");
    button.textContent = "Play";
  } else {
    example.classList.add("running");
    button.textContent = "Pause";
  }
});

語法

css
/* Default */
offset-path: none;

/* Line segment */
offset-path: ray(45deg closest-side contain);
offset-path: ray(contain 150deg at center center);
offset-path: ray(45deg);

/* URL */
offset-path: url("#my-circle");

/* Basic shape */
offset-path: circle(50% at 25% 25%);
offset-path: ellipse(50% 50% at 25% 25%);
offset-path: inset(50% 50% 50% 50%);
offset-path: polygon(30% 0%, 70% 0%, 100% 50%, 30% 100%, 0% 70%, 0% 30%);
offset-path: path("M 0,200 Q 200,200 260,80 Q 290,20 400,0 Q 300,100 400,200");
offset-path: rect(5px 5px 160px 145px round 20%);
offset-path: xywh(0 5px 100% 75% round 15% 0);

/* Coordinate box */
offset-path: content-box;
offset-path: padding-box;
offset-path: border-box;
offset-path: fill-box;
offset-path: stroke-box;
offset-path: view-box;

/* Global values */
offset-path: inherit;
offset-path: initial;
offset-path: revert;
offset-path: revert-layer;
offset-path: unset;

offset-path 屬性接受 <offset-path> 值、<coord-box> 值或兩者,或 none 關鍵字。<offset-path> 值可以是 ray() 函式、<url> 值或 <basic-shape> 值。

none

指定元素不遵循任何偏移路徑。none 值等同於元素沒有任何偏移變換。在這種情況下,元素的移動由其預設的位置屬性(如 topleft)決定,而不是偏移路徑。這是預設值。

<offset-path>

一個 ray() 函式、<url> 值或 <basic-shape> 值,用於指定幾何偏移路徑。如果省略,<coord-box> 值的路徑形狀為 inset(0 round X),其中 X 是建立包含塊的元素的 border-radius 值。

ray()

定義一條從設定位置開始、具有設定長度、並以指定角度延伸的線。ray() 函式最多接受四個引數——一個 <angle>、一個可選的大小值、可選的關鍵字 contain 和一個可選的 at <position>

<url>

指定一個 SVG 形狀元素的 ID。該路徑是 url() 函式中透過 id 引用的 SVG <circle><ellipse><line><path><polygon><polyline><rect> 元素的形狀。如果 URL 沒有引用形狀元素或因其他原因無效,則偏移路徑的解析值為 path("M0,0")(這是一個有效的 <basic-shape> 值)。

<basic-shape>

將偏移路徑指定為一個 CSS 基本形狀函式的等效路徑,例如 circle()ellipse()inset()path()polygon()rect()xywh()。例如,如果 <basic_shape> 是一個 ellipse() 函式,則路徑是橢圓的輪廓,從橢圓的最右點開始,順時針旋轉一整圈。對於接受 at <position> 引數的 ellipse()circle(),如果省略了 <position>,則位置預設為 center,除非元素指定了 offset-position。在這種情況下,offset-position 的值將用作 at <position> 引數。可以使用 shape() 函式定義更復雜的形狀。

<coord-box>

指定包含路徑的參考盒的大小資訊。參考盒派生自為此元素建立包含塊的元素。此引數是可選的。如果未指定,在 CSS 上下文中預設值為 border-box。在 SVG 上下文中,該值被視為 view-box。如果使用 ray()<basic-shape> 定義偏移路徑,<coord-box> 值分別為射線或 <basic-shape> 提供參考盒。如果使用 <url> 定義偏移路徑,<coord-box> 值為形狀元素提供了視口和使用者座標系,其原點 (0 0) 在左上角,大小為 1px

描述

offset-path 屬性定義了動畫元素可以遵循的路徑。偏移路徑可以是一個帶有一個或多個子路徑的指定路徑,也可以是一個未設定樣式的基本幾何形狀。元素在偏移路徑上的確切位置由 offset-distance 屬性決定。每個形狀或路徑必須為 offset-distance 計算值為 0 的情況定義一個初始位置,併為物件在初始位置的旋轉方向定義一個初始方向。

早期版本的規範將此屬性稱為 motion-path。後來改為 offset-path,因為該屬性描述的是靜態位置,而非運動。

正式定義

初始值none
應用於可變換元素
繼承性
計算值同指定值
動畫型別按計算值型別
建立層疊上下文

正式語法

offset-path = 
none |
<offset-path> || <coord-box>

<offset-path> =
<ray()> |
<url> |
<basic-shape>

<coord-box> =
<paint-box> |
view-box

<ray()> =
ray( <angle> &&
<ray-size>? &&
contain? &&
[ at <position> ]? )

<paint-box> =
<visual-box> |
fill-box |
stroke-box

<ray-size> =
closest-side |
closest-corner |
farthest-side |
farthest-corner |
sides

<position> =
<position-one> |
<position-two> |
<position-four>

<visual-box> =
content-box |
padding-box |
border-box

<position-one> =
left |
center |
right |
top |
bottom |
x-start |
x-end |
y-start |
y-end |
block-start |
block-end |
inline-start |
inline-end |
<length-percentage>

<position-two> =
[ left | center | right | x-start | x-end ] && [ top | center | bottom | y-start | y-end ] |
[ left | center | right | x-start | x-end | <length-percentage> ] [ top | center | bottom | y-start | y-end | <length-percentage> ] |
[ block-start | center | block-end ] && [ inline-start | center | inline-end ] |
[ start | center | end ]{2}

<position-four> =
[ [ left | right | x-start | x-end ] <length-percentage> ] && [ [ top | bottom | y-start | y-end ] <length-percentage> ] |
[ [ block-start | block-end ] <length-percentage> ] && [ [ inline-start | inline-end ] <length-percentage> ] |
[ [ start | end ] <length-percentage> ]{2}

<length-percentage> =
<length> |
<percentage>

示例

使用盒模型邊緣定位建立 offset-path

此示例演示了在 offset-path 屬性中使用各種 <coord-box> 值。

css
.box {
  width: 40px;
  height: 20px;
  animation: move 8000ms infinite ease-in-out;
}

.blueBox {
  background-color: blue;
  offset-path: border-box;
  offset-distance: 5%;
}

.greenBox {
  background-color: green;
  offset-path: padding-box;
  offset-distance: 8%;
}

.redBox {
  background-color: red;
  offset-path: content-box;
  offset-distance: 12%;
}

@keyframes move {
  0%,
  20% {
    offset-distance: 0%;
  }
  80%,
  100% {
    offset-distance: 100%;
  }
}

在這個示例中,外邊距、邊框和內邊距被有意地設定了較大的值,以演示藍色、綠色和紅色矩形在它們各自的 <coord-box> 邊緣(border-box、padding-box 和 content-box)上的位置。

The blue rectangle sits on the outer edge of the border box, the green rectangle is on the inner border edge, which is the outer edge of the padding box, and the red rectangle is on the outer edge of the content box.

結果

使用 path() 建立 offset-path

在此示例中,<svg> 元素建立了一個帶煙囪的房子,並定義了一把剪刀的兩半。房子和煙囪由矩形和多邊形組成,剪刀的兩半由兩個不同的 path 元素表示。在 CSS 程式碼中,offset-path 屬性用於為剪刀的兩半指定要遵循的路徑。這個 CSS 定義的路徑與 SVG 中 <path> 元素表示的路徑相同,即房子的輪廓,包括煙囪。

html
<svg
  xmlns="http://www.w3.org/2000/svg"
  width="700"
  height="450"
  viewBox="350 0 1400 900">
  <title>House and Scissors</title>
  <rect x="595" y="423" width="610" height="377" fill="blue" />
  <polygon points="506,423 900,190 1294,423" fill="yellow" />
  <polygon points="993,245 993,190 1086,190 1086,300" fill="red" />
  <path
    id="house"
    d="M900,190 L993,245 V201 A11,11 0 0,1 1004,190 H1075 A11,11 0 0,1 1086,201 V300 L1294,423 H1216 A11,11 0 0,0 1205,434 V789 A11,11 0 0,1 1194,800 H606 A11,11 0 0,1 595,789 V434 A11,11 0 0,0 584,423 H506 L900,190"
    fill="none"
    stroke="black"
    stroke-width="13"
    stroke-linejoin="round"
    stroke-linecap="round" />
  <path
    id="first-scissor-half"
    class="scissor-half"
    d="M30,0 H-10 A10,10 0 0,0 -20,10 A20,20 0 1,1 -40,-10 H20 A10,10 0 0,1 30,0 M-40,20 A10,10 1 0,0 -40,0 A10,10 1 0,0 -40,20 M0,0" />
  <path
    id="second-scissor-half"
    class="scissor-half"
    d="M30,0 H-10 A10,10 0 0,1 -20,-10 A20,20 0 1,0 -40,10 H20 A10,10 0 0,0 30,0 M-40,-20 A10,10 1 0,0 -40,0 A10,10 1 0,0 -40,-20 M0,0" />
</svg>
css
.scissor-half {
  offset-path: path(
    "M900,190 L993,245 V201 A11,11 0 0,1 1004,190 H1075 A11,11 0 0,1 1086,201 V300 L1294,423 H1216 A11,11 0 0,0 1205,434 V789 A11,11 0 0,1 1194,800 H606 A11,11 0 0,1 595,789 V434 A11,11 0 0,0 584,423 H506 L900,190"
  );
  transform: translate(0px, 0px);
  fill: green;
  stroke: black;
  stroke-width: 5px;
  stroke-linejoin: round;
  stroke-linecap: round;
  fill-rule: evenodd;
  offset-anchor: 0 0;
}

#first-scissor-half {
  animation:
    move 12s linear infinite,
    rotate-left 1s infinite;
}
#second-scissor-half {
  animation:
    move 12s linear infinite,
    rotate-right 1s infinite;
}

@keyframes move {
  from {
    offset-distance: 0%;
  }
  to {
    offset-distance: 100%;
  }
}

@keyframes rotate-left {
  0% {
    offset-rotate: auto 0deg;
  }
  50% {
    offset-rotate: auto -45deg;
  }
  100% {
    offset-rotate: auto 0deg;
  }
}

@keyframes rotate-right {
  0% {
    offset-rotate: auto 0deg;
  }
  50% {
    offset-rotate: auto 45deg;
  }
  100% {
    offset-rotate: auto 0deg;
  }
}

結果

如果沒有 offset-path 屬性,剪刀的兩半將預設位於畫布的左上角。然而,透過使用 offset-path,剪刀的兩半與 SVG 路徑的起點對齊,從而可以沿著它移動。

使用 url() 建立 offset-path

此示例說明了如何引用 SVG 形狀來定義元素可以遵循的路徑形狀。綠色圓圈(由 .target 定義)遵循一個矩形的路徑,該路徑是透過使用 url() 將 SVG 形狀的 ID(svgRect)傳遞給 offset-path 屬性來定義的。

此處顯示的定義路徑形狀的 SVG 矩形僅用於直觀地演示綠色圓圈確實在沿著此矩形定義的路徑移動。

html
<div class="outer">
  <div class="target"></div>
</div>
<svg width="400" height="200" xmlns="http://www.w3.org/2000/svg">
  <rect id="svgRect" x="50" y="50" width="200" height="100" />
</svg>
css
.target {
  width: 50px;
  height: 50px;
  border-radius: 50%;
  background-color: green;
  offset-path: url("#svgRect");
  offset-anchor: auto;
  animation: move 5s linear infinite;
}

#svgRect {
  fill: antiquewhite;
  stroke: black;
  stroke-width: 2;
}

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

不同的形狀

此示例涉及不同的 <basic-shape> 值:circle()ellipse()inset()polygon()

html
<div class="container">
  <div class="mover mover-path">path()</div>
  <div class="mover mover-circle">circle()</div>
  <div class="mover mover-ellipse">ellipse()</div>
  <div class="mover mover-inset">inset()</div>
  <div class="mover mover-polygon">polygon()</div>
</div>
css
.container {
  border: 1px solid black;
  width: 80vw;
  height: 80vh;
  position: relative;
  left: 10vw;
  top: 10vh;
}

.mover {
  width: 100px;
  height: 80px;
  border-radius: 50%;
  line-height: 80px;
  text-indent: 10px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' id='e644da42-a34e-4ceb-a89a-89a4eb6dcc51' data-name='Layer 1' viewBox='0 0 71.08 54.62'%3E%3Ctitle%3Epointer-hand%3C/title%3E%3Cpath d='M43.56,49.35a5.24,5.24,0,0,0-1.27-3.43,5.26,5.26,0,0,0,1.86-9,5.26,5.26,0,0,0-.5-9.53L66.12,27c2.28-.07,5-1.57,5-4.58a5.06,5.06,0,0,0-4.58-4.83L34.08,17c3.48-2.89,6.26-6.55,6.73-11.08C41.45-.14,36.07-1.15,35,1.09,32,7.11,23,12.75,17.42,15.52,8.64,19.08,0,19.77,0,34.56,0,42.7,2.7,47.94,9.42,51c5.51,2.52,13.71,3.59,25.36,3.59H38.3A5.27,5.27,0,0,0,43.56,49.35Z'/%3E%3C/svg%3E")
    no-repeat;
  background-size: cover;
  color: white;
  animation: move 10s linear infinite;
  font-family: monospace;
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  transform-origin: center center;
}
.mover-path {
  top: 50px;
  motion-path: path(
    "M18.45,58.46s52.87-70.07,101.25-.75,101.75-6.23,101.75-6.23S246.38,5.59,165.33,9.08s-15,71.57-94.51,74.56S18.45,58.46,18.45,58.46Z"
  );
  offset-path: path(
    "M18.45,58.46s52.87-70.07,101.25-.75,101.75-6.23,101.75-6.23S246.38,5.59,165.33,9.08s-15,71.57-94.51,74.56S18.45,58.46,18.45,58.46Z"
  );
}
.mover-circle {
  top: 150px;
  offset-path: circle(100px at 50px 50px);
  motion-path: circle(100px at 50px 50px);
}
.mover-ellipse {
  top: 250px;
  offset-path: ellipse(25% 40% at 50% 50%);
  motion-path: ellipse(25% 40% at 50% 50%);
}
.mover-inset {
  top: 350px;
  offset-path: inset(5% 20% 15% 10%);
  motion-path: inset(5% 20% 15% 10%);
}
.mover-polygon {
  top: 450px;
  offset-path: polygon(
    30% 0%,
    70% 0%,
    100% 30%,
    100% 70%,
    70% 100%,
    30% 100%,
    0% 70%,
    0% 30%
  );
  motion-path: polygon(
    30% 0%,
    70% 0%,
    100% 30%,
    100% 70%,
    70% 100%,
    30% 100%,
    0% 70%,
    0% 30%
  );
}

@keyframes move {
  100% {
    motion-offset: 100%;
    offset-distance: 100%;
  }
}

規範

規範
Motion Path Module Level 1
# offset-path 屬性

瀏覽器相容性

另見