JavaScript青少年简明教程:canvas(画布)元素入门

JavaScript青少年简明教程:canvas(画布)元素入门

<canvas>元素通常被称为“画布”(Canvas),它使用JavaScript可以在网页上动态地绘制图形、图像、动画等内容。

<canvas> 元素是 HTML5 引入的一种绘图元素,允许开发者使用 JavaScript 动态地绘制图形、渲染文本、处理动画和图像。通过结合 <canvas> 和 JavaScript,可以创建复杂的图形和视觉效果,包括游戏、图表、数据可视化等。

canvas坐标体系

电脑上的坐标系和数学上的坐标系稍微有点不同,坐标的原点在绘制区域(这里是Canvas)的左上角,X轴正向朝右,Y轴正向朝下,如下图:

canvas默认情况下以canvas的左上角为坐标原点(0,0),沿x轴向右为正值,沿y轴向下为正值。大小是 300 像素宽 × 150 像素高(可以修改canvas的大小)。坐标的单位是像素(pixel缩写px)。

设置 Canvas 大小,有两种方式:

1)HTML属性方式:
<canvas id="myCanvas" width="500" height="300"></canvas>
2)JavaScript方式:
let canvas = document.getElementById('myCanvas');
canvas.width = 500;
canvas.height = 300;

<canvas> 元素本身不提供绘图功能,仅提供了一个用于绘图的区域,但可被用来通过 JavaScript(Canvas API 或 WebGL API)绘制图形及图形动画。https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/canvas

下面是一个简单的使用 <canvas> 元素的例子,直观感受之,源码如下:

<!DOCTYPE html>  
<html lang="zh">  
<head>  
<meta charset="UTF-8">  
<meta name="viewport" content="width=device-width, initial-scale=1.0">  
<title>Canvas 示例</title>  
</head>  
<body>  
  
<canvas id="myCanvas" width="400" height="300" style="border:1px solid #000000;">  
    您的浏览器不支持Canvas  
</canvas>  
  
<script>  
// 获取canvas元素  
let canvas = document.getElementById('myCanvas');  
// 获取2D渲染上下文  
let ctx = canvas.getContext('2d');  
  
// 使用Canvas API绘制一个矩形  
ctx.fillStyle = '#FF0000'; // 设置填充颜色为红色  
ctx.fillRect(10, 10, 100, 100); // 在(10, 10)的位置绘制一个100x100的矩形  
</script>  
  
</body>  
</html>

例子中,我们创建了一个 400x300 像素的 <canvas> 元素,并使用 JavaScript 获取了它的 2D 渲染上下文(getContext('2d'))。然后,我们设置填充颜色为红色,并使用 fillRect 方法绘制了一个 100x100 像素的矩形。运行效果如下:

Canvas API 提供了许多其他的绘图方法和属性,如绘制圆形、线条、渐变、图像等。你可以通过 JavaScript 编程来创建复杂的动画和交互式的图形。

<canvas> 是HTML5 中新增的一项强大功能,它提供了一个用于绘图的区域。默认情况下,<canvas> 元素没有内容,需要通过 JavaScript 进行绘制。典型的 <canvas> 元素定义如下:

<canvas id="myCanvas" width="500" height="500"></canvas>

获取绘图上下文

要在 <canvas> 上进行绘图,首先需要获取其绘图上下文(context)。<canvas> 元素提供了多种上下文类型,但最常用的是 2D 上下文。

let canvas = document.getElementById('myCanvas');

let context = canvas.getContext('2d');

要在HTML5的<canvas>元素上进行2D绘图,我们需要先获取其2D渲染上下文。这是通过调用canvas元素的getContext('2d')方法来完成的。

什么是上下文?

在这里,“上下文”可以理解为一个绘图环境或者绘图API的集合。它提供了一系列用于在canvas上绘制图形、文本、图像等的方法和属性。例如:

fillRect(): 绘制填充矩形

