把網頁當畫布揮灑的 Canvas 10:極座標

小飛:「欸!你說的那家拉麵店在哪裡啊?」

阿宇:「以我們為原點,那家拉麵店在距離我們 x 軸正向 300 公尺, y 軸正向 400 公尺的地方。」

小飛:「講人話…」

阿宇:「那家拉麵店距離我們 500 公尺,角度我算一下哦…」

由此可見,對一般人來說,用直角座標描述真實世界的位置並不直覺。通常,我們會以「距離」和「方向(角度)」來描述物件的位置,這就是所謂的「極座標」。


直角坐標 v.s. 極座標

使用極座標(Polar Coordinate System)表示物件位置比直角座標來得更加直覺,也比較容易描述距離和角度的變化。在數學上,直角座標以 ( 物件距離原點的 x 軸距離, 物件距離原點的 y 軸距離) 表示,極座標則是以 (物件與原點的最短距離 r , 物件與 x 軸的夾角 a ) 表示,兩者之間可以互相轉換。

將直角座標 (x, y) 轉為極座標 (r, a)

距離/半徑 r = Vector.length = Math.sqrt( x^2 + y^2)

角度(deg) a * degToPi = Vector.angle = atan2(y, x) 弧度(rad)

先前我們在開發模板中的向量類別裡面,已經定義過 get length()get angle() ,因此直皆使用即可。關於 atan2 的數學方法,稍後在三角函數的文章中會提到。

⭐ 從極座標的角度 a (deg)繪製在 canvas 上必須乘上 degToPi = Math.PI / 180 ,轉成弧度(Rad),這一點必須特別注意,

直角座標與極座標的差異


極座標的應用

極座標應用相當廣泛,範圍掃描、行星公轉、憤怒鳥攻擊、不規則半徑螺旋等等互動效果,都能夠使用極座標來實現,這裡以「掃瞄範圍」進行實作練習。

Canvas%2010%203a5a7927342e4d67a428069cfb0cf4f0/Untitled%201.png

開啟一個新的 Canvas 開發模板,並在 draw() 函式裡繪圖,程式碼說明請參考註解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
function draw() {
// Clear the background of canvas
ctx.fillStyle = bgColor;
ctx.fillRect(0, 0, ww, wh);

// ------------------------------------------------- //
// Draw here ---------------------------------------

// degToPi is the ratio of pi and deg
const degToPi = Math.PI / 180

// Draw the X / Y axis
ctx.beginPath()
ctx.moveTo(ww / 2, 0)
ctx.lineTo(ww / 2, wh)
ctx.moveTo(0, wh / 2)
ctx.lineTo(ww, wh / 2)
ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)'
ctx.stroke()

ctx.save()
// Translate the path to the center
ctx.translate(ww / 2, wh / 2)

// Calcualte the differece between the origin and mouse position
let delta = mousePos.sub(new Vec2(ww / 2, wh / 2)) // Vector
let mouseAngle = delta.angle // Angle
let mouseDistance = delta.length // Distance
// The polar coordinate of the mouse position is (mouseDistance, mouseAngle)

// Draw a line from the origin to the mouse position
ctx.beginPath()
ctx.moveTo(0, 0)
ctx.lineTo(delta.x , delta.y)
ctx.stroke()

// Draw the circle
ctx.beginPath()
ctx.arc(0, 0, mouseDistance, 0, Math.PI * 2)
ctx.stroke()

// Draw the text showing the degree and the distance of the mouse position
ctx.fillStyle = 'white'
ctx.fillText(parseInt(mouseAngle / degToPi) + ' 度', 10, -10)
ctx.fillText('r = ' + mouseDistance, mouseDistance + 10, -10)

// Draw the range of light
ctx.beginPath()
ctx.moveTo(0, 0)
let light_r = mouseDistance
ctx.save()
ctx.rotate(mouseAngle - 10 * degToPi) // Rotate the canvas by -10 deg
ctx.lineTo(light_r, 0)
ctx.rotate(20 * degToPi) // Rotate the canvas by 20 deg
ctx.lineTo(light_r, 0)
ctx.fillStyle = 'gold'
ctx.fill()
ctx.restore()

// Draw enemies with polar coordinate system
let enemies = [
{r: 100, angle: 45},
{r: 30, angle: -113},
{r: 155, angle: 175},
{r: 274, angle: -24},
]

enemies.forEach(p => {
ctx.save()
ctx.beginPath()
ctx.rotate(p.angle * degToPi) // Rotate the canvas by p.angle degree
ctx.translate(p.r, 0) // Translate the canvas to the enemy point
ctx.arc(0, 0, 5, 0, Math.PI * 2)
let color = 'white'
if(Math.abs(p.angle * degToPi - mouseAngle) < 10 * degToPi && p.r < mouseDistance) {
color = 'red'
}
ctx.fillStyle = color
ctx.fill()
ctx.restore()
})
ctx.restore()

// ------------------------------------------------- //
}


參考資料

  1. 動畫互動網頁特效入門(JS/CANVAS):5-8 極座標的基礎概念
把網頁當畫布揮灑的 Canvas 09:粒子特效

評論

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×