canvas笔记

146 阅读4分钟

历史

Canvas由 Apple 在 Safari 1.3 Web 浏览器中引入。对 HTML 的这一根本扩展的原因在于,HTML 在 Safari 中的绘图能力也为 Mac OS X 桌面Dashboard 组件所使用,并且 Apple 希望有一种方式在 Dashboard 中支持脚本化的图形。Firefox 1.5 和 Opera 9 都跟随了 Safari 的引领。这两个浏览器都支canvas。我们甚至可以在 IE 中使用 。Canvas 的标准化的努力由一个 Web 浏览器厂商的非正式协会在推进,已经成为 HTML 5 草案中一个正式的标签。

概述

Canvas是一个矩形区域的画布,使用 js在网页上绘制图像,本身不具备绘图功能。Canvas API拥有多种绘制路径、矩形、圆形、字符以及添加图像等方法。

应用场景

1、可视化数据: 各类统计图表,如百度的echart等。

2、 场景秀:canvas实现动态的广告效果能够非常融洽的跨平台运行。

3、 游戏: canvas在基于Web的图像显示方面比Flash更加立体、更加精巧,成为H5小游戏开发首选。

4、图形编辑器: 一些图形编辑器将能够100%基于Web实现,如PS等。

使用

创建画布

绘画第一步,先拿张纸。通过html和js两张方式。

html创建

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Canvas</title>
</head>
<body>
  <canvas width="200" height="200">
    当前浏览器不支持canvas元素,请升级或更换浏览器!
  </canvas>
</body>
</html>

js创建

const canvas = document.createdElement('canvas')
canvas.width = 200
canvas.height = 200
document.body.appendChild(canvas)

创建渲染上下文

画布就相当于一张纸,光有纸,没有其他绘画工具是不行的,所以需要创建渲染上下文来获取这些工具。通过getContext() 方法。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Canvas</title>
</head>
<body>
  <canvas id="canvas" width="200" height="200">
    当前浏览器不支持canvas元素,请升级或更换浏览器!
  </canvas>
  <script>
    // 获取 canvas 元素
    const canvas = document.querySelector('#canvas');
​
    // 获取渲染上下文
    const ctx = canvas.getContext('2d');
  </script>
</body>
</html>

开始绘画

绘制图形

canvas为我们提供了一些现成的几何图形和线条等。

上面我们已经准备好了纸和工具,在开始绘画前还是要先了解一下工具的一些基本用法。

moveTo(x, y)

设置初始位置,相当于下笔点。

lineTo(x, y)

绘制一条从初始位置到指定位置的直线,相当于收笔点。

stroke()

通过线条来绘制图形轮廓,上面两步是规划,这是真正执行绘画动作。

fill()

通过填充路径的内容区域,上面两步是规划,这是真正执行绘画动作。

<!DOCTYPE html>
<html lang="en">
<head>...</head>
<body>
  <canvas id="canvas" width="500" height="500">
    当前浏览器不支持canvas元素,请升级或更换浏览器!
  </canvas>
  <script>
    const canvas = document.querySelector('#canvas');
    const ctx = canvas.getContext('2d');
      
    // 绘制一条从起点50,50到另一个点200,200的直线
    ctx.moveTo(50, 50);
    ctx.lineTo(200, 200);
    ctx.stroke();
    
    // 画个线框矩形 
    // strokeRect(起点x, 起点y, 宽, 高)
    ctx.strokeRect(50, 50, 200, 200)
    // 画个填充矩形 
    // fillRect(起点x, 起点y, 宽, 高)
    ctx.fillRect(300, 300, 100, 100)
    // 矩形擦除(矩形橡皮擦) 
    // clearRect(起点x, 起点y, 宽, 高)
    ctx.clearRect(300, 300, 50, 50)
      
    // 角度转弧度的表达式:弧度 = (Math.PI / 180) * 角度
      
    // 画个线框圆弧
    // arc(圆心x, 圆心y, 半径, 开始弧度, 结束弧度, 绘制方向-默认false,顺时针)
    ctx.beginPath()
    ctx.arc(250, 250, 100, 0 , 2 * Math.PI, false)
    ctx.stroke()
    ctx.closePath()
    // 画个填充圆弧
    ctx.beginPath()
    ctx.arc(250, 400, 50, 0 , 2 * Math.PI, false)
    ctx.fill()
    ctx.closePath()
    
    // 画个线框椭圆 
    // ellipse(圆心x, 圆心y, x轴半径, y轴半径, 旋转弧度, 开始弧度, 结束弧度, 绘制方向-默认false,顺时针)
    ctx.beginPath()
    ctx.ellipse(250, 250, 50, 100, Math.PI / 2, 0, 2 * Math.PI)
    ctx.stroke()
    // 画个填充椭圆
    ctx.beginPath()
    ctx.ellipse(250, 250, 50, 100, 1, 0, 2 * Math.PI)
    ctx.fill()
      
    // 二次贝塞尔曲线 
    // quadraticCurveTo(控制点x, 控制点y, 结束点x, 结束点y)
    ctx.beginPath()
    ctx.moveTo(50, 50)
    ctx.quadraticCurveTo(200, 200, 350, 50)
    ctx.stroke()
      
    // 三次贝塞尔曲线 
    // bezierCurveTo(控制点x1, 控制点y1, 控制点x2, 控制点y2, 结束点x, 结束点y)
    ctx.beginPath()
    ctx.moveTo(50, 200)
    ctx.bezierCurveTo(150, 50, 250, 350, 350, 200)
    ctx.stroke()
  </script>