strokeRect(): 绘制描边矩形

arc(): 绘制圆弧或圆

还可以通过上下文设置各种绘图属性,如fillStyle(填充颜色)、strokeStyle(描边颜色)、lineWidth(线宽)等。

为什么需要获取上下文?

canvas元素本身只是一个容器,它不提供任何绘图功能。要在canvas上绘图,我们需要使用特定的绘图API,这就是上下文所提供的。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Canvas 2D Context Example</title>
</head>
<body>
    <canvas id="myCanvas" width="300" height="200"></canvas>

    <script>
        // 获取canvas元素
        const canvas = document.getElementById('myCanvas');

        // 获取2D渲染上下文
        const ctx = canvas.getContext('2d');

        // 使用上下文进行绘图
        ctx.fillStyle = 'blue';  // 设置填充颜色
        ctx.fillRect(50, 50, 100, 75);  // 绘制一个填充矩形

        ctx.strokeStyle = 'red';  // 设置描边颜色
        ctx.lineWidth = 5;  // 设置线宽
        ctx.strokeRect(75, 75, 150, 100);  // 绘制一个描边矩形

        ctx.beginPath();  // 开始一个新的路径
        ctx.arc(150, 100, 50, 0, Math.PI * 2);  // 绘制一个圆
        ctx.fillStyle = 'green';
        ctx.fill();  // 填充圆
    </script>
</body>
</html>

在这个例子中:

我们首先通过getElementById获取了canvas元素。

然后使用getContext('2d')方法获取2D渲染上下文。这个方法返回一个CanvasRenderingContext2D对象,我们将它存储在ctx变量中。

获取上下文后,我们就可以使用它提供的各种方法进行绘图了。例如:fillRect(): 绘制填充矩形;设置各种绘图属性,如fillStyle(填充颜色)。

运行效果如下:

使用 2D 上下文绘图

通过 2D 上下文,可以使用各种方法来绘制图形、文本和图像。以下是一些常用的方法和操作:

1. 绘制矩形
// 填充矩形
context.fillStyle = 'blue';
context.fillRect(10, 10, 100, 100);

// 描边矩形
context.strokeStyle = 'red';
context.strokeRect(130, 10, 100, 100);

// 清除矩形区域
context.clearRect(50, 50, 60, 60);
2. 绘制路径
context.beginPath();
context.moveTo(150, 50);
context.lineTo(200, 50);
context.lineTo(175, 100);
context.closePath();
context.stroke();
3. 绘制圆和弧
context.beginPath();
context.arc(75, 75, 50, 0, Math.PI * 2, true); // 外圆
context.moveTo(110, 75);
context.arc(75, 75, 35, 0, Math.PI, false);  // 口 (顺时针)
context.moveTo(65, 65);
context.arc(60, 65, 5, 0, Math.PI * 2, true);  // 左眼
context.moveTo(95, 65);
context.arc(90, 65, 5, 0, Math.PI * 2, true);  // 右眼
context.stroke();
4. 绘制文本
context.font = '30px Arial';
context.fillText('Hello Canvas', 50, 50);

context.strokeText('Hello Canvas', 50, 100);
5. 绘制图像
let img = new Image();
img.onload = function() {
    context.drawImage(img, 0, 0);
};
img.src = 'path/to/your/image.jpg';

处理动画

通过不断更新画布并重绘,可以实现动画效果。典型的动画实现步骤如下:

清除画布

更新图形位置

重绘图形

使用 requestAnimationFrame 调度确保这个过程以一种高效且平滑的方式持续进行下一次绘制。

例、创建了一个在画布上四个方向上(上、下、左、右)移动的红色小球,源码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>画布动画示例</title>
    <style>
        canvas { border: 1px solid black; }
    </style>
