Canvas 奇妙历险(一)

2020-06-19 16:26:28 浏览数 (1)

阅读完本篇文章,我期望你对Canvas的基础API有一定的认识,在此基础上,结合自身情况做一些例子去巩固,不是一件蛮开心的事吗?

前期工作

前期工作中,介绍了canvas是什么,能够干什么?以及一些基础API。

canvas是什么?

canvas的中文名叫画布,是HTML5元素的一部分。最早是由苹果公司提出的这么一个概念,后来被应用在我们的网络技术上,结合Javascript脚本编程,我们可以创建动态渲染位图像,位图就是矢量图,即不管你放大缩小,它依旧是能够保持高逼真的姿态。

canvas能够干什么?

时至今日,canvas能做的事情已经很多了。例如做一些炫酷的特效,像什么粒子流,时钟,会动的阿猫阿狗,什么炫酷背景等等;远不止此,canvas在图表数据可视化方面也是非常强大,通过有效地学习我们可以很快地去画出一些像扇形、折现统计图、柱形统计图等;canvas在游戏方面也是应用相当广泛,老掉牙的例子就是贪吃蛇、坦克大战、飞机大战、2048、围棋,它也是可以做3D的游戏开发,这里有兴趣可以了解下three.js;canvas还可以应用在教育行业,做一些演示交互等等,毕竟人家叫画板嘛。

canvas与阿涛啦的关系?

并没有太大关系,起初在大一结束偶读《html程序设计(第2版)》的时候看到过,但那个时候懵懵懂懂,并不能够很好的运用它,也没有过多的去了解它能够干什么,仅仅是跟着课本的流水账一样。这次重新学习canvas的话,第一个是生活需要艺术,学习canvas能够DIY一些小东西出来玩玩,通过作品去表达我们的观点和灵感;第二个是UI(PS、AI)功底不是特别6,期望通过这个来弥补其一些不足,DIY出一些小清新的logo,因为我还是蛮喜欢去设计logo的,我自己的头像就是我亲自设计的,这样的例子我差不多设计了十来款,就这款中意一点;第三个是能力提升,这里指的是程序员处理数据时的输出,就是各种图表呗,将一些数据可视化,去直观地呈现给读者。

如何创建一个canvas元素?
代码语言:javascript复制
<!-- 这里不建议把canvas的宽高写在css里面,因为这样子会导致图片有伸缩失真等问题,建议写在JS脚本里-->
<canvas id="canvas" width="300" height="150">
  您的浏览器不支持canvas元素(此消息在浏览器不支持canvas元素时显示)
</canvas>
canvas常用API

楼下看到的这些,其中canvas简写成cas, CanvasRenderingContext2D对象简写成ctx,这里的知识点难度成梯度递增。

搞canvas对象啦:

代码语言:javascript复制
获取2d对象
let ctx = cas.getContext('2d')

设置宽高(默认是300*150)
cas.width、cas.height

开始绘制
ctx.beginPath()

结束绘制,闭合路径注意啦
ctx.closePath()

绘制文本
ctx.strokeText(txt, x, y)

填充文字
ctx.fillText(txt, x, y)

设置字体

ctx.font

水平对齐
ctx.textAlign = start | end | left |  right | center.

垂直对齐
ctx.textBaseline = top | middle | bottom | hanging | alphabetic | ideographic

绘制图像

ctx.drwaImage( img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight )

画布保存
cas.ToDataURL( type, encoderOptions )

保存状态
cas.save()

恢复已保存状态
cas.restore()

直来直去的线 勾 和 回:

代码语言:javascript复制
设置起点
ctx.moveTo(x, y)

设置点位
ctx.lineTo(x, y)

设置线宽
ctx.lineWidth

设置线末端类型
ctx.lineCap = butt | round | square

设置相交线的拐点
ctx.lineJoin = round | bevel | miter

描边绘制
ctx.stroke()

描边样式
ctx.strokeStyle

填充
ctx.fill()

填充样式
ctx.fillStyle

