2、動畫中的三角學

前言

三角學在動畫技術中被廣泛運用,使用三角學編程時,你幾乎不需要和數學打交道,只需關注於可視化的圖形以及它們之間的關係。大部分情況下,你只需要處理與位置、距離以及角相關的變量,而不太會跟實際的數字打交道,其重點在於掌握各種邊角關係。

 

一、角度與弧度

角的度量單位分別有角度和弧度兩大系統。相信你對角度是比較熟悉的,但是在衡量角的大小時,計算機更傾向於使用弧度的概念。所以在使用三角學知識進行編程開發之前,你需要了解弧度。

一弧度約等於 57.2958°。當你在 canvas 元素上旋轉一幅圖像時,會用到 context.rotate(angle) 方法,其中參數 angle 的大小就需要用弧度表示。

JavaScript與大多數的開發語言相似,都使用弧度作爲角的度量單位。你可能又會同時用到角度與弧度,因此你需要知道如何在兩者間進行轉換。

角度與弧度的轉換公式:

弧度:radians = degrees * Math.PI / 180; 
角度:degrees = radians * 180 / Math.PI;

 

二、canvas 座標系

最常見的二維座標系以 x 軸作爲水平座標,y 軸作爲垂直座標。

通常情況下(0,0)座標會顯示在空間的正中心,隨後 x 軸的座標值向右以正數形式不斷增大,向左以負數形式不斷變小,而 y 軸的座標值向上以正數形式不斷增大,向下以負數形式不斷變小。如下圖所示:

而 canvas 元素卻是基於視頻畫面的座標系,其中(0,0)處在空間的左上角,x 軸的座標值從左往右不斷增大,而 y 軸的座標系的變化卻與標準座標系相反,向下以正數形式不斷增大,向上以負數形式不斷變小。如下圖所示:

此外,在大多數系統中,角度的測量是以逆時針爲正值,0° 表示一條線沿着 x 軸正方形延伸。如下圖所示:

canvas 元素在這方面又顯示了它的與衆不同,在 canvas 元素中,順時針的角度纔是正值,逆時針的角度反而成了負值。如下圖所示:

 

三、反正切

反正切是正切的逆運算,輸入角的對邊與鄰邊的比率,通過計算反正切可以得到角的弧度。JavaScript 提供了兩個函數用於計算反正切。

第一個函數是 Math.atan(ratio),它接受角的對邊長度除以鄰邊長度所得的小數作爲參數。但這裏會存在一個情況,如下圖所示:

當我們用 Math.tan 函數來計算 A、B、C、D 四個角的正切值時,如下:

  • tan(A):-1 / 2 = -0.5;

  • tan(B):1 / 2 = 0.5;

  • tan(C):1 / (-2) = -0.5;

  • tan(D):(-1) / (-2) = 0.5;

然後把所得正切值 0.5,作爲參數輸入到 Math.atan(0.5),再將所得弧度轉化爲角度,就會得到一個接近 26.57° 的值。此時,你並無法分辨這個角度是指角 B 還是角 D,因爲這兩個角所對應的正切值都是0.5。

這時候,我們就需要用到 JavaScript 中的另一個反正切函數:Math.atan2(y, x)。該函數接受兩個參數:對邊的長度 y 與鄰邊的長度 x。

當我們把角 B 的對邊 1 和鄰邊 2 和角 D 的對邊 -1 和鄰邊 -2 作爲參數輸入到此反正切函數中,並把所得的弧度值轉換成角度。

let angle = Math.atan2(1, 2) * 180 / Math.PI;
console.log("角B的大小爲:" + angle);
​
angle = Math.atan2(-1, -2) * 180 / Math.PI;
console.log("角D的大小爲:" + angle);

得到如下結果:

即如下圖所示:

儘管從三角形 D 的底邊算起,它的內角 D 確實是 26.57°,但是在 canvas 中,角是從 x 正軸開始以逆時針方向計算的,得到的角度 -153.43° 纔是我們在 canvas 元素的視角中所關注的角 D 的角度。

因此,當你在 canvas 中進行動畫開發時,涉及到角度的計算,都可以使用 Math.atan2 函數來處理,就不必擔心所得的值不是你需要的那個角度的值。實際上你可能專門使用 Math.atan2 而很少會用到 Math.atan 來求角的角度。

 

四、三角函數在動畫開發中的應用

1、旋轉

繪製一個物體,並讓它隨着鼠標旋轉,使其總能指向鼠標。

首先我們先繪製一個箭頭 arrow,將其放置在 canvas 元素的正中央並使其指向正向的 x 軸方向,此時是它旋轉 0° 時的表象。

然後我們通過計算得出鼠標的座標值,再與箭頭的座標值進行相減得到兩個座標的差值,就可以計算三角形兩邊的長度 dy 和 dx。此時,只須通過 Math.atan2(dy, dx) 方法算出角度的大小,並將其賦值給箭頭對象的 rotation 屬性。如下圖:


 

dx = mouse.x - arrow.x;
dy = mouse.y - arrow.y;
arrow.rotation = Math.atan2(dy, dx);

實現代碼:下載查看文件夾 2_1

 

2、波

a、利用正弦函數繪製正弦波。

