画布元素可被用来通过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)
在渲染位置为100的边框线时,渲染范围是99.5-100.5像素,而再经过向上取整即实际渲染范围为99-101
若要绘制1px边框。我们需要指定渲染位置为100.5即可
// 填充矩形绘制
ctx.fillRect(0,0,100,100)
// 边框矩形绘制
ctx.strokeRect(100.5,100.5,100,100)
// 设置图形填充颜色
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会抬起画笔
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();
//线段末端形状 默认为butt方形;square方形,round圆角
/**square和round为在末端添加了一段宽度和线段相同,高度为线段一半的矩形区域**/
ctx.lineCap = "butt";
实现一个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;
}
}
角度与弧度的表达式 radians = (Math.PI/180)*degress
示例:
<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>
示例:绘制圆角
<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();
画布中的元素无法通过css控制变换,画布元素本身是一个DOM节点。
示例:
/***移动&旋转***/
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);
<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>
画布在高分辨率屏幕上模糊的解决方法
源码:
<!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)
<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>
var pattern = ctx.createPattern(img,"repeat");
ctx.fillStyle = pattern;
ctx.fillRect(0,0,400,400)
/******线性渐变******/
//线性渐变起始点和结束点,渐变线
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)
文本样式
字体样式指定 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)
ctx.fillText("text",(ctx.width - textwith)/2,ctx.height-fontsize,maxwidth)
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)
//图片对象
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
}
}