canvas - 基础了解

3,777 阅读8分钟

前言

  • canvas 是 HTML5 特性中最重要内容的之一,基础比较完善,应用广泛

  • canvas 指的是画布,类似于windows的画图工具;在HTML中,默认效果与div类似,背景色是默认色

  • 配合js,动态效果非常帅

  • 支持2D图像效果、3D图像效果

  • 注意:IE8及之前版本不支持canvas 大哥,它可不是一块真布奥 😁

  • 大家先来看一个小 demo ...好玩不?要想整出这玩意,首先还是得先从基础学起,所谓百炼成钢

基本使用

<canvas id="drawing" width="200" height="200">A drawing of something</canvas>
[A drawing of something是后备信息,如果浏览器不支持canvas,就会显示这些信息]
const cav = document.querySelector("#drawing");

if(cav.getContext){
  const context = cav.getContext("2d");
  // 使用 API 达到预期效果
}
  • 注意:
    • cav.getContext("2d"); 中的 d 只能小写,并且为字符串
    • 使用canvas来绘制的图像,只能出现在canvas规定的范围内,如果超出则会被隐藏

绘图

绘制图形

  • canvas 只支持一种基本形状――矩形,所以其它形状都是有一个或多个路径组合而成,还好,有一组路径绘制函数让我们可以绘制相当复杂的形状。

  • 原点:canvas的左上角(0,0),[x 和 y 指定矩形左上角(相对于原点)的位置]

  • fillRect(x,y,width,height) : 绘制一个实心矩形,默认背景颜色为黑色,没有边框

    • 填充(实心)配合fillRect(),并在fillRect()之前使用
    • fillStyle = "颜色值";
  • strokeRect(x,y,width,height) : 空心矩形,默认边框颜色为黑色,边框默认宽:1px

    • 描边(空心)配合strokeRect(),并在strokeRect()之前使用
    • strokeStyle = "颜色值";
  • clearRect(x,y,width,height) : 以矩形方式清除指定区域(类似橡皮擦) 放置在要清除的图形位置之后,即绘制一个,清除一下

  • 全局设置透明度(0~1)在颜色后设置透明度

    • globalAlpha = "透明值";
  • canvas是按顺序进行画图,后面的图形会将前面的覆盖,例如:

<canvas id="canvas" width="400" height="400" style="background: #eee;">浏览器不支持canvas</canvas>

function draw(id) {
  const canvas = document.getElementById(id);
  if (canvas.getContext) {
    const ctx = canvas.getContext('2d');
    ctx.fillRect(25, 25, 100, 100);
    ctx.clearRect(45, 45, 60, 60);
    ctx.strokeRect(50, 50, 50, 50);
  }
}
draw('canvas');

绘制渐变

将渐变基准线赋值给 fillStyle 或 strokeStyle

线性渐变

  • createLinearGradient(x1,y1,x2,y2);
  • 两点一线(渐变的基准线)

image.png

const canv = document.getElementById('canvas');
if (canv.getContext) {
  const context = canv.getContext('2d');
  // 创建渐变的基准线(水平)
  // const grad = context.createLinearGradient(0, 0, canv.width, 0);

  // 垂直
  // const grad = context.createLinearGradient(0, 0, 0, canv.height);

  // 斜着来
  const grad = context.createLinearGradient(0, 0, canv.width, canv.height);

  // 添加颜色
  grad.addColorStop(0, 'yellow');
  grad.addColorStop(0.5, 'red');
  grad.addColorStop(1, 'blue');

  // 画矩形
  context.fillStyle = grad; //将渐变加入到矩形中
  context.fillRect(0, 0, canv.width, canv.height);
}

圆形渐变

  • createRadialGradient(x1,y1,r1,x2,y2,r2);
  • x1,y1:表示第一个圆的圆心位置,r1:表示第一个圆的半径,圆柱体或圆锥体基准线

image.png...beautiful 不?

