本篇主要講座標旋轉及其應用,這是編程動畫必不可少的技術。
閱讀本篇前請先打好前面的基礎。
本人能力有限,歡迎牛人共同討論,批評指正。javascript
模擬場景:已知一箇中心點(centerX,centerY),旋轉前物體ball(x1,y1),旋轉弧度(rotation);求旋轉後物體(x2,y2)。(以下圖) html
座標旋轉就是說圍繞某個點旋轉座標,咱們要依據旋轉的角度(弧度),計算出物體旋轉先後的座標,通常有兩種方法:java
靈活運用前章節的三角函數知識能夠很容易解決,基本思路:git
下面是示例是採用這種方法的圓周運動,其中vr爲ball相對於中心點的弧度變化速度,因爲旋轉半徑是固定的,因此沒有在動畫循環裏每次都獲取。
完整示例:簡單座標旋轉演示github
/** * 簡單座標旋轉演示 * */ window.onload = function () { const canvas = document.getElementById('canvas'); const context = canvas.getContext('2d'); const ball = new Ball(); ball.x = 300; ball.y = 200; // 弧度變化速度 const vr = 0.05; // 中心點位置設定在畫布中心 const centerX = canvas.width / 2; const centerY = canvas.height / 2; // ball相對與中心點的距離 const dx = ball.x - centerX; const dy = ball.y - centerY; // ball相對與中心點的弧度 let angle = Math.atan2(dy, dx); // 旋轉半徑 const radius = Math.sqrt(dx ** 2 + dy ** 2); (function drawFrame() { window.requestAnimationFrame(drawFrame, canvas); context.clearRect(0, 0, canvas.width, canvas.height); ball.x = centerX + Math.cos(angle) * radius; ball.y = centerY + Math.sin(angle) * radius; angle += vr; ball.draw(context); }()); };
上面的方法對於單個物體來講是很合適的,特別是角度和半徑只需計算一次的狀況。可是在更動態的場景中,可能須要旋轉多個物體,而他們相對於中心點的位置各不相同。因此每一幀都要計算每一個物體的距離、角度和半徑,而後把vr累加在角度上,最後計算物體新的座標。這樣顯然不會是優雅的作法。
理想的作法是用數學方法推導出旋轉角度與位置的關係,直接每次代入計算便可。推導過程以下圖: 編程
其實推導過程不重要,咱們只須要記住以下兩組公式,其中dx2和dy2是ball結束點相對於中心點的距離,因此獲得物體結束點,還要分別加上中心點座標。canvas
// 正向選擇 dx2 = (x1 - centerX) * cos(rotation) - (y1 - centerY) * sin(rotation) dy2 = (y1 - centerY) * cos(rotation) + (x1 - centerX) * sin(rotation) // 反向選擇 dx2 = (x1 - centerX) * cos(rotation) + (y1 - centerY) * sin(rotation) dy2 = (y1 - centerY) * cos(rotation) - (x1 - centerX) * sin(rotation)
下面是示例是採用這種方法的圓周運動,其中dx1和dy1是ball起始點相對於中心點的距離,dx2和dy2是ball結束點相對於中心點的距離。
完整示例:高級座標旋轉演示函數
/** * 高級座標旋轉演示 * */ window.onload = function () { const canvas = document.getElementById('canvas'); const context = canvas.getContext('2d'); const ball = new Ball(); ball.x = 300; ball.y = 200; // 弧度變化速度 const vr = 0.05; // 中心點位置設定在畫布中心 const centerX = canvas.width / 2; const centerY = canvas.height / 2; // 因爲vr是固定的能夠先計算正弦和餘弦 const cos = Math.cos(vr); const sin = Math.sin(vr); (function drawFrame() { window.requestAnimationFrame(drawFrame, canvas); context.clearRect(0, 0, canvas.width, canvas.height); // ball相對與中心點的距離 const dx1 = ball.x - centerX; const dy1 = ball.y - centerY; // 代入公式求出ball在結束相對與中心點的距離 const dx2 = dx1 * cos - dy1 * sin; const dy2 = dy1 * cos + dx1 * sin; // 求出x2,y2 ball.x = centerX + dx2; ball.y = centerY + dy2; ball.draw(context); }()); };
前面的章節中咱們介紹過越界的一種處理辦法是反彈,因爲邊界是矩形,反彈面垂直或水平,因此能夠直接將對應軸的速度取反便可,但對於非垂直或水平的反彈面這種方法是不適用的。
座標旋轉常見的應用就是處理這種狀況,將不規律方向的複雜問題簡單化。
基本思路:(旋轉先後如圖)動畫
示例是一個球掉落到一條線上,球受到重力加速度影響下落,碰到斜面就會反彈,每次反彈都會損耗速度。
完整示例:斜面反彈示例spa
window.onload = function () { const canvas = document.getElementById('canvas'); const context = canvas.getContext('2d'); const ball = new Ball(); // line類構造函數參數(開始點x軸座標,開始點y軸座標,結束點x軸座標,結束點y軸座標) const line = new Line(0, 0, 500, 0); // 設置重力加速度 const gravity = 0.2; // 設置反彈係數 const bounce = -0.6; ball.x = 100; ball.y = 100; line.x = 0; line.y = 200; line.rotation = 10 * Math.PI / 180; const cos = Math.cos(line.rotation); const sin = Math.sin(line.rotation); (function drawFrame() { window.requestAnimationFrame(drawFrame, canvas); context.clearRect(0, 0, canvas.width, canvas.height); ball.vy += gravity; ball.x += ball.vx; ball.y += ball.vy; // 獲取ball與line的相對位置 let x1 = ball.x - line.x; let y1 = ball.y - line.y; // 旋轉座標系(反向) let y2 = y1 * cos - x1 * sin; // 依據旋轉值執行反彈 if (y2 > -ball.radius) { // 旋轉座標系(反向) const x2 = x1 * cos + y1 * sin; // 旋轉速度(反向) const vx1 = ball.vx * cos + ball.vy * sin; let vy1 = ball.vy * cos - ball.vx * sin; y2 = -ball.radius; vy1 *= bounce; // 將全部東西迴轉(正向) x1 = x2 * cos - y2 * sin; y1 = y2 * cos + x2 * sin; ball.vx = vx1 * cos - vy1 * sin; ball.vy = vy1 * cos + vx1 * sin; ball.x = line.x + x1; ball.y = line.y + y1; } ball.draw(context); line.draw(context); }()); };