[HTML5]快速掌握canvas

背景

canvas 是 html5 标准中提供的一个标签, 顾名思义是定义在浏览器上的画布

  1. 通过其强大的绘图接口,我们可以实现各种各样的图形,炫酷的动画,
  2. 甚至可以利用他开发小游戏,包括市面上很流行的数据可视化框架底层都用到了Canvas。
  3. 既然他这么强大,那我们还有什么理由不去学习它呢?

基础

渲染上下文

<canvas>是一个HTML元素,我们可以将它简单理解为一个画板,通过Canvas提供的绘制api我们就可以绘制出各种图形。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .canvas {
      background-color: pink;
    }
  </style>
</head>

<body>
  <canvas id="canvas" width="600" height="600" class="canvas" />

  <script>
    // 获取画布
    const c = document.getElementById("canvas")

    /**
     * 获取画笔
     * 可选参数: 
     * 2d: 获取平面画笔, 绘制2d图形
     * webgl: 获取3d画笔, 绘制3d图形 
     * 
    */
    const ctx = c.getContext("2d")

  </script>
</body>

</html>

canvas中的坐标系和html中的坐标系一样

  1. 坐标: 控制图形画在什么位置
  2. 尺寸: 控制图形画多大

绘制图形

1.1. 线(线, 三角形, 矩形)

重要的API

  • 绘制: moveTo, lineTo,stroke
  • 设置样式: lineWidth,strokeStyle
  • 路径: beginPath,closePath
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .canvas {
      background-color: pink;
    }
  </style>
</head>

<body>
  <canvas id="canvas" width="600" height="600" class="canvas" />

  <script>
    // 获取画布
    const c = document.getElementById("canvas")

    /**
     * 获取画笔
    */
    const ctx = c.getContext("2d")

    /**
     * 绘制线条 
     */
    ctx.moveTo(0, 10) // 确定画笔位置
    ctx.lineTo(200, 10) // 线的终点
    ctx.lineWidth = 50 // 设置线的宽度
    ctx.strokeStyle = 'red' // 设置画笔的颜色 (画笔是全局唯一实例)
    ctx.stroke() // 完成绘制

    /**
    * 绘制线条 
    */
    ctx.beginPath() // 新建路径, 不影响其他路径
    ctx.moveTo(0, 50)
    ctx.lineTo(200, 50)
    ctx.lineWidth = 10
    ctx.strokeStyle = 'blue'
    ctx.stroke() // stroke的绘制以beginPath为界

    /**
    * 绘制三角形
    */
    ctx.beginPath()
    ctx.moveTo(0, 70)
    ctx.lineTo(200, 70)
    ctx.lineTo(50, 200)
    // ctx.lineTo(0, 70) // 手动画线完成路径闭合
    ctx.closePath() // 让路径自动闭合

    ctx.lineWidth = 1
    ctx.fillStyle = 'green' // 设置填充颜色
    ctx.fill() // 完成填充

    ctx.strokeStyle = 'red' // 设置画笔颜色
    ctx.stroke() // 完成绘制

    /**
   * 绘制矩形1
   */
    ctx.beginPath()
    // 上定义矩形: 四个参数分别表示矩形左上角的 x 坐标、y 坐标,以及矩形的宽度和高度。
    ctx.rect(0, 220, 200, 100)
    ctx.strokeStyle = 'green'
    ctx.stroke()

    /**
   * 绘制矩形2
   */
   ctx.beginPath()
   // 绘制矩形并设置填充色
   ctx.fillStyle = 'red'
   ctx.fillRect(10, 350, 150 ,100)
   // 绘制矩形并设置边框颜色
   ctx.strokeStyle = 'green'
   ctx.strokeRect(50, 460, 150, 100)


  </script>
</body>

</html>

1.2. 弧线(弧, 圆弧, 圆)

arcTo(x1, y1, x2, y2, radius);

  1. x1和y1: 画笔的坐标
  2. x2和y2: 两个辅助点
  3. radius: 控制内切圆的半径, 半径越大弧越大
  4. 三个点相连组成角, 根据半径计算内切圆, 圆和三角相交的地方叫切点
  5. 切点相连就画出了一个弧, 弧与起点相连, 就是最终得到的弧线
  6. 示例
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .canvas {
      background-color: pink;
    }
  </style>
