Canvas API总结

1,183 阅读9分钟

概述

Canvas(画布)用于在网页实时生成图像,并且可以操作图像内容,基本上它是一个可以用JavaScript操作的位图(bitmap)。

开始使用


  1. 新建一个<canvas>网页元素。
<canvas id="myCanvas" width="400" height="200">
你的浏览器不支持canvas!
</canvas>
  • 替换内容
    • 支持<canvas>的浏览器将会忽略在容器中包含的内容,正常渲染canvas。
    • 不支持<canvas>的浏览器会显示代替内容
  • widthheight
    • 默认宽度为300像素,默认高度为150像素。
    • html属性设置width/height时只影响画布本身不影画布内容
    • css属性设置width/height时不但会影响画布本身的高宽,还会使画布中的内容等比例缩放(缩放参照于画布默认的尺寸)
  1. <canvas>元素只是创造了一个固定大小的画布,要想在它上面去绘制内容,需要用getContext()方法找到它的渲染上下文
//获取canvas容器
var canvas = document.getElementById('myCanvas');
//创建一个渲染上下文
if (canvas.getContext) {
  var ctx = canvas.getContext('2d');
}

上面代码中,getContext方法指定参数2d,表示该canvas节点用于生成2D图案(即平面图案)。如果参数是webgl,就表示用于生成3D图像(即立体图案),这部分实际上单独叫做WebGL API。

绘图方法

canvas画布提供了一个用来作图的平面空间,该空间的每个点都有自己的坐标,x表示横坐标,y表示竖坐标。原点(0, 0)位于图像左上角,x轴的正向是原点向右,y轴的正向是原点向下

样式和颜色

  • fillStyle :设置填充颜色,默认黑色
  • strokeStyle :设置轮廓颜色,默认黑色
  • lineWidth :设置线条宽度,属性值为任意正整数,默认值是1.0。
  • lineJoin:控制线条相交的方式(默认是miter
    • round : 圆角
    • bevel : 斜角
    • miter : 直角
  • lineCap:线段末端的形状(默认值是 butt)
    • butt :线段末端以方形结束。
    • round :线段末端以圆形结束
    • square:线段末端以方形结束,但是增加了一个宽度和线段相同,高度是线段厚度一半的矩形区域

save()、restore()、beginPath()

  • 样式容器
    • 每次调用样式api时,都会往样式容器里做登记
    • 调用save()时,将样式容器中的状态压栈,保存上下文环境
    • 调用restore()时,将样式栈的栈顶状态弹出到样式容器中,恢复到上一次保存的上下文环境
//用save方法,保存了当前设置
ctx.save();

ctx.shadowOffsetX = 10;
ctx.shadowOffsetY = 10;
ctx.shadowBlur = 5;
ctx.shadowColor = 'rgba(0,0,0,0.5)';

//绘制了一个有阴影的矩形
ctx.fillStyle = '#CC0000';
ctx.fillRect(10,10,150,100);

//使用restore方法,恢复了保存前的设置
ctx.restore();

//绘制了一个没有阴影的矩形
ctx.fillStyle = '#000000';
ctx.fillRect(180,10,150,100);
  • 路径容器
    • 每次调用路径api时,都会往路径容器里做登记
    • 调用beginPath时,清空整个路径容器
ctx.save();
//关于样式的设置
ctx.beginPath();
//关于路径的设置
ctx.restore();

变换

  • translate(x, y):将canvas坐标原点移动到(x,y),translate是累加
  • rotate(angle):围绕原点旋转图像angle弧度(顺时针),rotate是累加
  • scale(x, y):缩放图像;x和y分别是横轴和纵轴的缩放因子(正值),scale是累加
    • 比 1.0 小:缩小
    • 比 1.0 大:放大
    • 为 1.0 时什么效果都没有

(1)绘制矩形

//填充矩形(x, y是横纵坐标,原点在canvas的左上角)
ctx.fillRect(x, y, width, height);
//边框矩形,默认1px 黑色。   
ctx.strokeRect(x, y, width, height);
//清除指定的矩形区域,变为透明
ctx.clearRect(x, y, width, height);       //绘制动态效果时,常用来清除整个画布
  • rect(x, y, width, height):绘制矩形路径

(2)绘制路径

//新建路径,beginPath是绘制新图形的开始
ctx.beginPath()
//路径(线)的起点,一般在上面这条命令后执行
ctx.moveTo(x, y)  
//线的终点
ctx.lineTo(x, y)   
//闭合路径,不是必须的,如果线的终点跟起点一样,会自动闭合。
ctx.closePath()
//通过线条绘制轮廓(边框)
ctx.stroke() //不会自动调用closePath()  
//通过路径填充区域(实心)
ctx.fill()  //自动调用closePath()

例:绘制一个三角形

ctx.beginPath();
ctx.moveTo(75, 50);    //路径起点
ctx.lineTo(100, 75);
ctx.lineTo(100, 25);
ctx.fill();   //自动将路径闭合,并默认填充黑色。
  • quadraticCurveTo(cp1x, cp1y, x, y):绘制二次贝塞尔曲线,cp1x,cp1y为一个控制点,起始点为moveto时指定的点,x,y为结束点。
  • bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y):绘制三次贝塞尔曲线,cp1x,cp1y为控制点一,cp2x,cp2y为控制点二,起始点为moveto时指定的点,x,y为结束点。
  • ctx.isPointInPath(x, y):判断在当前路径中是否包含检测点
    • x:检测点的X坐标
    • y:检测点的Y坐标
    • 注意,此方法只作用于最新画出的canvas图像

