文章目录
01 canvas
1.1 canavs 的应用场景
- 游戏
- 图表
- 动画
- codepen.io(HTML5 动效)
1.2 canvas 发展历史
- 最早在 apple 的 safari 1.3 中引入
- ie9 之前的浏览器不支持 canvas
- http://caniuse.com/
1.3 如何使用 canvas
- 添加 canvas 标签
<canvas width=500 height=500></canvas>
- 获得 canavs 元素
var canvas =document.getElementById('myCanvas');
- 获得 canvas 上下文对象
var ctx = canvas.getContext('2d');
- WebGL 绘制 3d 图形时上下文时 ==>
var ctx = canvas.getContext('webgl');
- WebGL 绘制 3d 图形时上下文时 ==>
<!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>demo01</title>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// var ctx = canvas.getContext('webgl');
</script>
</body>
</html>
1.4 两个对象
- 元素对象(canvas 元素)和上下文对象(通过
getContext('2d')
方法获取到的CanvasRenderingContext2D 对象) - 元素对象相当于我们的画布,上下文对象相当于画笔,我们接下来的所有操作是基于上下文对象的
1.5 线段
ctx.moveTo(x, y)
起笔移动到 (x,y) 坐标点ctx.lineTo(x, y)
从当前点绘制直线到 (x,y) 点ctx.stroke()
描边,ctx.strokeStyle
设置边颜色ctx.fill()
填充,ctx.fillStyle
设置填充色ctx.lineWidth = 20
设置线段宽度ctx.closePath()
闭合当前路径 和回到起始点的区别- fill 和 stroke 方法都是作用在当前的所有子路径
- 完成一条路径后要重新开始另一条路径时必须使用 beginPath() 方法,betinPath 开始子路径的一个新的集合
<!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>demo01</title>
<style>
#canvas{
border: 2px solid #000;
}
</style>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.lineWidth = 20;
ctx.strokeStyle = 'red';
ctx.fillStyle = 'green';
ctx.moveTo(100, 100);
ctx.lineTo(200, 100);
ctx.lineTo(200, 200);
ctx.lineTo(100, 200);
ctx.lineTo(100, 100);
ctx.stroke();
ctx.fill();
</script>
</body>
</html>
1.6 矩形
ctx.rect(x, y, dx, dy);
画矩形,从 (x,y) 坐标开始画,dx 为宽,dy 为高ctx.fillRect(x, y, dx, dy);
beginPath() + rect() + fill()ctx.strokeRect(x, y, w, h);
beginPath() + rect() + stroke()
<!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>demo01</title>
<style>
#canvas{
border: 2px solid #000;
}
</style>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.rect(100, 100, 50, 100);
ctx.stroke();
ctx.beginPath();
ctx.strokeRect(100, 250, 50, 100);
ctx.beginPath();
ctx.fillRect(250, 100, 50, 100);
</script>
</body>
</html>
1.7 擦除当前区域
ctx.clearRect(x, y, dx, dy);
矩形擦除,从 (x,y) 开始擦,dx 为宽,dy 为高- 实现矩形落地动画
<!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>demo01</title>
<style>
#canvas{
border: 2px solid #000;
}
</style>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var y = 50;
function draw(y) {
// ctx.beginPath();
// ctx.rect(50, y, 20, 20);
// ctx.fill();
ctx.fillRect(50, y, 20, 20);
}
var timer = setInterval(function () {
ctx.clearRect(0, 0, 500, 500);
draw(y);
y += 1;
if( y > 480){
clearInterval(timer);
}
}, 5);
</script>
</body>
</html>
1.8 弧形
arc(x, y, r, 起始弧度, 结束弧度, 弧形的方向)
- (x,y) 圆心
- r 半径
- 0 顺时针,1 逆时针
<!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>demo01</title>
<style>
#canvas{
border: 2px solid #000;
}
</style>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(250, 250);
ctx.lineTo(350, 250);
ctx.arc(250, 250, 100, Math.PI * 0, Math.PI / 180 * -45, 1);
ctx.lineTo(250, 250);
ctx.stroke();
</script>
</body>
</html>
1.9 圆角
ctx.arcTo(x1, y1, x2, y2, r)
绘制的弧线与当前点 (x,y) 和 (x1,y1) 连线,(x1,y1) 和 (x2、y2) 连线都相切
<!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>demo01</title>
<style>
#canvas{
border: 2px solid #000;
}
</style>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(50, 200);
ctx.arcTo(300, 200, 250, 300, 50);
ctx.stroke();
</script>
</body>
</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>demo01</title>
<style>
#canvas{
border: 2px solid #000;
}
</style>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(200, 100);
ctx.arcTo(300, 100, 300, 300, 50);
ctx.arcTo(300, 300, 100, 300, 50);
ctx.arcTo(100, 300, 100, 100, 50);
ctx.arcTo(100, 100, 300, 100, 50);
ctx.closePath();
ctx.stroke();
</script>
</body>
</html>
1.10 贝塞尔曲线
https://blog.csdn.net/xiaozhangcsdn/article/details/98963937
-
quadraticCurveTo(x1, y1, ex, ey)
二次贝塞尔曲线- (x,y) 起始点 ==> P0
- (x1,y1) 控制点 ==> P1
-
(ex,ey) 结束点 ==> P2
-
bezierCurveTo(x1, y1, x2, y2, ex, ey)
三次贝塞尔曲线- (x,y) 起始点 ==> P0
- (x1,y1)、(x2,y2) 控制点 ==> P1、P2
- (ex,ey) 结束点 ==> P3
<!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>demo01</title>
<style>
#canvas{
border: 2px solid #000;
}
</style>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(100, 250);
ctx.quadraticCurveTo(100, 0, 300, 300)
ctx.stroke();
</script>
</body>
</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>demo01</title>
<style>
#canvas{
border: 2px solid #000;
}
</style>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(100, 400);
ctx.bezierCurveTo(100, 100, 350, 100, 450, 450);
ctx.stroke();
</script>
</body>
</html>
1.11 坐标轴转换,对画布进行操作
translate(dx, dy)
重新映射画布上的(0,0)位置scale(sx, sy)
缩放当前绘图,变为原来坐标轴的 sx、sy 倍rotate(Math.PI)
旋转当前的绘图,以画布原点为旋转点save()
保存当前图像状态的一份拷贝restore()
从栈中弹出存储的图形状态并恢复setTransform(a, b, c, d, e, f)
复合属性,先重置再变换- 参数:a 水平旋转、b 水平倾斜、c 垂直倾斜、d 垂直缩放、e 水平移动、f 垂直移动
transform(a, b, c, d, e, f)
复合属性,在之前的基础上变换- 参数:a 水平旋转、b 水平倾斜、c 垂直倾斜、d 垂直缩放、e 水平移动、f 垂直移动
<!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>demo01</title>
<style>
#canvas{
border: 2px solid #000;
}
</style>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.save();
ctx.beginPath();
ctx.translate(100, 100);
ctx.scale(2, 1);
ctx.rotate(Math.PI / 180 * 20);
ctx.fillRect(100, 100, 50, 50);
ctx.stroke();
ctx.beginPath();
ctx.fillRect(100, 200, 100, 100);
ctx.restore();
ctx.beginPath();
ctx.fillRect(100, 100, 100, 100);
</script>
</body>
</html>
1.12 填充图案
createPattern(image,"repeat|repeat-x|repeat-y|no-repeat")
默认为 no-repeat- img元素(image 对象),canvas 元素,video 元素(有图形的)
<!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>demo01</title>
<style>
#canvas{
border: 2px solid #000;
}
</style>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
<img src="./ocean.jpg">
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var oImg = document.getElementsByTagName('img')[0];
window.onload = function () {
ctx.beginPath();
ctx.fillStyle = ctx.createPattern(oImg, 'repeat');
ctx.fillRect(0, 0, 500, 500)
}
</script>
</body>
</html>
1.13 渐变
createLinearGradient(x1, y1, x2, y2);
线性渐变,必须在填充渐变的区域里定义渐变,否则没有效果,从(x1,y1)渐变到(x2,y2)createRadialGradient(x1, y1, r1, x2, y2, r2);
径向渐变,内圆环圆心为(x1,y1),半径为 r1,外圆环圆心为(x2,y2),半径为 r2addColorStop(p, color);
,p 表示颜色范围
<!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>demo01</title>
<style>
#canvas{
border: 2px solid #000;
}
</style>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
<!-- <img src="./ocean.jpg"> -->
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
var bg = ctx.createLinearGradient(0, 0, 0, 250);
bg.addColorStop(0, '#000');
bg.addColorStop(0.5, '#f00');
bg.addColorStop(1, '#0f0');
ctx.fillStyle = bg;
ctx.fillRect(0, 0, 250, 250);
ctx.beginPath();
var bg = ctx.createLinearGradient(0, 250, 250, 250);
bg.addColorStop(0, '#000');
bg.addColorStop(0.5, '#f00');
bg.addColorStop(1, '#0f0');
ctx.fillStyle = bg;
ctx.fillRect(0, 250, 250, 250);
ctx.beginPath();
var bg = ctx.createRadialGradient(375, 125, 50, 375, 125, 125);
bg.addColorStop(0, '#f0f');
bg.addColorStop(0.3, '#f00');
bg.addColorStop(0.5, '#000');
bg.addColorStop(0.8, '#00f');
bg.addColorStop(1, '#ff0');
ctx.fillStyle = bg;
ctx.fillRect(250, 0, 250, 250);
ctx.beginPath();
var bg = ctx.createRadialGradient(375, 425, 50, 375, 375, 125);
bg.addColorStop(0, '#f0f');
bg.addColorStop(0.3, '#f00');
bg.addColorStop(0.5, '#000');
bg.addColorStop(0.8, '#00f');
bg.addColorStop(1, '#ff0');
ctx.fillStyle = bg;
ctx.fillRect(250, 250, 250, 250);
</script>
</body>
</html>
1.14 阴影
ctx.shadowColor
阴影颜色ctx.shadowOffsetX
水平偏移量ctx.shadowOffsetY
竖直偏移量ctx.shadowBlur
模糊半径
PS:这里的偏移量不受坐标系变换的影响
<!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>demo01</title>
<style>
#canvas{
border: 2px solid #000;
}
</style>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
<!-- <img src="./ocean.jpg"> -->
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.arc(250, 250, 50, 0, Math.PI * 2);
ctx.shadowColor = 'red';
ctx.shadowOffsetX = 50;
ctx.shadowOffsetY = 50;
ctx.shadowBlur = 10;
ctx.fill();
</script>
</body>
</html>
1.15 文本
-
font
文本内容的字体属性 -
textAlign = 'center|end|left|right|start';
文本内容的对齐方式 -
textBaseline = 'alphabetic|top|hanging|middle|ideographic|bottom';
在绘制文本时使用的文本基线 -
fillText()
绘制“被填充的”文本 -
strokeText()
文本描边(无填充) -
measureText('hello world')
了解,返回包含指定文本宽度的对象console.log(ctx.measureText('hello world').width);
<!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>demo01</title>
<style>
#canvas{
border: 2px solid #000;
}
</style>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
<!-- <img src="./ocean.jpg"> -->
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.fillStyle = 'green';
ctx.font = '50px sans-serif';
ctx.fillText('hello', 100, 100);
ctx.beginPath();
ctx.strokeStyle = 'green';
ctx.font = '50px sans-serif';
ctx.strokeText('hello', 300, 100);
</script>
</body>
</html>
1.16 线段样式
lineCap = 'butt|round|square';
线条的结束端点样式,默认为 buttlineJoin = 'bevel|round|miter';
两条线相交时,所创建的拐角类型,默认为 miterlineWidth
线条宽度miterLimit
最大斜接长度
当 lineJoin 是 miter 时,用于控制斜接部分的长度
如果斜接长度超过 miterLimit 的值,变成 bevel
PS:实际运算是大于 miterLimit * lineWidth / 2 的值,了解就好
<!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>demo01</title>
<style>
#canvas{
border: 2px solid #000;
}
</style>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
<!-- <img src="./ocean.jpg"> -->
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(100, 100);
ctx.lineTo(200, 100);
ctx.lineWidth = 30;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(100, 150);
ctx.lineTo(200, 150);
ctx.lineWidth = 30;
ctx.lineCap = 'butt';
ctx.stroke();
ctx.beginPath();
ctx.moveTo(100, 200);
ctx.lineTo(200, 200);
ctx.lineWidth = 30;
ctx.lineCap = 'round';
ctx.stroke();
ctx.beginPath();
ctx.moveTo(100, 250);
ctx.lineTo(200, 250);
ctx.lineWidth = 30;
ctx.lineCap = 'square';
ctx.stroke();
ctx.beginPath();
ctx.moveTo(300, 50);
ctx.lineTo(400, 50);
ctx.lineTo(300, 100);
ctx.lineWidth = 30;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(300, 50);
ctx.lineTo(400, 50);
ctx.lineTo(300, 100);
ctx.lineWidth = 30;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(300, 150);
ctx.lineTo(400, 150);
ctx.lineTo(300, 200);
ctx.lineWidth = 30;
ctx.lineJoin = 'miter';
ctx.stroke();
ctx.beginPath();
ctx.moveTo(300, 250);
ctx.lineTo(400, 250);
ctx.lineTo(300, 300);
ctx.lineWidth = 30;
ctx.lineJoin = 'round';
ctx.stroke();
ctx.beginPath();
ctx.moveTo(300, 350);
ctx.lineTo(400, 350);
ctx.lineTo(300, 400);
ctx.lineWidth = 30;
ctx.lineJoin = 'bevel';
ctx.stroke();
</script>
</body>
</html>
1.17 裁剪
ctx.clip()
当前路径外的区域不再绘制
PS:可在 clip() 前用 save() 方法保存,后续通过 restore() 方法恢复
<!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>demo01</title>
<style>
#canvas{
border: 2px solid #000;
}
</style>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
<!-- <img src="./ocean.jpg"> -->
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.save();
ctx.beginPath();
ctx.arc(250, 250, 50, 0, Math.PI * 2);
ctx.stroke();
ctx.clip();
ctx.beginPath();
ctx.arc(300, 300, 50, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
ctx.beginPath();
ctx.fillRect(400, 400, 50, 50);
</script>
</body>
</html>
1.18 合成
ctx.globalCompositeOperation = 'source-over';
新像素和原像素的合并⽅方式- 11种值,默认 source-over,w3c标准
- source-over、source-atop、source-in、source-out、destination-over、destination-atop、destination-in、destination-out、copy、lighter、xor
- 常用 source-over、destination-over、copy
<!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>demo01</title>
<style>
#canvas{
border: 2px solid #000;
}
</style>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
<!-- <img src="./ocean.jpg"> -->
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.fillStyle = 'red';
ctx.fillRect(100, 100, 100, 100);
ctx.globalCompositeOperation = 'xor';
ctx.beginPath();
ctx.fillStyle = 'green';
ctx.arc(200, 200, 50, 0, Math.PI * 2);
ctx.fill();
</script>
</body>
</html>
1.19 全局透明度
globalAlpha
全局透明度ctx.globalAlpha = '0.5';
1.20 绘制图片
drawImage();
- 第一个参数是
img(Image,canvas,video)
,注:onload
- 第一个参数是
- 3个参数(x,y)
- 图片的(x,y)为起始点截取
- 5个参数(x,y,dx,dx)
- 以图片的(x,y)为起始点开始截取宽 dx 高 dy
- 9个参数(x1,y1,dx1,dy1,x2,y2,w2,h2)
- 前四个参数表示,以图片的(x1,y1)为起始点开始截取宽 dx1 高 dy1
- 后四个参数表示,在 canvas 上以(x1,y1)为起始点绘制宽 w2 高 h2
- 截到的图片会进行缩放以适应画布
<!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>demo01</title>
<style>
#canvas{
border: 2px solid #000;
}
</style>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
<!-- <img src="./ocean.jpg"> -->
<img src="./ocean.jpg" id="draw">
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var img = document.getElementsByTagName('img')[0];
ctx.beginPath();
window.onload = function () {
ctx.drawImage(img, 100, 100, 100, 300, 50, 50, 300, 300);
}
</script>
</body>
</html>
1.21 将 canvas 内容导出
canvas.toDataURL();
是 canvas 自身的方法不是上下文对象- 将 canvas 的内容抽取成⼀张图片,base64 编码格式
- 注:引用外部图片时同源策略的限制,需要开虚拟主机
- 将 canvas 的内容放入 img 元素里
<!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>demo01</title>
<style>
#canvas{
border: 2px solid #000;
}
</style>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
<img src="./ocean.jpg">
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var img = document.getElementsByTagName('img')[0];
ctx.beginPath();
ctx.drawImage(img, 0, 0);
var data = canvas.toDataURL();
var img = document.createElement('img');
img.src = data;
document.body.appendChild(img);
</script>
</body>
</html>
1.22 获取 canvas 像素信息
getImageData(x, y, dx, dy)
同源策略,开虚拟主机,获取 canvas 像素信息值console.log(ctx.getImageData(0, 0, 500, 500));
createImageData(w, h)
创建新的空白 ImageData 对象putImageData(imgData, x, y)
将图像数据放回画布上
<!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>demo01</title>
<style>
#canvas{
border: 2px solid #000;
}
</style>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
<!-- <img src="./ocean.jpg"> -->
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.fillRect(300, 300, 100, 100);
console.log(ctx.getImageData(0, 0, 500, 500));
ctx.beginPath();
var imgData = ctx.createImageData(100, 100);
for (var i = 0; i < imgData.data.length; i += 4){
imgData.data[i + 0] = 255;
imgData.data[i + 1] = 0;
imgData.data[i + 2] = 0;
imgData.data[i + 3] = 255;
}
ctx.putImageData(imgData, 10, 10);
</script>
</body>
</html>
1.23 RGBA 值
-
每个像素由 RGBA 四个值组成
-
R ==> 红色(0 - 255)
-
G ==> 绿色(0 - 255)
-
B ==> 蓝色(0 - 255)
-
A ==> alpha 通道(0 - 255;0 是透明的,255 是完全可见的)
1.24 命中检测
isPointInPath(x, y)
检测是否在区域内,chrome 与 safari 的区别isPointInStroke(x, y)
检测是否在线上- 还可以通过检测当前点的像素值,如果为透明,则该点不再路径上
<!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>demo01</title>
<style>
#canvas{
border: 2px solid #000;
}
</style>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
<!-- <img src="./ocean.jpg"> -->
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.rect(100, 100 ,100, 100);
ctx.stroke();
console.log(ctx.isPointInPath(150, 150));
console.log(ctx.isPointInStroke(150, 100));
ctx.beginPath();
ctx.strokeRect(300, 100 ,100, 100);
console.log(ctx.isPointInPath(350, 150));
console.log(ctx.isPointInStroke(350, 100));
</script>
</body>
</html>
1.25 非零绕数准则
判断点 p 是否在多边形内,从点 p 向外做一条射线(可以任意方向),多边形的边从左到右经过射线时环绕数减 1,多边形的边从右往左经过射线时环绕数加 1,最后环数不为 0,即表示在多边形内部
1.26 如何解决 canvas 高分屏模糊问题
在分辨率比较高的屏幕,例如 ip6/6s/mac 等机器上,因为 canvs 绘制的是位图,所以会导致模糊,解决方法是根据屏幕分辨率修改 canvas 样式代码中的宽和高与 canvas 的 width 和 height 属性的比例
在 CSS 样式表中将 canvas 的宽高设置小,canvas标签将画布设置大
<!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>demo01</title>
<style>
#canvas{
border: 2px solid #000;
height: 100px;
width: 100px;
}
</style>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
<!-- <img src="./ocean.jpg"> -->
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.rect(50, 50, 400, 400);
ctx.stroke();
</script>
</body>
</html>
02 SVG(Scalable Vector Graphics)可缩放矢量图形
https://www.w3school.com.cn/svg/index.asp
2.1 应用场景
- 图表
- 图标 icon
- 动效
- 矢量图
2.2 SVG 元素
- 开始
<svg width="500" height="500"></svg>
- 线段
<line x1="50" y1="20" x2="100" y2="20"></line>
- (x1,y1)起点,(x2,y2)终点
- CSS样式表:stroke 线条颜色,stroke-width 线条宽度
- 矩形
<rect x="50" y="50" width="100" height="100" rx="10" ry="20"></rect>
- (x,y)起点,rx 水平圆角半径,ry 垂直圆角半径
- CSS样式表:stroke 边框颜色,stroke-width 边框宽度,stroke-opacity 边框颜色的透明度,fill 填充颜色,fill-opacity 填充颜色透明度
- 圆形
<circle r="50" cx="250" cy="100"></circle>
- (cx,cy)圆心位置,r 圆半径
- 如果省略 cx 和 cy,圆的中心会被设置为(0,0)
- 椭圆
<ellipse rx="100" ry="50" cx="100" cy="200"></ellipse>
- (cx,cy)圆心位置,rx 水平半径,ry 垂直半径
- 折线
<polyline points="60 50, 75 35, 100 50, 125 35, 150 50, 175 35, 190 50"></polyline>
- 多边形
<polygon points="125 125,130 140,120 140"></polygon>
- ⽂本
<text x="125" y="220">hello,world</text>
<!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>demo02</title>
<style>
svg{
border: 2px solid #000;
}
line{
stroke: #0f0;
stroke-width: 10px;
stroke-linecap: round;
}
polyline{
fill: transparent;
stroke: #000;
stroke-width: 10px;
stroke-linecap: round;
stroke-linejoin: round;
}
polygon{
fill: transparent;
stroke: #000;
stroke-width: 1px;
}
</style>
</head>
<body>
<svg width="500" height="500">
<line x1="50" y1="20" x2="450" y2="20"></line>
<rect x="10" y="200" width="100" height="100" rx="10" ry="20"></rect>
<circle r="50" cx="200" cy="250"></circle>
<ellipse rx="100" ry="50" cx="390" cy="250"></ellipse>
<polyline points="60 100, 75 35, 100 100, 125 35, 150 100, 175 35, 190 100"></polyline>
<polygon points="125 125,130 200,200 140"></polygon>
<text x="350" y="50">hello,world</text>
</svg>
</body>
</html>
2.3 CSS 样式属性
fill
填充颜色fill-opacity
填充颜色透明度stroke
边框颜色stroke-width
边框宽度stroke-opacity
边框颜色透明度stroke-linecap
线条的结束端点样式stroke-linejoin
两条线相交时,所创建的拐角类型,默认为 miter
2.4 path 元素
https://segmentfault.com/a/1190000014620252
M/m (x, y)+
→ moveto,起始位置L /l (x, y)+
→ lineto,从当前位置绘制线段到指定位置H/h (x)+
→ horizontal lineto,从当前位置绘制一条水平线到达指定的 x 坐标V/v (y)+
→ vertical lineto,从当前位置绘制一条垂线达指定的 y 坐标C/c (x1, y1, x2, y2, x, y)+
→ curveto,从当前位置绘制三次贝塞尔曲线S/s (x2, y2, x, y)+
→ smooth curveto,从当前位置绘制光滑三次贝塞尔曲线,自动对称一个控制点,结束点(x,y)Q/q (x1, y1, x, y)+
→ quadratic Belzier curve,从当前位置绘制二次贝塞尔曲线T/t (x, y)+
→ smooth quadratic Belzier curveto,从当前位置绘制光滑二次贝塞尔曲线,自动对称一个控制点,结束点(x,y),就是一条直线A/a (rx, ry, xar, laf, sf, x, y)
→ elliptical Arc,从当前位置绘制弧线到指定位置Z/z
→ closepath,闭合当前路径- 大写表示绝对定位,小写表示相对定位
2.5 path 移动和直线命令
- M / m 指令和 L / l 指令
- M 指令和 L 指令
<path d = "M 10 10 L 20 10" />
- m 指令和 l 指令
<path d = "m 10 10 l 20 10" />
- 绝对坐标和相对坐标
- M 指令和 L 指令
- H 和 V 命令
<path d="M 100 100 H 200 V 200"/>
- Z 命令
<path d="M 100 100 H 200 V 200 z"/>
,注:Z 不区分大小写
<!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>demo02</title>
<style>
svg{
border: 2px solid #000;
}
path{
fill: transparent;
stroke: #000;
stroke-width: 1px;
}
</style>
</head>
<body>
<svg width="500" height="500">
<path d = "M 50 50 L 100 50" />
<path d = "m 150 50 l 200 50" />
<path d="M 100 100 H 200 V 200 z"/>
</svg>
</body>
</html>
2.6 圆弧指令
- A命令,七个参数,rx ry x-axis-rotation large-arc-flag sweep-flag x y
- rx ry:圆弧的 x 轴半径和 y 轴半径
- x-axis-rotation:圆弧相对 x 轴的旋转角度,默认是顺时针,可以设置负值
- large-arc-flag:表示圆弧路路径是大圆弧还是小圆弧,1大圆弧,0 小圆弧
- sweep-flag:表示从起点到终点是顺时针还是逆时针,1 表示顺时针,0 表示逆时针
- x y:表示终点坐标,绝对或相对
<path d="M 100 100 A 50 100 70 1 1 150 200"></path>
<!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>demo02</title>
<style>
svg{
border: 2px solid #000;
}
path{
fill: transparent;
stroke: #000;
stroke-width: 1px;
}
</style>
</head>
<body>
<svg width="500" height="500">
<path d="M 100 100 A 50 100 70 1 1 150 200"></path>
</svg>
</body>
</html>
2.7 贝塞尔曲线
https://blog.csdn.net/qq_41614928/article/details/90746382
https://blog.csdn.net/m0_50489096/article/details/115699025
- 二次贝塞尔曲线
- Q:控制点(x1,y1)、终点(x,y)
- T:终点(x,y),会自动补充一个控制点
- Q + T:它的第一个控制点,就会被假设成前一个控制点的对称点,相当于两段二次贝塞尔曲线
- 单独一个 T:控制点就会被认为和终点是同一个点,所以画出来的将是一条直线
- 三次贝塞尔曲线
- C:控制点(x1,y1)(x2,y2)、终点(x,y)
- S:控制点(x2,y2)、终点(x,y),会自动补充一个控制点
- C + S:它的第一个控制点,就会被假设成前一个控制点的对称点,相当于两段三次贝塞尔曲线
- 单独一个 S:它的两个控制点就会被假设为同一个点
<!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>demo02</title>
<style>
svg{
border: 2px solid #000;
}
path{
fill: transparent;
stroke: #000;
stroke-width: 1px;
}
</style>
</head>
<body>
<svg width="500" height="500">
<path d="M 100 150 Q 200 50 300 300 T 500 100"></path>
</svg>
</body>
</html>
2.8 自动生成路径
Method Draw
地址:https://editor.method.ac/
2.9 SVG 渐变
- 线性渐变
- (x1,y1)→(x2,y2)渐变方向
<!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>demo02</title>
<style>
svg{
border: 2px solid #000;
}
</style>
</head>
<body>
<svg width="500" height="500">
<defs>
<linearGradient id="bg1" x1="0" y1="0" x2="0" y2="100%">
<stop offset="0%" style="stop-color:rgb(255,255,0);"/>
<stop offset="100%" style="stop-color:rgb(255,0,0);"/>
</linearGradient>
</defs>
<rect x="0" y="0" width="500" height="500" style="fill:url(#bg1)"/>
</svg>
</body>
</html>
- 径向渐变
- (cx,cy)中心点坐标
- fx,fy 半径
<!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>demo02</title>
<style>
svg{
border: 2px solid #000;
}
</style>
</head>
<body>
<svg width="500" height="500">
<defs>
<radialGradient id="bg2" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
<stop offset="0%" style="stop-color:green;"/>
<stop offset="100%" style="stop-color:red;"/>
</radialGradient>
</defs>
<rect x="0" y="0" width="500" height="500" style="fill:url(#bg2)"/>
</svg>
</body>
</html>
2.10 SVG 滤镜
- 高斯滤镜
<!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>demo02</title>
<style>
svg{
border: 2px solid #000;
}
</style>
</head>
<body>
<svg width="500" height="500">
<defs>
<filter id="Gaussian_Blur">
<feGaussianBlur in="SourceGraphic" stdDeviation="50"/>
</filter>
</defs>
<rect x="0" y="0" width="500" height="500" fill=”yellow” style="filter:url(#Gaussian_Blur)"/>
</svg>
</body>
</html>
- 其他滤镜
- http://www.w3school.com.cn/svg/svg_filters_intro.asp
2.11 SVG 路径动画
stroke-dasharray
- 单个值,实线虚线为同一个
- 可设置多个值,依次为实线、虚线、实线、虚线…
stroke-dashoffset
动画实现原理,通过修改 stroke-dashoffset
的值让路路径慢慢地展现出来
<!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>demo02</title>
<style>
svg{
border: 2px solid #000;
}
path{
fill: transparent;
stroke: #000;
stroke-width: 1px;
stroke-dasharray: 300px;
animation: dash 2s linear alternate infinite;
}
@keyframes dash {
from{
stroke-dashoffset: 0px;
}
to{
stroke-dashoffset: 300px;
}
}
</style>
</head>
<body>
<svg width="500" height="500">
<path d="M 100 100 L 400 100"></path>
</svg>
</body>
</html>
getTotalLength()
路径总长度getPointAtLength(x)
路径上与起始点距离为 x 的点的坐标
注:严格来说上面两方法只适用于 path 元素,但各个浏览器实现起来都会有一点区别。例如谷歌浏览器也能获取到 line 元素的路径长度
<!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>demo02</title>
<style>
svg{
border: 2px solid #000;
}
path{
fill: transparent;
stroke: #000;
stroke-width: 1px;
}
</style>
</head>
<body>
<svg width="500" height="500">
<path d="M 100 100 L 400 100 400 400"></path>
</svg>
<script>
var path = document.getElementsByTagName('path')[0];
console.log(path.getTotalLength());
console.log(path.getPointAtLength(200));
</script>
</body>
</html>
2.12 ViewBox
https://zhuanlan.zhihu.com/p/422609203
https://blog.csdn.net/zhy13087344578/article/details/80336044
<svg width="500" height="500" viewBox="0,0,50,50"></svg>
- viewBox 用来设置 svg 的大小,以上代码相当于放大了 10 倍
- preserveAspectRatio
- xMin xMid xMax ==> x轴 左中右对齐
- yMin yMid yMax ==> y轴 左中右对齐
- meet slice none ==> 设置填充方式
<!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>demo02</title>
<style>
svg{
border: 2px solid #000;
}
path{
fill: transparent;
stroke: #000;
stroke-width: 1px;
}
</style>
</head>
<body>
<svg width="500" height="500" viewBox="0 0 50 50">
<path d="M 10 10 L 40 10 40 40"></path>
</svg>
</body>
</html>
2.13 JS 生成 SVG 元素
- 创建 SVG 元素需要指定命名空间
- SVG 元素对象一般通过调用 setAttribute() 方法来设定属性
<!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>demo02</title>
<style>
svg{
border: 2px solid #000;
}
</style>
</head>
<body>
<script>
var str = 'http://www.w3.org/2000/svg',
svg = document.createElementNS(str, 'svg');
svg.setAttribute('width', 500);
svg.setAttribute('height', 500);
var rect = document.createElementNS(str, 'rect');
rect.setAttribute('x', 100);
rect.setAttribute('y', 100);
rect.setAttribute('width', 100);
rect.setAttribute('height', 100);
rect.setAttribute('fill', '#0fc');
svg.appendChild(rect);
document.body.appendChild(svg);
</script>
</body>
</html>
03 requestAnimationFrame
requestAnimationFrame 是浏览器用于定时循环操作的一个接口,类似 setTimeout,主要用途是按帧对网页进行重绘。
设置这个 API 的目的是为了让各种网页动画效果(DOM 动画、Canvas 动画、SVG 动画、WebGL 动画)能够有一个统一的刷新机制,从而节省系统资源,提高系统性能,改善视觉效果。代码中使用这个 API,就是告诉浏览器希望执行一个动画,让浏览器在下一个动画帧安排一次网页重绘。
requestAnimationFrame 的优势,在于充分利用显示器的刷新机制,比较节省系统资源。显示器有固定的刷新频率(60Hz 或 75Hz),也就是说,每秒最多只能重绘 60 次或 75 次,requestAnimationFrame 的基本思想就是与这个刷新频率保持同步,利用这个刷新频率进行页面重绘。此外,使用这个 API,一旦页面不处于浏览器的当前标签,就会自动停止刷新。这就节省了 CPU、GPU 和电力。
不过有一点需要注意,requestAnimationFrame 是在主线程上完成。这意味着,如果主线程非常繁忙,requestAnimationFrame 的动画效果会大打折扣。
requestAnimationFrame 使用一个回调函数作为参数。这个回调函数会在浏览器重绘之前调用。
1、页面刷新前执行一次
2、浏览器大概 1000ms 刷新 60 次 ==> 换算下来大概 16ms 刷新一次
3、cancelAnimationFrame
requestAnimationFrame(f)
cancelAnimationFrame(id)
4、用法和 setTimeout 类似
5、兼容性
// 兼容性写法
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
window.cancelAnimFrame = (function(){
return window.cancelAnimationFrame ||
window.webkitCancelAnimationFrame ||
window.mozCancelAnimationFrame ||
window.oCancelAnimationFrame ||
window.msCancelAnimationFrame ||
function (id) {
window.clearTimeout(id);
};
})();
<!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>demo03</title>
<style>
div{
position: absolute;
left: 0;
width: 50px;
height: 50px;
background: #f00;
}
</style>
</head>
<body>
<div></div>
<script>
var oDiv = document.getElementsByTagName('div')[0];
// requestAnimFrame 绘制动画
function fun() {
var le = oDiv.offsetLeft;
oDiv.style.left = le + 1 +'px';
if(le < 500){
var req = requestAnimationFrame(fun);
}else{
oDiv.style.left = 500 +'px';
cancelAnimationFrame(req)
}
}
fun();
// setInterval 绘制动画
// var timer = setInterval(function () {
// var le = oDiv.offsetLeft;
// oDiv.style.left = le + 1 +'px';
// if(le > 500){
// clearInterval(timer);
// }
// }, 16);
</script>
</body>
</html>
04 客户端存储
4.1 storage
需要开启服务器,同源策略限制
-
localstorage
-
seesionstorage
4.1.1 如何存储数据
localStrorage.name = 'aimee';
存一个数据localStorage.info = JSON.stringify({name:'aimee,company: 'duyi'});
存一组数据,转换为字符串形式seesionstorage.name = 'aimee';
存一个数据seesionstorage.info = JSON.stringify({name:'aimee,company: 'duyi'});
存一组数据,转换为字符串形式
4.1.2 如何取出数据
localStrorage.name;
取一个数据JSON.parse(localStorage.info);
取一组数据,转换为对象形式seesionstorage.name;
取一个数据JSON.parse(seesionstorage.info);
取一组数据,转换为对象形式
4.1.3 存储的有效期
-
localStorage ==> 永久的,除非手动删除
-
sessionStorage ==> 临时的,窗口关闭就没有了
4.1.4 作用域
- localStorage ==> 文档源限制(同源限制)
- sessionStorage ==> 文档源限制(同源限制) + 窗口
4.1.5 API
setItem(name,val)
:设置属性值getItem(name)
:获得属性值removeItem(name)
:移除属性clear()
:清除所有属性
4.2 cookie
不开启服务器也可以用
-
存储信息到用户的设备上,数据量较小
-
navigator.cookieEnabled
,检测是否启用了 cookie
4.2.1 设置 cookie 值
document.cookie = "name=aimee";
每次只能设置一个值,因为浏览器会认为后面的键值对是这个cookie的属性
4.2.2 获得 cookie 值
- document.cookie:获取所有 cookie 值,需要自己封装方法获取个别 cookie 值
console.log(document.cookie);
不建议出现分号,逗号,空格的奇怪的符号,需要转义
- encodeURIComponent():存入 cookie 时,把特殊字符转义
document.cookie = "name=" + encodeURIComponent('aim;ee');
- decodeURIComponent():获取 cookie 时,解码
console.log(decodeURIComponent(document.cookie));
/**
* 封装 getCookie 方法
* @param {*} name
* @returns
*/
function getCookie(name) {
var name = name + "=";
var ca = document.cookie.split(';');
for(var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1);
if (c.indexOf(name) != -1) return c.substring(name.length, c.length);
}
return "";
}
document.cookie = "name=scott";
document.cookie = "age=1000";
console.log(document.cookie);
console.log(getCookie('name'));
4.2.3 设置 cookie 存储期限
不设置时间默认为 session
- max-age 设置时间段,单位:秒
document.cookie = "name=scott;max-age=1000";
- expires 当前时间加上保存时间
// 设置为北京时间,保存时间为三天
var oDate = new Date();
oDate.setDate(oDate.getDate() + 3);
document.cookie = "name=aimee;expires=" + oDate;
// 设置为格林威治时间,保存时间为 10000s
var timestamp = (new Date()).getTime() + 10000;
var expires = new Date(timestamp).toGMTString();
document.cookie = "name=scott;expires=" + expires;
4.2.4 domain
域名,要使两个 cookie 可以相互访问,可将 domain 设为同一个域名
4.2.5 path
cookie 设置的路径
4.2.6 删除 cookie
- 设置 max-age=0,需要带上键值对
document.cookie = 'name=scott;max-age=0';
- 设置 expires 为之前的时间
document.cookie = 'name=scott;expires= …';
05 history
- history.back():回退
- history.forward():前进
- history.go(n)
- SPA
5.1 通过修改 hash 和 hashchange 事件来实现历史纪录管理
-
pushState,
history.pushState(state, title, url);
添加一条历史记录 -
replaceState,
history.replaceState(state, title, url);
替换当前的历史记录
5.1.1 参数
-
state:一个与指定网址相关的状态对象,popstate 事件触发时,该对象会传入回调函数中。如果不需要这个对象,此处可以填 null
-
title:新页面的标题,但是所有浏览器目前都忽略这个值,因此这里可以填 null
-
url:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址
5.1.2 事件
- popstate 事件:历史记录发生改变时触发
- 调用 history.pushState() 或者 history.replaceState() 不会触发 popstate 事件
- hashchange 事件:当页面的 hash 值改变的时候触发,常用于构建单页面应用
06 Worker
worker 可以开启分线程
var worker = new Worker('worker.js');
worker 文件必须和主文件满足同源策略
6.1 worker 和主线程之间的通信
- postMessage(n) 方法:允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递
- message 事件:响应 postMessage(n) 方法
<!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>demo04</title>
</head>
<body>
<script>
var worker = new Worker('./worker.js');
worker.postMessage(10);
worker.onmessage = function (e) {
console.log(e.data);
}
</script>
</body>
</html>
onmessage = function (e) {
var num = e.data;
for(var i = 0; i < 1000000000; i++){
num += 1;
}
this.postMessage(num);
}
6.2 结束一个 worker
- close():worker 自己关掉,在 worker 作用域中调用(worker.js 里面
close();
) - terminate():主进程里面关掉 worker,在 worker 对象上调用(主进程的 worker 对象上
worker.terminate();
)
6.3 其他特性
importScripts('./math1.js','./math2.js')
worker 只是 window 的子集,只能实现部分功能,不能获取到window.document
,所以这里不要引 juery,zepto。可以引入一些计算类的库- 作用域 globalWorkerScope
- 可以继续生成 worker 对象(暂时还不支持)
- navigator
- XMLHttpRequest
- setTimeout / serInterval
07 多媒体 — audio 和 video
7.1 标签属性
- 和设置 weight 和 height
- autoplay:自动播放
- controls:设置控件
- preload:预加载
- none:默认值,不需要预加载
- metadata:元数据,诸如时长、比特率、帧大小这样的原数据而不是媒体内容需要加载的
- auto:浏览器应当加载它认为适量的媒体内容
- loop:是否循环播放音频/视频
- poster:video独有,当视频不可用或未开始播放时,使用一张图片替代,否则是空白
- 多类型资源
<audio id="music"><source src="happy.mp3" type="audio/mpeg"><source src="happy.ogg" type="audio/ogg"></audio>
<!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>demo06</title>
</head>
<body>
<audio id="music">
<source src="happy.mp3" type="audio/mpeg">
<source src="happy.ogg" type="audio/ogg">
</audio>
</body>
</html>
7.2 脚本化
7.2.1 创建 audio/video 对象
var audio = doxument.getElementById('audio');
var audio = new Audio(./happy.mp3);
var audio = doxument.createElement('video');
7.2.2 设置属性
controls:控制条
audio.controls = true;
autoplay:自动播放
audio.autoplay = true;
loop:循环播放
audio.loop = true;
preload:预加载
audio.preload = 'auto';
volume:音量
- 表示播放音量,介于0(静音)~1(最大音量)之间,默认1。将 muted 属性设置为 true 则会进入静音模式,设置为 false 则会恢复之前指定的音量继续播放
- 超过范围会报错(0-1)
playbackRate:播放速率
- 用于指定媒体播放的速度。该属性值为 1.0 表示正常速度,大于1则表示快进,0-1之间表示慢放,负值表示回放
- 每个浏览器实现的会有差别,具体看浏览器实现
currentSrc:返回资源链接,媒体数据的 url 地址
console.log(audio.currentSrc);
- 注意
windows.onload
currentTime:设置或返回音频/视频播放的当前位置
- 注意
windows.onload
duration:返回当前音频/视频的时长,单位为秒
- 注意
windows.onload
played/buffered/seekable
- played:返回已经播放(看过)的时间段
- buffered:返回当前已经缓冲的时间段
- seekable:返回用户可以跳转的时间段
- 这三个属性都是 TimeRanges 对象,每个对象都有一个 length 属性以及 start() 和 end() 方法
- length:表示当前的一个时间段
- start() 与 end():返回当前时间段的起始时间点和结束时间点(单位是秒,起始参数是0)
- 下面代码确定当前缓存内容的百分比:
var percent_loaded = Math.floor(song.buffered.end(0) / song.duration*100);
paused/seeking/ended:这三个属性用来查询媒体播放状态
- paused 为 true 表示播放器暂停
- seeking 为 true 表示播放器正在调到一个新的播放点
- 如果播放器播放完媒体并且停下来,则 ended 属性为 true
readyState:表示音频元素的就绪状态
-
0 = HAVE_NOTHING:没有关于音频是否就绪的信息
-
1 = HAVE_METADATA:关于音频就绪的元数据
-
2 = HAVE_CURRENT_DATA:关于当前播放位置的数据是可用的,但没有足够的数据来播放下一帧/毫秒
-
3 = HAVE_FUTURE_DATA:当前及至少下一帧的数据是可用的
-
4 = HAVE_ENOUGH_DATA:可用数据足以开始播放
-
networkState:表示音频元素的当前网络状态
- 0 = NETWORK_EMPTY:音频尚未初始化
- 1 = NETWORK_IDLE:音频是活动的且已选取资源,但并未使用网络
- 2 = NETWORK_LOADING:浏览器正在下载数据
- 3 = NETWORK_NO_SOURCE:未找到音频来源
-
error:MediaError 对象的 code 属性返回一个数字值,它表示音频的错误状态
- 1 = MEDIA_ERR_ABORTED:取回过程被用户中止
- 2 = MEDIA_ERR_NETWORK:当下载时发生错误
- 3 = MEDIA_ERR_DECODE:当解码时发生错误
- 4 = MEDIA_ERR_SRC_NOT_SUPPORTED:不支持音频/视频
<!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>demo06</title>
</head>
<body>
<audio id="audio" src=""></audio>
<script>
var audio = document.getElementById('audio');
audio.src = './happy.mp3';
audio.controls = true;
audio.autoplay = true;
audio.loop = true;
audio.preload = 'auto';
</script>
</body>
</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>demo06</title>
</head>
<body>
<script>
var audio = new Audio('./happy.mp3');
document.body.appendChild(audio);
audio.controls = true;
audio.autoplay = true;
audio.loop = true;
audio.preload = 'auto';
</script>
</body>
</html>
7.2.3 方法
- play():播放视频/音频元素
- pause():暂停视频/音频元素
- load():重新加载视频/音频元素,用于在更改来源或其他设置后对音频/视频元素进行更新
- canPlayType():音频/视频格式是否支持
7.2.4 事件
- play:开始播放触发
- pause:暂停触发
- loadedmetadata:浏览器获取完媒体的元数据触发
- loaddeddata:浏览器已经加载完当前帧数据,准备播放时触发
- ended:当前播放结束后触发
<!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>demo06</title>
</head>
<body>
<button id="play">play</button>
<button id="pause">pause</button>
<button id="load">load</button>
<button id="down">-</button>
<button id="up">+</button>
<button id="slow"><<</button>
<button id="quick">>></button>
<button id="more">more</button>
<button id="both">both</button>
<br><br>
<script>
var audio = new Audio('./happy.mp3');
document.body.appendChild(audio);
audio.controls = true;
var play = document.getElementById('play');
var pause = document.getElementById('pause');
var load = document.getElementById('load');
var down = document.getElementById('down');
var up = document.getElementById('load');
var slow = document.getElementById('slow');
var quick = document.getElementById('quick');
var more = document.getElementById('more');
var both = document.getElementById('both');
play.onclick = function () {
audio.play();
}
pause.onclick = function () {
audio.pause();
}
load.onclick = function () {
audio.load();
}
down.onclick = function () {
if(audio.volume > 0.1){
audio.volume -= 0.1;
}
}
up.onclick = function () {
audio.volume += 0.1;
}
quick.onclick = function () {
audio.playbackRate += 0.1;
}
slow.onclick = function () {
audio.playbackRate -= 0.1;
}
more.onclick = function () {
console.log(audio.played);
}
both.onclick = function () {
if(audio.pause){
audio.play();
}else{
audio.pause();
}
}
</script>
</body>
</html>
08 geolocation 对象
8.1 查看 geolocation 对象
- window.navigator.geolocation 对象:查看 geolocation 对象
8.2 getCurrentPosition()
- getCurrentPosition(s, e, p): 获取当前的位置信息
- success:成功的回调(必须)
- error:失败的回调
- options:参数
- 需要翻墙
- Geoposition:成功获取位置时返回的对象
- latitude:纬度
- longitude:经度
- altitude:海拔
- accuracy:定位精准度,单位 m
- altitudeAccuracy:海拔精准度,单位 m
- heading:方向
- speed:速度
- https://dev.w3.org/geo/api/spec-source.html#coordinates_interface
-
PositionError:获取位置失败时返回的对象
- 用户拒绝 code = 1
- 获取不到 code = 2
- 连接超时 code = 3
- https://dev.w3.org/geo/api/spec-source.html#position_error_interface
-
options 配置参数
- enableHighAccuracy:是否需要高精度位置默认 false
- timeout:单位ms,请求超时时间 默认 infinity
- maximumAge:单位ms,位置信息过期时间,设置为 0 就无条件获取新的地理位置信息,默认0
- https://dev.w3.org/geo/api/spec-source.html#position_options_interface
<!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>demo07</title>
</head>
<body>
222
<script>
var options = {
enableHighAccuracy: true,
timeout: 8000
}
// 特别注意,需要在连上vpn的时候才能获取到
function s(e) {
console.log('success');
console.log(e);
}
function e(e) {
console.log('error');
console.log(e);
}
navigator.geolocation.getCurrentPosition(s, e, options);
</script>
</body>
</html>
8.3 watchPosition()
- watchPosition():用于注册监听器,在设备的地理位置发生改变的时候自动被调用
var id = geolocation.watchPosition()
- 参数与 getCurrentPosition 相同
8.4 clearWatch()
- clearWatch(id):使用 clearWatch 清除监听
09 devicemotion 对象
devicemotion 主要用于移动端
window.addEventListener('devicemotion', function(e){
console.log(e);
});
均为只读属性:
- accelerationIncludingGravity:(包括重心引力)重力加速度
- acceleration:(需要陀螺仪支持)重力加速度
- rotationRate(alpha, beta, gamma):旋转速率
- interval:获取的时间间隔
<!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>demo07</title>
</head>
<body>
<div id="container"></div>
<script>
// 演示获取运动加速度
var c = document.getElementById('container');
window.addEventListener('devicemotion', function (e) {
console.log(e);
var h = c.innerHTML + e.accelerationIncludingGravity.x + '-' + e.accelerationIncludingGravity.y + '-' + e.accelerationIncludingGravity.z + '<br>';
c.innerHTML = h;
})
</script>
</body>
</html>
10 drag & drop
- 常用于各种拖动操作中
- 创建可拖动元素
<div draggable="true"></div>
10.1 拖拽相关事件
- dragstart:被拖拽元素,开始被拖拽时触发
e.dataTransfer.setData("data", e.target.id)
- dragend:被拖拽元素,拖拽完成时触发
- dragenter:目标元素,拖拽元素进入目标元素
- dragover:目标元素,拖拽元素在目标元素上移动
- drop:目标元素,被拖拽元素在目标元素上同时鼠标放开触发的事件
e.dataTransfer.getData("data")
- 需要阻止 dragover 的默认行为才会触发 drop 事件
10.2 DragEvent 事件对象
- 传值
e.dataTransfer.setData("data", e.target.id)
- 取值
e.dataTransfer.getData("data")
<!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>demo08</title>
<style>
#drag{
width: 100px;
height: 100px;
background: #000;
}
#box{
height: 300px;
width: 300px;
border: 2px solid #000;
}
</style>
</head>
<body>
<div id="drag" draggable="true"></div>
<br>
<div id="box"></div>
<script>
var drag = document.getElementById('drag');
var box = document.getElementById('box');
drag.addEventListener('dragstart', function (e) {
console.log('dragstart');
e.dataTransfer.setData("data", 111);
});
drag.addEventListener('dragend', function () {
console.log('dragend');
});
box.addEventListener('dragenter', function () {
console.log('dragenter');
});
box.addEventListener('dragover', function (e) {
console.log('dragover');
e.preventDefault();
});
box.addEventListener('drop', function (e) {
console.log('drop');
console.log(e);
console.log(e.dataTransfer.getData("data"));
});
</script>
</body>
</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>demo08</title>
<style>
#drag1{
width: 100px;
height: 100px;
background: #000;
}
#drag2{
width: 100px;
height: 100px;
background: #00f;
}
#box{
height: 300px;
width: 300px;
border: 2px solid #000;
}
</style>
</head>
<body>
<div id="drag1" draggable="true"></div>
<div id="drag2" draggable="true"></div>
<br>
<div id="box"></div>
<script>
var drag1 = document.getElementById('drag1');
var drag2 = document.getElementById('drag2');
var box = document.getElementById('box');
drag1.addEventListener('dragstart', function (e) {
e.dataTransfer.setData("data", 1);
});
drag2.addEventListener('dragstart', function (e) {
e.dataTransfer.setData("data", 2);
});
box.addEventListener('dragover', function (e) {
e.preventDefault();
});
box.addEventListener('drop', function (e) {
var num = e.dataTransfer.getData("data");
console.log(num);
if(num == 1){
box.appendChild(drag1);
}else{
box.appendChild(drag2);
}
});
</script>
</body>
</html>
11 FileReader 构造函数
https://blog.csdn.net/weixin_44116302/article/details/91554835
11.1 FileReader 方法
- abort():终止读取
- 通过不同的方式读取文件:
- readAsText(file, [encoding]):将文件读取为文本,常用
- readAsBinaryString(file):将文件读取为二进制编码
- readAsDataURL(file):将文件读取为 DataURL 编码
- readAsArrayBuffer(file):将文件读取为 arraybuffer
11.2 FileReader 事件
- onloadstart:读取文件开始时触发
- onprogress:读取过程中触发,会返还本次读取文件的最大字节数和已经读取完毕的字节数,可以用来做进度条
- onload:读取完成时触发
- onloadend:读取结束后触发,不论成功还是失败都会触发,触发时机在 onload 之后
- onabort:读取中断时触发
- onerror:读取文件失败时触发
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<form>
<fieldset>
<legend>分步读取文件:</legend>
<input type="file" id="File">
<input type="button" value="中断" id="Abort">
<p>
<lable>读取进度:</lable>
<progress id="Progress" value="0" max="100"></progress>
</p>
</fieldset>
</form>
<script src="./loadFile.js"></script>
<script>
var progress = document.getElementById('Progress');
var events = {
load: function () {
console.log('loaded');
},
progress: function (percent) {
progress.value = percent;
},
success: function () {
console.log('success');
}
};
var loader;
// 选择好要上传的文件后触发onchange事件
document.getElementById('File').onchange = function () {
var file = this.files[0];
console.log(this.files)
console.log(file);
//loadFile.js
loader = new FileLoader(file, events);
};
document.getElementById('Abort').onclick = function () {
loader.abort();
}
</script>
</body>
</html>
/*
* 文件读取模块
* file 文件对象
* events 事件回掉对象 包含 success , load, progress
*/
var FileLoader = function (file, events) {
this.reader = new FileReader();
this.file = file;
this.loaded = 0;
this.total = file.size;
//每次读取1M
this.step = 1024 * 1024;
this.events = events || {};
//读取第一块
this.readBlob(0);
this.bindEvent();
}
FileLoader.prototype = {
bindEvent: function (events) {
var _this = this,
reader = this.reader;
reader.onload = function (e) {
_this.onLoad();
};
reader.onprogress = function (e) {
_this.onProgress(e.loaded);
};
// start 、abort、error 回调暂时不加
},
// progress 事件回掉
onProgress: function (loaded) {
var percent,
handler = this.events.progress;
this.loaded += loaded;
percent = (this.loaded / this.total) * 100;
handler && handler(percent);
},
// 读取结束(每一次执行read结束时调用,并非整体)
onLoad: function () {
var handler = this.events.load;
// 应该在这里发送读取的数据
handler && handler(this.reader.result);
// 如果未读取完毕继续读取
if (this.loaded < this.total) {
this.readBlob(this.loaded);
} else {
// 读取完毕
this.loaded = this.total;
// 如果有success回掉则执行
this.events.success && this.events.success();
}
},
// 读取文件内容
readBlob: function (start) {
var blob,
file = this.file;
// 如果支持 slice 方法,那么分步读取,不支持的话一次读取
if (file.slice) {
blob = file.slice(start, start + this.step);
} else {
blob = file;
}
this.reader.readAsText(blob);
},
// 中止读取
abort: function () {
var reader = this.reader;
if(reader) {
reader.abort();
}
}
}
12 WebSocket
- WebSocket 对象提供了一组APl,用于创建和管理 webSocket 连接,以及通过连接发送和接收数据
- Websocket 其实是一个新协议,跟 HTTP 协议基本没有关系,只是为了兼容现有浏览器的握手规范而己,借用了HTTP的协议来完成握手
- WebSocket 提供了一个专门用来测试 WebSocket 的服务器 — ws://echo.websocket.org
- ws 相当于 http,wss 相当于 https
- 产生原因
- 在 HTTP/1.0 中,大多实现为每个请求/响应交换使用新的连接
- 在 HTTP/1.1 中 一个连接可用于一次或多次请求/响应交换
- HTTP 协议中,服务端不能主动联系客户端,只能有客户端发起
- webSoket 服务器和客户端均可主动发送数据
12.1 建立连接的握手
- 当 web 应用程序调用 new WebSocket(url) 接口时,Browser 就开始了与地址为 url 的 WebServer 建立握手连接的过程
- Browser 与 WebSocket 服务器通过 TCP 握手建立连接,如果这个建立连接失败,那么后面的过程就不会执行,Web 应用程序将收到错误消息通知
- 在 TCP 建立连接成功后,Browser 通过 http 协议传送 WebSocket 支持的版本号,协议的字版本号,原始地址,主机地址等等一些列字段给服务器端
- WebSocket服务器收到 Browser 发送来的请求后,如果数据包数据和格式正确,客户端和服务器端的协议版本号匹配等等,就接受本次握手连接,并给出相应的数据回复,同样回复的数据包也是采用 http 协议传输
- Browser 收到服务器回复的数据包后,如果数据包内容、格式都没有问题的话,就表示本次连接成功,触发 onopen 消息,此时 web 开发者就可以在此时通过 send 接口向服务器发送数据。否则,握手连接失败,Web 应用程序会收到 onerror 消息,并且能知道连接失败的原因
12.2 HTTP 三次握手
- 第一次握手:建立连接时,客户端 A 发送 SYN 包(SYN = j)到服务器 B,并进入 SYN_SEND 状态,等待服务器 B 确认
- 第二次握手:服务器 B 收到 SYN 包,必须确认客户 A 的 SYN(ACK = j + 1),同时自己也发送一个 SYN 包(SYN = k),即 SYN + ACK 包,此时服务器 B 进入 SYN_RECV 状态
- 第三次握手:客户端 A 收到服务器 B 的 SYN+ACK 包,向服务器 B 发送确认包 ACK(ACK = k + 1),此包发送完毕,客户端 A 和服务器 B 进入 ESTABLISHED 状态,完成三次握手
- 完成三次握手,客户端与服务器开始传送数据
12.3 创建 WebSocket
var Socket = new WebSocket;
12.4 WebSocket 方法
- Socket.send():send(data) 方法使用连接传输数据
- Socket.close():close() 方法用于终止任何现有的连接
12.5 WebSocket 事件
事件 | 事件处理程序 | 描述 |
---|---|---|
open | Socket.onopen | 建立 socket 连接时触发这个事件 |
message | Socket.onmessage | 客户端从服务器接收数据时触发 |
error | Socket.onerror | 连接发生错误时触发 |
close | Socket.onclose | 连接关闭时触发 |
<!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>demo10</title>
</head>
<body>
<script>
var socket = new WebSocket('ws://localhost:8888');
socket.onopen = function () {
console.log('open');
socket.send('hello');
}
socket.onmessage = function (e) {
console.log('message');
console.log(e);
console.log(e.data);
socket.close();
}
socket.onclose = function () {
console.log('close');
}
socket.onerror = function () {
console.log('error');
}
</script>
</body>
</html>
12.6 WebSocket 的优点
- 客户端与服务器都可以主动传送数据给对方
- 不用频率创建 TCP 请求及销毁请求,减少网络带宽资源的占用,同时也节省服务器资源