虚线
ctx.lineDashOffset
ctx.getLineDash()
ctx.setLineDash()

几何图形:

代码语言:javascript复制
画矩形
ctx.strokeRect(x, y, w, h) = ctx.rect(x, y , w, h)   ctx.stroke()

填充矩形
ctx.fillRect()

清楚矩形(这里如果清楚画布的话,用cas.w,cas.h代替, 坐标原点)
ctx.clearRect(...)

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

画相切弧
ctx.arcTo(x1, y1, x2, y2, radius)

搞特效:

代码语言:javascript复制
常用变换
ctx.transform(a, b, c, d, e, f)
a 表示水平缩放.
b 表示水平倾斜.
c 表示垂直倾斜.
d 表示垂直缩放.
e 表示水平移动.
f 表示垂直移动.
ctx.setTransForm()


缩放
ctx.scale(x, y)

平移
ctx.translate(x, y)

旋转
ctx.rotate(radian)

线性渐变
ctx.createLinearGradient(x0, y0, x1, y1)

放射渐变
ctx.createRadialGradient(x0, y0, r0, x1, y1, r1)

阴影
ctx.shadowBlur 属性表示模糊程度.
ctx.shadowColor 属性表示模糊颜色.
ctx.shadowOffsetX 属性表示模糊位置 x 坐标偏移.
ctx.shadowOffsetY 属性表示模糊位置 y 坐标偏移.

开动脑筋,实战见真知

语言总是苍白的,无力的。只有把它转换成生产力才能硬道理,而这个刚开始我认为是靠想,想象。那我们下面就一起来跟随作者实现一下一些意向。

那,刚开始我们去尝试练习下一些线性描点连线,这里的话我想到的是一个房子,它的实现过程如下,其实你只要记住一点,把二维坐标系的点位搞对,图像自然不是什么大问题,我这边没有精确的计算过,用眼睛瞄了下,画的矬别打我,主要就意思下啊。

代码语言:javascript复制
   // 线性
    ctx.beginPath();
    ctx.moveTo(50, 280);
    ctx.lineTo(104, 246);
    ctx.lineTo(104, 220);
    ctx.lineTo(140, 220);
    ctx.lineTo(140, 240);
    ctx.lineTo(180, 192);
    ctx.lineTo(265, 192);
    ctx.lineTo(320, 280);
    ctx.lineTo(285, 280);
    ctx.lineTo(285, 480);
    ctx.lineTo(85, 480);
    ctx.lineTo(85, 280);
    ctx.closePath();
    ctx.stroke();
    ctx.beginPath();
    ctx.moveTo(112, 392);
    ctx.lineTo(112, 352 );
    ctx.lineTo(152, 352);
    ctx.lineTo(152, 392);
    ctx.closePath();
    ctx.stroke();
    ctx.beginPath();
    ctx.moveTo(132, 352);
    ctx.lineTo(132, 392);
    ctx.moveTo(112, 372);
    ctx.lineTo(152, 372);
    ctx.closePath();
    ctx.stroke();
    ctx.beginPath();
    ctx.moveTo(190, 480);
    ctx.lineTo(190, 340);
    ctx.lineTo(250, 340);
    ctx.lineTo(250, 480);
    ctx.closePath();
    ctx.stroke();

然后我们在左上角画个太阳吧,再在门把手那里画一个把手,主要是用到画圆和圆弧的知识,然后我们充分利用下基础API,太阳用圆弧画实心,然后门把手用圆画空心。

这里在画太阳的时候,如果你是用fill()或者closePath()要注意了,它是默认是不闭合和,所以要凑一个原点。

具体的实现如下:

代码语言:javascript复制
   ctx.beginPath();
    ctx.fillStyle = "red";
    ctx.arc(0, 0, 100, 0, Math.PI / 2);
    ctx.lineTo (0, 0);
    ctx.closePath();
    ctx.fill();
    ctx.beginPath();
    ctx.arc(240, 390, 2, 0, Math.PI * 2);
    ctx.stroke();

