blog-frontend icon indicating copy to clipboard operation
blog-frontend copied to clipboard

HTML5 Canvas核心技术读书笔记

Open Caaalabash opened this issue 4 years ago • 0 comments

1. 第一章: 基础知识

1.1 在设置canvas的宽度和高度时,不能使用px后缀

其实是可以的,书中观点是:从canvas规范来说不使用px后缀更加规范

good

<canvas width='300' height='150'></canvas>

bad

<canvas width='300px' height='150px'></canvas>

1.2 Canvas元素的大小和绘图表面的大小

在看文档的时候,通常都会得到这样一个信息:不应该使用css属性来改变canvas元素的大小

原因在于:canvas元素有两套尺寸,一套是元素本身的大小,还有一个就是元素绘图表面的大小,当设置元素的width/height时实际是同时修改了两者,如果使用CSS属性来设定canvas元素的大小,那么只会改变元素本身的大小

当元素大小与绘图表面的大小不一致时,浏览器会对绘图表面进行缩放,使其符合元素的大小,这样就会导致不合预期的效果

元素大小 !== 绘图表面大小

<style>
#canvas {
  width: 600px;
  height: 300px;
}
</style>

<canvas id='canvas'></canvas>

let c = document.getElementById('canvas')
let ctx = c.getContext('2d')
ctx.fillText("我是风儿",100,100)

元素大小 === 绘图表面大小

<canvas id='canvas' width='600' height='300'></canvas>

2.第二章: 绘制

2.1 坐标系统

坐标系统不是固定的,可以通过平移(translate),旋转(rotate),缩放(scale),创建自定义的变换方式来变换坐标系统

2.2 Canvas绘制模型

2.3 基本操作API(参考MDN)

2.3.1 矩形的绘制

  • clearRect(x,y,w,h) 清除指定区域矩形,使其完全透明

  • strokeRect(x,y,w,h) 矩形的边框

  • fillRect(x,y,w,h) 填充的矩形

  • rect(x,y,w,h) 将一个矩形增加到当前路径上

2.3.2 绘制路径

  • beginPath() 新建一条路径

  • moveTo(x,y) 移动画笔

  • lineTo(x,y) 绘制直线

  • acr(x,y,radius,startAngle,endAngle,acticlockwise) 绘制圆弧

  • quadraticCurveTo(cp1x, cp1y, x, y) 绘制二次贝塞尔曲线

  • bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) 绘制三次贝塞尔曲线

  • closePath() 闭合一条路径

  • stroke() 绘制轮廓

  • fill() 填充内容

2.3.3 颜色与透明度

  • fillStyle = color 图形的填充颜色

  • strokeStyle = color 图形轮廓的颜色

2.3.4 线型

  • lineWidth = value 线条宽度

  • lineCap = type 线条末端样式

  • lineJoin = type 线条间接合处样式

  • miterLimit = value 限制两条线相交时交接处的最大长度

  • setLineDash(segements) 设置虚线样式,一个数组,指定虚线与间隙的宽度

  • lineDashOffset = value 设置虚线样式的起始偏移量(水平)

lineCap: butt, round, square

lineJoin: round, bevel, miter

蚂蚁线效果

var ctx = document.getElementById('canvas').getContext('2d');
var offset = 0;

function draw() {
  ctx.clearRect(0,0, canvas.width, canvas.height);
  ctx.setLineDash([4, 2]);
  ctx.lineDashOffset = -offset;
  ctx.strokeRect(10,10, 100, 100);
}

function march() {
  offset++;
  if (offset > 16) {
    offset = 0;
  }
  draw();
  setTimeout(march, 20);
}

march();

2.3.5 渐变

创建一个canvaGradient对象,指定起点与终点

  • createLinearGradient(x1,y1,x2,y2) : 线性渐变

  • createRadialGradient(x1,y1,r1,x2,y2,r2): 径向渐变

  • gradient.addColorStop(position,color): 渐变中颜色所在的相对位置

2.3.6 绘制文本

  • fillText(text,x,y,[maxWidth]) 指定位置填充文本

  • strokeText(text,x,y,[maxWidth]) 指定位置绘制文本边框

文本样式,接近与css

  • font = value (与css font属性有相同的语法)

  • textAlign = value

  • textBaseline = value

  • direction = value

  • measureText() 返回一个TextMetrics对象的宽度,所在像素

阴影,等同于text-shadow属性

  • shadowOffsetX = float

  • shadowOffsetY = float

  • shadowBlur = float

  • shadowColor = color

2.3.7 使用图片

浏览器支持的任意格式的外部图片都可以使用,甚至视频,另一个canvas

  • drawImage(image,x,y,w,h) 绘制图片,将其渲染到canvas中,后两个参数指定了缩放大小

  • createPattern(image,type) 类似于background-image属性,图案样式,type取值等同于background-report

2.3.8 变形

改变坐标系

  • translate(x,y)

  • rotate(angle)

  • scale(x,y)

  • transform 逃

3. 第四章: 图像与视频

第三章为文本,暂时没啥兴趣,先学习第四章吧

四个用于绘制以及操作图像的方法

  • drawImage() 将图像绘制到canvas中

  • getImageData() 获取图像中的底层像素

  • putImageData() 将修改后的像素放回图像中去

  • createImageData() 创建一个表示空白图像的数据对象