</body>
</html>

绘制样式

好看的画面肯定少不了好看颜色、漂亮的图案和不同样式的线条等。canvas提供了一些属性来实现这些。

线条样式

lineWidth 设置当前绘线的粗细,值必须为正数,默认1。

lineCap 设置线段两端样式,可选butt,round 和 square,默认butt。

lineJoin 设置两线段连接处所显示的样子,可选round,bevel和miter,默认miter。

miterLimit 限制当两段线相交时交接处内角顶点到外角顶点的长度。如果交点距离大于miterLimit值,连接处会变成了lineJoin为bevel的效果。

setLineDash 设置线条为虚线,如果传参为奇数,会复制一份数组补全为偶数。

getLineDash 返回线条虚线设置的样式,长度为非负偶数的数组。

lineDashOffset 设置虚线样式的起始偏移量。

<!DOCTYPE html>
<html lang="en">
<head>...</head>
<body>
  <canvas id="canvas" width="300" height="300">
    当前浏览器不支持canvas元素,请升级或更换浏览器!
  </canvas>
  <script>
    var canvas = document.getElementById('canvas');
    // 通过判断getContext方法是否存在来判断浏览器的支持性
    if(canvas.getContext) {
      // 获取绘图上下文
      var ctx = canvas.getContext('2d');
      // 绘制一条宽度为10的直线
      ctx.beginPath()
      ctx.lineWidth = 10;
      ctx.moveTo(50, 20);
      ctx.lineTo(250, 20);
      ctx.stroke();
      ctx.closePath();
      // 绘制一条宽度为20的直线
      ctx.beginPath()
      ctx.lineWidth = 20;
      ctx.moveTo(50, 50);
      ctx.lineTo(250, 50);
      ctx.stroke();
      ctx.closePath();
        
      // lineWidth 设置当前绘线的粗细,值必须为正数,默认1。
      // 绘制一条宽度为10的直线
      ctx.beginPath()
      ctx.lineWidth = 10;
      ctx.moveTo(50, 20);
      ctx.lineTo(250, 20);
      ctx.stroke();
​
      // lineCap 设置线段两端样式,可选butt,round 和 square,默认butt。
      // butt
      ctx.beginPath()
      ctx.lineCap='butt'
      ctx.moveTo(50, 20);
      ctx.lineTo(250, 20);
      ctx.stroke();
​
      // round
      ctx.beginPath()
      ctx.lineCap='round'
      ctx.moveTo(50, 50);
      ctx.lineTo(250, 50);
      ctx.stroke();
    
      // square
      ctx.beginPath()
      ctx.lineCap='square'
      ctx.moveTo(50, 80);
      ctx.lineTo(250, 80);
      ctx.stroke();
​
      // lineJoin 设置两线段连接处所显示的样子,可选round,bevel和miter,默认miter。
      // miter
      ctx.beginPath()
      ctx.lineJoin='miter'
      ctx.moveTo(50, 20);
      ctx.lineTo(100, 60);
      ctx.lineTo(150, 20);
      ctx.lineTo(200, 60);
      ctx.lineTo(250, 20);
      ctx.stroke();
    
      // round
      ctx.beginPath()
      ctx.lineJoin='round'
      ctx.moveTo(50, 100);
      ctx.lineTo(100, 140);
      ctx.lineTo(150, 100);
      ctx.lineTo(200, 140);
      ctx.lineTo(250, 100);
      ctx.stroke();
​
      // bevel
      ctx.beginPath()
      ctx.lineJoin='bevel'
      ctx.moveTo(50, 180);
      ctx.lineTo(100, 220);
      ctx.lineTo(150, 180);
      ctx.lineTo(200, 220);
      ctx.lineTo(250, 180);
      ctx.stroke();
​
      // miterLimit 限制当两段线相交时交接处内角顶点到外角顶点的长度。
      // 如果交点距离大于miterLimit值,连接处会变成了lineJoin为bevel的效果。
      ctx.beginPath()
      ctx.lineWidth = 5;
      ctx.lineJoin='miter'
      ctx.miterLimit = 10
      ctx.moveTo(0, 100);
      for (i = 0; i < 30 ; i++) {
        const dy = i % 2 == 0 ? 200 : 100;
        ctx.lineTo(Math.pow(i, 1.5) * 2, dy);
      }
      ctx.stroke();
      ctx.closePath();
​
      // setLineDash 设置线条为虚线,如果传参为奇数,会复制一份数组补全为偶数。
      // getLineDash 返回线条虚线设置的样式,长度为非负偶数的数组。
      // 绘制一条虚线
      ctx.setLineDash([5, 10, 20]);
      console.log(ctx.getLineDash()); // [5, 10, 20, 5, 10, 20]
      ctx.beginPath();
      ctx.moveTo(0,100);
      ctx.lineTo(400, 100);
      ctx.stroke();
​
      // 再绘制一条虚线
      ctx.setLineDash([5, 10, 20, 40]);
      console.log(ctx.getLineDash()); // [5, 10, 20, 40]
      ctx.beginPath();
      ctx.moveTo(0,200);
      ctx.lineTo(400, 200);
      ctx.stroke();
​
      // lineDashOffset 设置虚线样式的起始偏移量。
      // 起始点向左位移了3像素
      ctx.lineDashOffset = 3;
    }
  </script>