</head>

<body>
  <canvas id="canvas" width="600" height="600" class="canvas" />

  <script>
    // 获取画布
    const c = document.getElementById("canvas")

    // 获取画笔
    const ctx = c.getContext("2d")

    // 画弧线
    ctx.beginPath()
    ctx.moveTo(200, 10) // 画笔位置就是起点
    ctx.arcTo(200, 300, 0, 10, 80) // 画弧
    ctx.lineWidth = 5
    ctx.strokeStyle = 'green'
    ctx.stroke()

    // 画辅助线帮助理解
    ctx.strokeStyle = 'red'
    ctx.lineWidth = 1
    ctx.beginPath()
    ctx.moveTo(200, 10)
    ctx.lineTo(200, 300)
    ctx.lineTo(200, 300)
    ctx.lineTo(0, 10)
    ctx.closePath()
    ctx.stroke()


  </script>
</body>

</html>

arc(x, y,radius, startAngle, endAngle, anticlockwise);

  1. 参数x, y: 圆心的坐标
  2. 参数radius: 圆的半径
  3. 参数startAngle, endAngle: 起始角度和终止角度
  4. 参数anticlockwise: 顺时针还是逆时针, 默认顺时针
  5. 示例代码
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .canvas {
      background-color: pink;
    }
  </style>
</head>

<body>
  <canvas id="canvas" width="600" height="600" class="canvas" />

  <script>
    // 获取画布
    const c = document.getElementById("canvas")
    // 获取画笔
    const ctx = c.getContext("2d")

    // 画弧
    ctx.beginPath()
    ctx.arc(400, 400, 100, 0, Math.PI / 6, true)
    ctx.strokeStyle = "red"
    ctx.stroke()

  </script>
</body>

</html>

1.3. 贝塞尔曲线

贝塞尔曲线二阶

quadraticCurveTo(cpx, cpy, x, y);

  1. 参数说明
  • cpx, cpy 是控制点的坐标
  • x,y 是结束点的坐标
  • 通过调整控制点, 就可以生成各种曲线
  1. 二次贝塞尔曲线调试工具:

Canvas Quadratic Curve Example

  1. 示例代码
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .canvas {
      background-color: pink;
    }
  </style>
</head>

<body>
  <canvas id="canvas" width="600" height="600" class="canvas" />

  <script>
    // 获取画布
    const c = document.getElementById("canvas")

    // 获取画笔
    const ctx = c.getContext("2d")

    // ==================================
    // 画二阶
    ctx.beginPath()
    ctx.moveTo(100, 100) // 起始点坐标
    ctx.quadraticCurveTo(200, 500, 400, 400)
    ctx.stroke()

    // 辅助点
    ctx.fillStyle = 'red'
    ctx.fillRect(100, 100, 10, 10)
    ctx.fillRect(200, 500, 10, 10)
    ctx.fillRect(400, 400, 10, 10)
    // ==================================


  </script>
</body>

</html>

贝塞尔曲线三阶

bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)

  1. 参数说明
  • 三阶就是比二阶多了一个控制点
  • cpx1, cpy1 是控制点1的坐标
  • cpx2, cpy2 是控制点2的坐标
  • x,y 是结束点的坐标
  • 通过调整控制点, 就可以生成各种曲线
  1. 三次贝塞尔调试工具:

Canvas Bézier Curve Example

  1. 示例代码
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .canvas {
      background-color: pink;
    }
  </style>
</head>

<body>
  <canvas id="canvas" width="600" height="600" class="canvas" />

  <script>
    // 获取画布
    const c = document.getElementById("canvas")

    // 获取画笔
    const ctx = c.getContext("2d")

    // ==================================
    // 画三阶
    ctx.beginPath()
    ctx.moveTo(20, 20)
    ctx.bezierCurveTo(20, 700, 200, 100, 700, 500)
    ctx.stroke()
    // ==================================

  </script>
</body>

</html>

绘图样式