(3)绘制文本

  • fillText(string, x, y):在指定的(x,y)位置填充指定的文本
    • 4个参数:文本字符串、起点的x坐标、y坐标、可选的最大像素宽度。
  • strokeText(string, x, y):在指定的(x,y)位置填充指定的文本
// 设置字体,必须要有大小和字体
ctx.font = "Bold 20px Arial"; 
// 设置对齐方式,可选值包括: left, right  center
ctx.textAlign = "left";
// 设置填充颜色
ctx.fillStyle = "#008600"; 
// 设置字体内容,以及在画布上的位置
ctx.fillText("Hello!", 10, 50); 
// 绘制空心字
ctx.strokeText("Hello!", 10, 100); 
  • measureText() 方法:返回一个TextMetrics 对象,包含关于文本尺寸的信息(例如文本的宽度)

(4)绘制圆形和扇形

//绘制圆形
ctx.arc(x, y, r, start, end, true/false)   //(x, y)圆心,r半径,start和end是开始和结束角度,false表示顺时针(默认),true表示逆时针。
//绘制弧线
ctx.arcTo(x1, y1, x2, y2, r);    //当前端点、(x1,y1)和(x2,y2)这三个点连成的弧线,r是半径。

例:绘制实心的圆形

ctx.beginPath(); 
ctx.arc(60, 60, 50, 0, Math.PI*2, true); 
ctx.fillStyle = "#000000"; 
ctx.fill();

例:绘制空心圆形

ctx.beginPath(); 
ctx.arc(60, 60, 50, 0, Math.PI*2, true); 
ctx.lineWidth = 1.0; 
ctx.strokeStyle = "#000"; 
ctx.stroke();

(5)设置阴影

ctx.shadowOffsetX = 10; // 设置水平位移
ctx.shadowOffsetY = 10; // 设置垂直位移
ctx.shadowBlur = 5; // 设置模糊度
ctx.shadowColor = "rgba(0,0,0,0.5)"; // 设置阴影颜色

ctx.fillStyle = "#CC0000"; 
ctx.fillRect(10,10,200,100);

(6)设置渐变

  • createLinearGradient(x1, y1, x2, y2):创建一个canvas渐变(线性渐变)
    • 渐变起点 (x1,y1) 与终点 (x2,y2)
  • gradient.addColorStop(position, color)
    • gradient:createLinearGradient的返回值
    • addColorStop 方法接受 2 个参数:
      • position 参数必须是一个 0.0 与 1.0 之间的数值,表示渐变中颜色所在的相对位置,例如,0.5 表示颜色会出现在正中间。
      • color 参数必须是一个有效的 CSS 颜色值,如 #FFF, rgba(0,0,0,1)等等
//创建一个canvas线性渐变,返回`CanvasGradient`对象的实例
var myGradient = ctx.createLinearGradient(0, 0, 0, 160); 

myGradient.addColorStop(0, "#BABABA"); 

myGradient.addColorStop(1, "#636363");
//绘制渐变填充的矩形
ctx.fillStyle = myGradient;
ctx.fillRect(10,10,200,100);
  • createRadialGradient(x1, y1, r1, x2, y2, r2):canvas渐变(径向渐变)
    • 前三个参数则定义另一个以(x1,y1) 为原点,半径为 r1 的圆
    • 后三个参数则定义另一个以 (x2,y2) 为原点,半径为 r2 的圆

图像处理方法

drawImage()方法

Canvas API 允许将图像文件插入画布,做法是读取图片后,使用drawImage方法在画布内进行重绘。

  • canvas操作图片时,必须要等图片加载完才能操作
  • drawImage(image, x, y, width, height)
    • image:图像文件的DOM元素
    • xy :绘制该图像的起始坐标
    • widthheight:目标宽高