const canv = document.querySelector('#drawing');
if (canv.getContext) {
  const context = canv.getContext('2d');
  const g = context.createRadialGradient(50, 100, 0, 100, 100, 100);
  g.addColorStop(0.0, '#fff');
  g.addColorStop(1.0, '#000');
  context.translate(20, 20);
  context.scale(2, 2);

  context.fillStyle = g;
  context.arc(100, 100, 100, 0, 2 * Math.PI);
  context.fill();
}

绘制路径

  • 用 beginPath() 创建一个路径。在内存里,路径是以一组子路径(直线,弧线等)的形式储存的,它们共同构成一个图形。每次调用 beginPath,子路径组都会被重置,然后可以绘制新的图形。

  • 调用 closePath 方法,它会尝试用直线连接当前端点与起始端点来关闭路径,但如果图形已经关闭或者只有一个点,它会什么都不做。这一步不是必须的

  • 只有在调用 stroke 或 fill 方法时,图形才会真实的绘制到 canvas 上

    • stroke()绘制路径的轮廓
    • fill()绘制路径的填充
    • clip()在上下文中设置裁剪区域(注意:再绘制图像,只能在该区域中)
    • 注意:当调用 fill 时,开放的路径会自动闭合,而无需再调用 closePath

图形渐远

function draw(id) {
  var canvas = document.getElementById(id);
  if (canvas) {
    var ctx = canvas.getContext('2d');
    ctx.fillStyle = '#eeeeef';
    ctx.fillRect(0, 0, 600, 700);
    for (var i = 0; i <= 10; i++) {
      ctx.beginPath();
      ctx.arc(i * 25, i * 25, i * 10, 0, Math.PI * 2, true);
      ctx.closePath();
      ctx.fillStyle = 'rgba(255,0,0,0.25)';
      ctx.fill();
    }
  }
}

创建形状

矩形

rect(x, y, width, height)

圆形

arc(x, y, radius, startAngle, endAngle, [direction])

  • 以(x,y)为圆心绘制一条弧线,弧线半径为radius,起始角度和结束角度(用弧度表示 [角度 * Math.PI/2])

image.png

  • direction: startAngle和endAngle是否按逆时针方向计算(false表示顺时针) 默认是顺时针

image.png

var canv = document.querySelector('#drawing');
if (canv.getContext) {
  var context = canv.getContext('2d');
  context.strokeStyle = 'red';
  context.fillStyle = 'green';

  /*矩形*/
  context.beginPath();
  context.rect(10, 10, 100, 100);
  context.stroke();

  context.beginPath();
  context.rect(120, 10, 100, 100);
  context.fill();

  context.beginPath();
  context.rect(230, 10, 100, 100);
  context.stroke();
  context.fill();

  /*圆形*/
  context.beginPath();
  context.arc(55, 170, 50, 0, (Math.PI * 3) / 2);
  context.stroke();

  context.beginPath();
  context.arc(165, 170, 50, 0, (Math.PI * 3) / 2);
  context.fill();

  context.beginPath();
  context.arc(275, 170, 50, 0, (Math.PI * 3) / 2);
  context.fill();
  context.stroke();

  /*使用closePath*/
  context.beginPath();
  context.arc(55, 280, 50, 0, (Math.PI * 3) / 2);
  context.closePath();
  context.stroke();

  context.beginPath();
  context.arc(165, 280, 50, 0, (Math.PI * 3) / 2);
  context.closePath();
  context.fill(); //没有区别

  context.beginPath();
  context.arc(275, 280, 50, 0, (Math.PI * 3) / 2);
  context.fill();
  context.closePath();
  context.stroke();
}

五角星

/**
 * 绘制五角星
 * @param  {[type]} ctx
 * @param  {[type]} x           小圆圆心位置
 * @param  {[type]} y           小圆圆心位置
 * @param  {[type]} r           小圆半径
 * @param  {[type]} R           大圆半径
 * @param  {[type]} rotate      旋转角度
 * @param  {[type]} lineJoinStyle 设置两条线连接点的形状
 * @param  {[type]} borderWidth [description]
 */