3.1 drawImage三种形态

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

let img = new Image()

img.onload = function(){
  ctx.drawImage(this,xxxxxxxxxx)
}
img.src = 'https://static.calabash.top/img/1.jpg'
  • drawImage(img,x,y) : 从点(x,y)开始绘制,不压缩图像

  • drawImage(img,x,y,w,h) : 压缩图像,图像的宽高为w,h, 可以在canvas范围之外绘制图像,只需要将绘制点指定在canvas外

  • drawImage(img,x,y,w,h,sx,sy,sw,sh) 主要用作切片

3.2 离屏canvas

离屏canvas经常用来存放临时性的图像信息,它可以提高绘制效率,假设一个类似于放大镜的功能,每次更改放大倍数的时候都会清空绘制区域,重新绘制图片,再根据缩放比例,重新绘制

如果使用离屏canvas,可以这样做:用一个离屏canva存放原图,更改了放大倍数之后,将离屏canvas里的内容复制到正在显示的canvas之中

创建离屏canvas的四个步骤

  1. 创建用作离屏canvas的元素

  2. 设置其宽高

  3. 在离屏canvas中进行绘制

  4. 将离屏canvas复制到正在显示的canvas中

let canvas = document.getElementById('canvas')
let ctx = canvas.getContext('2d')
let offscreencanvas = document.createElement('canvas')
let offctx = offscreencanvas.getContext('2d')

let img = new Image()

img.onload = function(){
  offctx.drawImage(this,0,0)
}
img.src = 'https://static.calabash.top/img/1.jpg'

setTimeout(()=>{
  ctx.drawImage(offscreencanvas,0,0,100,100)
},1000)

离屏canvas大多数情况下是应用在html5游戏中的,将静态数据绘制在离屏canvas上面,在当前的canvas上面绘制离屏canvas,在再将动态数据绘制在当前canvas上面,这样就能减少静态数据的绘制次数。

3.3 操作图像的像素

通过getImageData()方法获取一个指向ImageData对象的引用,该对象包含以下三个属性

  • width: 以设备像素为单位的图像数据宽度

  • height: 数据高度

  • data: 包含各个设备像素的一维数组

data属性指向一个包含8位二进制整数的数组,这些整数的值位于0~255,分别表示一个像素的r,g,b,a分量,从左到右从上到下

这里就举一个图片滤镜的例子

let canvas = document.getElementById('canvas')
let ctx = canvas.getContext('2d')
canvas.width = 600
canvas.height = 300

let img = new Image()
img.crossOrigin = ''
img.src = 'https://static.calabash.top/img/file-1535213207419.jpg'

img.onload = function(){
  ctx.drawImage(this,0,0,300,150)
  let imageData = ctx.getImageData(0,0,300,150)
  changeImageData(imageData.data)
  ctx.putImageData(imageData,0,150)
}
img.onerror = function(e){
  console.log(e)
}
function changeImageData(data){
  //修改R通道的值
  for(let i=0; i<data.length; i++){
    if(i % 4 === 0){
      data[i] = 0
    }
  }
}

3.4 以图像制作动画

这里就以将图像从canvas中淡出为例

let canvas = document.getElementById('canvas')
let ctx = canvas.getContext('2d')
canvas.width = 600
canvas.height = 300

let counter = 0
let originalData 

let img = new Image()
img.crossOrigin = ''
img.src = 'https://static.calabash.top/img/file-1535213207419.jpg'

img.onload = function(){
  ctx.drawImage(this,0,0,300,150)
  let imageData = ctx.getImageData(0,0,300,150)
  originalData = imageData
}
function changeImageData(data){
  for(let i=0; i<data.length; i++){
    data[i] > 10 
      ? data[i] -= 10
      : data[i] = 0
  }
}
function start(){
  ctx.clearRect(0,0,600,300)
  changeImageData(originalData.data)
  ctx.putImageData(originalData,0,0)
}

setInterval(()=>{
  start();},1000)

3.5 性能

值得关注但不是当前的首要内容先跳过把

3.6 视频处理

drawImage方法除了能绘制图像还可以把视频文件的某一阵绘制到canvas中

let video = document.getElementById('video')

context.drawImage(video,0,0)

实战一下嗷

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
  <canvas id="canvas" ></canvas>
  <video src='https://static.calabash.top/img/2.mp4' 
         id='video'
         style="display:none"
  ></video>

</body>
</html>

JS

let video = document.getElementById('video')
let canvas = document.getElementById('canvas')
let ctx = canvas.getContext('2d')


function animate(){
  if(!video.ended){
    ctx.drawImage(video,0,0,canvas.width,canvas.height)
    window.requestAnimationFrame(animate)
  }
}

setTimeout(()=>{
  video.play()
  animate()
},2000)

效果

这样就实现了将不可见的video元素之中的视频绘制到了一个可见的canvas元素中,就可以进行视频处理了

比如只需要在animate()函数中添加

    ctx.textAlign = 'center'
    ctx.font = '20px microsoft yahei'
    ctx.fillStyle = 'rgba(184, 184, 184, 0.8)',
    ctx.fillText('盗版必究',250,125)

就能实现水印的操作~

Caaalabash avatar May 02 '20 08:05 Caaalabash