欢迎光临
我们一直在努力

30分钟带你入门 Canvas(笔触、填充、圆弧、动画、使用图片)

之前我们学习了Canvas概述和Hello World的例子,今天就让我们一起来进入Canvas大门吧。

笔触和填充

Canvas中能够产生颜色的是两个东西,一个叫做笔触(我们通常叫描边),一个叫做填充。

1、快速绘制矩形

canvas提供了三种方法绘制矩形:

fillRect(x, y, width, height) 绘制一个填充的矩形

strokeRect(x, y, width, height) 绘制一个矩形的边框

clearRect(x, y, width, height) 清除指定矩形区域,让清除部分完全透明。

上面提供的方法之中每一个都包含了相同的参数。x与y指定了在canvas画布上所绘制的矩形的左上角(相对于原点)的坐标。widthheight设置矩形的尺寸。

stroke

<script>
    // 使用DOM方法得到画布
    var myCanvas = document.querySelector("myCanvas");
    // 使用画布的上下文
    var ctx = myCanvas.getContext("2d");
    // 笔触
    ctx.strokeRect(50, 50, 300, 50);
</script>

对比效果:

strokeRect

设置笔触的颜色:

ctx.strokeStyle = "red"; // 设置笔触颜色
ctx.strokeRect(50, 50, 300, 50); // 画一个边框

strokeStyle

到现在我们只能画一个矩形,如果我们想要画任意笔触呢?

2、线

canvas中可以允许我们绘制自定义的笔触,首先我们先来看看怎么绘制直线:

画线

ctx.beginPath(); // 准备开始一个路径
ctx.moveTo(100, 100); // 将画笔移动到一个位置
ctx.lineTo(300, 300); // 用画笔画,此时是抽象的一个线,没有显示在画布上
ctx.stroke(); // 划线(这时画布上才会显示我们画的线)

也可以多次使用lineTo:

画线

绘制的线,也可以用fill填充,并且可以用strock来描边。需要注意的是,形状如果不封闭就fill了,此时形状会自动封闭。也可以用ctx.lineWidth = "5" 来设置笔画的宽度。如下:

弧和圆形

绘制圆弧或者圆,我们使用arc()方法。

公式:

ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise)

画一个以(x,y)为圆心的以radius为半径的圆弧(圆),从startAngle开始到endAngle结束,按照anticlockwise给定的方向(默认为顺时针)来生成。

一定要注意,canvas中角度是弧度制。复习一下弧度制,就是在一个圆形披萨饼中,切一片披萨,让弧度边长等于圆的半径。此时无论圆形多大,此时切下来的角度是固定的,都是57.29度,成为1弧度,记做1rad。

1弧度

角度值和弧度制的换算:360° = 2 * PI rad

例:画1弧度的圆弧

// 使用DOM方法得到画布
var myCanvas1 = document.querySelector("#myCanvas1");
// 使用画布的上下文
var ctx = myCanvas1.getContext("2d");

// 弧也是笔触
ctx.beginPath();
ctx.arc(200, 200, 100, 0, 1, true); // 设置路径
ctx.stroke(); // 画圆弧

画圆弧

例:绘制一个完整的圆形

ctx.arc(200, 200, 100, 0, 2 * Math.PI, false);

圆形

完整的圆形,无所谓顺时针还是逆时针。

Canvas的动画

要让元素在canvas上运动起来,需要引入时间的概念,即需要使用setInterval()

canvas使用了一个特别特殊的模式:上屏的元素,立刻被像素化。也就是说,上屏幕的元素,你将得不到这个“对象”的引用。比如,一个圆形画到了ctx上面,此时就是一堆像素点,不是一个整体的对象了,你没有任何变量能够得到这个圆形,改变这个圆形的x、y。也就是说,这种“改变”的思路在canvas中是行不通的。

要实现动画,我们必须:每帧重新画一个

所以canvas的画图的原理就是:

清屏 → 重绘 → 清屏 → 重绘 → 清屏 → 重绘 → 清屏 → 重绘 → …… 的一个过程

例:向右移动的圆形

// 使用DOM方法得到画布
var myCanvas1 = document.querySelector("#myCanvas1");
// 使用画布的上下文
var ctx = myCanvas1.getContext("2d");

var x = 100;
setInterval(function() {
    if (x > 500) x = 100;
    x++;
    // 清屏
    ctx.clearRect(0, 0, 600, 400);
    // 绘制一个新圆形
    ctx.beginPath();
    ctx.arc(x, 100, 50, 0, Math.PI * 2, true);
    ctx.closePath();
    ctx.fillStyle = "orange";
    ctx.fill();
}, 20);