a. 线条样式

  1. lineWidth: 设置线条的宽度
  2. lineCap: 设置线条两端的形状

  1. setLineDash: 设置线条为虚线
  2. 代码示例
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .canvas {
      background-color: pink;
    }
  </style>
</head>

<body>
  <canvas id="canvas" width="600" height="600" class="canvas" />

  <script>
    // 获取画布
    const c = document.getElementById("canvas")

    // 获取画笔
    const ctx = c.getContext("2d")

    // 线段样式
    ctx.lineWidth = 15
    ctx.lineCap = 'round'

    ctx.beginPath()
    ctx.moveTo(50, 50)
    ctx.lineTo(300, 50)
    ctx.stroke()

    // 绘制虚线
    ctx.beginPath()
    ctx.moveTo(50, 150)
    // 参数的是数组, 控制一组线段
    // 20是第一个线段长度,40是间隔距离, 50是第二个线段长度 
    ctx.setLineDash([20, 40, 50])
    ctx.lineWidth = 1;
    ctx.lineTo(500, 150)
    ctx.stroke()

  </script>
</body>

</html>

b. 渐变

  1. 线性渐变 ctx.createLinearGradient(x0,y0,x1, y1);
  • 参数说明
  • x0,y0是渐变的起点坐标
  • x1, y1是渐变的终点坐标
  • 有了起点和终点就确定了渐变的方向
  1. 径向渐变 ctx.createRadialGradient(x0,y0,r0, x1, y1, r1)
  • 参数说明
  • x0, y0是第一个圆的坐标(圆心), r0是半径
  • x1, y1是第二个圆的坐标(圆心), r1是半径
  1. 示例代码
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .canvas {
      background-color: pink;
    }
  </style>
</head>

<body>
  <canvas id="canvas" width="600" height="600" class="canvas" />

  <script>
    // 获取画布
    const c = document.getElementById("canvas")

    // 获取画笔
    const ctx = c.getContext("2d")

    // 线性渐变
    let gradient = ctx.createLinearGradient(0, 0, 200, 0) // 设置渐变
    gradient.addColorStop(0, 'green') // 设置渐变点和颜色值
    gradient.addColorStop(1, 'blue')  // 设置渐变点和颜色值
    ctx.fillStyle = gradient
    ctx.fillRect(50, 200, 200, 100) // 绘制矩形

    // 径向渐变
    let gradient2 = ctx.createRadialGradient(150, 450, 150, 150, 450, 0) // 设置渐变
    gradient2.addColorStop(0, 'white')
    gradient2.addColorStop(1, 'green')
    ctx.fillStyle = gradient2
    ctx.fillRect(50, 350, 200, 200)

  </script>
</body>

</html>

c. 纹理样式

  1. ctx.createPattern(image, repetition);

参数说明

  • image: 把图片作为纹理进行填充
  • repetition: 是否重复

代码示例

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .canvas {
      background-color: pink;
    }
  </style>
</head>

<body>
  <canvas id="canvas" width="600" height="600" class="canvas" />

  <script>
    // 获取画布
    const c = document.getElementById("canvas")

    // 获取画笔
    const ctx = c.getContext("2d")

    // 纹理样式
    let img = new Image()
    img.src = 'images/DE.jpg'
    img.onload = function () {
      let pattern = ctx.createPattern(img, 'repeat')
      ctx.fillStyle = pattern
      ctx.fillRect(50, 200, 300, 100)
    }

  </script>
</body>

</html>

绘制文本

绘制方式

  • 轮廓文本 stokeText()
  • 填充文本 fillText()

绘制样式

  • font、textAlign(设置水平对齐方式)、direction(设置文字方向)、textBaseline(设置垂直对齐方式)
  • 阴影: shadowOffsetX和shadowOffsetY、shadowBlur、shadowColor

示例代码

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .canvas {
      background-color: pink;
    }
  </style>
</head>