正弦函數 y = sin(x) 對應的圖像如下:

我們可以通過不斷增加角的度數,模擬實現從 0 到 1 再到 -1 最後回到 0 的效果。通過不斷增加角的度數就可以不斷得到上下波動的波形圖如下所示:

此時如果將正弦值乘以更大的數字,比如 100,就可以得到一系列從 -100 到 100 不斷變化的數值。

首先我們先繪製一個球形 ball,將其放置在 canvas 元素的正中央。然後定義一個 angle 變量並將其初始化爲 0。

在 drawFrame 函數中,將 angle 的正弦值 Math.sin(angle) 乘以 range=50,這樣這些乘積處在 -50 到 50 的範圍內,以便於我們更明顯的查看運動的效果。如果再將該值加上 canvas 高度的一半,就會得到介於 150 ~ 250 的數值(以一個高度爲 400 像素的 canvas 元素作爲參考標準)。以此作爲球形的 y 座標,並在循環中以 speedy=0.1 弧度的步長不斷增加 angle 的大小。這樣就可以獲得一個基於 y 軸上不斷波形移動的球形。

x 軸方向上的處理則簡單些,只須定義 x 方向上的速度 speedx,並初始化爲 2,然後球形的 x 座標不斷加上 speedx,即可實現水平方向上的運動。

ball.x += speedx;
ball.y = canvasH / 2 + Math.sin(angle) * range;
angle += speedy;

實現代碼:下載查看文件夾 2_2

 

b、使用繪圖API繪製波

接下來我們直接使用 canvas 的繪圖 API 繪製正弦波而不再通過 ball 對象。需要注意的是,在 drawFrame 函數中取消了對 context.clearRect 的調用,這樣 canvas 元素就不會在每一幀開始的時候擦除之前繪製的圖像,使得每一幀繪製的圖像都保留在 canvas 上。

context.beginPath();
context.moveTo(xpos, ypos);
​
xpos += xspeed;
ypos = canvasH / 2 + Math.sin(angle) * range;
angle += yspeed
​
context.lineTo(xpos, ypos);
context.stroke();

效果如下:

 

實現代碼:下載查看文件夾 2_3

 

3、圓與橢圓

a、圓周運動

當餘弦配合正弦使用時,會獲得一個更常用並更有價值的功能:讓物體做圓周運動。我們先來看看物體做圓周運動時所處的多個位置的情況:

如果你從上圖圓形的正右方觀察時,會發現物體在做圓周運動時,實際是在不斷的上下垂直運動,如下圖所示:

上下運動的中心點是圓心所在的位置,而上下運動的範圍恰好是圓的半徑 radius。物體在運動中所處的位置即 y 座標,也就是所處三角形的對邊,因此該對邊的長度 y 爲物體所處角度 angle 的正弦值與圓的半徑的乘積,即

y = radius * Math.sin(angle);

現在,再想象你從圓的正下方進行觀察,則會發現物體在做圓周運動時,實際是在不斷的左右水平運動,如下圖所示:

此時,在計算物體在運動中所處的位置即 x 座標,也就是所處三角形的鄰邊時,就使用餘弦函數。

x = radius * Math.cos(angle);

因此,實現圓周運動,只需讓物體 x 座標做餘弦運動,y 座標做正弦運動:

ball.x = centerX + radius * Math.cos(angle);
ball.y = centerY + radius * Math.sin(angle);
angle += speed;

實現代碼:下載查看文件夾 2_4

 

b、橢圓運動

當物體進行圓周運動時,上下沿 y 軸運動和前後沿 x 軸運動的範圍是一致的,都是 radius,這種情況下我們得到了一個完美的圓形。

爲了獲得橢圓,可以使用不同的半徑計算 x 與 y 的座標位置,即上下沿 y 軸運動的範圍 radiusY 和前後沿 x 軸運動的範圍 radiusX 是不相等的。如此我們即可得到了一個橢圓。

ball.x = centerX + radiusX * Math.cos(angle);
ball.y = centerY + radiusY * Math.sin(angle);
angle += speed;

代碼:下載查看文件夾 2_5

 

4、勾股定理

勾股定理:直接三角形的兩條直角邊的平方和等於斜邊的平方。

在動畫中,我們可以利用勾股定理來求兩點間的距離。假設有兩個問題,它們的座標分別是(x1, y1)、(x2, y2),如下圖:

現在我們需要求出兩物體間的距離 dist 是多少。我們把兩個物體間的連線作爲三角形的斜邊,轉化爲一個直角三角形,如下圖:

上圖的 dx 表示兩個物體在 x 軸上的距離,dy 爲它們在 y 軸上的距離。由圖可知

dx = x2 - x1 = 58 - 50 = 8;
dy = y2 - y1 = 50 - 56 = -6;

根據勾股定理,將兩個值取平方後相加,得到兩物體間的距離 dist 的平方,即 (-6)² + 8² = dist²,最後取平方根,即求得了 dist 的值:

dx = x2 - x2;
dy = y2 - y1;
dist = Math.sqrt(dx * dx, dy * dy);

實現效果如下:

實現代碼:下載查看文件夾 2_6

 

源碼下載地址:

https://gitee.com/dplate/trigonometry_in_animation.git

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章