ZetCode

HTML5 Canvas 中的变换

最后修改于 2023 年 7 月 17 日

在本篇 HTML5 Canvas 教程中,我们将讨论变换。

仿射变换 由零个或多个线性变换(旋转、缩放或剪切)和翻译(平移)组成。多个线性变换可以组合成一个单一的矩阵。旋转是将刚体围绕一个固定点移动的变换。缩放是放大或缩小对象的变换。缩放因子在所有方向上都是相同的。平移是将每个点在指定方向上移动恒定距离的变换。剪切是将对象沿给定轴垂直移动的变换,在轴的一侧比另一侧具有更大的值。

有一个 transform 方法,它将当前变换与方法参数描述的矩阵相乘。我们可以缩放、旋转、移动和剪切上下文。还有执行特定变换的方法:translaterotatescale

平移

以下示例展示了一个简单的平移。

translation.html
<!DOCTYPE html>
<html>
<head>
<title>HTML5 canvas translation</title>
<style>
    canvas {border: 1px solid #bbbbbb}
</style>    
<script>
    function draw() {
        var canvas = document.getElementById('myCanvas');
        var ctx = canvas.getContext('2d');
    
        ctx.fillStyle = 'gray';
        ctx.translate(canvas.width/2, canvas.height/2);
        ctx.beginPath();
        ctx.arc(0, 0, 30, 0, 2*Math.PI);
        ctx.fill();
    }    
</script>
</head>
    
<body onload="draw();">
    <canvas id="myCanvas" width="150" height="150">
    </canvas>
</body>
</html>

该示例在画布中间绘制一个圆。

ctx.translate(canvas.width/2, canvas.height/2);

translate 方法将坐标系的原点移动到画布的中心。

ctx.beginPath();
ctx.arc(0, 0, 30, 0, 2*Math.PI);
ctx.fill();

绘制了一个圆。它的中心点是画布的中心。

Translation
图:平移

旋转

下一个示例演示了旋转。

rotation.html
<!DOCTYPE html>
<html>
<head>
<title>HTML5 canvas rotation</title>
<script>
    function draw() {
        var canvas = document.getElementById('myCanvas');
        var ctx = canvas.getContext('2d');
    
        ctx.fillStyle = 'gray';
        ctx.rotate(Math.PI/10);
        ctx.fillRect(50, 10, 120, 80);
    }    
</script>
</head>
    
<body onload="draw();">
    <canvas id="myCanvas" width="200" height="200">
    </canvas>
</body>
</html>

该示例对一个矩形执行旋转。

ctx.rotate(Math.PI/10);

rotate 方法执行旋转。角度参数表示顺时针旋转角度,以弧度表示。

Rotation
图示:旋转

缩放

缩放使用 scale 方法完成。该方法接受两个参数:x 缩放因子和 y 缩放因子。

scaling.html
<!DOCTYPE html>
<html>
<head>
<title>HTML5 canvas scaling</title>
<script>
    function draw() {
        var canvas = document.getElementById('myCanvas');
        var ctx = canvas.getContext('2d');
    
        ctx.fillStyle = 'gray';
        ctx.fillRect(20, 20, 80, 50);

        ctx.save();
        ctx.translate(110, 22);
        ctx.scale(0.5, 0.5);
        ctx.fillRect(0, 0, 80, 50);
        ctx.restore();

        ctx.translate(170, 20);
        ctx.scale(1.5, 1.5);
        ctx.fillRect(0, 0, 80, 50);
    }    
</script>
</head>
    
<body onload="draw();">
    <canvas id="myCanvas" width="300" height="200">
    </canvas>
</body>
</html>

在示例中,有一个矩形对象。首先,我们将其缩小,然后将其稍微放大。

ctx.save();
ctx.translate(110, 22);
ctx.scale(0.5, 0.5);
ctx.fillRect(0, 0, 80, 50);
ctx.restore();

变换操作是累加的。为了隔离平移和缩放操作,我们将它们放在 saverestore 方法之间。save 方法保存画布的整个状态,restore 方法恢复它。

Scaling
图:缩放

剪切

剪切是一种变换,它沿着一个或两个轴扭曲对象的形状。与缩放和平移一样,剪切可以沿着一个坐标轴或两个坐标轴进行。

shearing.html
<!DOCTYPE html>
<html>
<head>
<title>HTML5 canvas shearing</title>
<script>
    function draw() {
        var canvas = document.getElementById('myCanvas');
        var ctx = canvas.getContext('2d');
        
        ctx.translate(0.5, 0.5);
        ctx.setLineDash([2]);
        
        ctx.save();
        ctx.strokeStyle = 'green';
        ctx.strokeRect(50, 90, 160, 50);
        ctx.restore();
        
        ctx.save();
        ctx.strokeStyle = 'blue';
        ctx.transform(1, 1, 0, 1, 0, 0);
        ctx.strokeRect(50, 40, 80, 50);
        ctx.restore();
        
        ctx.save();
        ctx.strokeStyle = 'red';
        ctx.transform(1, 1, 0, 1, 0, -130);
        ctx.strokeRect(130, 10, 80, 50);
        ctx.restore();        
    }    
</script>
</head>
    
<body onload="draw();">
    <canvas id="myCanvas" width="300" height="300">
    </canvas>
</body>
</html>

没有专门用于剪切的方法,我们使用通用的 transform 方法。

ctx.translate(0.5, 0.5);

这一行使得矩形的线更平滑。

ctx.save();
ctx.strokeStyle = 'blue';
ctx.transform(1, 1, 0, 1, 0, 0);
ctx.strokeRect(50, 40, 80, 50);
ctx.restore();

蓝色矩形是水平剪切(倾斜)的。transform 方法的参数是:水平缩放、水平剪切、垂直剪切、垂直缩放、水平平移和垂直平移。

Shearing
图:剪切

甜甜圈

在下面的示例中,我们通过旋转一个椭圆来创建一个复杂的形状。

donut.html
<!DOCTYPE html>
<html>
<head>
<title>HTML5 canvas donut</title>
<script>
    function draw() {
        var canvas = document.getElementById('myCanvas');
        var ctx = canvas.getContext('2d');
    
        ctx.fillStyle = 'gray';
        ctx.translate(0.5, 0.5);
        
        var x = canvas.width/2;
        var y = canvas.height/2;

        for (var deg = 0; deg < 360; deg += 5) {
            
            var rad = deg * Math.PI / 180; 
            ctx.beginPath();
            ctx.ellipse(x, y, 30, 130, rad, 0, 2*Math.PI);
            ctx.stroke();
        }
    }    
</script>
</head>
    
<body onload="draw();">
    <canvas id="myCanvas" width="300" height="300">
    </canvas>
</body>
</html>

该示例使用了 ellipse 方法,在撰写本教程时,该方法尚未得到所有浏览器的支持。该示例在 Chrome 和 Opera 上运行。

var x = canvas.width/2;
var y = canvas.height/2;

椭圆的中心点位于画布的中心。

for (var deg = 0; deg < 360; deg += 5) {
    
    var rad = deg * Math.PI / 180; 
    ctx.beginPath();
    ctx.ellipse(x, y, 30, 130, rad, 0, 2*Math.PI);
    ctx.stroke();
}

我们创建了 36 个椭圆。这些椭圆被旋转了。ellipse 方法接受以下参数:椭圆中心点的 x 和 y 坐标、椭圆的长半轴、椭圆的短半轴、旋转角度、起始角度和结束角度。

在本篇 Java 2D 教程中,我们已经讨论了变换。