要创建一个canvas,其实只要在HTML中添加标签:<canvas></canvas>
就行了。
若是考虑浏览器的兼容问题,只需在标签中加上一行文字:
即可达到「 友好提示用户 」的效果。
如果要“画东西”,也就是所谓的“操纵canvas元素”,则必要借助于DOM事件:
其中“上下文对象”你可以理解为是“绘图的环境(参数)”,而“2d”代表“要绘制2d(平面)图形” —— 同样的,“3d”表示“绘制3d(立体)图形”。
canvas坑一:
若要改变canvas“画布”的大小,最好在canvas标签的style属性中或在JS中动态进行。若通过class/id-style中修改(执行),会造成“整体缩放”——包括后面说到的lineTo线条也是这样。
JS操控canvas
上面说了,通过JS操作canvas元素前要先
- 得到元素
- 获取环境(参数)
下面的一些demo就省去这两行代码了。。。
首先,你要画比如一条线/一个图的话,假如canvas现在是你手中的一杆画笔,那你就要先“提笔” —— 从哪开始画:
moveTo的两个参数分别是:起始坐标X、Y。
然后
若是画直线,可以调用lineTo - API:
然后其实可以继续lineTo()…
这时,这些线条还只是在【内存】中,要其真正显示到页面上,则需调用stroke():
ctx坑二:
若要再画一条线,不少初学者可能会将以上“绘制”部分代码再CV一遍,改变坐标即“大功告成”,但是真是这样的吗?
经检验,第一条线要“深色”一些:这是因为整段代码有两个stroke() —— 即第一条线被“画”了两次。
其实若要画多个图形,只要先把moveTo、lineTo…全部完成,再最后“一次性”调用 ctx.stroke();
即可。
但这样一来又有一个问题:若是就想让两条线颜色不一样怎么办?
canvas API提供了下面的函数:
只要按原来的方法绘制时把这个函数加到“第二条绘制线”的moveTo函数前即可。
canvas画图形
上面是画线,那么诸如圆、长方形…这些图形怎么画?
圆形:ctx.arc(参数1,参2,参3,起始弧度,终止弧度,顺时针/逆时针画);
- 参1:圆心坐标X
- 参2:圆心坐标Y
- 参3:半径r
- 最后一个参数:true代表“逆时针”,false为“顺时针”
矩形:ctx.strokeRect(左上角X,左上角Y,宽,高);
(因为是strokeRect这种stroke前缀的函数其实都是经过stroke()封装过的,所以可以直接显示在屏幕上)
其实,矩形还有两种画法:
- 通过lineTo画四条线,最后一条的终点坐标和第一条的起点坐标相同
- 通过lineTo画三条线,最后通过“闭合函数”:
ctx.closePath()
将图形路径闭合
曾经看到好多“图片+JS雪花掉落效果”的文章,其实这个用canvas也可以轻易实现:
JS填充canvas与canvas描边
上面的图形画着是挺爽的,就是颜色未免太单一了一些:
我们可以通过 ctx.fill()
填充函数填充整个图形(内部)为“黑色”(默认颜色)(视觉上看“自动闭合了路径”)emmmmmmm,这样全部都变成黑色了,不过我可以在前面用 ctx.fillStyle="颜色值"
来改变整个图形的颜色 —— 这之后fill()就只剩“填充”的功能了。
若是要只改变图形边框的颜色呢?其实上面说的一个函数本身就有“描边”的功能:ctx.stroke()
。
你当然可以理解为“只有为图形边框添加了颜色,才能显示出来”:毫无疑问,它默认也是黑色的。不过我们也可以用API:ctx.strokeStyle="颜色值"
来改变图形边框的颜色。
我们还可以通过 ctx.lineWidth=数字值;
来改变边框的宽度。
canvas中允许“边框设置”和“填充设置”同时出现。
canvas中还有一个比较重要的地方是:保存和恢复绘制状态
需要用到两个“上下文”函数:context.save();
和 context.restore();
他们是基于【栈】的:你可以多次save,但是restore时也只能一次一次的“恢复到上一次的状态”!
他们就像是“合并图形”一样,可以让“save()前的”、“save()后的”、“restore()后的”绘制共存于一个canvas中!通常我们会这么使用:制造一个“合理的”canvas环境,然后save保存,然后绘制一个图形,再restore恢复save前的状态(切记:此时save后绘制的图形并不会消失!这就是“合并”),然后再去绘制下一个图形。
canvas使用渐变色
canvas可以“分层”设置不同线性渐变色:
JS图形变换(平移、旋转、缩放)
如上面“画线”所说:
平移:ctx.translate(X方向,Y方向);
大家可以试下分别将translate函数放在moveTo前、lineTo前、最后,分别有什么效果!
旋转:ctx.rotate(旋转角度);
—— 以弧度为单位:
缩放:ctx.scale(X轴缩放,Y轴缩放);
图形变换的效果也会“自上而下叠加”!
如果觉得叠加效果并不是想要的,可以将某个变换片段代码放到 ctx.save()
【保存环境函数】和 ctx.restore()
【恢复环境函数】(恢复到save()函数之前的环境)的“包裹”中。
canvas文字
我们怎么把展示到canvas画布中呢?
当然,这里的坐标位置你也可以“自定义”,比如:在整个canvas中水平居中,距离画布底部30的位置?
context.fillText(mxcIdText,originWidth/2,originHeight-30);
填充:
能不能改变他的样式?
(开头说了一句“canvas里能直接改变元素CSS样式值”不知道大家还记得不记得)
并且加上strokeText函数:
也可以改变文字的位置,如:
既然能控制大小和位置,肯定也能获取一些自身信息:
这样,我们就能做一些有意义的事,比如文本X/Y坐标是多少时设置Align才能让水平居中、比如根据一个元素的width(文字字数)控制另一个元素的位置。。。这些都在末尾demo中有用到。
很遗憾的是,canvas并不支持获取文本的“高度”
2020-08-30更新
但是经过计算,对于大多数字型来说,将字母“M”的宽度再稍微增加一点(一般是1/6),就可以得出几乎近似的文本高度了:
在canvas中展示图片
canvas作为一个“特殊的结构”,其图片的展示方式肯定也不同寻常:
然后我们“自信满满地”打开图片,发现…没有东西!!!
这是因为Image()的加载需要一定时间,而我们直接插入了src:切记!一定要在load中完成img的“展示”。(和H5的File()加载展示一样的道理)
这样完成是完成了,但图片样式是固定的,不好看啊。
没关系,我们有“第二种展示方法”,可以将其进行缩放:
然后我们又发现缩放后图片中图标是挺好看的,但文字太模糊了,难受的一批,于是我们想:怎么把图标单独显示出来呢?
没关系,我们还有“第三种展示方法”:
其实“图片”的展示还有一种方式 —— 图形画刷 ,它可以将图片为背景填充展示到canvas区域:
其中,“模式”和CSS中【背景图片】的展示也很类似:
- no-repeat
- repeat
- repeat-x
- repeat-y
你可能想不到:最后生成的canvas是可以直接“图片另存为”/“复制”的 —— canvas确实是image的一种“变异形式”?
不行,这用着不放心啊,还是图片巴适些:
我们常用这个API来将canvas转为png图片格式 —— 大概是因为png好操作吧。
它返回的是一个base64的链接 —— 可直接装到img的src上!
其实这个API还有第二个参数,不太常用:
encoderOptions(可选) 图片质量,取值范围为 0 到 1 。
如果指定图片格式为 image/jpeg 或 image/webp,而且超出取值范围,将会使用默认值 0.92,其他参数会被忽略。
canvas阴影设置
- X轴偏移:
ctx.shadowOffsetX=数字值;
(数字值相对于图形) - Y轴偏移:
ctx.shadowOffsetY=数字值;
(数字值相对与图形) - 阴影颜色:
ctx.shadowColor='颜色值如:rgba(0,0,0,0.2)';
- 模糊半径:
ctx.shadowBlur=数字值;
阴影会作用于其下所有设置的canvas上(文本、图形、图片…)
离屏canvas
这个在大加载量、频繁JS动画 and DOM重绘量巨大的场景下应用极广。它基于这样的原理:把涉及大量DOM重绘、频繁加载的元素(比如小球运动场景下的背景格的样式改变)单独拿出来放在某位置地方(display:none;
)自己加载,在触发操作后,通过:
将元素的“样子”刻画到主要显示的canvas上!
要记得“适当地”擦干画布:
canvas元素上下文对象.clearRect()
2020-12-19更新
发现其实离屏(双重)技术还有一种更为便捷的使用方式:
但是使用场景并不常见,猜测可能是和 save 和 restore 函数冲突有关吧。
canvas名片生成程序代码
代码较多,已打包到百度网盘,可直接免费下载:
链接 | 提取码 |
xqwk |