【HTML5 笔记】基础内容

文章目录

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');
<!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),半径为 r2
  • addColorStop(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'; 线条的结束端点样式,默认为 butt
  • lineJoin = 'bevel|round|miter'; 两条线相交时,所创建的拐角类型,默认为 miter
  • lineWidth 线条宽度
  • 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" />
    • 绝对坐标和相对坐标
  • 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 事件

事件事件处理程序描述
openSocket.onopen建立 socket 连接时触发这个事件
messageSocket.onmessage客户端从服务器接收数据时触发
errorSocket.onerror连接发生错误时触发
closeSocket.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 请求及销毁请求,减少网络带宽资源的占用,同时也节省服务器资源
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值