夜听城嚣 夜听城嚣
首页
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 前端基建与架构
  • 专题分享

    • Git入门与开发
    • 前端面试题汇总
    • HTML和CSS知识点
  • 项目实践
  • 抓包工具
  • 知识管理
  • 工程部署
  • 团队规范
bug知多少
  • 少年歌行
  • 青年随笔
  • 文海泛舟
  • 此事躬行

    • 项目各工种是如何协作的
    • TBA课程学习
收藏

dwfrost

前端界的小学生
首页
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 前端基建与架构
  • 专题分享

    • Git入门与开发
    • 前端面试题汇总
    • HTML和CSS知识点
  • 项目实践
  • 抓包工具
  • 知识管理
  • 工程部署
  • 团队规范
bug知多少
  • 少年歌行
  • 青年随笔
  • 文海泛舟
  • 此事躬行

    • 项目各工种是如何协作的
    • TBA课程学习
收藏
  • 第1章 什么是JavaScript
  • 第2章 HTML中的JavaScript
  • 第3章 语言基础
  • 第4章 变量、作用域与内存
  • 第5章 基本引用类型
  • 第6章 集合引用类型
  • 第7章 迭代器与生成器
  • 第8章 对象、类与面向对象编程
  • 第9章 代理与反射
  • 第10章 函数
  • 第11章 异步编程
  • 第12章 BOM
  • 第14章 DOM
  • 第15章 DOM扩展
  • 第16章 DOM2和DOM3
  • 第17章 事件
  • 第18章 动画与Canvas图形
    • 第19章 表单脚本
    • 第20章 JavaScript API
    • 第21章 错误处理与调试
    • 第23章 JSON
    • 第24章 网络请求与远程资源
    • 第25章 客户端存储
    • 第26章 模块
    • 第27章 工作者进程
    • 第28章 最佳实践
    • 《JavaScript高级程序设计》
    frost
    2022-02-20

    第18章 动画与Canvas图形

    本章节涉及动画与Canvas,由于web开发较少涉及Canvas和WebGL,所以本章只介绍requestAnimationFrame和Canvas部分。

    # requestAnimationFrame

    早期使用js来执行动画是通过setTimeout和setInterval实现,不过这种动画的问题在于浏览器不知道js动画何时开始,以及最佳间隔是多少(不同系统有差异)。

    一般来说,计算机显示器的屏幕刷新率是60Hz,即每秒绘制60次,所以平滑动画最佳的重绘间隔是1000/60,约16.67ms。

    不过浏览器对于setTimeout和setInterval的计时器精度也是大小不一,即使将时间间隔设置为最优,但受限于浏览器的计时器精度,也只能得到近似的结果。

    此时requestAnimationFrame应运而生,它会通知浏览器js代码要执行动画了,这样浏览器就可以对动画进行适当优化。

    <!DOCTYPE html>
    <head>
      <style>
        .progress{
          width: 0;
          height: 100px;
          background-color: pink;
        }
      </style>
    </head>
    <body>
        <button>button</button>
        <div class="progress"></div>
    </body>
    </html>
    
    <script>
      const btn = document.querySelector('button')
      const progress = document.querySelector('.progress')
      const updateProgress =()=>{
        progress.style.width = (parseInt(progress.style.width,10)+1) + '%'
        if(progress.style.width!=='100%'){
          requestAnimationFrame(updateProgress)
        }
      }
      btn.onclick = function(){
        progress.style.width = '0%'
        requestAnimationFrame(updateProgress)
      }
    </script>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30

    requestAnimationFrame会返回一个请求id,可以通过canceAnimationFrame来取消重绘任务。

    # 通过requestAnimationFrame节流

    requestAnimationFrame可以保证每次重绘只调用一次回调函数,可以达到节流的效果。对于频繁发生的行为(如页面滚动),可以进行节流来提升性能。

      const log = ()=>{
        console.log('Invoked at: ',Date.now())
      }
      window.addEventListener('scroll',()=>{
        requestAnimationFrame(log)
      })
    
    1
    2
    3
    4
    5
    6

    # Canvas

    canvas是一个html标签,用于绘制图形。绘制时是绘制在画布上,此时有个对象叫上下文,通过它调取canvas的API来进行绘制。

    const canvas = document.querySelector('canvas')
    let ctx = canvas.getContext('2d')
    
    1
    2

    2D上下文的坐标原点(0,0)在<canvas>元素的左上角,x坐标向右增长,y坐标向下增长。

    # 填充和描边

    • fillStyle: 填充颜色,可以是CSS支持的任意格式,默认"#000000"。

    • strokeStyle: 描边颜色,格式同上。

    # 绘制矩形

    绘制矩形的方法有:

    • fillRect(): 用于以指定颜色在画布上绘制并填充矩形。
    • strokeRect():在画布上绘制矩形轮廓。
    • clearRect():擦除画布中某个区域。
      const canvas = document.querySelector('canvas')
      const ctx = canvas.getContext('2d')
    
      // 绘制蓝色矩形
      ctx.fillStyle = 'blue'
      ctx.fillRect(10,10,50,50)
    
      // 绘制红色描边矩形
      ctx.strokeStyle = 'red'
      ctx.strokeRect(30,30,50,50)
    
      // 在重叠区擦除一个矩形区
      ctx.clearRect(40,40,10,10)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    # 绘制路径

    • beginPath():创建新路径。
    • arc(x,y,radius,startAngle,endAngle,counterclockwise):绘制弧线。
    • arcTo(x1,y1,x2,y2,radius):绘制弧线。
    • bezierCurveTo(c1x,c1y,c2x,c2y,x,y):绘制弧线(三次贝塞尔曲线)。
    • lineTo(x,y):绘制到(x,y)的直线。
    • moveTo(x,y):不绘制,画笔移到(x,y)。
    • quadraticCurveTo(cx,cy,x,y):绘制弧线(二次贝塞尔曲线)。
    • rect(x,y,width,height):绘制矩形路径。
    • closePath():绘制一条返回起点的路径。

    路径绘制完成后,可以使用fill()方法填充,也可以使用stroke()来描边。

    下面的例子绘制了一个不带数字的表盘:

      const canvas = document.querySelector('canvas')
      const ctx = canvas.getContext('2d')
      canvas.width  = window.innerWidth;
      canvas.height = window.innerHeight;
    
      // 创建路径
      ctx.beginPath()
    
      // 绘制外圆
      ctx.arc(100, 100, 99, 0, 2 * Math.PI, false)
    
      // 绘制内圆
      ctx.moveTo(194,100)
      ctx.arc(100,100,94,0,2*Math.PI,false)
    
      // 绘制分针
      ctx.moveTo(100,100)
      ctx.lineTo(100,15)
    
      // 绘制时针
      ctx.moveTo(100,100)
      ctx.lineTo(35,100)
    
      // 描边
      ctx.stroke()
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25

    # 绘制文本

    • fillText(): 填充文字。

    • strokeText():描边文字。

      ctx.font = '30px Arial'
      ctx.fillText('hello',100,120)
      ctx.strokeText('world',100,140)
    
    1
    2
    3

    有一个辅助确定文本大小的measureText()方法,可以返回文本的宽度。下面的例子演示了字体大小从100px开始计算,不断递减,直到将文本刚好放到对应像素宽度的矩形中。

      function setSize(text,maxWidth=140){
      let fontSize = 100
      ctx.font = fontSize + 'px Arail'
        while(ctx.measureText(text).width>maxWidth){
          fontSize--
          ctx.font = fontSize + 'px Arail'
        }
      }
    
      setSize('Hello world',140)
      ctx.fillText('Hello world',10,100)
    
      setSize('Hello world',280)
      ctx.fillText('Hello world',10,200)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    # 变换

    以下方法用于改变绘制上下文的变换矩阵。

    • rotate(angle):围绕原点把图像旋转angle弧度。
    • scale(scaleX,scaleY),缩放。
    • translate(x,y):把原点移动到(x,y)。
    • transform(m1_1,m1_2,m2_1,m2_2,dx,dy):通过矩阵乘法直接修改矩阵。

    上面表盘的例子中,可以改动如下:

      ctx.translate(100,100)
    
      // 绘制分针
      ctx.moveTo(0,0)
      ctx.lineTo(0,15-100)
    
      // 绘制时针
      ctx.moveTo(0,0)
      ctx.lineTo(35-100,0)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    即将原点平移到表盘中心,之后的坐标都-100即可。

    在变换过程中,变换的状态可以保存和弹出。

    save():保存当前上下文的属性和状态,此时的设置会被暂存到栈中。

    restore():应用完当前状态后,恢复(回滚)到上一个状态。

    # 绘制图像

    drawImage(img,x,y,width,height,targetX,targetY,targetWidth,targetHeight):把图像或canvas绘制到画布上,前3个参数必填。

    # 阴影(略)

    非常规绘图,参考阴影 (opens new window)。

    # 渐变(略)

    非常规绘图,参考渐变 (opens new window)。

    # 图像数据(略)

    可以通过getImageData() (opens new window)获取原始图像数据,比如RGB的大小信息。并可以通过putImageData()重新绘制。

    # 合成(略)

    有2个属性可以全局应用:globalAlpha (opens new window)和globalCompositeOperation (opens new window)。

    #读书笔记
    上次更新: 2022/02/20, 17:01:59
    第17章 事件
    第19章 表单脚本

    ← 第17章 事件 第19章 表单脚本→

    最近更新
    01
    提交代码时修改commit消息
    04-09
    02
    如何快速定位bug
    02-20
    03
    云端web项目开发踩坑
    08-25
    更多文章>
    Theme by Vdoing | Copyright © 2021-2025 dwfrost | 粤ICP备2021118995号
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式
    ×