CanvasRenderingContext2D: arcTo() 方法

Baseline 已廣泛支援

此特性已相當成熟,可在許多裝置和瀏覽器版本上使用。自 ⁨2015 年 7 月⁩以來,各瀏覽器均已提供此特性。

Canvas 2D API 的 CanvasRenderingContext2D.arcTo() 方法使用給定的控制點和半徑,在當前子路徑上新增一個圓形弧線。如果需要,弧線會自動透過直線連線到路徑的最新點,例如,如果起點和控制點在一條直線上。

此方法常用於製作圓角。

注意: 使用相對較大的半徑可能會得到意想不到的結果:弧線的連線線將以任何必要的方式延伸以滿足指定的半徑。

語法

js
arcTo(x1, y1, x2, y2, radius)

引數

x1

第一個控制點的 x 軸座標。

y1

第一個控制點的 y 軸座標。

x2

第二個控制點的 x 軸座標。

y2

第二個控制點的 y 軸座標。

半徑

弧線的半徑。必須為非負數。

用法說明

假設P0 是呼叫arcTo()時路徑上的點,P1 = (x1, y1) 和P2 = (x2, y2) 分別是第一個和第二個控制點,而r 是呼叫中指定的radius

  • 如果r 為負數,則會丟擲IndexSizeError 異常
  • 如果r 為 0,arcTo() 的行為就像P0P1P2 共線(在一條直線上)。
  • 在所有點共線的情況下,會繪製一條從P0P1 的直線,除非點P0P1 重合(具有相同的座標),在這種情況下,將不繪製任何內容。

這些條件可以在下面的 Constructing an arcTo() path 示例中建立,以檢視結果。

返回值

無(undefined)。

異常

IndexSizeError DOMException

如果 radius 是負值,則會丟擲此異常。

示例

arcTo() 的工作原理

理解arcTo() 的一種方式是想象兩條直線段:一條從起點到第一個控制點,另一條從那裡到第二個控制點。如果沒有arcTo(),這兩條線段會形成一個尖角:arcTo() 在這個角處建立一個圓弧並使其平滑。換句話說,圓弧與這兩條線段都相切。

HTML

html
<canvas id="canvas"></canvas>

JavaScript

js
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");

// Tangential lines
ctx.beginPath();
ctx.strokeStyle = "gray";
ctx.moveTo(200, 20);
ctx.lineTo(200, 130);
ctx.lineTo(50, 20);
ctx.stroke();

// Arc
ctx.beginPath();
ctx.strokeStyle = "black";
ctx.lineWidth = 5;
ctx.moveTo(200, 20);
ctx.arcTo(200, 130, 50, 20, 40);
ctx.stroke();

// Start point
ctx.beginPath();
ctx.fillStyle = "blue";
ctx.arc(200, 20, 5, 0, 2 * Math.PI);
ctx.fill();

// Control points
ctx.beginPath();
ctx.fillStyle = "red";
ctx.arc(200, 130, 5, 0, 2 * Math.PI); // Control point one
ctx.arc(50, 20, 5, 0, 2 * Math.PI); // Control point two
ctx.fill();

結果

在此示例中,由arcTo() 建立的路徑是粗黑線。切線為灰色,控制點為紅色,起點為藍色。

建立圓角

此示例使用arcTo() 建立圓角。這是該方法最常見的用途之一。

HTML

html
<canvas id="canvas"></canvas>

JavaScript

弧線從moveTo() 指定的點 (230, 20) 開始。它被塑造成以 (90, 130) 和 (20, 20) 為控制點,半徑為 50。lineTo() 方法用直線將弧線連線到 (20, 20)。請注意,弧線的第二個控制點和lineTo() 指定的點是相同的,這會產生一個完全平滑的角。

js
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const p0 = { x: 230, y: 20 };
const p1 = { x: 90, y: 130 };
const p2 = { x: 20, y: 20 };

const labelPoint = (p) => {
  const offset = 10;
  ctx.fillText(`(${p.x},${p.y})`, p.x + offset, p.y + offset);
};

ctx.beginPath();
ctx.lineWidth = 4;
ctx.font = "1em sans-serif";
ctx.moveTo(p0.x, p0.y);
ctx.arcTo(p1.x, p1.y, p2.x, p2.y, 50);
ctx.lineTo(p2.x, p2.y);

labelPoint(p0);
labelPoint(p1);
labelPoint(p2);

ctx.stroke();

結果

大半徑的結果

如果使用相對較大的半徑,圓弧可能會出現在意想不到的位置。在此示例中,圓弧的連線線向上延伸,而不是向下延伸到moveTo() 指定的座標。這是因為半徑太大,圓弧無法完全位於起點下方。

HTML

html
<canvas id="canvas"></canvas>

JavaScript

js
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");

ctx.beginPath();
ctx.moveTo(180, 90);
ctx.arcTo(180, 130, 110, 130, 130);
ctx.lineTo(110, 130);
ctx.stroke();

結果

Constructing an arcTo() path

該演示顯示了用於確定arcTo() 渲染路徑的半無限直線以及中心為C、在T1T2 處與直線相切的圓。

請注意,當所有點共線時,arcTo 會建立一條從P0P1 的直線。此外,如果P0P1 的座標相同,arcTo 不會繪製任何內容。

除了可以透過滑塊設定圓弧半徑外,還可以透過按住滑鼠左鍵拖動來移動初始點P0 和控制點P1P2。還可以編輯數值,並使用箭頭鍵更改處於焦點下的下劃線元素。

arcTo() 繪製新增動畫

對於此示例,您可以嘗試調整圓弧半徑,以檢視路徑如何變化。路徑從起點p0 開始,使用具有控制點p1p2arcTo() 以及半徑在 0 到滑塊選擇的最大半徑之間變化。然後,呼叫lineTo() 將路徑完成到p2

HTML

html
<div>
  <label for="radius">Radius: </label>
  <input name="radius" type="range" id="radius" min="0" max="100" value="50" />
  <label for="radius" id="radius-output">50</label>
</div>
<canvas id="canvas"></canvas>

JavaScript

js
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const controlOut = document.getElementById("radius-output");
const control = document.getElementById("radius");
let radius = control.value; // match with init control value
control.oninput = () => {
  controlOut.textContent = radius = control.value;
};

const p1 = { x: 100, y: 100 };
const p2 = { x: 150, y: 50 };
const p3 = { x: 200, y: 100 };

function labelPoint(p, offset, i = 0) {
  const { x, y } = offset;
  ctx.beginPath();
  ctx.arc(p.x, p.y, 2, 0, Math.PI * 2);
  ctx.fill();
  ctx.fillText(`${i}:(${p.x}, ${p.y})`, p.x + x, p.y + y);
}

function drawPoints(points) {
  points.forEach((p, i) => {
    labelPoint(p, { x: 0, y: -20 }, `p${i}`);
  });
}

// Draw arc
function drawArc([p0, p1, p2], r) {
  ctx.beginPath();
  ctx.moveTo(p0.x, p0.y);
  ctx.arcTo(p1.x, p1.y, p2.x, p2.y, r);
  ctx.lineTo(p2.x, p2.y);
  ctx.stroke();
}

function loop(t) {
  const angle = (t / 1000) % (2 * Math.PI);
  const rr = Math.abs(Math.cos(angle) * radius);

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  drawArc([p1, p2, p3], rr);
  drawPoints([p1, p2, p3]);
  requestAnimationFrame(loop);
}

loop(0);

結果

規範

規範
HTML
# dom-context-2d-arcto-dev

瀏覽器相容性

另見