九、變換 Transformations
譯者注:變換計算中需要用到很多矩陣變換運算,如果不熟悉矩陣變換運算,那麼理解以下代碼會有一定困難,建議先熟悉矩陣變換運算再閱讀以下內容。這裏有一篇很好的文章詳細解釋了矩陣運算:淺談矩陣變換——Matrix。
9.1平移 Translate
爲了在HTML5畫布上實現平移,可以使用上下文對象的translate()方法。平移是一種移動整個畫布的方法。例如,有一個在畫布上繪製複雜圖形的函數,並且需要平移圖形,那麼平移上下文要比調整組成圖形的所有點的x和y位置容易得多。
平移首先通過變換畫布上下文,然後再繪製圖形。在本教程中,將平移畫布上下文,然後在位置(0, 0)繪製矩形。由於畫布上下文被平移,矩形也將被平移。
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="578" height="200"></canvas>
<script>
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var rectWidth = 150;
var rectHeight = 75;
// translate context to center of canvas
context.translate(canvas.width / 2, canvas.height / 2);
context.fillStyle = 'blue';
context.fillRect(rectWidth / -2, rectHeight / -2, rectWidth, rectHeight);
</script>
</body>
</html>
以上代碼演示了在畫布上平移一個矩形到居中位置。
9.2縮放變換 Scale Transform
要縮放HTML5畫布,可以使用scale()方法,此方法可縮放畫布的X和Y兩個方向。
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="578" height="200"></canvas>
<script>
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var rectWidth = 150;
var rectHeight = 75;
// translate context to center of canvas
context.translate(canvas.width / 2, canvas.height / 2);
// scale y component
context.scale(1, 0.5);
context.fillStyle = 'blue';
context.fillRect(rectWidth / -2, rectHeight / -2, rectWidth, rectHeight);
</script>
</body>
</html>
以上代碼演示了在畫布上繪製一個矩形,並在Y方向縮放0.5倍。
9.3旋轉變換 Rotate Transform
要旋轉HTML5畫布,可以使用rotate()轉換方法。旋轉變換需要提供轉過的弧度(注意不是角度)。爲了定義旋轉點,需要首先平移畫布上下文對象,以便上下文的左上角位於所需的旋轉點上。在本教程中,平移了畫布上下文,使得上下文的左上角直接位於矩形的中心,從而產生圍繞矩形中心的旋轉。
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="578" height="200"></canvas>
<script>
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var rectWidth = 150;
var rectHeight = 75;
// translate context to center of canvas
context.translate(canvas.width / 2, canvas.height / 2);
// rotate 45 degrees clockwise
context.rotate(Math.PI / 4);
context.fillStyle = 'blue';
context.fillRect(rectWidth / -2, rectHeight / -2, rectWidth, rectHeight);
</script>
</body>
</html>
以上代碼演示了平移畫布,繪製矩形,並將其旋轉一定角度。
9.4自定義變換 Custom Transform
爲了將自定義轉換矩陣應用到HTML5畫布,可以使用transform()方法。根據以下約定,該方法需要3×3矩陣的六個分量:
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="578" height="200"></canvas>
<script>
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var rectWidth = 150;
var rectHeight = 75;
// translation matrix:
// 1 0 tx
// 0 1 ty
// 0 0 1
var tx = canvas.width / 2;
var ty = canvas.height / 2;
// apply custom transform
context.transform(1, 0, 0, 1, tx, ty);
context.fillStyle = 'blue';
context.fillRect(rectWidth / -2, rectHeight / -2, rectWidth, rectHeight);
</script>
</body>
</html>
以上代碼演示了自定義變換。
9.5斜切變換 Shear Transform
爲了在HTML5畫布上實現斜切變換,可以使用transform()方法進行矩陣轉換。SX定義水平方向切變,SY定義垂直方向的切變。
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="578" height="200"></canvas>
<script>
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var rectWidth = 150;
var rectHeight = 75;
// shear matrix:
// 1 sx 0
// sy 1 0
// 0 0 1
var sx = 0.75;
// .75 horizontal shear
var sy = 0;
// no vertical shear
// translate context to center of canvas
context.translate(canvas.width / 2, canvas.height / 2);
// apply custom transform
context.transform(1, sy, sx, 1, 0, 0);
context.fillStyle = 'blue';
context.fillRect(-rectWidth / 2, rectHeight / -2, rectWidth, rectHeight);
</script>
</body>
</html>
以上代碼演示了在畫布上繪製一個藍色矩形,並進行X方向的斜切變換。
9.6鏡像變換 Mirror Transform
要使用HTML5 Canvas創建鏡像變換,我們可以在上下文對象的x方向上應用負比例來水平翻轉,或者可以在上下文對象的y方向上應用負比例來垂直翻轉。
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="578" height="200"></canvas>
<script>
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
// translate context to center of canvas
context.translate(canvas.width / 2, canvas.height / 2);
// flip context horizontally
context.scale(-1, 1);
context.font = '30pt Calibri';
context.textAlign = 'center';
context.fillStyle = 'blue';
context.fillText('Hello World!', 0, 0);
</script>
</body>
</html>
以上代碼演示了在畫布的X方向反轉一段文字。
9.7重置變換 Reset Transform
爲了重置HTML5 Canvas矩陣變換,可以使用setTransform()方法執行以下矩陣變換,將轉換矩陣設置爲其默認狀態:
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="578" height="200"></canvas>
<script>
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var rectWidth = 150;
var rectHeight = 75;
// translate context to center of canvas
context.translate(canvas.width / 2, canvas.height / 2);
context.fillStyle = 'blue';
context.fillRect(-rectWidth / 2, rectHeight / -2, rectWidth, rectHeight);
// Reset Transform
// 1 0 0
// 0 1 0
// 0 0 1
// apply custom transform
context.setTransform(1, 0, 0, 1, 0, 0);
context.fillStyle = 'red';
context.fillRect(0, 0, rectWidth, rectHeight);
</script>
</body>
</html>
以上代碼演示了在畫布上重置變換,並用紅色繪製默認狀態的圖形。
9.8狀態棧變換 Transformation State Stack
爲了用HTML5 Canvas保存和恢復不同的變換狀態,可以使用canvas上下文對象的save()和restore()方法。
在本教程中,將通過在每次轉換之前將狀態推入狀態堆棧來保存變換狀態,也就是爲上下文對象設置保存點,以便上下文能準確回覆到保存點的狀態。首先繪製一個藍色矩形,恢復並彈出最後一個轉換狀態;再繪製一個紅色矩形,恢復並彈出最後一個轉換狀態;再繪製一個黃色矩形,最後恢復並彈出最終轉換狀態;再繪製一個綠色矩形。
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="578" height="200"></canvas>
<script>
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var rectWidth = 150;
var rectHeight = 75;
context.save();
// save state 1
context.translate(canvas.width / 2, canvas.height / 2);
context.save();
// save state 2
context.rotate(Math.PI / 4);
context.save();
// save state 3
context.scale(2, 2);
context.fillStyle = 'blue';
context.fillRect(rectWidth / -2, rectHeight / -2, rectWidth, rectHeight);
context.restore();
// restore state 3
context.fillStyle = 'red';
context.fillRect(rectWidth / -2, rectHeight / -2, rectWidth, rectHeight);
context.restore();
// restore state 2
context.fillStyle = 'yellow';
context.fillRect(rectWidth / -2, rectHeight / -2, rectWidth, rectHeight);
context.restore();
// restore state 1
context.fillStyle = 'green';
context.fillRect(rectWidth / -2, rectHeight / -2, rectWidth, rectHeight);
</script>
</body>
</html>
以上代碼演示了在畫布上保存和彈出狀態變換。
9.9橢圓 Oval
要使用HTML5 Canvas創建一個橢圓形,可以爲上下文狀態設置一個保存點,然後水平拉伸畫布上下文,畫一個圓圈,然後再恢復畫布到保存點的狀態,最後應用樣式。
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="578" height="200"></canvas>
<script>
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var centerX = 0;
var centerY = 0;
var radius = 50;
// save state
context.save();
// translate context
context.translate(canvas.width / 2, canvas.height / 2);
// scale context horizontally
context.scale(2, 1);
// draw circle which will be stretched into an oval
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
// restore to original state
context.restore();
// apply styling
context.fillStyle = '#8ED6FF';
context.fill();
context.lineWidth = 5;
context.strokeStyle = 'black';
context.stroke();
</script>
</body>
</html>
以上代碼演示了在畫布上設置保存點,然後使畫布在X方向放大2倍,再繪製一個圓(由於受到X方向放大兩倍的影響,這個圓變成了橢圓),然後恢復到保存點並應用樣式,最後爲橢圓描邊的過程。