</head>
<body>
    <canvas id="myCanvas" width="400" height="200"></canvas>

    <script>
        const canvas = document.getElementById('myCanvas');
        const ctx = canvas.getContext('2d');

        let x = canvas.width / 2;
        let y = canvas.height / 2;
        let dx = 2;
        let dy = -2;
        let radius = 20;

        function drawBall() {
            ctx.beginPath();
            ctx.arc(x, y, radius, 0, Math.PI * 2);
            ctx.fillStyle = 'red';
            ctx.fill();
            ctx.closePath();
        }

        function animate() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);

            drawBall();

            // 更新球的位置
            x += dx;
            y += dy;

            // 碰到左右边界时改变水平方向
            if (x + radius > canvas.width || x - radius < 0) {
                dx = -dx;
            }

            // 碰到上下边界时改变垂直方向
            if (y + radius > canvas.height || y - radius < 0) {
                dy = -dy;
            }

            requestAnimationFrame(animate);
        }

        animate();
    </script>
</body>
</html>

交互操作

结合事件监听器,可以使 <canvas> 元素响应用户的交互,例如允许用户通过点击画布来添加一个新的球,在四个方向上(上、下、左、右)移动,并且移动鼠标改变背景颜色,源码:

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>交互式画布动画</title>
    <style>
        canvas { border: 1px solid black; cursor: pointer; }
    </style>
</head>
<body>
    <canvas id="myCanvas" width="400" height="200"></canvas>

    <script>
        const canvas = document.getElementById('myCanvas');
        const ctx = canvas.getContext('2d');

        let balls = [];

        function createBall(x, y) {
            return {
                x: x,
                y: y,
                dx: (Math.random() - 0.5) * 4,
                dy: (Math.random() - 0.5) * 4,
                radius: 10,
                color: `rgb(${Math.random()*255},${Math.random()*255},${Math.random()*255})`,
                draw: function() {
                    ctx.beginPath();
                    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
                    ctx.fillStyle = this.color;
                    ctx.fill();
                    ctx.closePath();
                },
                update: function() {
                    if (this.x + this.radius > canvas.width || this.x - this.radius < 0) {
                        this.dx = -this.dx;
                    }
                    if (this.y + this.radius > canvas.height || this.y - this.radius < 0) {
                        this.dy = -this.dy;
                    }
                    this.x += this.dx;
                    this.y += this.dy;
                    this.draw();
                }
            };
        }

        function animate() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            balls.forEach(ball => ball.update());
            requestAnimationFrame(animate);
        }

        // 事件监听器:点击画布添加新球
        canvas.addEventListener('click', function(event) {
            const rect = canvas.getBoundingClientRect();
            const x = event.clientX - rect.left;
            const y = event.clientY - rect.top;
            balls.push(createBall(x, y));
        });

        // 事件监听器:鼠标移动时改变背景色
        canvas.addEventListener('mousemove', function(event) {
            const rect = canvas.getBoundingClientRect();
            const x = event.clientX - rect.left;
            const y = event.clientY - rect.top;
            const r = Math.round(x / canvas.width * 255);
            const g = Math.round(y / canvas.height * 255);
            const b = Math.round((x + y) / (canvas.width + canvas.height) * 255);
            canvas.style.backgroundColor = `rgb(${r},${g},${b})`;
        });

        animate();
    </script>
</body>
</html>

使用class(类)的方法通常被认为是更现代和更易于维护的方法,特别是对于更复杂的对象和继承结构。使用 class 来实现前面例子效果的源码:

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>交互式画布动画</title>
    <style>
        canvas { border: 1px solid black; cursor: pointer; }
    </style>