var image = new Image();
image.src = 'image.png';

image.onload = function() {
  var canvas = document.createElement('canvas');
  canvas.width = image.width;
  canvas.height = image.height;
  canvas.getContext('2d').drawImage(image, 0, 0);
  // 插入页面底部
  document.body.appendChild(image);
  return canvas;
}


createPattern()方法:设置背景模式

  • createPattern(image, repetition)
    • image:图像源
    • epetition
      • repeat
      • repeat-x
      • repeat-y
      • no-repeat

一般情况下,我们都会将fillstyle的值设置为createPattern返回的对象,只表示在某个特定的区域内显示重复的图像。

getImageData()方法,putImageData()方法

通过getImageData方法和putImageData方法,可以处理每个像素,进而操作图像内容。

  • ctx.getImageData(sx, sy, sw, sh):获得一个包含画布场景像素数据的ImageData对象,它代表了画布区域的对象数据
    • 参数
      • sx:要被提取的画面区域的 x 坐标。
      • sy:要被提取的画面区域的 y 坐标。
      • sw:要被提取的像素宽度。
      • sh:要被提取的像素高度。
    • ImageData对象中存储着canvas对象真实的像素数据,它包含以下几个只读属性:
      • width:图片宽度,单位是像素
      • height:图片高度,单位是像素
      • data:Uint8ClampedArray类型的一维数组, 包含着RGBA格式的整型数据,范围在0至255之间(包括255)
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
  • putImageData(myImageData, dx, dy):把图像数据绘制到画布上
context.putImageData(imageData, 0, 0);
  • 套路代码

假定filter是一个处理像素的函数,那么整个对Canvas的处理流程,可以用下面的代码表示。

if (canvas.width > 0 && canvas.height > 0) {
    var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
    
    filter(imageData);

    context.putImageData(imageData, 0, 0);

}

createImageData()方法

  • ctx.createImageData(width, height):创建一个ImageData对象
    • width : ImageData 新对象的宽度。
    • height: ImageData 新对象的高度。
    • 默认创建出来的是透明的

合成

  • globalAlpha = value:设置全局透明度,这个属性影响到 canvas 里所有图形的透明度
    • 有效的值范围是 0.0 (完全透明)到 1.0(完全不透明)
    • 默认是 1.0
  • globalCompositeOperation:覆盖合成(source--->新的图像(源);destination--->已经绘制过的图形(目标))
    • source-over(默认值):源在上面,新的图像层级比较高
    • source-in :只留下源与目标的重叠部分(源的那一部分)
    • source-out :只留下源超过目标的部分
    • source-atop:砍掉源溢出的部分
    • destination-over:目标在上面,旧的图像层级比较高
    • destination-in:只留下源与目标的重叠部分(目标的那一部分)
    • destination-out:只留下目标超过源的部分
    • destination-atop:砍掉目标溢出的部分

toDataURL():将画布导出为图像

注意是canvas元素接口上的方法

function convertCanvasToImage(canvas) {
  var image = new Image();
  image.src = canvas.toDataURL('image/png');
  return image;
}

上面的代码将Canvas数据,转化成PNG data URI。

常见效果

灰度效果

灰度图(grayscale)就是取红、绿、蓝三个像素值的算术平均值。假定d[i]是像素数组中一个象素的红色值,则d[i+1]为绿色值,d[i+2]为蓝色值,d[i+3]就是alpha通道值。转成灰度的算法,就是将红、绿、蓝三个值相加后除以3,再将结果写回数组。

grayscale = function (pixels) {

	var d = pixels.data;

    for (var i = 0; i < d.length; i += 4) {
      var r = d[i];
      var g = d[i + 1];
      var b = d[i + 2];
      d[i] = d[i + 1] = d[i + 2] = (r+g+b)/3;
    }

    return pixels;

};

复古效果

复古效果(sepia)则是将红、绿、蓝三个像素,分别取这三个值的某种加权平均值,使得图像有一种古旧的效果。

sepia = function (pixels) {

    var d = pixels.data;

    for (var i = 0; i < d.length; i += 4) {
      var r = d[i];
      var g = d[i + 1];
      var b = d[i + 2];
      d[i]     = (r * 0.393)+(g * 0.769)+(b * 0.189); // red
      d[i + 1] = (r * 0.349)+(g * 0.686)+(b * 0.168); // green
      d[i + 2] = (r * 0.272)+(g * 0.534)+(b * 0.131); // blue
    }

    return pixels;

};