<body>
  <canvas id="canvas" width="600" height="600" class="canvas" />

  <script>
    // 获取画布
    const c = document.getElementById("canvas")

    // 获取画笔
    const ctx = c.getContext("2d")

    // 设置文字样式 (字号: 88px 字体: 罗马字体)
    ctx.font = "88px Times New Roman"
    // 设置阴影
    ctx.shadowOffsetX = 2 //偏移
    ctx.shadowOffsetY = 2 //偏移
    ctx.shadowBlur = 2    //模糊度
    ctx.shadowColor = "rgba(255,0,0,0.5)"
    // 绘制文本
    ctx.fillText('hello canvas', 100, 100)


    // 纹理背景文字
    let img = new Image()
    img.src = 'images/DE.jpg'
    img.onload = function () {
      let pattern = ctx.createPattern(img, 'repeat')
      ctx.fillStyle = pattern
      ctx.fillText('hello canvas2', 100, 400)
    }

  </script>
</body>

</html>

绘制图片

drawimage用法

  1. drawlmage(image, dx, dy);
  2. drawlmage(image, dx, dy, dWidth, dHeight);
  3. drawlmage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .canvas {
      background-color: pink;
    }
  </style>
</head>

<body>
  <canvas id="canvas" width="600" height="600" class="canvas" />

  <script>
    // 获取画布
    const c = document.getElementById("canvas")

    // 获取画笔
    const ctx = c.getContext("2d")

    // 绘制图片
    let img = new Image()
    img.src = 'images/DE.jpg'
    img.onload = function () {
      // 1. 在指定位置画一张图
      // drawlmage(image, dx, dy); 
      ctx.drawImage(img, 0, 0)

      // 2.在指定位置画一张图, 并设置图的尺寸
      // drawlmage(image, dx, dy, dWidth, dHeight);
      ctx.drawImage(img, 0, 120, 50, 50)


      // 3.指定一张图(image), 选择抠图的位置(sx, sy), 设置抠取的尺寸(sWidth, sHeight), 把抠出来的图放在哪(dx, dy), 放多大(dWidth, dHeight)
      // drawlmage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
      ctx.drawImage(img, 0, 0, 80, 80, 100, 200, 50, 50)
    }
  </script>
</body>

</html>

进阶

变形

  1. 平移, 旋转, 缩放

平移(translate)、旋转(rotate)、缩放(scale)

平移改变的是画布的坐标系位置

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .canvas {
      background-color: pink;
    }
  </style>
</head>

<body>
  <canvas id="canvas" width="600" height="600" class="canvas" />

  <script>
    // 获取画布
    const c = document.getElementById("canvas")
    // 获取画笔
    const ctx = c.getContext("2d")

    //==================================
    // 测试平移的效果
    // 画个矩形
    ctx.fillStyle = 'red'
    ctx.fillRect(0, 0, 100, 100)

    // 平移坐标后再画一个矩形
    ctx.translate(400, 400)
    ctx.fillRect(0, 0, 100, 100)

    // 把坐标平移回原点, 再画一个矩形
    ctx.translate(-400, -400)
    ctx.fillStyle = 'black'
    ctx.fillRect(0, 0, 50, 50)

  </script>
</body>

</html>

旋转和缩放是对图形的操作

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .canvas {
      background-color: pink;
    }
  </style>
</head>

<body>
  <canvas id="canvas" width="600" height="600" class="canvas" />

  <script>
    // 获取画布
    const c = document.getElementById("canvas")
    // 获取画笔
    const ctx = c.getContext("2d")

    // 旋转图形
    ctx.fillStyle = 'yellow'
    ctx.rotate(Math.PI / 6) // 旋转60度
    ctx.fillRect(100, 0, 100, 100)
    ctx.restore()

    // 缩放图形
    ctx.fillStyle = 'blue'
    ctx.scale(0.5, 0.5)
    ctx.fillRect(400, 400, 100, 100)
    ctx.restore()

  </script>
</body>

</html>

  1. 状态的保存和恢复 (栈结构)

画布的状态是可以保存和恢复的

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .canvas {
      background-color: pink;
    }
  </style>
</head>

