跳至主要內容

Canvas 使用

Yang大约 19 分钟JavaScript

描述

是一门使用 JavaScript 操作 Canvas 元素的技术

  • <canvas> 是一个行内元素块(inline-block
  • 一般需要在 <canvas> 元素上指定三个属性 idwidthheight
    • 默认宽度(300px),默认高度(150px)
    • 如果在 CSS 中定义 <canvas> 的宽高,在使用 JavaScript 获取元素宽高时得到的是默认宽高而非设置的
  • 所有操作均使用 W3C 坐标系,而非数学坐标系
<canvas
  width="1000"
  height="480"
  id="canvas"
  style="border: 1px dashed #666"
></canvas>

Canvas 与 SVG

  • Canvas 使用 Javascript 动态生成,SVG 使用 XML 静态描述
  • Canvas 基于位图(放大后会失真),SVG 基于矢量图(放大后不会失真)
  • 如果发生修改,Canvas 会重绘,SVG 不需要重绘

操作步骤

  • 获取 Canvas 对象
    • var myCanvas = document.getElementById('canvas')
  • 获取上下文环境对象 context(所有 Canvas 图形操作都是基于 context 对象的)
    • var ctx = myCanvas.getContext('2d')
  • 开始绘制图形
    • ctx.stroke()

常用属性

width

获取 Canvas 对象的宽度

var myCanvas = document.getElementById('canvas')
console.log(myCanvas.width)

height

获取 Canvas 对象的高度

var myCanvas = document.getElementById('canvas')
console.log(myCanvas.height)

常用方法

getContext("2d")

获取 Canvas 2D 上下文环境对象

var myCanvas = document.getElementById('canvas')
var ctx = myCanvas.getContext('2d')

toDataURL(type)

获取 Canvas 对象产生的位图的字符串

  • type:可选参数,表示输出的 MIME 类型,如果被省略,将使用 image/png 类型
var myCanvas = document.getElementById('canvas')
var imgUrl = myCanvas.toDataURL()
console.log(imgUrl)

上下文全局属性

globalAlpha

定义 Canvas 环境的透明度,对整个画布都起作用

ctx.globalAlpha = '0.3'

globalCompositeOperation

定义全局图形交叉显示方式

ctx.globalCompositeOperation = 'source-over'
属性值说明
source-over默认值,新图形覆盖旧图形
source-atop只显示新图形中与旧图形重叠的部分以及旧图形的其余部分,其他部分做透明处理
source-in只显示新图形中与旧图形重叠部分,其他部分做透明处理
source-out只显示新图形中与旧图形不重叠部分,其他部分做透明处理
destination-oversource-over 相反,旧图形覆盖新图形
destination-atop只显示旧图形中与新图形重叠的部分以及新图形的其余部分,其他部分做透明处理
destination-in只显示旧图形中与新图形重叠部分,其他部分做透明处理
destination-out只显示旧图形中与新图形不重叠部分,其他部分做透明处理
lighter两种图形都显示,在图形重叠部分,颜色由两种图形的颜色值相加后形成
copy只显示新图形,旧图形做透明处理
xor两种图形都绘制,其中重叠部分做透明处理
darker两种图形都挥之,在重叠部分,颜色由两种图形的颜色值相减后形成

直线图形

直线

moveTo(x,y)

将画笔移动到 (x,y) 坐标处,然后开始绘图

ctx.moveTo()

lineTo(x,y)

画笔从起点开始画直线,一直画到 (x,y) 坐标处

  • 如果之前未设置起点,则 (x,y) 即为之后绘画起点
  • 第一次使用该方法后,画笔自动移动到终点,即 (x,y) 坐标的位置
ctx.lineTo()

stroke()

真正开始绘制直线,其他方法只是用来确定坐标位置

ctx.stroke()

描边矩形

strokeStyle

边框颜色属性,有 颜色值渐变色图案 三种类型的值

  • strokeStyle 属性必须在 strokeRect() 方法调用前定义,否则无效
ctx.strokeStyle = '#f00'

strokeRect(x,y,w,h)

立即绘制描边矩形,前两个参数为矩形左上角坐标,后两个参数分别为矩形的宽度和高度

填充矩形

fillStyle

填充颜色属性,有 颜色值渐变色图案 三种类型的值

  • fillStyle 属性必须在 fillRect() 方法调用前定义,否则无效
ctx.fillStyle = '#f00'

fillRect(x,y,w,h)

立即绘制填充矩形,前两个参数为矩形左上角坐标,后两个参数分别为矩形的宽度和高度

ctx.fillRect(50, 50, 100, 100)

rect(x,y,w,h)

绘制矩形的另一种方法

该方法调用之后不会立即绘制,而是在其后调用其他方法来实现绘制描边矩形或填充矩形

// 描边矩形
ctx.strokeStyle = '#f00'
ctx.rect(50, 50, 100, 100)
ctx.stroke()
// 上述代码等价于
ctx.strokeStyle = '#f00'
ctx.strokeRect(50, 50, 100, 100)
// 填充矩形
ctx.fillStyle = '#f00'
ctx.rect(50, 50, 100, 100)
ctx.fill()
// 上述代码等价于
ctx.fillStyle = '#f00'
ctx.fillRect(50, 50, 100, 100)

clearRect(x,y,w,h)

清空指定矩形区域,前两个参数为被清空矩形左上角坐标,后两个参数分别为矩形的宽度和高度

var myCanvas = document.getElementById('canvas')
var ctx = myCanvas.getContext('2d')
ctx.clearRect(x, y, w, h)

曲线图形

可以用来绘制圆形或者弧线

语法:arc(x, y, 半径, 开始角度, 结束角度, anticlockwise)

此方法只是对圆的描述,具体绘制是需要调用 stroke() 或者 fill() 方法来进行描边或填充

  • xy:表示圆心的横纵坐标

  • 开始角度、结束角度:以 弧度 为单位,推荐写法 角度数 * Math.PI / 180(可以方便一眼看出角度值)

  • anticlockwise:布尔值,默认为 false

    • false:顺时针绘制
    • true:逆时针绘制

圆形

window.onload = function () {
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')
  ctx.beginPath()
  ctx.arc(50, 50, 50, 0, (180 * Math.PI) / 180, true)
  ctx.closePath()
  // 描边
  ctx.strokeStyle = '#00f'
  ctx.stroke()
  // 填充
  ctx.fillStyle = '#f0f'
  ctx.fill()
}

弧线

// 方法一:使用 arc() 函数,不用 closePath() 闭合路径
window.onload = function () {
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')
  ctx.beginPath()
  ctx.arc(50, 50, 50, 0, (180 * Math.PI) / 180, true)
  ctx.strokeStyle = '#f00'
  ctx.stroke()
}

// 方法二:使用 arcTo() 函数,待读...

线条操作

必须使用 beginPath() 方法才能开始新的路径,否则之前定义的状态会被一直使用

lineWidth

定义线条宽度,整数,默认为 1,默认单位 px

ctx.lineWidth = 10

lineCap

定义线条开始出和结束处的线帽样式,定义线帽会使线条稍微变长

  • butt:(默认值)无线帽,不做任何处理
  • round:线条开始处和结尾处都增加一个半圆,半圆的直径为线宽
  • square:线条开始处和结尾处都增加一个长方形,长方形长度为线宽的一半,高度为线宽
ctx.lineCap = 'round'

lineJoin

定义两个线条交接处样式

  • miter:(默认值)线条在交接处延伸至交于一点
  • round:交接处是一个圆角,远交所在圆的直径等于线宽
  • bevel:交接处是一个斜角,斜角所在正方形的对角线长等于线宽
ctx.lineJoin = 10

setLineDash(array)

定义线条的虚实样式,参数是一个数组组合,数组元素为线条长度和空白长度的重复排列

window.onload = function () {
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')
  ctx.moveTo(20, 20)
  ctx.lineTo(180, 20)
  ctx.setLineDash([2, 2])
  ctx.stroke()

  ctx.beginPath()
  ctx.moveTo(20, 40)
  ctx.lineTo(180, 40)
  ctx.setLineDash([5, 5])
  ctx.stroke()

  ctx.beginPath()
  ctx.moveTo(20, 60)
  ctx.lineTo(180, 60)
  ctx.setLineDash([10, 5])
  ctx.stroke()

  ctx.beginPath()
  ctx.moveTo(20, 80)
  ctx.lineTo(180, 80)
  ctx.setLineDash([10, 5, 5, 5])
  ctx.stroke()

  ctx.beginPath()
  ctx.moveTo(20, 100)
  ctx.lineTo(180, 100)
  ctx.setLineDash([10])
  ctx.stroke()
}

文本操作

font

定义字体样式(大小、粗细等)

ctx.font = 'font-style font-weight font-size/line-height font-family'
ctx.font = '30px bold 微软雅黑'

textAlign

定义文本水平对齐方式

startend 与阅读方向有关,从左到右阅读,start 对应左边,end 对应右边。从右到左阅读,start 对应右边,end 对应左边

leftright 始终是指文字的左右方向,与阅读方向无关

  • start:文本在指定的横坐标开始

  • end:文本在指定的横坐标结束

  • left:文本左对齐(类似于 start

  • right:文本右对齐(类似于 end

  • center:文本的中心在指定的横坐标

textBaseline

定义文本垂直对齐方式

  • alphabetic:(默认)文本基线是普通英文字母的基线
  • top:文本基线是 em 方框的顶端
  • middle:文本基线是 em 方框的中心
  • bottom:文本基线是 em 方块的底端
window.onload = function () {
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')

  var text = '测试'
  ctx.font = '20px bold'
  ctx.textBaseline = 'alphabetic'
  ctx.fillText(text, 20, 60)

  var text1 = '文本'
  ctx.font = '20px bold'
  ctx.textBaseline = 'top'
  ctx.fillText(text1, 60, 60)

  var text2 = '对齐'
  ctx.font = '20px bold'
  ctx.textBaseline = 'middle'
  ctx.fillText(text1, 100, 60)

  var text3 = '方式'
  ctx.font = '20px bold'
  ctx.textBaseline = 'bottom'
  ctx.fillText(text1, 140, 60)
}

fillStyle

定义画笔填充路径的颜色,与 fillText() 配合使用,用于绘制填充文本

x ctx.fillStyle = "#f00"

strokeStyle

定义画笔描边路径的颜色,与 strokeStyle() 配合使用,用于绘制描边文本

ctx.strokeStyle = '#f00'

direction

当前文本方向,此功能某些浏览器尚在开发中,存在兼容性问题

  • ltr:文本方向从左向右
  • rtl:文本方向从右向左
  • inherit:根据情况继承 <canvas> 元素或者 Document
ctx.direction = 'ltr' || 'rtl' || 'inherit'

fillText()

绘制填充文本

  • text:要绘制的字符串文本

  • x:要绘制文本的横坐标,也就是文本最 边的坐标

  • y:要绘制文本的纵坐标,也就是文本最 边的坐标

  • maxWidth:可选,表示允许的文本最大宽度,单位 px,如果文本超过 maxWidth 会被压缩至 maxWidth 的宽度

ctx.fillText(text, 30, 60, 100)

strokeText(text,x,y,maxWidth)

绘制描边文本

  • text:要绘制的字符串文本

  • x:要绘制文本的横坐标,也就是文本最 边的坐标

  • y:要绘制文本的纵坐标,也就是文本最 边的坐标

  • maxWidth:可选,表示允许的文本最大宽度,单位 px,如果文本超过 maxWidth 会被压缩至 maxWidth 的宽度

ctx.strokeText(text, 30, 60, 100)

measureText()

用于 获取文本长度

window.onload = function () {
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')
  var text = '获取文本宽度'
  ctx.fillText(text, 30, 60)
  var textWidth = ctx.measureText(text).width
  console.log(textWidth)
}

图片操作

必须等图片完全载入后才能开始绘制图片

d:destination,目标图片

s:source,源图片

绘制图片

绘制原大小的图片

drawImage(image, dx, dy)

  • image:表示页面中的图片,该图片可以是页面中的 img 元素,也可以是 JavaScript 创建的 Image 对象

  • dx:表示图片左上角的横坐标

  • dy:表示图片左上角的纵坐标

window.onload = function () {
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')
  var image = new Image()
  image.src = 'img/test.jpg'
  image.onload = function () {
    ctx.drawImage(image, 20, 20)
  }
  // 或
  // var image = document.getElementById('image')
  // ctx.drawImage(image, 20, 20)
}

绘制固定大小的图片

drawImage(image, dx, dy, dw, dh),用于绘制一张大小和原图不一样的图片

  • 前三个参数同 drawImage(image, dx, dy)

  • dw:定义图片的宽度

  • dy:定义图片的高度

ctx.drawImage(image, 20, 20, 100, 80)

复制固定大小的图片

ctx.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh),用于将图片某一部分复制到 Canvas 中,图像只需要加载一次,可以极大提高页面的加载速度

  • imagedxdydwdh 四个参数同 drawImage(image, dx, dy)

  • sx:表示源图片被截取部分的横坐标

  • sy:表示源图片被截取部分的纵坐标

  • sw:表示源图片被截取部分的宽度

  • sh:表示源图片被截取部分的高度

window.onload = function () {
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')
  var image = new Image()
  image.src = 'img/test.jpg'
  image.onload = function () {
    ctx.drawImage(image, 1939, 1447, 2476, 2056, 20, 20, 190, 140)
    console.log(myCanvas.toDataURL())
  }
}

平铺图片

createPattren(image, type)

  • image:表示被平铺的图片对象或其他 Canvas 元素
  • type:表示图片的平铺方式
    • repeat:(默认值)在水平方向和垂直方向同时平铺图片
    • repeat-x:只在水平方向上平铺图片
    • repeat-y:只在垂直方向上平铺图片
    • no-repeat:不平铺(只显示一次)

使用图像平铺

window.onload = function () {
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')
  var image = new Image()
  image.src = 'img/pingpu.jpg'
  image.onload = function () {
    var pattern = ctx.createPattern(image, 'repeat')
    ctx.fillStyle = pattern
    ctx.fillRect(0, 0, myCanvas.width, myCanvas.height)
  }
}

使用 canvas 对象平铺

window.onload = function () {
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')
  var canvas = document.createElement('canvas')
  canvas.width = 20
  canvas.height = 20
  var canvasCtx = canvas.getContext('2d')
  canvasCtx.beginPath()
  canvasCtx.arc(10, 10, 10, 0, 2 * Math.PI)
  canvasCtx.closePath()
  canvasCtx.fillStyle = '#f00'
  canvasCtx.fill()
  var pattern = ctx.createPattern(canvas, 'repeat')
  ctx.fillStyle = pattern
  ctx.fillRect(0, 0, myCanvas.width, myCanvas.height)
}

在文字上平铺图片

window.onload = function () {
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')
  var image = document.createElement('img')
  image.src = 'img/test.jpg'
  image.onload = function () {
    var text = '获取文本宽度'
    ctx.font = '60px bold'
    var pattern = ctx.createPattern(image, 'repeat')
    ctx.fillStyle = pattern
    ctx.fillText(text, 30, 60)
  }
}

切割图片或图形

ctx.clip(),用于切割图片或为 Canvas 划分一个显示区域

不支持 strokeRect()fillRect() 方法,只能使用 fill()rect() 等方法代替

window.onload = function () {
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')

  // 1. 绘制基本图形
  ctx.beginPath()
  ctx.arc(100, 100, 100, 0, 2 * Math.PI)
  ctx.closePath()
  ctx.fillStyle = 'transparent'
  ctx.fill()

  // 2. 使用 clip() 方法,使得切割区域为上面绘制的基本图形
  ctx.clip()

  // 3. 绘制图片
  var image = document.createElement('img')
  image.src = 'img/test.jpg'
  image.onload = function () {
    ctx.drawImage(image, 1939, 1287, 2400, 2400, 0, 0, 200, 200)
  }
}

变形操作

变形相关方法必须在 fillRect() 等类似绘画动作之前调用,否则无效

平移

ctx.translate(x, y)

  • x:表示在 x 轴方向运动的距离(带符号),默认单位 px
  • y:表示在 y 轴方向运动的距离(带符号),默认单位 px
window.onload = function () {
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')
  ctx.fillStyle = '#f00'
  ctx.translate(50, 50)
  ctx.fillRect(0, 0, 50, 50)
}

缩放

ctx.scale(x, y),一般情况况下 xy 值都是正数。取值为 0~1 时图像缩小,大于 1 时图像放大

每一次缩小或放大的依据都是上次的图像,图形缩放时,左上角的坐标及线条宽度等其他属性也会跟着缩放

  • x:表示图形在 x 轴上的缩放倍数
  • y:表示图形在 y 轴上的缩放倍数

旋转

ctx.rotate((角度 * Math.PI) / 180)

  • 参数为旋转的角度,使用弧度表示的(Canvas 所有涉及角度的方法,都是用弧度表示的)
  • 旋转的中心为 W3C 坐标系原点
window.onload = function () {
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')
  ctx.fillStyle = '#f00'
  ctx.fillRect(50, 50, 50, 50)
  ctx.rotate((-45 * Math.PI) / 180)
  ctx.fillRect(50, 50, 50, 50)
}
// 改变旋转中心
window.onload = function () {
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')
  var i = 0
  var revctWidth = 100
  var rectHeight = 100
  setInterval(() => {
    i++
    ctx.clearRect(0, 0, myCanvas.width, myCanvas.height)
    ctx.save()
    ctx.translate(myCanvas.width / 2, myCanvas.height / 2)
    ctx.rotate(Math.PI * (i / 360))
    console.log(i)
    ctx.fillStyle = '#f00'
    ctx.fillRect(-revctWidth / 2, -rectHeight / 2, revctWidth, rectHeight)
    ctx.restore()
  }, 1)
}
// 有趣的图形
window.onload = function () {
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')
  ctx.translate(150, 0)
  ctx.fillStyle = 'rgba(255,0,0,0.25)'

  for (let i = 0; i < 100; i++) {
    ctx.translate(25, 25)
    ctx.scale(0.95, 0.95)
    ctx.rotate((18 * Math.PI) / 180)
    ctx.fillRect(0, 0, 100, 50)
  }
}

像素操作

获取图片像素数据

getImageData(x, y, width, height),该方法返回的是一个 canvasPixelArray 对象(imgData),对象中有一个 data 属性(imgData.data),这个属性是一个保存了这张图片像素数据的数组,取值为 [r1,g1,b1,a1,r2,g2,b2,a2,r3...],每 4 个数存存储着 1 个像素的 RGBA 颜色值

  • xy:表示所选图片区域左上角的横纵坐标
  • widthheight:表示所选图片区域的宽度和高度

输出图片像素数据

putImageData(image, x, y),简单来说就是在 Canvas 中显示一张图片

  • image:表示重新绘制的图形,就是使用 getImageData() 获取的 canvasPixelArray 对象
  • xy:表示重新绘制图形左上角的横纵坐标

创建像素区域

createImageData(sw, sh)

固定宽高 创建一个像素区域

createImageData(imgData)

按照传入 像素对象 的宽高创建一个像素区域

实例

反转颜色

指颠倒图片的颜色

算法: 255 - 原值

window.onload = function () {
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')
  var image = new Image()
  image.src = 'img/test.jpg'
  image.onload = function () {
    ctx.drawImage(image, 0, 0, 384, 256)
    var imgData = ctx.getImageData(0, 0, 384, 256)
    var data = imgData.data
    for (let i = 0; i < imgData.data.length; i += 4) {
      data[i + 0] = 255 - data[i + 0]
      data[i + 1] = 255 - data[i + 1]
      data[i + 2] = 255 - data[i + 2]
      // 不需要操作透明度值
    }
    ctx.putImageData(imgData, 394, 0)
  }
}

黑白效果

指将图片变成只含黑白灰的颜色

算法:红绿蓝三通道取平均值,然后让三通道都等于这个值,也可以对三通道加权取平均值

window.onload = function () {
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')
  var image = new Image()
  image.src = 'img/test.jpg'
  image.onload = function () {
    ctx.drawImage(image, 0, 0, 384, 256)
    var imgData = ctx.getImageData(0, 0, 384, 256)
    var data = imgData.data
    for (let i = 0; i < imgData.data.length; i += 4) {
      var average = (data[i + 0] + data[i + 1] + data[i + 2]) / 3
      // 加权
      // var average = (data[i + 0]*0.3 + data[i + 1]*0.6 + data[i + 2]*0.1) / 3
      data[i + 0] = average
      data[i + 1] = average
      data[i + 2] = average
    }
    ctx.putImageData(imgData, 394, 0)
  }
}

亮度效果

指让图片变得更亮或者更暗

算法:将红绿蓝三个通道同时加上一个正值(变亮)或一个负值(变暗)

window.onload = function () {
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')
  var image = new Image()
  image.src = 'img/test.jpg'
  image.onload = function () {
    ctx.drawImage(image, 0, 0, 384, 256)
    var imgData = ctx.getImageData(0, 0, 384, 256)
    var data = imgData.data
    for (let i = 0; i < imgData.data.length; i += 4) {
      var value = 50
      data[i + 0] += value
      data[i + 1] += value
      data[i + 2] += value
    }
    ctx.putImageData(imgData, 394, 0)
  }
}

复古效果

指使一张图片有一种复古的效果

算法:分别取 红、绿、蓝 三个通道的某种加权平均值

window.onload = function () {
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')
  var image = new Image()
  image.src = 'img/test.jpg'
  image.onload = function () {
    ctx.drawImage(image, 0, 0, 384, 256)
    var imgData = ctx.getImageData(0, 0, 384, 256)
    var data = imgData.data
    for (let i = 0; i < imgData.data.length; i += 4) {
      var r = data[i + 0]
      var g = data[i + 1]
      var b = data[i + 2]
      data[i + 0] = r * 0.39 + g * 0.76 + b * 0.18
      data[i + 1] = r * 0.35 + g * 0.68 + b * 0.16
      data[i + 2] = r * 0.27 + g * 0.53 + b * 0.13
    }
    ctx.putImageData(imgData, 394, 0)
  }
}

红色蒙版

指让图片呈现一种偏红的效果

算法:将红通道赋值为 红、绿、蓝 三通道的平局值,且将 绿、蓝 两个通道都赋值为 0

window.onload = function () {
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')
  var image = new Image()
  image.src = 'img/test.jpg'
  image.onload = function () {
    ctx.drawImage(image, 0, 0, 384, 256)
    var imgData = ctx.getImageData(0, 0, 384, 256)
    var data = imgData.data
    for (let i = 0; i < imgData.data.length; i += 4) {
      var average = (data[i + 0] + data[i + 1] + data[i + 2]) / 3
      data[i + 0] = average
      data[i + 1] = 0
      data[i + 2] = 0
    }
    ctx.putImageData(imgData, 394, 0)
  }
}

透明效果

使图片变得透明,只需要改变 alpha 通道的值

不能使用 ctx.globalAlpha,会对整个 Canvas 其作用

window.onload = function () {
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')
  var image = new Image()
  image.src = 'img/test.jpg'
  image.onload = function () {
    ctx.drawImage(image, 0, 0, 384, 256)
    var imgData = ctx.getImageData(0, 0, 384, 256)
    var data = imgData.data
    for (let i = 0; i < imgData.data.length; i += 4) {
      data[i + 3] *= 0.3
    }
    ctx.putImageData(imgData, 394, 0)
  }
}

渐变

线性渐变

使用 createLinearGradient(x1, y1, x2, y2)addColorStop(value, color) 配合实现

  • 调用 createLinearGradient(x1, y1, x2, y2) 方法创建一个 linearGradient 对象
    • 如果 y1y2 相等,则表示沿水平方向从左到右或从右到左的渐变
    • 如果 x1x2 相等,则表示沿垂直方向从上到下或从下到上的渐变
    • 如果 x1x2 不相等,且 y1y2 也不相等,则沿矩形对角线方向渐变
  • 对得到的 linearGradient 对象调用 n 次 addColorStop(value, color) 方法设置颜色
  • linearGradient 对象赋值给 fillStyle 属性
  • 调用 fill()fillRect()fillText() 等方法绘制颜色渐变
window.onload = function () {
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')
  var gnt = ctx.createLinearGradient(
    0,
    myCanvas.height,
    myCanvas.width,
    myCanvas.height
  )
  gnt.addColorStop(0, '#f00')
  gnt.addColorStop(0.2, '#0f0')
  gnt.addColorStop(0.4, '#00f')
  gnt.addColorStop(0.6, '#ff0')
  gnt.addColorStop(0.8, '#f0f')
  gnt.addColorStop(1, '#0ff')
  ctx.fillStyle = gnt
  ctx.fillRect(0, 0, myCanvas.width, myCanvas.height)
}

径向渐变

使用 createRadialGradient(x1, y1, r1, x2, y2, r2)addColorStop(value, color) 配合实现

  • 调用 createRadialGradient(x1, y1, r1, x2, y2, r2) 方法创建一个 radialGradient 对象
    • x1y1:表示渐变开始时圆心的坐标
    • r1:表示渐变开始时圆的半径
    • x2y2:表示渐变结束时圆心的坐标
    • r2:表示渐变结束时圆的半径
  • 对得到的 radialGradient 对象调用 n 次 addColorStop(value, color) 方法设置颜色
  • linearGradient 对象赋值给 fillStyle 属性
  • 调用 fill() 等方法绘制颜色渐变
window.onload = function () {
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')
  ctx.arc(100, 100, 100, 0, (360 * Math.PI) / 180)
  var gnt = ctx.createRadialGradient(100, 100, 100, 150, 50, 50)
  gnt.addColorStop(0, '#f00')
  gnt.addColorStop(0.2, '#0f0')
  gnt.addColorStop(0.4, '#00f')
  gnt.addColorStop(0.6, '#ff0')
  gnt.addColorStop(0.8, '#f0f')
  gnt.addColorStop(1, '#0ff')
  ctx.fillStyle = gnt
  ctx.fill()
}

阴影

window.onload = function () {
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')
  ctx.fillStyle = '#f00'
  ctx.shadowOffsetX = -20
  ctx.shadowOffsetY = -20
  ctx.shadowBlur = 10
  ctx.shadowColor = '#00f'
  ctx.fillRect(100, 100, 100, 100)
}

shadowOffsetX

阴影与图形的水平距离,默认值为 0,大于 0 时向右偏移,小于 0 时向左偏移

shadowOffsetY

阴影与图形的垂直距离,默认值为 0,大于 0 时向下偏移,小于 0 时向上偏移

shadowColor

阴影的颜色,默认为黑色

shadowBlur

阴影的模糊值,默认为 0,值越大模糊度越强,值越小模糊度越弱

路径

判断是否属于同一条路径的标准是 是否使用了 beginPath() 方法,而不是视觉上是否有首尾相连

beginPath()

开始一条新路径,并且只有使用此方法开会开始一条新路径

  • Canvas 是基于 状态 来绘制图形的,每一次绘制前,Canvas 会检查整个程序定义的所有状态(包括 strokeStylefillStylelineWidth)等
    • 当一个状态值没有改变时,Canvas 一直使用最初的状态
    • 当一个状态发生改变时
      • 如果使用 beginPath() 开始一个新路径,则不同的路径使用不同的值
      • 如果没有开始新路径,则后面的值会覆盖前面的值,导致路径使用相同属性,绘制出相同的路径
ctx.beginPath()

closePath()

关闭当前路径,指将同一个路径的起点与终点连接起来,使其成为一个封闭的图形,并不会开始一条新路径

  • 首尾相连的路径,一般最后一步使用 closePath() 进行 关闭,否则可能会在连接点处出现缺口
ctx.closePath()

isPointInPath(x, y)

判断某一个点是否存在于当前路径内

该方法不支持 strokeRect()fillRect(),需要用 rect() 代替

  • 如果点 (x, y) 在当前路径中,则返回 true
  • 如果点 (x, y) 不在当前路径中,则返回 false

状态

save()restore() 一般都是成对出现的

save()

保存当前状态

  • 保存的状态包括三种,分别是 绘图状态、剪切状态、变形状态

  • 不能保存路径状态,如果想开始新的路径,必须使用 beginPath()

  • 该方法只能保存状态,不能保存图形,因为 Canvas 只有一个上下文环境,如果要恢复图形,只能清空画布再重绘

ctx.save()

restore()

回复之前保存的状态

ctx.restore()

事件

鼠标事件

键盘事件

循环事件

图形绘制实例

<canvas
  id="canvas"
  width="300"
  height="300"
  style="border: 1px dashed #666"
></canvas>

正多边形

/*
 * n:正多边形边数
 * dx、dy:表示正 n 边形中心点坐标
 * size:表示正 n 边形的大小,也就是外接圆半径
 */
function createPolygon(ctx, n, dx, dy, size) {
  ctx.beginPath()
  var degree = (2 * Math.PI) / n
  for (var i = 0; i < n; i++) {
    var x = Math.cos(i * degree)
    var y = Math.sin(i * degree)
    ctx.lineTo(x * size + dx, y * size + dy)
  }
  ctx.closePath()
}

window.onload = function () {
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')
  var i = 3
  var timer = setInterval(() => {
    if (i == 100) {
      clearInterval(timer)
    }
    ctx.clearRect(0, 0, myCanvas.width, myCanvas.height)
    createPolygon(ctx, i, 100, 75, 50)
    ctx.fillStyle = 'HotPink'
    ctx.fill()
    i++
  }, 200)
}

五角形

window.onload = function () {
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')
  createStar(ctx, 25, 50)
}

/*
 * r1:五角形内接圆半径
 * r2:五角形外接圆半径
 */
function createStar(ctx, r1, r2) {
  ctx.beginPath()
  for (var i = 0; i < 5; i++) {
    ctx.lineTo(
      Math.cos(((18 + i * 72) * Math.PI) / 180) * r2 + 100,
      -Math.sin(((18 + i * 72) * Math.PI) / 180) * r2 + 100
    )
    ctx.lineTo(
      Math.cos(((54 + i * 72) * Math.PI) / 180) * r1 + 100,
      -Math.sin(((54 + i * 72) * Math.PI) / 180) * r1 + 100
    )
  }
  ctx.closePath()
  ctx.stroke()
}

调色板

window.onload = function () {
  var myCanvas = document.getElementById('canvas')
  var ctx = myCanvas.getContext('2d')
  var r = 255
  var g = 0
  var b = 0
  for (var i = 0; i < 150; i++) {
    if (i < 25) {
      g += 10
    } else if (i > 25 && i < 50) {
      r -= 10
    } else if (i > 50 && i < 75) {
      g -= 10
      b += 10
    } else if (i > 75 && i < 100) {
      r += 10
    } else {
      b -= 10
    }
    ctx.fillStyle = 'rgb(' + r + ',' + g + ',' + b + ')'
    ctx.fillRect(3 * i, 0, 3, myCanvas.height)
  }
}
上次编辑于:
贡献者: sunzhenyang