</body>
</html>
透明度

globalAlpha 设置透明度值,取值0~1。

<!DOCTYPE html>
<html lang="en">
<head>...</head>
<body>
  <canvas id="canvas" width="300" height="300">
    当前浏览器不支持canvas元素,请升级或更换浏览器!
  </canvas>
  <script>
    const canvas = document.querySelector('#canvas');
    const ctx = canvas.getContext('2d');
​
    // 绘制一个圆
    ctx.beginPath()
    ctx.fillStyle = "rgba(255, 255, 0, 1)";
​
    // globalAlpha 设置透明度值,0 ~ 1。
    ctx.globalAlpha = 0.5;
    ctx.arc(200, 200, 100, 0, Math.PI*2, true);
    ctx.fill();
  </script>
</body>
</html>
渐变

createLinearGradient(起点x, 起点y, 终点x, 终点y) 设置线性渐变。

createRadialGradient(圆心x0, 圆心y0, 半径r0, 圆心x1, 圆心y1, 半径r1) 设置径向渐变。

addColorStop(offset, color) 添加渐变的颜色,offset颜色偏移值,取值0 ~1,color颜色。

<!DOCTYPE html>
<html lang="en">
<head>...</head>
<body>
  <canvas id="canvas" width="300" height="300">
    当前浏览器不支持canvas元素,请升级或更换浏览器!
  </canvas>
  <script>
    const canvas = document.querySelector('#canvas');
    const ctx = canvas.getContext('2d');
​
    // 创建线性渐变
    const gradient1 = ctx.createLinearGradient(10, 10, 400, 10);
    gradient1.addColorStop(0, "#00ff00");
    gradient1.addColorStop(1, "#ff0000");
    const gradient2 = ctx.createLinearGradient(10, 10, 400, 10);
    
    // 从0.5的位置才开始渐变
    gradient2.addColorStop(0.5, "#00ff00");
    gradient2.addColorStop(1, "#ff0000");
    ctx.beginPath()
    ctx.fillStyle = gradient1;
    ctx.fillRect(10, 10, 400, 100);
    ctx.beginPath();
    ctx.fillStyle = gradient2;
    ctx.fillRect(10, 150, 400, 100);
  </script>
</body>
</html>
图案样式

createPattern(image, type) 绘制图案效果,image 图片对象,type 绘制方式,可选repeat,repeat-x,repeat-y 和 no-repeat。