后面的话我们好像还有矩形的API没用,所以就用它来修饰下房顶。

具体的实现如下:

代码语言:javascript复制
ctx.strokeRect(195,172, 60, 20);
ctx.strokeRect(205, 162, 40, 10);
ctx.strokeRect(215, 152, 20, 8);
ctx.fillStyle = "gray";
ctx.fillRect(222, 130, 5, 20);

这里的话,忘记截图了,但是保存了一张在桌面,可以看到它是一张画出来的图片哈。

小学的时候,这样画,及格应该是有了就纯粹应付下作业是够了,但是我们是有追求的人,“智慧树上智慧果,智慧树下你和我。”,抱歉目前暂时智慧树上没用智慧果。

这里我们通过photoShop来裁剪出卡帕的两个人,然后通过drawImage画上去。

具体的实现如下:

代码语言:javascript复制
 ctx.fillStyle = "#8B4726";
ctx.fillRect(500, 280, 40, 200);
ctx.fillStyle = "green";
ctx.arc(520, 240, 100, 0, Math.PI * 2);
ctx.fill();
let img1 = new Image();
img1.src = "./assert/img/kapa1.png";
let img2 = new Image();
img2.src = "./assert/img/kapa2.png";
img1.onload = () => {
    ctx.drawImage(img1, 400, 380, 100, 100);
};
img2.onload = () => {
    ctx.drawImage(img2, 540, 380, 100, 100);
};

我是不会画一群鸟在剩下的地方飞的,画需要留白的艺术,最后的话,美术老师会让我们在画上写上名字。

用到了

代码语言:javascript复制
ctx.font = "italic small-caps  12px arial";
ctx.strokeText("六(3)班, 郑江涛", 660, 30);

好了,交作业去了,转眼,时光一晃,已经十年了,毕业快乐,哈哈。

问题汇总

问题的发现,很多时候就是好奇,为啥就一定是这样的。

问题一: ctx.moveTo(x, y)是设置绘画的起点,那要是后面不用lineTo(x, y),还是用moveTo(x, y),它是会描出很多点?

答: 描不出来点的,这里的话你期望是描很多个点,但是让它们啊,不要连线。两种做法,第一种用线画,就是说你把它围成一个小小的矩形然后给它填实心可以近似看出一个点,不要耍小聪明,把moveTo(x, y)和lineTo(x, y)坐标设成一样,也是画不出来的,但要是你是围成的那又是可以的。第二个就是用几何图形如圆、矩形。

问题二: 如果我用lineTo(x, y)超过了canvas原有的宽高,它能不能画?

答: 很显然可以,就像你画画, 画出边界么,最后轨迹停留的地方就是和边界相交的地方。

问题三: 在考虑线性的基础上,画一个闭合的矩形,moveTo和lineTo的总和个数最少需要几个,最笨又需要几个?

答: 最少需要4个点,最笨是5个点。一个正方形好了,有四个顶点,那你至少是设置一个顶点是moveTo(x, y), 剩下的三个依据顺时针或者逆时针的顺序,去构成一个正方形,但是当你用stroke画的时候,它默认是不闭合的,就是说你还需要补一刀在它的起点处,终点亦是起点,所以需要五个。在stoke的前面加一个closePath()的话,那就是4个点,它会自动帮你闭合的。

问题四: 这个楼上说的closePath(), 那它开始需要beginPath()吗?

答: 为啥会有这样的想法,看起来对称舒服呗,但实际上你用closePath()闭合的时候可以没有beginPath(),那它们的应用场景是什么?我认为是结界,不一定对啊。

问题五: canvas绘制出来的是什么?

答: 是一张图片哦

这里的话就抛砖引玉讲到这里,canvas的学习还需要大家开动脑筋,例如根据数据生成相应的统计图,这个地方的话为啥不介绍呢,因为这个像echart库之类的,已经很成熟了,你搞不过的,直接拿来用好了。有兴趣的话,可以试试去画一些品牌车的logo,或者一些方向性的标志对于初学者都是挺好的。

0 人点赞