注意
這是一個沒學過圖學、線性代數也亂學的人寫出來的東西 (艸
名詞定義
input:
- 起始點
S
座標為 (sx, sy) - 終止點
E
座標為 (ex, ey) - 比例參數:
rx1
、rx2
、ry1
、ry2
。 - 角度:
d
- 在上圖中
d = 0
。箭頭向下是d=90
。
- 在上圖中
變數:
w
:abs(ex - sx)h
:abs(ey - sy)x2
:w * rx2 / (rx1 + rx2)y2
:h * ry2 / (ry1 + ry2) / 2
所以各點座標為:
A
:(ex, (sy + ey) / 2)B1
:(sx + x2, sy)B2
:(sx + x2, sy + y2)C1
:(sx, sy + y2)C2
:(sx, ey - y2)B3
:(sx + x2, ey - y2)B1
:(sx + x2, ey)
實作
以下是 JS,canvas
是 CanvasRenderingContext2D object。
var points = [];
points.push([ex, (sy + ey) / 2]); //A
points.push([sx + x2, sy]); //B1
points.push([sx + x2, sy + y2]); //B2
points.push([sx, sy + y2]); //C1
points.push([sx, ey - y2]); //C2
points.push([sx + x2, ey - y2]); //B3
points.push([sx + x2, ey]); //B4
canvas.beginPath();
canvas.moveTo(points[0][0], points[0][1]);
for (var i = 0; i < points.length; i++) {
canvas.lineTo(points[i][0], points[i][1]);
}
canvas.fill();
canvas.closePath();
旋轉
CanvasRenderingContext2D 有 rotate()
,但它的運算邏輯是對於原點作 rotate,所以假設要用上面那組 points
搭配 rotate()
畫出向右的箭頭:
var d = 180;
d = d * Math.PI / 180; //rotate() 吃的是弳度,所以要轉換
canvas.rotate(d * Math.PI / 180);
canvas.beginPath();
//後略......
基本上永遠看不到任何東西,因為 rotate 完座標都是負數。所以對人類而言的旋轉(我稱之為原地旋轉)其實是:
- 把箭頭中心移動到原點:
translate(-tx, -ty)
- 旋轉:
rotate(d)
- 把箭頭中心移動回原位:
translate(tx, ty)
把箭頭視為一個矩形,所以 tx = (sx + ex) / 2
、ty = (sy + ey) / 2
。後頭的 code 基本上是 API 對應的寫法,實際補上這段然後開始作 canvas.beginPath()
會發現還是只有 rotate(d)
的效果,因為步驟 1 跟步驟 3 互相抵銷…… Orz
所以必須要回到最基本的 transform matrix 來畢其功於一役,細節與原理請參閱這份文件,總之程式碼會是:
//這裡的 d 已經轉換成弳度了
var sinD = Math.sin(d),
cosD = Math.cos(d);
canvas.transform(
cosD, sinD,
-sinD, cosD,
tx * (1 - cosD)+ ty * sinD, ty * (1 - cosD) - tx * sinD
);
canvas.beginPath();
//中間一樣,略
canvas.closePath();
//把 transform matrix 設回正常的樣子以免妨礙後續操作
canvas.setTransform(1, 0, 0, 1, 0, 0);
90 / 270 度的問題
若 d
為 90 / 270 時,會發現產生的箭頭形狀似乎不如預期。這是因為最終箭頭的 y 軸長度,其實是來自於 w
。也就是說本來預期會出現一個細長的向下箭頭,實則出現的是一個粗短的向下箭頭。以此類推,在 d
落在 45~135 或是 225~315 區間時,要給另一組 points
才能接近預期。
if ((degree > 45 && degree < 135) || (degree > 225 && degree < 315)) {
//points[] 的內容值自己算...
d = d - 90;
} else {
//原本向右的 points
}
//後面都一樣
當然,如果要繼續講究下去,在 d
越接近 {45 + 90n, n = 0, 1, 2, 3} 時,長出來的箭頭就會越看不出跟 S
、E
點的關聯…… 我只能說我的智商沒辦法解這種問題…… [死]
沒有留言:
張貼留言