注意API:ctx.clearRect(x, y, w, h); 清除一个矩形区域,事实上,我们永远清除整个画布。

上升到面向对象

再来一个圆形向右移动,注意不管页面上有几个元素在运动,一定只有1个定时器。

定时器负责每帧清屏一次,然后重绘所有元素。但是每一个元素的信息、状态都不同,这时我们难以维护。

所以,我们不要在用全局变量来维护某一个小球的x、y、r、speed等信息,而是应该用对象来封装它。

// 使用DOM方法得到画布
var myCanvas1 = document.querySelector("#myCanvas1");
// 使用画布的上下文
var ctx = myCanvas1.getContext("2d");

// 构造函数,圆类
function Ball(x, y, r, speed, color) {
    this.x = x; // 坐标x
    this.y = y; // 坐标y
    this.r = r; // 半径
    this.speed = speed; // 速度
    this.color = color; // 颜色
    // 将自己推入数组
    actorsArr.push(this);
}
// 渲染方法
Ball.prototype.render = function() {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2, false);
    ctx.closePath();
    ctx.fillStyle = this.color;
    ctx.fill();
}
// 更新方法(移动)
Ball.prototype.update = function() {
    if (this.x > 500) this.x = 70;
    this.x += this.speed;
}

// 演员数组
var actorsArr = [];
// 实例化一些小球,他们都自动可以动画
new Ball(70, 70, 30, 1, "red");
new Ball(70, 150, 40, 2, "orange");
new Ball(70, 250, 50, 3, "blue");
new Ball(70, 370, 60, 4, "green");

// 帧编号
var fno = 0;
// 唯一的一个定时器
setInterval(function() {
    // 清屏
    ctx.clearRect(0, 0, myCanvas1.width, myCanvas1.height);
    // 打印帧编号
    fno++;
    ctx.fillStyle = "black";
    ctx.fillText("帧编号:" + fno, 20, 20)
    // 更新、渲染所有演员
    for (var i = 0; i < actorsArr.length; i++) {
        actorsArr[i].update();
        actorsArr[i].render();
    }
}, 20);

让所有的演员(就是上画布的元素),都要进入数组。在定时器中,每一帧更新所有演员,渲染所有演员。

canvas的动画_上升到面向对象

使用图片

canvas中不可能所有的形状都自己画,一定是设计师给我们素材,我们使用。

canvas中使用图片,需使用drawImage函数,但是有一个事儿必须注意,我们必须等img完全加载之后才能呈递图片。

标准套路如下:

// 创建一个img对象的节点
var image = new Image();
image.src = "./img/cat_200.jpg";
// 一定要等到这个图片loading之后,然后渲染它
image.onload = function() {
    ctx.drawImage(image, 30, 30);
}

image.onload这个壳子不能省略。

(1)、上面的例子,我们仅仅使用两个数字参数,表示图片的x、y:

ctx.drawImage(图片对象, dx, dy);

使用图片

图片会1:1的呈递。

(2)、可以使用4个数字参数,此时可以设置图片的高度、宽度,如下:

ctx.drawImage(image, 30, 30, 200, 140);

使用图片

(3)、实际上我们还可以使用类似CSS中精灵的切片,比如,我们现在想在canvas上面呈递切片中的位置:

使用图片

将这个切片,放置在画布的100,100,显示的宽度、高度是切片2倍,如下:

ctx.drawImage(image, 299, 94, 93, 76, 100, 100, 93 * 2, 76 * 2);

说明:

使用图片_切片_说明

来看看MDN上面的解释:链接

drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)

第一个参数和其它的是相同的,都是一个图像或者另一个 canvas 的引用。其它8个参数,前4个是定义图像源的切片位置和大小,后4个则是定义切片的目标显示位置和大小。

使用图片_切片_说明

实例:使用精灵制作小女孩的行走

实现代码:

var image = new Image();
image.src = "./img/girl.png";

var step = 0;
var x = 50;
image.onload = function() {
    setInterval(function() {
        //清屏
        ctx.clearRect(0, 0, 800, 400);
        step++;
        if (step > 7) {
            step = 0;
        }
        if (x > 700) x = 50;
        x += 4;
        ctx.drawImage(image, 79 * step, 108 * 2, 79, 108, x, 100, 79, 108)
    }, 40);
}

到此,我们已经学会了Canvas的基本功能和API,更多功能及API请查看MDN的文档 链接

赞(1) 打赏
未经允许不得转载:前端学习分享网 » 30分钟带你入门 Canvas(笔触、填充、圆弧、动画、使用图片)

评论 抢沙发

评论前必须登录!

 

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