function drawStar(ctx, x, y, r, R, rotate, borderWidth, borderColor, fillColor, lineJoinStyle) {
  ctx.beginPath();
  for (var i = 0; i < 5; i++) {
    ctx.lineTo(
      Math.cos(((18 + i * 72 - rotate) / 180) * Math.PI) * R + x,
      Math.sin(((18 + i * 72 - rotate) / 180) * Math.PI) * R + y
    );
    ctx.lineTo(
      Math.cos(((54 + i * 72 - rotate) / 180) * Math.PI) * r + x,
      Math.sin(((54 + i * 72 - rotate) / 180) * Math.PI) * r + y
    );
  }
  ctx.closePath();

  if (fillColor) {
    ctx.fillStyle = fillColor;
  }
  if (borderColor) {
    ctx.strokeStyle = borderColor;
  }

  ctx.lineWidth = borderWidth;
  ctx.lineJoin = lineJoinStyle;

  fillColor && ctx.fill();
  ctx.stroke();
}

绘制三角形

image.png

var drawing = document.querySelector('#drawing');
if (drawing.getContext) {
  var context = drawing.getContext('2d');
  context.beginPath();
  context.moveTo(10, 10);
  context.lineTo(100, 10);
  context.lineTo(55, 100);
  context.fill();

  // ------------------ 
  context.beginPath();
  context.moveTo(55, 100);
  context.lineTo(10, 200);
  context.lineTo(100, 200);
  context.closePath();
  // context.stroke();
}

设置路径

  • moveTo(x,y): 将当前坐标移动到指定坐标(就是起点)
  • lineTo(x,y): 将当前坐标到指定坐标绘制一条新线(就是终点)

image.png

var canvas = document.getElementById('canvas');
if (canvas.getContext('2d')) {
  var ctx = canvas.getContext('2d');
  //canvas是基于状态变化来进行绘图的
  //1.画直线
  ctx.moveTo(100, 100); //状态,设置笔尖设置位置
  ctx.lineTo(400, 400); //状态,要从笔尖位置连接到当前位置
  ctx.lineTo(200, 400); //状态
  ctx.lineTo(100, 100); //状态,与笔尖开始的坐标首尾相接,就形成了一个多边形
  ctx.lineWidth = 5; //状态,设置线的宽度
  ctx.strokeStyle = '#e8393c'; //状态,设置绘制的颜色
  ctx.stroke(); //这才是真正的绘制
  ctx.fillStyle = 'rgba(0,0,0,.6)';
  ctx.fill();

  // 因为是基于状态的绘制,所以想另画东西,需要使用路径
  ctx.beginPath();
  ctx.moveTo(500, 100);
  ctx.lineTo(600, 200);
  ctx.stroke();
}

设置线形

  • lineWidth: 设置线的宽度,默认为 1

  • lineCap: 设置线端点的形状

    • butt:默认: 与边平行
    • round:圆
    • square:正方形,与butt效果一致
  • lineJoin: 设置两条线连接点的形状

    • round:圆角
    • bevel:斜角
    • miter:默认,尖角
  • miterLimit: 设置线条交接点的延伸范围(0~5:5最大,尖角;0平角)

    • 与lineJoin配合使用,仅当lineJoin="miter"时有效

image.png

var canv = document.querySelector('#drawing');
if (canv.getContext) {
  var context = canv.getContext('2d');
  context.beginPath();
  context.lineWidth = '20';
  context.lineCap = 'round';
  // context.lineJoin = "bevel";
  context.lineJoin = "round";
  context.miterLimit = '3';
  context.moveTo(110, 10);
  context.lineTo(110, 110);
  context.lineTo(200, 40);
  context.stroke();
}