<!DOCTYPE html>
<html lang="en">
<head>...</head>
<body>
  <canvas id="canvas" width="300" height="300">
    当前浏览器不支持canvas元素,请升级或更换浏览器!
  </canvas>
  <script>
    const canvas = document.querySelector('#canvas');
    const ctx = canvas.getContext('2d');
​
    // 创建Image对象
    const img = new Image();
    img.src = "./image.png";
    img.onload = () => {
        // 创建图案
        // const ptrn = ctx.createPattern(img, 'repeat');
        // const ptrn = ctx.createPattern(img, 'repeat-x');
        // const ptrn = ctx.createPattern(img, 'repeat-y');
        const ptrn = ctx.createPattern(img, 'no-repeat');
        ctx.fillStyle = ptrn;
        ctx.fillRect(0, 0, 500, 500);
    }
  </script>
</body>
</html>

绘制文本

canvas提供了描边和填充两种方法来渲染文本。

strokeText(文本, 起点x, 起点y[, 最大宽度]) 描边创建文本。

fillText(文本, 起点x, 起点y[, 最大宽度]) 填充填充文本。

<!DOCTYPE html>
<html lang="en">
<head>...</head>
<body>
  <canvas id="canvas" width="300" height="300">
    当前浏览器不支持canvas元素,请升级或更换浏览器!
  </canvas>
  <script>
    const canvas = document.querySelector('#canvas');
    const ctx = canvas.getContext('2d');
​
    // 设置文本大小和字体
    ctx.font = "50px serif";
    ctx.strokeText("Canvas", 50, 50);
    ctx.fillText("Canvas", 50, 250);
  </script>
</body>
</html>

绘制图片

drawImage(image[, sx, sy, swidth, sheight], dx, dy[, width, height]) 绘制图片、视频和别的Canvas对象等。image为一个Image对象,sx、sy为图片裁切位置,swidth,sheight为图片裁切的宽高,dx、dy为图片位置,width,height为绘制图片的宽高。

<!DOCTYPE html>
<html lang="en">
<head>...</head>
<body>
  <canvas id="canvas" width="300" height="300">
    当前浏览器不支持canvas元素,请升级或更换浏览器!
  </canvas>
  <script>
    const canvas = document.querySelector('#canvas');
    const ctx = canvas.getContext('2d');
​
    const image = new Image()
    image.src = '/assets/images/dog.png'
    image.onload = () => {
​
      // 将图片以自身尺寸绘制在画布0,0(左上角)的位置
      ctx.drawImage(image, 0, 0)
​
      // 将图片以100 x 100的尺寸绘制在画布0,0(左上角)的位置
      ctx.drawImage(image, 0, 0, 100, 100)
​
      // 将图片从图上10,10的位置裁切一个宽高为100 x 100的部分,再以200 x 200的尺寸绘制在画布20,20(左上角)的位置
      ctx.drawImage(image, 10, 10, 100, 100, 20, 20, 200, 200)
    }
  </script>
</body>
</html>

变形

translate(x, y) 移动,x 是左右偏移量,y 是上下偏移量。

rotate(angle) 旋转,angle是旋转的角度,它是顺时针旋转,以弧度为单位的值。

scale(x, y) 缩放,x 为水平缩放的值,y 为垂直缩放得值。x和y的值小于1则为缩小,大于1则为放大。默认值为 1。

<!DOCTYPE html>
<html lang="en">
<head>
  <style>
    body {
      margin: 0;
      padding: 0;
    }
  </style>
</head><body>
  <canvas id="canvas" width="300" height="300">
    当前浏览器不支持canvas元素,请升级或更换浏览器!
  </canvas>
  <script>
    const canvas = document.querySelector('#canvas');
    const ctx = canvas.getContext('2d');
​
    ctx.fillStyle = '#ee7034';
​
    // 移动原点到100,100
    ctx.translate(100, 100);
​
    // 画一个100,100的矩形,由于上面移动了原点,所以其实是从画布100,100开始绘制的
    ctx.fillRect(0, 0, 100, 100);
​
    // 旋转了45度,Math.PI=180度
    ctx.rotate(Math.PI / 4);
    ctx.fillRect(0, 0, 100, 100);
​
    // 缩放
    ctx.scale(2, 2);
    ctx.fillRect(100, 100, 100, 100);
  </script>
</body>
</html>