blog-frontend
blog-frontend copied to clipboard
HTML5 Canvas核心技术读书笔记
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的四个步骤
-
创建用作离屏canvas的元素
-
设置其宽高
-
在离屏canvas中进行绘制
-
将离屏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)
就能实现水印的操作~