H5 画布基础 一
画布元素可被用来通过JavaScript(Canvas API 或 WebGL API)绘制图形及图形动画。
本文为H5 画布学习笔记。
基本用法
-
canvas 需要有闭合标签</ canvas >。
-
画布大小在标签中可以直接指定,或者使用JavaScript指定。 也可以使用CSS指定高度和宽度。在渲染的过程中< canvas >元素中的内容会根据情况缩放来适应需要的大小。
注意:画布元素不可以使用CSS控制样式。 -
canvas绘制是同步的,代码执行有别于浏览器本身渲染机制,样式定义必须在绘制之前。
-
画布具有覆盖渲染的效果。
-
最大画布尺寸
浏览器 | 最大高度 | 最大宽度 | 最大面积 |
---|---|---|---|
Chorme | 32,767 px | 32,767 px | 268,435,456 px |
Firefox | 32,767 px | 32,767 px | 742,907,776 px |
Safari | 32,767 px | 32,767 px | 268,435,456 px |
IE | 819,2px | 819,2px | ? |
- 使用示例
<canvas id="canvas" width="300" height="300">
抱歉,您的浏览器不支持canvas元素
(标签内内容会在不支持<canvas>元素的浏览器或是禁用了JavaScript的浏览器内渲染并展现)
</canvas>
矩形绘制
- 绘图上下文
<script type="text/jscript">
window.onload = function(){
// 用querySelector拿到画布
var canvas = document.querySelector("#test");
if(canvas.getContext){
// 画布调用,需要先判断是否存在
var ctx = canvas.getContext("2d");
}
}
</script>
- 绘制填充矩形
// 填充矩形绘制(x,y,width,height)
ctx.fillRect(0,0,100,100)
- 绘制边框矩形
// 填充矩形绘制(x,y,width,height)
ctx.strokeRect(100,100,100,100)
- 绘制透明清除矩形
//清除指定矩形区域,让清除部分完全透明
ctx.clearRect (100,100,100,100)
- 使用画布绘制边框矩形时,渲染如上代码:
而渲染宽度默认为1px的情况下,矩形边框经过放大可以看到并非是1px而是2px
在渲染位置为100的边框线时,渲染范围是99.5-100.5像素,而再经过向上取整即实际渲染范围为99-101
若要绘制1px边框。我们需要指定渲染位置为100.5即可
// 填充矩形绘制
ctx.fillRect(0,0,100,100)
// 边框矩形绘制
ctx.strokeRect(100.5,100.5,100,100)
效果:
9. 添加样式和颜色
// 设置图形填充颜色
ctx.fillStyle = "deeppink";
//设置图形轮廓颜色
/**默认线条和填充均为黑色,颜色值#000000 **/
ctx.strokeStyle = "pink";
//设置边框宽度 默认值1.0
/**属性必须为正值,0、负数、Infinity和NaN会被忽略**/
ctx.lineWidth = 10;
//线条样式LineJoin round圆角 bevel斜角 miter直角
ctx.lineJoin = "round";
路径绘制
路径即点的集合。闭合路径可以得到图形。绘制路径的时候需要定义点的集合,即描点。
- 尝试绘制三角形
实例代码:
//路径描点
//将笔触移动到指定位置x,y,会抬起画笔
ctx.moveTo(10,10);
//定义一条当前笔触位置到x,y的直线
ctx.lineTo(50,100);
ctx.lineTo(100,100);
//闭合路径,此次相当于ctx.lineTo(10,10);
ctx.closePath();
//绘制线条,不会自动闭合路径,需要closePath
ctx.stroke();
//填充图形,会自动闭合路径,不需要closePath
ctx.fill();
//清空路径容器
/**定义的路径被存储在路径容器中,
绘制时读取**/
ctx.beginPath();
ctx.moveTo(110,110);
ctx.lineTo(150,50);
ctx.lineTo(200,250);
ctx.closePath();
ctx.stroke();
绘制效果:
- moveTo抬起笔触效果
//moveTo会抬起画笔
ctx.moveTo(10,10);
ctx.lineTo(50,100);
ctx.lineTo(100,100);
ctx.closePath();
ctx.moveTo(110,110);
ctx.lineTo(150,50);
ctx.lineTo(200,250);
ctx.closePath();
ctx.stroke();
绘制效果:
2. 线段末端
//线段末端形状 默认为butt方形;square方形,round圆角
/**square和round为在末端添加了一段宽度和线段相同,高度为线段一半的矩形区域**/
ctx.lineCap = "butt";
Save & Restore
- Canvas 的状态就是当前画面应用的所有样式和变形的一个快照。
- Canvas 状态是以堆(stack)的方式保存的。每一次调用save()方法,当前的状态就会被保存进堆中(类似数组的push());而每一次调用restore()方法,就会将当前状态从堆中移除(类似数组的pop())。这种状态包括 :
当前应用的变形(即移动,旋转和缩放)
当前的裁切路径(使用clip()方法裁切)
strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin, miterLimit, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, globalCompositeOperation,font,textAlgin和textBaseline的值.
你可以调用任意多次 save ()方法。 每一次调用 restore() 方法,上一个保存的状态就从堆中弹出,所有设定都恢复。
canvas 签名画板
实现一个canvas鼠标移动绘制的实例。
window.onload = function(){
//画布获取
var canvas = document.getElementById("test");
if(canvas.getContext){
var ctx = canvas.getContext("2d");
}
//点击事件
canvas.onmousedown = function(ev){
ev = ev || window.event;
//全局捕获(ie适配)
if(canvas.setCapture){
canvas.setCapture();
}
//点击后移动绘点
ctx.beginPath();
ctx.moveTo(ev.clientX - canvas.offsetLeft,ev.clientY - canvas.offsetTop);
//点击时鼠标移动
document.onmousemove = function(ev){
ev = ev || event;
ctx.lineTo(ev.clientX - canvas.offsetLeft,ev.clientY - canvas.offsetTop);
ctx.stroke();
}
document.onmouseup = function(){
document.onmousemove = document.onmouseup = null;
if(document.releaseCapture){
document.releaseCapture();
}
}
return false;
}
}
效果:
canvas 曲线
角度与弧度的表达式 radians = (Math.PI/180)*degress
- 方法:arc(x,y,radius,startAngle,endAngle,anticlockwise)
示例:
<script type="text/jscript">
window.onload = function(){
//画布获取
var canvas = document.getElementById("test");
if(canvas.getContext){
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(100,100);
//画一个以x,y 为圆心以radius为半径的圆弧,从startAngle开始到endAngle结束,
//anticlockwise给定方向,true 逆时针,false 顺时针
ctx.arc(100,100,80,0,310*Math.PI/180,true);
ctx.closePath();
ctx.stroke();
}
}
</script>
结果:
- 方法:arcTo(x1,y1,x2,y2,radius)
示例:绘制圆角
<script type="text/jscript">
window.onload = function(){
//画布获取
var canvas = document.getElementById("test");
if(canvas.getContext){
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(20,20); // 创建起始点
ctx.lineTo(100,20); // 创建水平线
ctx.arcTo(150,20,150,70,50); //创建以50为半径的圆弧
ctx.lineTo(150,150); //画竖线
ctx.stroke();
}
}
</script>
效果:
- 二次贝塞尔曲线
方法:ctx.quadraticCurveTo(cpx,cpy,x,y);
、
示例:
if(canvas.getContext){
var ctx = canvas.getContext("2d");
/***二次贝塞尔曲线,必过两个控制点***/
ctx.beginPath();
ctx.moveTo(20,20);//起始点
ctx.quadraticCurveTo(20,100,200,20);//控制点(20,100),结束点(200,20)
ctx.stroke();
}
结果:
- 三次贝塞尔曲线
方法:ctx.bezierCurveTo(cpx,cpy,x,y);
示例:
ctx.beginPath();
ctx.moveTo(20,20);//起始点
//控制点1(20,100),控制点2(200,100),结束点(200,20)
ctx.bezierCurveTo(20,100,200,100,200,20);
ctx.stroke();
结果
canvas 变换
画布中的元素无法通过css控制变换,画布元素本身是一个DOM节点。
- Translate(x,y)-移动
与css中不同的是画布中Translate进行累加操作而非覆盖操作。
它用来移动canvas原点的位置,接收x,y两个偏移量参数。 - Rotate(angle)-旋转
接收一个角度参数,顺时针方向以弧度为单位的旋转角度,旋转中心为Canvas原点。 - Scale(x,y)-缩放
接收两个值,缩放因子x,y。缩放因子为1.0表示不进行缩放,比1.0小即为缩小,比1.0大即为放大。
注意:如果缩放绘图,所有未来的绘图也将被缩放。定位也将被缩放。如果按比例缩放(2,2),则绘图的位置是画布左边和顶部的两倍。
示例:
/***移动&旋转***/
ctx.translate(100, 100);//原点移动到(100,100)
ctx.rotate(45*Math.PI/180); //顺时针旋转45°
ctx.beginPath();
ctx.fillStyle = "blue";
ctx.fillRect(0,0,100,100);
/***缩放***/
ctx.translate(70, 70);//原点移动到(100,100)
ctx.fillStyle = "blue";
ctx.fillRect(0,0,100,100);
ctx.fillStyle = "pink";
ctx.scale(0.5,0.5);
ctx.fillRect(0,0,100,100);
实例1:旋转缩放动画
<script type="text/jscript">
window.onload = function() {
//画布获取
var canvas = document.getElementById("test");
if (canvas.getContext) {
var flag = 0,angle = 0,temp;
var ctx = canvas.getContext("2d");
ctx.save();
setInterval(function(){
angle++ ;
ctx.clearRect(0,0,canvas.width,canvas.height)
ctx.save()
ctx.translate(150,150)
ctx.rotate(angle*Math.PI/180)
if( flag == 100){ temp = -1 }
else if(flag == 0){ temp = 1}
flag += temp;
ctx.scale(flag*0.01,flag*0.01)
ctx.beginPath()
ctx.fillRect(-50,-50,100,100)
ctx.restore()
},1500/144)
ctx.restore()
}
}
</script>
canvas-实例1(变换) :钟表
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
overflow: hidden;
}
body {
background-color: darksalmon;
}
#clock {
background-color: black;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
margin: auto;
}
</style>
</head>
<body>
<canvas id="clock" height="800" width="800" style="width: 400px ;height: 400px;">
<span>不支持canvas</span>
</canvas>
</body>
<script type="text/javascript">
window.onload = function() {
//画布获取
var clock = document.getElementById("clock");
if (clock.getContext) {
var ctx = clock.getContext("2d");
ctx.translate(400,400)
//定时器每秒刷新2次
setInterval(function(){
ctx.clearRect(-400,-400,800,800);//每次绘制完清除画布
/*************表盘**************/
//分针刻度
//刻度线条样式
ctx.strokeStyle = "#3E3E3E";
ctx.lineCap = "square";
ctx.lineWidth = 2;
ctx.save()
for(var i=0;i<60;i++){
ctx.rotate(6*Math.PI/180)
ctx.beginPath();
ctx.moveTo(236,0);
ctx.lineTo(240,0);
ctx.stroke()
}
ctx.restore();
//时针刻度
//刻度线条样式
ctx.strokeStyle = "white";
ctx.lineCap = "square";
ctx.lineWidth = 4;
ctx.save()
for(var i=0;i<12;i++){
ctx.rotate(30*Math.PI/180)
ctx.beginPath();
ctx.moveTo(230,0);
ctx.lineTo(240,0);
ctx.stroke()
}
ctx.restore();
//中轴
ctx.save()
ctx.lineWidth = 7;
ctx.beginPath()
ctx.arc(0,0,8,0,360*Math.PI/180)
ctx.cl
ctx.stroke()
ctx.restore();
//获取时间
var mydate = new Date;
var second = mydate.getSeconds(); //获取当前秒数(0-59)
var min = mydate.getMinutes() + second/60; //获取当前分钟数(0-59)
var hour = mydate.getHours()+min/60; //获取当前小时数(0-23)
hour = hour>12?hour-12:hour;
/*************指针*************/
ctx.save()
//时钟指针
ctx.save()
ctx.fillStyle = "#E9967A";
ctx.lineWidth = 1;
ctx.beginPath()
ctx.rotate(hour*30*Math.PI/180)
ctx.scale(0.5,0.5)
ctx.moveTo(3,-20)
ctx.lineTo(3,-40)
ctx.lineTo(8,-50)
ctx.lineTo(8,-225)
ctx.lineTo(0,-240)
ctx.lineTo(-8,-225)
ctx.lineTo(-8,-50)
ctx.lineTo(-3,-40)
ctx.lineTo(-3,-20)
ctx.fill()
ctx.restore()
//分针刻度
ctx.save()
ctx.fillStyle = "white";
ctx.lineWidth = 1;
ctx.beginPath()
ctx.rotate(min*6*Math.PI/180)
ctx.scale(0.5,0.5)
ctx.moveTo(3,-20)
ctx.lineTo(3,-40)
ctx.lineTo(8,-50)
ctx.lineTo(8,-365)
ctx.lineTo(0,-380)
ctx.lineTo(-8,-365)
ctx.lineTo(-8,-50)
ctx.lineTo(-3,-40)
ctx.lineTo(-3,-20)
ctx.fill()
ctx.restore()
//秒针刻度
ctx.save()
ctx.fillStyle = "beige";
ctx.lineWidth = 1;
ctx.beginPath()
ctx.rotate(second*6*Math.PI/180)
ctx.scale(0.5,0.5)
ctx.moveTo(3,60)
ctx.lineTo(3,-40)
ctx.lineTo(2,-50)
ctx.lineTo(2,-400)
ctx.lineTo(0,-410)
ctx.lineTo(-2,-400)
ctx.lineTo(-2,-50)
ctx.lineTo(-3,-40)
ctx.lineTo(-3,60)
ctx.fill()
ctx.restore()
ctx.restore()
},1000/2)
}
}
</script>
</html>
效果:
图片
(以200x200图片为例,画布大小400X400)
- 在画布上插入图片
drawImage(img,x,y,width,height)
<script type="text/javascript">
window.onload = function() {
//画布获取
var canvas = document.getElementById("test");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
//图片对象
var img = new Image();
img.src = "../img/Aulac10622_square.jpg"
img.onload = function(){
draw();
}
function draw(){
//插入图片(图片,位置,大小)
ctx.drawImage(img,0,0,200,200)
}
}
}
</script>
- 使用图片填充背景
createPattern(img,“repetition”),其中repetition取值如下图
var pattern = ctx.createPattern(img,"repeat");
ctx.fillStyle = pattern;
ctx.fillRect(0,0,400,400)
- 渐变填充背景
线性渐变
渐变实际上是两种或多种颜色之间的平滑过渡。而线性渐变是多种颜色沿着一条直线 (称为渐变线)过渡。
渐变的实现由两部分组成:渐变线和色标。渐变线用来控制发生渐变的方向;色标包含一个颜色值和一个位置,用来控制渐变的颜色变化。浏览器从每个色标的颜色淡出到下一个,以创建平滑的渐变,通过确定多个色标可以制作多色渐变效果。
径向渐变
径向渐变使用 createRadialGradient 方法创建一个指定了开始和结束圆的 CanvasGradient 对象。绘制了一系列从中心点放射到边缘形状的同心圆。
/******线性渐变******/
//线性渐变起始点和结束点,渐变线
var g = ctx.createLinearGradient(0,0,400,400)
//(offset:偏移量,css有效颜色值)色标
g.addColorStop(0,"#FFA113")
g.addColorStop(0.5,"#FF8214")
g.addColorStop(1,"#FF1F3E")
ctx.fillStyle = g;
ctx.fillRect(0,0,400,400)
/******径向渐变******/
//径向渐变前三个参数定义一个x1,y1,r1的圆,
//后三个参数定义一个x2,y2,r2的圆
var g = ctx.createRadialGradient(200,200,100,200,200,200)
//(offset:偏移量,css有效颜色值)
g.addColorStop(0,"#FFA113")
g.addColorStop(0.5,"#FF8214")
g.addColorStop(1,"#FF1F3E")
ctx.fillStyle = g;
ctx.fillRect(0,0,400,400)
文本
-
文本有两种绘制方法
fillText(“text”,x,y,maxwidth)
strokeText(“text”,x,y,maxwidth)当maxwidth < 文本原始宽度时会压缩字符间距
-
文本样式
字体样式指定 font = “size fontname”
文本对齐 textAligin = “mode”
文本基线 textBaseline 取值见下图 默认值为alphabetic
与css不同,画布中文本相对于x坐标(示例为200)进行对齐操作
//canvas仅支持一种字体:sans-serif
ctx.font = "12px sans-serif"
ctx.fillStyle = "#00BFFF"
ctx.fillText("Definiteness of purpose is the starting point of all achievement.",200,100,400)
ctx.font = "30px sans-serif"
ctx.textAlign = "center"
ctx.strokeText("Definiteness",200,200,800)
- 文本水平居中
measureText(“text”)返回一个TextMetrics值。
使用此方法可以对文本进行水平居中:
ctx.fillText("text",(ctx.width - textwith)/2,ctx.height-fontsize,maxwidth)
4. 文本阴影
- shadowColor:阴影颜色(必需项)
- shadowOffsetX :阴影x轴偏移量(延伸距离)
- shadowOffsetY阴影Y轴偏移量(延伸距离)
- shadowBlur: 阴影模糊程度
ctx.font = "30px sans-serif"
ctx.shadowColor = "deepskyblue"
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
ctx.shadowBlur = 3;
ctx.textBaseline = "top"
ctx.fillText("Definiteness",100,100,800)
像素操作
- ImageData对象
ImageData对象中存储着canvas对象真实的像素数据,它包含以下几个只读属性:
width 图片宽度,单位是像素
height 图片高度,单位是像素 data 包含着RGBA格式的整型数据,范围在0至255之间(包括255)。
我们具体来看看data属性:
data 属性返回一个对象,该对象包含指定的 ImageData 对象的图像数据。
对于 ImageData 对象中的每个像素,都存在着四方面的信息,即 RGBA 值:
R - 红色(0-255)
G - 绿色(0-255)
B - 蓝色(0-255)
A - alpha 通道(0-255; 0 是透明的,255 是完全可见的)
color/alpha 信息以数组形式存在,并存储于 ImageData 对象的 data 属性中。
- 得到像素数据 getImageData(sx, sy, sw, sh)
sx:将要被提取的图像数据矩形区域的左上角X坐标。
sy:将要被提取的图像数据矩形区域的左上角y坐标。
sw:将要被提取的图像数据矩形区域的宽度。
sh:将要被提取的图像数据矩形区域的高度。 - 创建ImageData对象
createImageData(width,height)
以指定的尺寸(以像素计)创建新的 ImageData 对象,一个新的具体特定尺寸的ImageData对像。所有像素被预设为透明黑。
createImageData(imageData);
创建与指定的另一个 ImageData 对象尺寸相同的新 ImageData 对象(不会复制图像数据)。 - 写入像素数据
putImageData(imgData,x,y,dirtyX,dirtyY,dirtyWidth,dirtyHeight)
imgData :规定要放回画布的 ImageData 对象。
x :ImageData 对象左上角的 x 坐标,以像素计。
y :ImageData 对象左上角的 y 坐标,以像素计。
dirtyX :可选。水平值(x),以像素计,在画布上放置图像的位置。
dirtyY :可选。垂直值(y),以像素计,在画布上放置图像的位置。
dirtyWidth :可选。在画布上绘制图像所使用的宽度。
dirtyHeight :可选。在画布上绘制图像所使用的高度。
5.实例- 颜色反转
//图片对象
var img = new Image();
img.src = "../img/Aulac10622_square.jpg"
img.onload = function(){
draw();
}
function draw(){
ctx.drawImage(img,0,0);
var imgData=ctx.getImageData(0,0,200,200);
var data=imgData.data;
for(var i=0;i<data.length;i+=4){
data[i]=255-data[i];
data[i+1]=255-data[i+1];
data[i+2]=255-data[i+2];
data[i+3]=255;
}
ctx.putImageData(imgData,200,200);
}
合成 & 裁剪
绘制图形时,不同的图形会因为绘制的先后而有了层级关系(多个图层)。如果新绘制的图形和原有内容有重叠部分,在默认情况下,新绘制的图形是会覆盖在原有内容之上。
在HTML中可以添加z-index来修改层级关系,在canvas里可以利用globalCompositeOperation 属性来改变。
ctx.globalCompositeOperation ="source-over"
source-over | (默认值) 新图形会覆盖在原有内容之上 |
---|---|
source-in | 新图形仅仅会出现与原有内容重叠的部分,其他区域都变成透明的 |
source-out | 只有新图形中与原有内容不重叠的部分会被绘制出来 |
source-atop | 新图形中与原有内容重叠部分会被绘制,并覆盖于原有内容之上 |
lighter | 两图形中重叠部分作加色处理 |
xor | 重叠部分会变成透明 |
destination-over | 会在原有内容之上绘制新图形 |
destination-in | 原有内容与新图形重叠的部分会被保留,其他部分变成透明的 |
destination-out | 原有内容中与新图形不重叠的部分会被保留 |
destination-atop | 原有内容中与新图形重叠部分会被保留,并会在原有内容之上绘制新图形 |
darker | 两图形重叠部分作减色处理 |
copy | 只有新图形会被保留,其他都被清除掉 |
生成图片&保存
在canvas中绘出的图片只是canvas标签而已,并非是真正的图片,我们并不能保存,不过我们可以利用canvas.toDataURL()这个方法把canvas绘制的图形生成一幅图片,生成图片后,就能对图片进行相应的操作了。
首先我们定义用一个a标签定义下载的链接,然后再给a设置下载的链接。
<a id="download" download="aa.png">下载</a>
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
//图片对象
var img = new Image();
img.src = "../img/Aulac10622_square.jpg"
img.onload = function(){
draw();
}
function draw(){
ctx.drawImage(img,0,0);
var imgData=ctx.getImageData(0,0,200,200);
var data=imgData.data;
for(var i=0;i<data.length;i+=4){
data[i]=255-data[i];
data[i+1]=255-data[i+1];
data[i+2]=255-data[i+2];
data[i+3]=255;
}
ctx.putImageData(imgData,200,200);
//截图保存颜色反转画布
var imageURL=canvas.toDataURL("image/jpeg")
document.getElementById("download").href=imageURL
}
}