图形组合

  • ctx 属性 globalCompositeOperation = type 属性,如下
    • source-atop : 绘制原有图层被新图层覆盖的部分和新图层其它部分
    • source-in : 只绘制新图形与原图形重合的部分,其他部分透明
    • source-out: 只绘制新图形与与原图不重合的部分,重合部分透明
    • source-over: 新图形在原有图形上面,覆盖(默认)
    • destination-atop:
    • destination-in:
    • destination-out:
    • destination-over:
    • ighter:重叠部分颜色加深
    • copy:
    • xor:只绘制重叠部分

image.png

function draw(id) {
  var canvas = document.getElementById(id);
  var ctx = canvas.getContext('2d');
  var oprtns = new Array(
    'source-atop',
    'source-in',
    'source-out',
    'source-over',
    'destination-atop',
    'destination-in',
    'destination-out',
    'destination-over',
    'lighter',
    'copy',
    'xor'
  );
  
  const i = 8;

  ctx.fillStyle = 'blue';
  ctx.fillRect(10, 10, 60, 60);
  ctx.globalCompositeOperation = oprtns[i];
  ctx.beginPath();
  ctx.fillStyle = 'red';
  ctx.arc(60, 60, 30, Math.PI * 2, false);
  ctx.closePath();
  ctx.fill();
  ctx.stroke();
}

绘制文字

  • font: 与css使用相同
  • textAlign: 设置水平位置 - left、right、center
  • start: 效果与left相同
  • end: 效果与right相同
  • textBaseline: 设置垂直位置 - top、bottom、middle
  • hanging: 悬挂基线
  • alphabetic: 字母基线
  • strokeText(text,x,y); 绘制指定文字的轮廓
  • fillText(text,x,y); 绘制指定文字的填充

艺术字

var canv = document.querySelector('#drawing');
if (canv.getContext) {
  var context = canv.getContext('2d');
  context.font = 'bold 40px Arial';
  var g = context.createLinearGradient(200, 0, 400, 0);
  g.addColorStop(0.0, 'red');
  g.addColorStop(1.0, 'green');
  context.fillStyle = g;
  context.fillText('We are family', 200, 200);
  context.strokeStyle = 'blue';
  context.strokeText('We are family', 200, 400);
}

文字度量

  • context.measureText(str).width - str 文字字符串
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
context.font = "italic bold 24px Arial";
var s = '文艺青年';
context.fillText(s, 50, 200);
console.log(context.measureText(s).width); // 96

文字对齐

image.png

var canvas = document.getElementById('canvas'); // 800 x 500
var context = canvas.getContext('2d');
context.font = 'italic bold 24px Arial';
context.textAlign = 'center';
context.textBaseline = 'middle';
context.fillText('canvas', 400, 250);
context.moveTo(0, 250);
context.lineTo(800, 250);
context.stroke();
context.moveTo(400, 0);
context.lineTo(400, 500);
context.stroke();

绘制阴影

  • shadowColor: 设置阴影颜色,默认为黑色
  • shadowOffsetX: 表示当前阴影为水平效果,值为数字( +阴影在右,-阴影在左边
  • shadowOffsetY: 表示当前阴影为垂直效果,值为数字( +阴影在下,-阴影在上边
  • shadowBlur: 设置当前阴影效果,值为数字(值越大,越模糊 ,最小值为0

image.png

var canv = document.querySelector('#drawing');
if (canv.getContext) {
  var context = canv.getContext('2d');
  context.fillStyle = 'green';
  context.shadowColor = 'gray';
  context.shadowOffsetX = 10;
  context.shadowOffsetY = 10;
  // 图二
  // context.shadowOffsetX = 100;
  // context.shadowOffsetY = 100;
  context.shadowBlur = 50;
  context.fillRect(0, 0, 100, 100);
  
  // 图三
  // context.shadowOffsetX = -10;
  // context.shadowOffsetY = -10;
  // context.shadowBlur = 100;
  // context.fillRect(40, 40, 100, 100);
}

扩展:canvas - little demo