# canvas
<canvas> 可以用于 绘制图形、照片、动画,甚至进行实时视频处理或渲染。
# 在网页上画一个圆
图片
border+border-radiussvg
Canvas + JS动态画圆
clip-path 分析一下以上几种方式的优劣性:
使用 div + CSS3 适用于单个的圆,实现简单,代价小,但增加没有意义DOM 节点,不符语义化。
#k{
width:500px;
height:500px;
background: blue;
clip-path: circle(50%);
}
# 什么是 svg
svg(Scalable Vector Graphics,可缩放矢量图形)是基于 XML(可扩展标记语言,标准通用标记语言的子集),用于描述二维矢量图形的一种图形格式。
简单的说就是,svg 可以用来定义 XML 格式的矢量图形。因为其本质是 XML 文件,所以 svg 是使用 XML 文档描述来绘图的。
# Canvas 和 svg 的区别
svg 本质上是一种使用 XML 描述 2D 图形的语言。
svg 创建的每一个元素都是一个独立的 DOM 元素,既然是独立的 DOM 元素,那么可以通过 css 和 JS来操控 dom。可以对每一个 DOM 元素进行监听。
并且因为每一个元素都是一个 DOM 元素,所以修改 svg 中的 DOM 元素,系统会自动进行 DOM 重绘。
Canvas 通过 JavaScript 来绘制 2D 图形,Canvas 只是一个 HTML 元素,其中的图形不会单独创建 DOM 元素。因此不能通过 JS 操控 Canvas 内单独的图形,不能对其中的具体图形进行监控。
在 Canvas 中,一旦图形被绘制完成,就不会继续得到浏览器关注。如果位置发生变化,那么整个场景也需要重绘,包括任何或许已被图形覆盖的对象。
# 创建 Canvas 画布
如果没有设置宽高,那么会自动创建一个 300 * 150 的画布。
改变画布的大小呢,有三种方式
- HTML 设置
width、height; - CSS 设置
width、height; - JS 动态设置
width、height。
# HTML 属性设置 width、height
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
#canvas {
background: rgb(220,50,100)
}
</style>
</head>
<body>
<canvas id="canvas" width="400" height="400">
</canvas>
<script>
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
context.beginPath();
context.arc(150, 150, 50, 0, Math.PI * 2, true);
context.closePath();
context.fillStyle = 'rgb(255,255,255)';
context.fill();
</script>
</body>
</html>
Canvas 画布的宽度为 400,高度为 400,背景颜色为红色。在 Canvas 上画了一个圆心坐标为 150px、150px,半径为 50px 的白色的圆。
# CSS 属性设置 width、height
#canvas {
background: rgb(220,50,100) ;
width: 400px;
height: 400px;
}
如果使用 CSS 来设置宽高,画布就会按照 300 * 150 的比例进行缩放,也就是将 300 * 150 的页面显示在 400 * 400 的容器中。
# JS 属性设置 width、height
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var cx = canvas.width = 400;
var cy = canvas.height = 400;
.......
图片正常canvas.width canvas.height不需要style.width
所以尽量使用 HTML 的width 和 height 属性或者直接使用 JS 动态来设置宽高,不要使用 CSS 设置。
# 获取 Canvas 对象
创建好 Canvas 画布,第二步要做的就是获取到 Canvas 的上下文环境:
canvas.getContext(contextType, contextAttributes);
- 上下文类型(contextType):
- 2d:代表一个二维渲染上下文
- webgl(或"experimental-webgl"):代表一个三维渲染上下文
- webgl2(或"experimental-webgl2"):代表一个三维渲染上下文;这种情况只能在浏览器中实现 WebGL 版本2。
第二个参数并不是经常用到~
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
# 绘制路径
| 方法 | 描述 |
|---|---|
fill() | 填充路径 |
stroke() | 描边 |
arc() | 创建圆弧 |
rect() | 创建矩形 |
fillRect() | 绘制矩形路径区域 |
strokeRect() | 绘制矩形路径描边 |
clearRect() | 在给定的矩形内清除指定的像素 |
arcTo() | 创建两切线之间的弧/曲线 |
beginPath() | 起始一条路径,或重置当前路径( 清除之前路径,防止改变之前绘图出来的内容被干扰 ) |
moveTo() | 把路径移动到画布中的指定点,不创建线条 |
lineTo() | 添加一个新点,然后在画布中创建从该点到最后指定点的线条 |
closePath() | 创建从当前点回到起始点的路径, 闭合当前的路径 |
clip() | 从原始画布剪切任意形状和尺寸的区域 |
quadraticCurveTo() | 创建二次方贝塞尔曲线 |
bezierCurveTo() | 创建三次方贝塞尔曲线 |
isPointInPath() | 如果指定的点位于当前路径中,则返回 true,否则false |
# 使用 Canvas 画一个点
context.beginPath(); // 起始一条路径,或重置当前路径
context.arc(100, 100, 1, 0, Math.PI * 2, true); // 创建弧/曲线
context.closePath(); // 创建从当前点回到起始点的路径
context.fillStyle = 'rgb(255,255,255)'; // 设置或返回用于填充绘画的颜色、渐变或模式
context.fill(); // 填充当前绘图(路径)
使用 Canvas 绘制图像的步骤:
# 绘制弧/曲线 arc
arc() 方法创建弧/曲线(用于创建圆或部分圆)。
context.arc(x,y,r,sAngle,eAngle,counterclockwise);
- x:圆心 x 坐标
- y:圆心 y 坐标
- r:圆半径
- sAngle:起始角,以弧度计( 弧的圆形的三点钟位置是 0 度 )
- eAngle:结束角,以弧度计
- counterclockwise:可选。规定应该逆时针还是顺时针绘图。false 为顺时针,true 为逆时针
比如想画一个顺时针的四分之一圆
context.arc(100, 100, 50, 0, Math.PI * 0.5, false);
# arcTo
在画布上创建介于两个切线之间的弧
arcTo(x1,y1,x2,y2,radius)
# bezierCurveTo 三次贝塞尔曲线
bezierCurveTo(c1x,c1y,c2x,c2y,x,y)
- 开始点:moveTo(20,20)
- 控制点 1:bezierCurveTo(20,100,200,100,200,20)
- 控制点 2:bezierCurveTo(20,100,200,100,200,20)
- 结束点:bezierCurveTo(20,100,200,100,200,20)
# quadraticCurveTo 二次贝塞尔曲线
context.quadraticCurveTo(cpx,cpy,x,y);
通过使用表示二次贝塞尔曲线的指定控制点,向当前路径添加一个点
提示:二次贝塞尔曲线需要两个点。第一个点是用于二次贝塞尔计算中的控制点,第二个点是曲线的结束点。曲线的开始点是当前路径中最后一个点。如果路径不存在,那么请使用 beginPath() 和 moveTo() 方法来定义开始点。
- 开始点:moveTo(20,20)
- 控制点:quadraticCurveTo(20,100,200,20)
- 结束点:quadraticCurveTo(20,100,200,20)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title></title>
<style type="text/css">
*{
margin: 0;
padding:0
}
#k{
background: skyblue;
}
</style>
</head>
<body>
<canvas id="k" width="400" height="400"></canvas>
</body>
<script type="text/javascript">
var c=document.getElementById("k");
var ctx=c.getContext("2d");
ctx.beginPath();
ctx.moveTo(20,20); // 创建开始点
ctx.lineTo(100,20); // 创建水平线
ctx.arcTo(150,20,150,70,20); // 创建弧
ctx.strokeStyle="blue"
ctx.lineTo(150,120); // 创建垂直线
ctx.stroke(); // 进行绘制
ctx.beginPath();
ctx.moveTo(20,20);
ctx.bezierCurveTo(20,100,200,200,200,20);
ctx.strokeStyle="green"
ctx.stroke();
var ctx=c.getContext("2d");
ctx.beginPath();
ctx.moveTo(20,20);
ctx.quadraticCurveTo(20,100,200,20);
ctx.strokeStyle="orange"
ctx.stroke();
</script>
</html>
# stroke() fill()
stroke() 和 fill() 的区别,根据上面的两个例子,我们很容易看出这两个函数的区别:一个是描边,一个是填充。
stroke():描边fill():填充
我们可以通过 strokeStyle属性 和 fillStyle属性来设置描边和填充的颜色。这里不仅可以设置单一的颜色,还可以设置渐变。
# 绘制直线moveTo lineTo
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var cx = canvas.width = 400;
var cy = canvas.height = 400;
context.beginPath();
context.moveTo(50,50);
context.lineTo(100,100);
context.strokeStyle = '#fff';
context.stroke();
在绘制直线的例子中,我们使用了
moveTo(x,y):把路径移动到画布中的指定点,不创建线条lineTo(x,y):添加一个新点,然后在画布中创建从该点到最后指定点的线条
这里需要注意以下几点:
- 如果没有 moveTo,那么第一次 lineTo 的就视为 moveTo
- 每次 lineTo 后如果没有 moveTo,那么下次 lineTo 的开始点为前一次 lineTo 的结束点。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
#canvas {
background: rgb(220,50,100) ;
}
</style>
</head>
<body>
<canvas id="canvas">
</canvas>
<script>
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var cx = canvas.width = 400;
var cy = canvas.height = 400;
context.beginPath();
context.lineTo(200, 200);
context.lineTo(200, 100);
context.moveTo(300, 100);
context.lineTo(100,50);
context.strokeStyle = '#fff';
context.stroke();
</script>
</body>
</html>
| 样式 | 描述 |
|---|---|
lineCap | 设置或返回线条的结束端点样式 |
lineJoin | 设置或返回两条线相交时,所创建的拐角类型 |
lineWidth | 设置或返回当前的线条宽度 |
miterLimit | 设置或返回最大斜接长度 |
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var cx = canvas.width = 400;
var cy = canvas.height = 400;
context.beginPath();
context.moveTo(10,10);//起点
context.lineTo(100,100);//重点
context.lineWidth = 10;//线条宽度
context.lineCap = 'round';//顶端弧形
context.strokeStyle = '#fff';//描边白色
context.stroke()
总结
- closePath 闭合路径,如果不调用,路径就不会起点到终点间加上连接线(如果在style之后调用,也不会闭合路径了)
- 同一个canvas上样式要单独设,否则样式合并,可能会后者覆盖前者
- lineWidth=10不需要px
- closePath最好放在style样式之前,否则闭环不生效。
# 绘制矩形 rect fillRect strokeRect clearRect
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var cx = canvas.width = 400;
var cy = canvas.height = 400;
context.beginPath();
context.fillStyle = '#fff';
context.fillRect(10, 10, 100, 100);
context.strokeStyle = '#fff';
context.strokeRect(130, 10, 100, 100);
fillRect(x,y,width,height):绘制一个实心矩形strokeRect(x,y,width,height):绘制一个空心矩形- rect:绘制矩形
clearRect(x,y,width,height):擦除一个区域
通过 fillStyle 和 strokeStyle 来设置填充的颜色和描边的颜色。
# 绘制文本fillText() strokeText()
二者都需要的传参
(要传输的字符串,x坐标,y坐标[,最大像素宽度])
context.font="blod 14px" //设置css样式
context.textAlign="center" //文本对齐方式:start end center [left, right](优先使用start/end)
context.textBaseline="middle"//文本基线:top hanging middle alphaabetic bottom...
context.strokeStyle="cadetblue"
context.strokeText("12",100,20)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title></title>
<style type="text/css">
*{
margin: 0;
padding:0
}
#k{
background: skyblue;
}
</style>
</head>
<body>
<canvas id="k" width="400" height="400"></canvas>
</body>
<script type="text/javascript">
let drawing = document.getElementById("k");
if (drawing.getContext) {
let context = drawing.getContext("2d");
context.beginPath();
context.strokeStyle="green"
context.arc(100, 100, 99, 0, 2 * Math.PI, false);
context.stroke();
context.beginPath()
context.arc(100, 100, 94, 0, 2 * Math.PI, false);
context.strokeStyle="orange"
context.moveTo(100, 100);
context.lineTo(100, 15);
context.moveTo(100, 100);
context.lineTo(35, 100);
context.stroke();
context.beginPath()
context.font="blod 14px"
context.textAlign="center"
context.textBaseline="middle"
context.strokeStyle="cadetblue"
context.strokeText("12",100,20)
}
</script>
</html>
# measureText
measureText() 方法返回包含一个对象,该对象包含以像素计的指定字体宽度。
context.measureText(text).width;
let drawing = document.getElementById("k");
if (drawing.getContext) {
let context = drawing.getContext("2d");
context.beginPath();
let fontSize = 100;
context.font = fontSize + "px Arial";
while(context.measureText("Hello world!").width > 200) {
fontSize--;
console.log(fontSize)
context.font = fontSize + "px Arial";
}
context.textBaseline="top"
context.fillText("Hello world!", 10, 10);
context.fillText("Font size is " + fontSize + "px", 10, 50);
}
# 颜色、样式和阴影
上面几个函数教大家怎么绘制点、线、以及圆形和矩形,都是通过先创建路径,然后再使用 fill() 或 stroke() 进行填充或者描边。
| 属性 | 描述 |
|---|---|
fillStyle | 设置或返回用于填充绘画的颜色、渐变或模式 |
strokeStyle | 设置或返回用于笔触的颜色、渐变或模式 |
shadowColor | 设置或返回用于阴影的颜色 |
shadowBlur | 设置或返回用于阴影的模糊级别 |
shadowOffsetX | 设置或返回阴影距形状的水平距离 |
shadowOffsetY | 设置或返回阴影距形状的垂直距离 |
# 设置阴影
设置阴影我们用到的是 shadowBlur 这个属性。
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var cx = canvas.width = 400;
var cy = canvas.height = 400;
context.beginPath();
context.arc(100,100,50,0,2*Math.PI,false);
context.fillStyle = '#fff';
context.shadowBlur = 20;
context.shadowColor = '#fff';
context.fill()
同样的方法,只要在 fill() 方法之前设置模糊指数(shadowBlur)和颜色(shadowColor)就可以了。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title></title>
<style type="text/css">
*{
margin: 0;
padding:0
}
#k{
background: skyblue;
}
</style>
</head>
<body>
<canvas id="k" width="400" height="400"></canvas>
</body>
<script type="text/javascript">
let drawing = document.getElementById("k");
// make sure <canvas> is completely supported
if (drawing.getContext) {
let context = drawing.getContext("2d");
// start the path
context.beginPath();
// draw outer circle
context.arc(100, 100, 99, 0, 2 * Math.PI, false);
context.closePath()
context.strokeStyle="green"
context.stroke();
// draw inner circle
// context.moveTo(194, 100);
context.beginPath()
context.arc(100, 100, 94, 0, 2 * Math.PI, false);
context.strokeStyle="plum"
// stroke the path
context.stroke();
context.beginPath()
// draw minute hand
context.moveTo(100, 100);
context.lineTo(100, 15);
// draw hour hand
context.moveTo(100, 100);
context.lineTo(35, 100);
context.strokeStyle="red"
// stroke the path
context.stroke();
}
</script>
</html>
# 设置渐变
| 方法 | 描述 |
|---|---|
createLinearGradient() | 创建线性渐变(用在画布内容上) |
createPattern() | 在指定的方向上重复指定的元素 |
createRadialGradient() | 创建放射状/环形的渐变(用在画布内容上) |
addColorStop() | 规定渐变对象中的颜色和停止位置 |
其中绘制渐变主要用到了 createLinearGradient() 方法, context.createLinearGradient(x0,y0,x1,y1);
- x0:开始渐变的 x 坐标
- y0:开始渐变的 y 坐标
- x1:结束渐变的 x 坐标
- y1:结束渐变的 y 坐标
这是设置比如说我们下一个粉色到白色的由上向下的渐变:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
#canvas {
background: rgb(220,50,100) ;
}
</style>
</head>
<body>
<canvas id="canvas">
</canvas>
<script>
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var cx = canvas.width = 400;
var cy = canvas.height = 400;
var grd = context.createLinearGradient(100,100,100,200);
grd.addColorStop(0,'pink');
grd.addColorStop(1,'orange');
context.fillStyle = grd;
context.fillRect(100,100,200,200);
</script>
</body>
</html>
可以看出,createLinearGradient() 的参数是两个点的坐标,这两个点的连线实际上就是渐变的方向。我们可以使用 addColorStop() 方法来设置渐变的颜色。
gradient.addColorStop(stop,color);:
stop:介于 0.0 与 1.0 之间的值,表示渐变中开始与结束之间的位置color:在结束位置显示的 CSS 颜色值
我们可以设置多个颜色断点,比如,要实现一个彩虹的效果,只需要多增加几个颜色断点就可以了~
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var cx = canvas.width = 400;
var cy = canvas.height = 400;
var grd = context.createLinearGradient(0,0,0,400);
grd.addColorStop(0,'rgb(255, 0, 0)');
grd.addColorStop(0.2,'rgb(255, 165, 0)');
grd.addColorStop(0.3,'rgb(255, 255, 0)');
grd.addColorStop(0.5,'rgb(0, 255, 0)');
grd.addColorStop(0.7,'rgb(0, 127, 255)');
grd.addColorStop(0.9,'rgb(0, 0, 255)');
grd.addColorStop(1,'rgb(139, 0, 255)');
context.fillStyle = grd;
context.fillRect(0,0,400,400);
效果如下:
context.createRadialGradient(x0,y0,r0,x1,y1,r1);
var grd=ctx.createRadialGradient(75,50,5,90,60,100);
grd.addColorStop(0,"red");
grd.addColorStop(1,"white");
draw('repeat')//repeat-x repeat-y no-repeat
function draw(direction)
{
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.clearRect(0,0,c.width,c.height);
var img=document.getElementById("lamp")
var pat=ctx.createPattern(img,direction);
ctx.rect(0,0,220,128);
ctx.fillStyle=pat;
ctx.fill();
}
# 合成globalAlpha
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title></title>
<style type="text/css">
*{
margin: 0;
padding:0
}
#k{
background: skyblue;
}
</style>
</head>
<body>
<canvas id="k" width="100" height="100"></canvas>
</body>
<script type="text/javascript">
let drawing = document.getElementById("k");
// make sure <canvas> is completely supported
if (drawing.getContext) {
let context = drawing.getContext("2d");
context.fillStyle = "#ff0000";
context.fillRect(10, 10, 50, 50);
// change the global alpha
context.globalAlpha = 0.9;
// draw a blue rectangle
context.fillStyle = "rgba(0,0,255,1)";
context.fillRect(30, 30, 50, 50);
// reset
context.globalAlpha = 1;
context.fillStyle = "orange";
context.fillRect(0, 0, 50, 50);
}
</script>
</html>
# globalCompositeOperation
globalCompositeOperation 属性设置或返回如何将一个源(新的)图像绘制到目标(已有)的图像上。
源图像 = 您打算放置到画布上的绘图。
目标图像 = 您已经放置在画布上的绘图。
| 值 | 描述 |
|---|---|
| source-over | 默认。在目标图像上显示源图像。 |
| source-atop | 在目标图像顶部显示源图像。源图像位于目标图像之外的部分是不可见的。 |
| source-in | 在目标图像中显示源图像。只有目标图像内的源图像部分会显示,目标图像是透明的。 |
| source-out | 在目标图像之外显示源图像。只会显示目标图像之外源图像部分,目标图像是透明的。 |
| destination-over | 在源图像上方显示目标图像。 |
| destination-atop | 在源图像顶部显示目标图像。源图像之外的目标图像部分不会被显示。 |
| destination-in | 在源图像中显示目标图像。只有源图像内的目标图像部分会被显示,源图像是透明的。 |
| destination-out | 在源图像外显示目标图像。只有源图像外的目标图像部分会被显示,源图像是透明的。 |
| lighter | 显示源图像 + 目标图像。 |
| copy | 显示源图像。忽略目标图像。 |
| xor | 使用异或操作对源图像与目标图像进行组合。 |
let context = drawing.getContext("2d");
context.fillStyle = "#ff0000";
context.fillRect(10, 10, 50, 50);
context.globalCompositeOperation = "source-in";
context.fillStyle = "rgba(0,0,255,1)";
context.fillRect(30, 30, 50, 50);
# 图形转换
| 方法 | 描述 |
|---|---|
scale() | 缩放当前绘图至更大或更小 context.scale(scalewidth,scaleheight); |
rotate() | 旋转当前绘图 |
translate() | 重新映射画布上的 (0,0) 位置,translate(x,y):把原点移动到(x,y)。执行这个操作后,坐标(0,0)就会变成(x,y) |
transform() | 替换绘图的当前转换矩阵 |
setTransform() | 将当前转换重置为单位矩阵,然后运行 transform() |
# 缩放
绘制一个矩形;放大到 200%,再次绘制矩形;放大到 200%,然后再次绘制矩形;放大到 200%,再次绘制矩形:
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var cx = canvas.width = 400;
var cy = canvas.height = 400;
context.strokeStyle = 'white';
context.strokeRect(40,40,50,25);
context.scale(2,2);
context.strokeRect(1,1,50,25);
context.scale(2,2);
context.strokeStyle="orange";
context.strokeRect(5,5,50,25);
只是使用 scale() 方法就可以实现缩放的效果,我们再来看一下浏览器中的显示情况:
可以看到,在设置 scale() 方法之后再设置的矩形,无论是线条的宽度还是坐标的位置,都被放大了。并且 scale() 的效果是可以叠加的,也就是说,我们在上面的例子中使用了两次 scale(2,2) 那么,最后一个矩形相对于第一个矩形长和宽,以及坐标的位置就放大了 4 倍。
# 旋转
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var cx = canvas.width = 400;
var cy = canvas.height = 400;
context.fillStyle = 'white';
context.rotate(20*Math.PI/180);
context.fillRect(70,30,200,100);
我们使用的是 rotate() 方法 context.rotate(angle);
angle: 旋转角度,以弧度计。 如需将角度转换为弧度,请使用degrees*Math.PI/180公式进行计算。 举例:如需旋转 5 度,可规定下面的公式:5*Math.PI/180。
在刚刚的例子中,我们将画布旋转了 20°,然后再画了一个矩形。
通过上述两个例子,我们会发现一个特点,在进行图形变换的时候,我们需要画布旋转,然后再绘制图形。
这样的结果是,我们使用的图形变换的方法都是作用在画布上的,既然对画布进行了变换,那么在接下来绘制的图形都会变换。这点是需要注意的。
比如我对画布使用了 rotate(20*Math.PI/180) 方法,就是将画布旋转了 20°,然后之后绘制的图形都会旋转 20°。
ctx.translate(70,70);//写在绘制之前
ctx.fillRect(10,10,100,50);
# restore() save()
- save方法:保存,所有这一时刻的设置会被放到一个暂存栈中
- restore方法:恢复之前的上下文,从暂存栈中取出并恢复之前保存的设置
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title></title>
<style type="text/css">
*{
margin: 0;
padding:0
}
#k{
background: skyblue;
}
</style>
</head>
<body>
<canvas id="k" width="400" height="400"></canvas>
</body>
<script type="text/javascript">
let drawing = document.getElementById("k");
// make sure <canvas> is completely supported
if (drawing.getContext) {
let context = drawing.getContext("2d");
context.fillStyle = "#ff0000";
context.save();
context.fillStyle = "#00ff00";
context.translate(100, 100);
context.save();
context.fillStyle = "#0000ff";
context.fillRect(0, 0, 100, 200); // draws blue rectangle at (100, 100)
context.restore();
context.fillRect(10, 10, 100, 200); // draws green rectangle at (110, 110)恢复成绿色,坐标原点(100,100)
context.restore();
context.fillRect(0, 0, 100, 200); // draws red rectangle at (0,0),恢复成红色,原点(0,,0)
}
</script>
</html>
# 图像绘制drawImage
Canvas 还有一个经常用的方法是drawImage()。向画布上绘制图像、画布或视频
context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);
img:规定要使用的图像、画布或视频sx:可选。开始剪切的 x 坐标位置sy:可选。开始剪切的 y 坐标位置swidth:可选。被剪切图像的宽度sheight:可选。被剪切图像的高度x:在画布上放置图像的 x 坐标位置y:在画布上放置图像的 y 坐标位置width:可选。要使用的图像的宽度(伸展或缩小图像)height:可选。要使用的图像的高度(伸展或缩小图像)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style media="screen">
body {background:black; text-align:center;}
#c1 {background:white;}
</style>
<script>
window.onload=function (){
let oC=document.getElementById('c1');
let gd=oC.getContext('2d');
let pause=false;
let i=0;
let x=100;
let frameCounter=0;
let oImg=new Image();
oImg.src='fish1.png';
oImg.onload=function (){
requestAnimationFrame(next);
function next(){
if(!pause){
gd.clearRect(0, 0, oC.width, oC.height);
if(frameCounter%4==0){
i++;
if(i==4)i=0;
}
x+=1.5;
gd.drawImage(
oImg,
//sx, sy, sw, sh
0, i*37, 55, 37,
//dx, dy, dw, dh
x, 100, 55, 37
);
frameCounter++;
}
requestAnimationFrame(next);
}
};
document.onclick=function (){
pause=!pause;
};
};
</script>
</head>
<body>
<canvas id="c1" width="800" height="600"></canvas>
</body>
</html>
# getImageData() putImageData() 修改图片颜色
- getImageData():获取原始图像数据
- putImageData(): 把图像数据再绘制到画布
getImageData(x,y,width,height)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title></title>
<style type="text/css">
*{
margin: 0;
padding:0
}
#k{
background: skyblue;
}
</style>
</head>
<body>
<img src="crz1.png" alt="" id="img">
<canvas id="k" width="100" height="100"></canvas>
</body>
<script type="text/javascript">
let drawing = document.getElementById("k");
// make sure <canvas> is completely supported
if (drawing.getContext) {
let context = drawing.getContext("2d"),
image = document.images[0],
imageData, data,
i, len, average,
red, green, blue, alpha;
// draw regular size
context.drawImage(image, 0, 0);
// get the image data
imageData = context.getImageData(0, 0, image.width, image.height);
data = imageData.data;
for (i=0, len=data.length; i < len; i+=4) {
red = data[i];
green = data[i+1];
blue = data[i+2];
alpha = data[i+3];
// get the average of rgb 每次循环红绿蓝求平均,实际相当于过滤掉颜色信息,只留下类似亮度的灰度信息
average = Math.floor((red + green + blue) / 3);
// set the colors, leave alpha alone
data[i] = average;
data[i+1] = average;
data[i+2] = average;
}
// assign back to image data and display
imageData.data = data;
context.putImageData(imageData, 0, 0);
}
</script>
</html>
<!-- 图片转为黑白色 -->
img {
-webkit-filter: grayscale(100%); /* Chrome, Safari, Opera */
filter: grayscale(100%);
}
# HTMLCanvasElement.toDataURL()
方法返回一个包含图片展示的 data URI 。可以使用 type 参数其类型,默认为 PNG 格式。图片的分辨率为96dpi。
canvas.toDataURL(type, encoderOptions);
type 可选 图片格式,默认为 image/png encoderOptions 可选 在指定图片格式为 image/jpeg 或 image/webp的情况下,可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92。其他参数会被忽略。
# canvas压缩图片
# 源码
本小册中各种特效的源码地址:sunshine940326/canvas (opens new window)
# canvas画电子章
let mini ={
cstartAngle:142,
cendAngle:45,
cratioX:120,
cratioY:72,
cradiusX:57,
cradiusY:22,
fillText1:5,
fillText2:5
}
//11<=x<15
let small ={
cstartAngle:168,
cendAngle:12,
cratioX:120,
cratioY:72,
cradiusX:57,
cradiusY:24,
fillText1:0,
fillText2:5
}
//15<=X<19
let middle ={
cstartAngle:180,
cendAngle:5,
cratioX:122,
cratioY:78,
cradiusX:60,
cradiusY:28,
fillText1:3,
fillText2:5
}
//X>=19
let big= {
cstartAngle:196,
cendAngle:6,
cratioX:124,
cratioY:82,
cradiusX:60,
cradiusY:28,
fillText1:10,
fillText2:5
}
let params = big
function createSealEx(company,taxNumber){
let length = company.length;
if(length>=19){
params = big
}else if(15<=length&&length<19){
params = middle
}else if(11<=length&&length<15){
params = small
}else if(length<11){
params = mini
}
var sealdiv = document.createElement("canvas");
sealdiv.width =280
sealdiv.height =280
// sealdiv.innerHTML ="<canvas id='sealCanvas' width='280' height='280'></canvas>";
sealdiv.id='sealCanvas'
sealdiv.setAttribute('style', 'position:fixed;top:200px;right:200px;display:none')
document.body.appendChild(sealdiv)
// var w = sealdiv.width;
// var h = sealdiv.height;
var ctx=sealdiv.getContext("2d");
ctx.clearRect(0, 0, 280, 280);
let url =createSeal2(company,taxNumber);
return url
}
function createSeal2(company,taxNumber){
var canvas = document.getElementById("sealCanvas");
var context = canvas.getContext("2d");
context.strokeStyle="red";//设置文本颜色
context.textBaseline = 'middle';//设置文本的垂直对齐方式
context.textAlign = 'center'; //设置文本的水平对对齐方式
context.lineWidth = 3;//椭圆1宽度
//3个参数: 左边距 上边据 宽度 椭圆扁度
BezierEllipse4(context, 140, 135, 135, 95); //椭圆1
context.lineWidth = 1;
// 绘制印章类型
if(taxNumber){
context.font = '18px FangSong';
context.lineWidth=1;
context.fillStyle = '#f00';
context.fillText(taxNumber,canvas.width/2-2,canvas.height/2+5);
context.font = '22px FangSong';
context.fillText('收款专用章',canvas.width/2-2,canvas.height/2+30);
}else{
context.lineWidth=1;
context.fillStyle = '#f00';
context.font = '22px FangSong';
context.fillText('收款专用章',canvas.width/2-2,canvas.height/2+20);
}
context.save();
//绘制中文
var ccircle={
x:canvas.width/2,
y:canvas.height/2,
radius:70
};
var cstartAngle=params.cstartAngle;//控制字符起始位置度数
var cendAngle =params.cendAngle;//首位字符相隔度数
var cradius=ccircle.radius //圆的半径
var cangleDecrement=(cstartAngle-cendAngle)/(company.length-1)//每个字母占的弧度
context.font="14px FangSong"
var cratioX = params.cratioX / ccircle.radius; //横轴缩放比率
var cratioY = params.cratioY / ccircle.radius; //纵轴缩放比率
//进行缩放(均匀压缩)
context.scale(cratioX, cratioY);
var cindex=0;
for(var cindex=0;cindex<company.length;cindex++){
context.save()
context.beginPath()
//绘制点
context.translate(ccircle.x+Math.cos((Math.PI/180)*cstartAngle)*cradius-params.cradiusX,ccircle.y-Math.sin((Math.PI/180)*cstartAngle)*cradius-params.cradiusY)
context.rotate((Math.PI/2)-(Math.PI/180)*cstartAngle) //Math.PI/2为旋转90度 Math.PI/180*X为旋转多少度
context.fillText(company.charAt(cindex),params.fillText1,params.fillText2)
//context.strokeText(company.charAt(cindex),params.fillText1,params.fillText2)
cstartAngle-=cangleDecrement
context.restore()
}
let url = canvas.toDataURL('image/png');
console.log(url)
document.body.removeChild(canvas)
return url
}
function BezierEllipse4(ctx, x, y, a, b){
var k = .5722848,
ox = a * k, // 水平控制点偏移量
oy = b * k; // 垂直控制点偏移量</p> <p>
ctx.beginPath();
//从椭圆的左端点开始顺时针绘制四条三次贝塞尔曲线
ctx.moveTo(x - a, y);
ctx.bezierCurveTo(x - a, y - oy, x - ox, y - b, x, y - b);
ctx.bezierCurveTo(x + ox, y - b, x + a, y - oy, x + a, y);
ctx.bezierCurveTo(x + a, y + oy, x + ox, y + b, x, y + b);
ctx.bezierCurveTo(x - ox, y + b, x - a, y + oy, x - a, y);
ctx.closePath();
ctx.stroke();
};
// document.getElementById('markpic').onclick =function(){
// var img = document.getElementById('myimg');
// img.src = canvas.toDataURL('image/png');
// console.log(img)
// }
export default createSealEx