- canvas元素本身是没有绘图能力的,所有的绘制工作必须在 JavaScript 内部完成。
var cv = document.getElementById("myCanvas");
var context = cv.getContext("2d"); //申请一个绘制2d动画的环境 - 检测浏览器是否支持:也可以用js脚本检查if(canvas.getContext)
在不支持时可以在canvas标签文本中插入提示不支持信息。复制代码
- 常规绘制例子
-
用canvas绘制直线
(a) context.moveTo(起点x,起点y)
(b) context.lineTo(终点x,终点y):从moveTo提供的起点开始,到lineTo的坐标绘制一条直线;若没有moveTo,则lineTo相当于moveTo。若在一个lineTo后再增设lineTo,即从上一lineTo的坐标开始向下一lineTo坐标延伸直线。
(c) context.stroke():延给定的坐标画一条直线,也可以指定绘制直线的样式,颜色粗细等。 -
用canvas绘制矩形
(a) context.fillStyle = "color":填充颜色
(b) context.fillRect(起点x,起点y,宽,高):矩形起点和大小,或者绘制一个没有填充颜色只有边框的矩形 (a) context.strokeStyle = "color" :边框颜色
(b) context.strokeRect(起点x,起点y,宽,高) :只描边不填充 -
用canvas绘制圆形
(a) context.fillStyle = "color";
(b) context.beginPath():表示开始绘制
(c) context.arc(圆心坐标x,圆心坐标y,半径,开始角度,结束角度,是否为按照给定的开始角度和结束角度的按顺时针方向绘制)
其中:开始角度为0,结束角度为0.5 PI(正下方),1 PI和1.5 PI(正上方),为画饼图提供了扇形范围的依据.第五个参数是弧长Math.PI*2就是整个圆,Math.PI是半圆
(d) context.closePath():表示结束绘制,会将结束点和开始点通过一点直线连在一起, 即将起点和终点封闭起来,有时可以不要
(e) context.fill():表示填充图形,有时可以用context.stroke()代替
例子:
for(var i = 0;i<=15;i++){
context.beginPath();
context.arc(150,150,i*10,0,Math.PI*0.5,true); //在0开始,顺时针Math.PI*0.5结束,即圆正下方结束
context.stroke(); //不填充而只画边框
}
复制代码
for(var i = 0;i<=15;i++){
context.beginPath();
context.arc(150,150,i*10,0,Math.PI*1,true);//在0开始,顺时针Math.PI结束,即圆正下方半圆结束
context.stroke(); //不填充而只画边框
}
复制代码
for(var i = 0;i<=15;i++){
context.beginPath();
context.arc(150,150,i*10,0,Math.PI*1.5,true);//在0开始,顺时针Math.PI*1.5结束,即圆正上方结束
context.stroke(); //不填充而只画边框
}
复制代码
for(var i = 0;i<=15;i++){
context.strokeStyle = "red"; //设置边框颜色
context.beginPath();
context.arc(150,150,i*10,0,Math.PI*2,true);
context.stroke(); //不填充而只画边框
}
复制代码
- 用canvas绘制三角形
- //空心三角形
context.beginPath(); //开始绘制路径
context.moveTo(100,100); //起点坐标设为(100,100)
context.lineTo(150,150); //下一点的坐标设为(150,150)
context.lineTo(50,150); //下一点的坐标设为(50,150)
context.closePath();//连接起点与终点
context.stroke(); //绘画线条
复制代码
- //实心三角形
context.fillStyle = "red"; //设置填充颜色
context.moveTo(150,50); //设置起点为(150,50)
context.lineTo(250,250);//下一点的坐标设为(150,150)
context.lineTo(50,250);//下一点的坐标设为(50,150)
context.fill(); //填充颜色,因为会整个图像填充,所以可以有closePath(),也可以没有
复制代码
- //清除画布[或某部分]
context.fillStyle = "red";
context.moveTo(150,50);
context.lineTo(250,250);
context.lineTo(50,250);
context.closePath();
context.fill();
var clearBtn = document.getElementById("clearCanvas");
clearBtn.onclick=function(){
context.clearRect(0,0,150,300); //清除某一部分画布图案
//context.clearRect(起点x,起点y,画布宽,画布高);若宽高和整个画布大小一样即清除整个画布
};
复制代码
- 绘制二次方贝塞尔曲线(quadraticCurveTo)
context.strokeStyle = "black"; //设置线条颜色
context.beginPath(); //开始绘制
context.moveTo(0,200); //定义开始点,从(0,200)开始绘制
//context.quadraticCurveTo(控制点x,控制点y,结束点x,结束点y),
//控制点为与其与开始坐标和结束坐标连线的两直线的交点 context.quadratciCurveTo(150,100,300,200);
context.stroke(); //绘制线条
context.globalCompositeOperation = "source-over";
//globalCompositeOperation 属性一般用于图形组合,设置如何将一个源(新)图像绘制到目标(已有)的图像上。
//源图像 = 您打算放置到画布上的绘图。目标图像 = 您已经放置在画布上的绘图。
//源图像:↓(二次贝塞尔曲线是根据直线部分画出来的)
context.strokeStyle = "red";
context.beginPath();
context.moveTo(150,100);
context.lineTo(0,200);
context.moveTo(150,100);
context.lineTo(300,200);
context.stroke();
复制代码
- 绘制三次方贝塞尔曲线(bezierCurveTo)
参考理解:https://blog.csdn.net/cdnight/article/details/48468653
context.strokeStyle = "black";
context.beginPath(); //开始绘制
context.moveTo(0,200); //从(0,200)开始绘制
context.bezierCurveTo(25,50,75,50,300,200);
//context.bezierCurveTo(控制点1坐标x,控制点1坐标y,控制点2坐标x,控制点2坐标2,终点坐标x,终点坐标y)
context.bezierCurveTo(25,50,75,50,300,200);
context.stroke();
context.globalCompositeOperation = "source-over";
context.strokeStyle = "red";
context.beginPath();
//控制点1到开始点的直线
context.moveTo(25,50);
context.lineTo(0,200);
//控制点1到控制点2(三次贝塞尔曲线就是根据这条线画出来的)
context.moveTo(25,50);
context.lineTo(75,50);
//控制点2到结束点直线
context.moveTo(75,50);
context.lineTo(300,200);
context.stroke();
复制代码
-
画布的图像可以按照当前状态以堆stack的方式保存下来和按照上一次状态恢复
A. save::用来保存Canvas的状态。save之后,可以调用Canvas的平移、放缩、旋转、错切、裁剪等操作.
B. restore:用来恢复Canvas之前保存的状态。防止save后对Canvas执行的操作对后续的绘制有影响.
注意:save表示保存save函数之前的状态,restore表示获取save保存的状态例子:
//矩形一,填充色为橙色,边框为红色
context.fillStyle = "orange";
context.strokeStyle = "red";
context.fillRect(20,20,100,100);
context.strokeRect(20,20,100,100);
context.fill();
context.stroke();
//保持当前canvas状态
context.save();
//绘制矩形二,填充色为黄色,边框色为粉红色
context.fillStyle="yellow";
context.strokeStyle="pink";
context.fillRect(150,20,100,100);
context.strokeRect(150,20,100,100);
context.fill();
context.stroke();
//恢复上一个矩形的状态来进行绘画
// context.restore();
context.fillRect(20,150,100,100);
context.strokeRect(20,150,100,100);
复制代码
无context.restore()时的画面↓:保存的是最后一次绘画样式
有context.restore()时的画面↓:保存的是context.save()的绘画样式
8. 移动坐标空间,绘制渐变伞效果
画布默认左上角为坐标原点,向右为x轴正向,向下为y轴正向。坐标空间的单位为像素,在绘制图像时,可以使用translate来移动坐标空间,使坐标空间原点移动到指定位置。
例子:
var canvasMethod = {
drawTop:function(context,fillColor){
context.fillStyle = fillColor;
context.strokeStyle = fillColor;
context.beginPath();
//从坐标0,0开始,画一个半径为30像素的正半上方圆
context.arc(0,0,30,0,Math.PI,true);
context.closePath();
context.fill();
context.save();//保存画布当前状态,移动位置,fillStyle,strokeStyle为fillColor
},
drawStick:function(context){
context.fillRect(-1.5,0,1,40);//绘制一个矩形,但这个矩形其实是向左偏移1.5像素,宽为1高为40的一条直线,根据save的样式进行填充
context.beginPath();
context.strokeStyle = "blue";
context.arc(-5,40,4,Math.PI,Math.PI*2,true);//圆心为(-5,40),半径为4,顺从从PI到2PI的方向,画的下半圆,即终点在(-1,40)
context.restore();//采用save中的画布样式,即fillStyle,strokeStyle为fillColor
context.stroke();//根据save的样式进行描边
},
printUmbrella:function(){
var context = document.getElementById("canvas1").getContext("2d");
context.translate(80,80);//第一次移动坐标原点到(80,80)
//绘制十把伞
for (var i = 0; i < 10; i++) {
context.save();//保存画布上一次状态,移动位置,fillStyle,strokeStyle默认为黑
context.translate(60*i,0);//不断改变坐标原点,每次向右移60像素(圆的直径)
this.drawTop(context,"rgb("+(30*i)+","+(255-30*i)+",255)");//绘制半圆并改变填充色彩
this.drawStick(context);//绘制雨伞柄
context.restore();//采用画布save的设置,移动位置,fillStyle,strokeStyle默认为黑
}
}
};
window.onload = function(){
canvasMethod.printUmbrella();
}
复制代码
9. 旋转坐标空间
利用rotate()方法指定一个角度,改变了画布坐标和Web浏览器中的Canvas元素的像素之间的映射,使得任意后续绘图在画布中都显示为旋转的。但它并没有旋转Canvas元素本身。 只有一个参数,为旋转的角度。
接着上一个雨伞例子,改变canvasMethod.printUmbrella()方法。
printUmbrella:function(){
var context = document.getElementById("canvas1").getContext("2d");
context.translate(150,150);//第一次移动坐标原点到(150,150)
//绘制8把伞
for (var i = 0; i < 8; i++) {
context.save();//保存画布上一次的移动状态
context.rotate(Math.PI*0.25*i); //2PI/8=0.25PI,每次旋转0.25PI
context.translate(0,-100);//不断改变坐标原点,每次向上移100像素
this.drawTop(context,"rgb("+(30*i)+","+(255-30*i)+",255)");//绘制半圆并改变填充色彩
this.drawStick(context);//绘制雨伞柄
context.restore();//采用画布save的设置
}
}
复制代码
10. 缩放图形
画布通过scale()去改变canvas上下文对象(context)中横纵方向的像素数目来改变图形的大小。
接收两个参数分别是x轴方向缩放倍数和y轴方向缩放倍数,都必须为正数,若要比原来大则>1,比原来小则<1。
例如,传递一个值 2.0 和 0.5 将会导致绘图路径宽度变为原来的两倍,而高度变为原来的 1/2。
例子:
function scaleCircle(context){
context.translate(250,20);
for (var i = 0; i < 80; i++) {
context.save(); //保存上一次画布的设置
context.translate(30,30);//每次迭代新的原点都移动到上一次原坐标的(30,30)
context.scale(0.95,0.95);//每次迭代横纵因子都缩小到原来的0.9倍
context.rotate(Math.PI/12)//旋转
context.beginPath();//开始进行绘制
// context.fillStyle = "red";
// context.globalAlpha="0.4";//设置颜色透明度
context.fillStyle= "rgba(155, 187, 89, 0.7)";
context.arc(0,0,50,0,Math.PI*2,true);//绘制圆
context.closePath();
context.fill();
}
}
window.onload = function(){
scaleCircle(context);
}
复制代码
11. 矩阵变换
引用了理解网站内容:http://jo2.org/html5-canvas-transform/
canvas上下文对象(context)每次产生一个图像都会创建一个对应的矩阵对象,可直接对canvas变形矩阵作修改,可通过矩阵变换,使图形产生移动、旋转、切片、镜像反射的效果。
那么问题来了:图形都有矩阵,那一个图形的默认矩阵是什么样子的? 答案是:(1,0,0,1,0,0).
①为什么不全为0?其中:一个图形在没有缩放,旋转,位移什么的时候,他也会有一个属性会是1,就是缩放!因为在没有缩放的情况下,图形的缩放其实是原大小的1倍.所以,这个默认矩阵里面才会有两个1. 所以参数位置1上的1,是表示x轴上的缩放,参数位置4上的1是表示y轴上的缩放!
即:缩放可以根据:context.transform(scaleX,0,0,scaleY,0,0);来设置效果。
举一反三:
②根据图形默认矩阵,知矩阵中的最后两位参数就是表示位移距离的数字(在没有位移的情况下为0)。 即:移动可以根据:context.transform(scaleX,0,0,scaleY,transX,transY);来设置效果。
③那么剩下的两个数字(参数位置2,参数位置3)是不是表示旋转呢? 不是,他们是表示斜切。什么是斜切?把一个矩形的任一条边用力一拉,变成平行四边形,这就是斜切。 例子:
未进行斜切之前
ctx.arc(200,50,50,0,Math.PI*2)
.fillRect(200,100,50,50)
.stroke()
复制代码
斜切之后
ctx.transform(1,Math.tan(Math.PI/180*30),0,1,0,0)//矩形X轴产生了斜切效果
.arc(200,50,w/2,0,Math.PI*2)
.fillRect(200,100,50,50)
.stroke()
复制代码
代码中我们可以看到使用了一个tan函数,如果要用斜切,比如想斜切30度,那么就必须用tan把30度包起来,x/y轴都是如此.
可以理解为图形矩阵为ctx.transform(scaleX,skewX,skewY,scaleY,transX,transY);
新坐标x' = (scaleX)x + (skewY)y + transX
新坐标y' = (skewX)x + (scaleY)y + transY
④那怎么用transform实现图形转换呢?
旋转的效果要斜切配合缩放实现的。
比如,其他的都不变,只把图形旋转30度,代码:
var deg = Math.PI/180;
ctx.transform(Math.cos(30*deg),Math.sin(30*deg),-Math.sin(30*deg),Math.cos(30*deg),0,0)
.arc(200,50,50,0,Math.PI*2)
.fillRect(200,100,50,50)
.stroke()
复制代码
其中:
cos(30*deg),
sin(30*deg),
-sin(30*deg),
cos(30*deg)
复制代码
在使用transform方法实现旋转时,旋转N度时,N*deg不变,只要记住对应位置的对应三角函数cos,sin,-sin,cos就行。
原博主传授的记忆方法:CS-SC=初三-上床,不要忘记负号.
注意:canvas也提供setTransform方法直接把矩阵设为你传给他的值,会清空前面所有的transform造成的效果。
一个简单的应用例子:
context.translate(200,20);
for (var i = 0; i < 80; i++) {
context.save();
context.transform(0.95,0,0,0.95,30,30); //缩至0.95倍,移动至(30,30)
context.rotate(Math.PI/12);
context.beginPath();
context.fillStyle = "red";
context.globalAlpha="0.4";//设置颜色透明度
//其中可以利用globalAlpha()为设置透明度,也可以利用fillStyle=rgva()设置透明度
context.arc(0,0,50,0,Math.PI*2,true);
// context.closePath();
context.fill();
}
context.setTransform(1,0,0,1,10,10); //清空之前的transform样式,设置新的图形位置
context.fillStyle = "yellow";
context.fillRect(0,0,50,50);
context.fill();
复制代码
12. 图形组合
默认情况下,两个图形有重叠部分时,另外一个图形会将另一个图形覆盖掉。
可以通过globalCompositeOperation属性设置。
懒得敲之属性展示系列:
-
裁切路径
使用clip(),:一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内(不能访问画布上的其他区域).
或者可以理解为,在使用clip()之后,只看到clip之前信息,而clip之后的只能显示在clip所选定的区域中的图形部分,而其他会被隐藏起来。
参考理解网址:https://www.w3cplus.com/canvas/clip.html -
应用不同的线型
利用lineWidth、lineGap、lineJoin、miterLimit设置不同线形样式。
lineWidth:线粗细,默认为1
lineGap:线端点样式,默认为butt,还有round,square可选
lineJoin:线段连接的样式,默认为round(圆形),还有bevel(斜角), miter(垂直矩形)可选
miterLimit:线段相交交叉点的粗细,越大lineJoin所选的样式越明显 -
绘制线性渐变
一般用到createLinearGradient()创建线性的渐变对象, 再用addColorStop对线性渐变对象分段添加不同颜色达到渐变效果.
例子:
// createLinearGradient(渐变起始点x,渐变起始点y,渐变结束点x,渐变结束点y)
var shadow = context.createLinearGradient(0,0,200,0); //也可以理解为渐变的方向的坐标
shadow.addColorStop(0,'#ff0000');
shadow.addColorStop(1/7,'#ff9900');
shadow.addColorStop(2/7,'#ffff00');
shadow.addColorStop(3/7,'#00ff00');
shadow.addColorStop(4/7,'#00ffff');
shadow.addColorStop(5/7,'#0000ff');
shadow.addColorStop(6/7,'#ff00ff');
shadow.addColorStop(1,"#ff0000");
context.fillStyle =shadow;
context.strokeStyle = shadow;
context.fillRect(10,10,200,200);
复制代码
16. 绘制径向渐变
利用createRadialGradient()创建径向渐变对象, 再用addColorStop对径向渐变对象发射状添加不同颜色达到径向渐变效果.
例子:
// createRadialGradient(渐变开始圆坐标x0.渐变开始圆坐标y0,开始圆半径r0,渐变结束圆坐标x1,渐变结束圆坐标y1,结束圆半径r1)
var shadow = context.createRadialGradient(85,85,3,100,100,100);
shadow.addColorStop(0,"white");
shadow.addColorStop(1,"orange");
context.fillStyle= shadow;
context.fillRect(10,10,200,200);
复制代码
17. 绘制图案
A.context.createPattern(image,"repeat|repeat-x|repeat-y|no-repeat")
方法在指定的方向内重复指定的元素.
和设置图片背景有点类似,先new Image()对象,再将img对象传进createPattern(img,type),再将其赋予给fillStyle,即可达到填充图片的效果。
例子:
var img = new Image(); //创建新的Image对象
img.src = "https://www.easyicon.net/api/resizeApi.php?id=1209623&size=128"; //设置图像路径
img.onload = function(){ //加载图像
//创建图案
var pattern = context.createPattern(img,'repeat');
context.fillStyle = pattern;
context.fillRect(0,0,380,190);
}
复制代码
B.drawImage()方法
①在画布上定位图像:context.drawImage(img,x,y);
②画布上定位图像,并规定图像的宽度和高度:context.drawImage(img,x,y,width,height);
③剪切图像,并在画布上定位被剪切的部分:context.drawImage(img,开始剪切坐标位置x,开始剪切坐标位置y,被剪切图像宽度swidth,被剪切图像高度sheight,图片位置x,图片位置y,显示图片宽度width,显示图片高度height);
var img = new Image(); //创建新的Image对象
img.src = "https://www.easyicon.net/api/resizeApi.php?id=1209623&size=128"; //设置图像路径
img.onload = function(){ //加载图像
//创建图案
context.drawImage(img,0,0);
context.font = "nomal 100px SimHei";
context.shadowOffsetX = 3;
context.shadowOffsetY = 3;
context.shadowBlur = 3;
context.shadowColor = "pink";
context.createPattern(img,'repeat');
context.fillStyle = "white";
context.fillText("wowww",50,165);
}
复制代码
18. 创建阴影
例子:
//设置阴影
context.shadowOffsetX =3; //x轴方向的阴影偏移量,负数为向右偏移量
context.shadowOffsetY =3; //y轴方向的阴影偏移量,负数为向上偏移量
context.shadowBlur = 2;//阴影模糊强度
context.shadowColor = "pink";
//绘制矩形
context.fillStyle = "orange";
context.fillRect(20,20,300,80);
//绘制文本
context.font = "nomal 45px SimHei"; //设置文本字体样式
context.fillStyle = "white"; //设置文本颜色,填充颜色
context.fillText("HTML5/CSS3" ,30,64)//设置文本内容和文本位置
复制代码
注意:在设置了阴影后,文字和矩形都会有阴影。
- 绘制填充/轮廓文字
A.context.fillText(文字内容,位置x,位置y[,最大宽度]),若文字内容长度超过最大宽度,文字会自动被压缩
//绘制填充文本
context.font = "nomal 45px SimHei"; //设置文本字体样式
context.fillStyle = "white"; //设置文本填充颜色
context.fillText("HTML5/CSS3" ,30,64)//设置文本内容和文本位置
复制代码
B.context.strokeText(文字内容,位置x,位置y[,最大宽度]),若文字内容长度超过最大宽度,文字会自动被压缩
//绘制轮廓文本
context.font = "nomal 45px SimHei"; //设置文本字体样式
context.strokeStyle = "white"; //设置文本轮廓颜色
var text = "HTML5/CSS3";
context.strokeText(text, ,30,64)//设置文本内容和文本位置
复制代码
其中:可以通过context.measureText(text)来获取对应text的宽度.
小白第一次在掘金写学习总结,从自己理解的角度记录笔记,可能对某些方法理解存在错误,欢迎指出!持续学习更新中....