<body>
  <canvas id="canvas" width="600" height="600" class="canvas" />

  <script>
    // 获取画布
    const c = document.getElementById("canvas")
    // 获取画笔
    const ctx = c.getContext("2d")

    // 上案例的写法需要不断平移坐标, 比较麻烦
    // 通过保存和恢复画布状态, 简化对画布的操作

    // 画个矩形
    ctx.fillStyle = 'red'
    ctx.fillRect(0, 0, 100, 100)

    // 平移前先保存画布的状态信息
    ctx.save()
    // 平移坐标后再画一个矩形
    ctx.translate(400, 400)
    ctx.fillRect(0, 0, 100, 100)
    // 绘制完平移的图形后,恢复之前的状态信息
    ctx.restore()

    // 再绘制图形就不受平移的影响了
    ctx.fillStyle = 'black'
    ctx.fillRect(0, 0, 50, 50)

  </script>
</body>

</html>

  1. transform, setTransform

矩阵变换

语法: transform(a, b, c, d, e, f);

参数:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .canvas {
      background-color: pink;
    }
  </style>
</head>

<body>
  <canvas id="canvas" width="600" height="600" class="canvas" />

  <script>
    // 获取画布
    const c = document.getElementById("canvas")
    // 获取画笔
    const ctx = c.getContext("2d")

    // 矩阵变换-平移
    ctx.transform(1, 0, 0, 1, 400, 400)
    ctx.fillStyle = 'red'
    ctx.fillRect(0, 0, 100, 100)
    ctx.restore()

    // 矩阵变换-缩放
    ctx.transform(0.5, 0, 0, 0.5, 10, 10)
    ctx.fillStyle = 'green'
    ctx.fillRect(0, 0, 100, 100)
    ctx.restore()

    // 矩阵变换-倾斜
    ctx.transform(1, 0.2, 0.2, 1, 10, 10)
    ctx.fillStyle = 'blue'
    ctx.fillRect(0, 0, 100, 100)
    ctx.restore()

  </script>
</body>

</html>

合成

Canvas 提供了26 种图片混排模式

  1. 语法: ctx.globalCompositeOperation = type;
  2. 重点参数: destination-out, 可以实现镂空效果

  1. 其他的合成模式

  1. 示例代码
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .canvas {
      background-color: pink;
    }
  </style>
</head>

<body>
  <canvas id="canvas" width="600" height="600" class="canvas" />

  <script>
    // 获取画布
    const c = document.getElementById("canvas")
    // 获取画笔
    const ctx = c.getContext("2d")


    ctx.fillStyle = 'blue'
    ctx.fillRect(10, 10, 100, 100)

    // source-over: 默认值, 后绘制的图形覆盖先绘制的图形
    // ctx.globalCompositeOperation = 'source-over'

    // destination-over: 先绘制的图形覆盖后绘制的图形
    // ctx.globalCompositeOperation = 'destination-over'

    // destination-out: 图形相交的部分镂空
    ctx.globalCompositeOperation = 'destination-out'

    ctx.fillStyle = 'red'
    ctx.fillRect(50, 50, 100, 100)

  </script>
</body>

</html>

裁剪

clip() 需要配置路径使用

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .canvas {
      background-color: pink;
    }
  </style>
</head>

<body>
  <canvas id="canvas" width="600" height="600" class="canvas" />

  <script>
    // 获取画布
    const c = document.getElementById("canvas")
    // 获取画笔
    const ctx = c.getContext("2d")

    ctx.rect(0, 0, 200, 100) // 矩形区域
    ctx.clip() // 超出矩形区域的文字会被裁剪

    ctx.fillStyle = 'red'
    ctx.font = '44px Times New Roman'
    ctx.fillText('hello canvas', 50, 50)

  </script>
</body>

</html>

实战

1. 放大镜效果

技术点: 离屏渲染

将一个canvas对象绘制到另一个canvas对象上 (离开用户可视范围内进行染)

2. 刮刮乐效果

技术点: 图像合成

利用图像合成让绘制的内容与原矩形重合部分清空

3. 滤镜效果

技术点: 单位像素处理

这里我们主要借用 getlmageData 函数,他返回每个像素的 RGBA 值。借助图像处理公式,操作像素进行相应的、数学运算即可。

  • getlmageData()
  • putlmageData()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值