</head>
<body>
    <canvas id="myCanvas" width="400" height="200"></canvas>

    <script>
        const canvas = document.getElementById('myCanvas');
        const ctx = canvas.getContext('2d');

        let balls = [];

        class Ball {
            constructor(x, y) {
                this.x = x;
                this.y = y;
                this.dx = (Math.random() - 0.5) * 4;
                this.dy = (Math.random() - 0.5) * 4;
                this.radius = 10;
                this.color = `rgb(${Math.random()*255},${Math.random()*255},${Math.random()*255})`;
            }

            draw() {
                ctx.beginPath();
                ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
                ctx.fillStyle = this.color;
                ctx.fill();
                ctx.closePath();
            }

            update() {
                if (this.x + this.radius > canvas.width || this.x - this.radius < 0) {
                    this.dx = -this.dx;
                }
                if (this.y + this.radius > canvas.height || this.y - this.radius < 0) {
                    this.dy = -this.dy;
                }
                this.x += this.dx;
                this.y += this.dy;
                this.draw();
            }
        }

        function animate() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            balls.forEach(ball => ball.update());
            requestAnimationFrame(animate);
        }

        // 事件监听器:点击画布添加新球
        canvas.addEventListener('click', function(event) {
            const rect = canvas.getBoundingClientRect();
            const x = event.clientX - rect.left;
            const y = event.clientY - rect.top;
            balls.push(new Ball(x, y));
        });

        // 事件监听器:鼠标移动时改变背景色
        canvas.addEventListener('mousemove', function(event) {
            const rect = canvas.getBoundingClientRect();
            const x = event.clientX - rect.left;
            const y = event.clientY - rect.top;
            const r = Math.round(x / canvas.width * 255);
            const g = Math.round(y / canvas.height * 255);
            const b = Math.round((x + y) / (canvas.width + canvas.height) * 255);
            canvas.style.backgroundColor = `rgb(${r},${g},${b})`;
        });

        animate();
    </script>
</body>
</html>

清除 canvas

要清除整个canvas,可以使用:

ctx.clearRect(0, 0, canvas.width, canvas.height);

保存和恢复canvas状态

canvas提供了保存和恢复绘图状态的方法:

ctx.save(); // 保存当前状态

// 进行一些绘制操作

ctx.restore(); // 恢复到之前保存的状态

canvas坐标系变换

虽然默认坐标系是固定的,但我们可以通过以下方法来改变绘图上下文的坐标系:

a. 平移 (Translate):

ctx.translate(x, y);

这会将坐标系的原点移动到 (x, y) 点。

b. 旋转 (Rotate):

ctx.rotate(angle);

这会围绕当前原点旋转坐标系,angle 是弧度。

c. 缩放 (Scale):

ctx.scale(scaleX, scaleY);

这会按照给定的比例因子缩放坐标系。

示例:

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>变换示例</title>
</head>
<body>
<canvas id="myCanvas" width="400" height="200"></canvas>
<script>
    let canvas = document.getElementById('myCanvas');
    let ctx = canvas.getContext('2d');

    // 保存初始状态
    ctx.save();

    // 平移坐标系
    ctx.translate(100, 100);

    // 旋转坐标系
    ctx.rotate(Math.PI / 4);

    // 在新坐标系中绘制一个矩形
    ctx.fillStyle = 'blue';
    ctx.fillRect(0, 0, 100, 50);

    // 恢复到初始状态
    ctx.restore();

    // 在原始坐标系中绘制一个圆
    ctx.beginPath();
    ctx.arc(50, 50, 25, 0, 2 * Math.PI);
    ctx.fillStyle = 'red';
    ctx.fill();
</script>
</body>
</html>

顺便一提,<canvas> 还支持 3D 绘图,通过获取 WebGL 上下文,可以进行 3D 渲染。当涉及到完整的 WebGL 代码时,它通常会变得相当长且复杂,因为需要设置顶点着色器、片段着色器、顶点数据、索引数据、矩阵变换等等,对此我没有深入学习,就不多说了。

进一步学习,可见:

用HTML5的<canvas>元素实现刮刮乐游戏https://blog.csdn.net/cnds123/article/details/136388100

HTML5 canvas 图形标签的用法快速入门 https://blog.csdn.net/cnds123/article/details/112916903

OK!

  • 13
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学习&实践爱好者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值