canvas动画及案例

文章介绍了如何利用JavaScript和HTML5CanvasAPI创建动画,强调了使用requestAnimationFrame优化性能的重要性。示例包括太阳系动画,展示了如何绘制并动态旋转太阳、地球和月亮,以及创建实时更新的时钟动画,显示小时、分钟和秒针的转动。
摘要由CSDN通过智能技术生成

介绍

由于我们是用 JavaScript 去操控 对象,这样要实现一些交互动画也是相当容易的。
可能最大的限制就是图像一旦绘制出来,它就是一直保持那样了。如果需要移动它,我们不得不对所有东西(包括之前的)进行重绘。重绘是相当费时的,而且性能很依赖于电脑的速度。

动画基本步骤

  1. 清空 canvas 除非接下来要画的内容会完全充满 canvas(例如背景图),否则你需要清空所有。最简单的做法就是用 clearRect 方法。
  2. 保存 canvas 状态 如果你要改变一些会改变 canvas 状态的设置(样式,变形之类的),又要在每画一帧之时都是原始状态的话,你需要先保存一下。
  3. 绘制动画图形(animated shapes) 这一步才是重绘动画帧。
  4. 恢复 canvas 状态 如果已经保存了 canvas 的状态,可以先恢复它,然后重绘下一帧

更新画布

  1. setInterval(function, delay)(不推荐,不准确,宏任务)
    当设定好间隔时间后,function 会定期执行。

  2. setTimeout(function, delay)(不推荐,不准确,宏任务)
    在设定好的时间之后执行函数

  3. requestAnimationFrame(callback)
    告诉浏览器你希望执行一个动画,并在重绘之前,请求浏览器执行一个特定的函数来更新动画。
    一般每秒钟回调函数执行 60 次

太阳系动画

<script>
        window.onload= function() {
            const canvasEl = document.getElementById('myCanvas')
            if (!canvasEl.getContext) {
                return
            }
            const ctx = canvasEl.getContext('2d')
            let sun = new Image()
            sun.src = '../images/canvas_sun.png'
            let earth = new Image()
            earth.src = '../images/canvas_earth.png'
            let moon = new Image()
            moon.src = '../images/canvas_moon.png'
            requestAnimationFrame(draw)
            // 一秒钟大概重绘60次
            function draw() {
                ctx.clearRect(0,0,300,300)
                ctx.save()
                // 绘制背景
                drawSun()
                // 绘制地球
                drawEarth()
                ctx.restore()
                requestAnimationFrame(draw)
            }
        
            function drawSun() {
                ctx.save()
                ctx.drawImage(sun,0,0)
                // 绘制轨道线
                ctx.translate(150,150)
                ctx.strokeStyle = 'rgba(255,255,255,0.3)'
                ctx.beginPath()
                ctx.arc(0,0,100,0,Math.PI * 2)
                ctx.stroke()
                ctx.restore()
            }
            function drawEarth() {
                ctx.save()
                ctx.translate(150,150)
                // 地球旋转(一分钟一周)
                // 获取当前时间miao 
                let Seconds = new Date().getSeconds()
                let milliseconds = new Date().getMilliseconds()
                // 一周Math.PI * 2
                // 180 Math.PI * 1
                // 一秒:Math.PI * 2 / 60
                // 一毫秒:Math.PI * 2 / 60 / 1000
                ctx.rotate(Math.PI * 2 / 60 * Seconds + Math.PI * 2 / 60 / 1000 * milliseconds)
                ctx.translate(100,0)
                ctx.drawImage(earth,-12,-12)
                
                // 绘制月球
                drawMoon(Seconds,milliseconds)
                // 绘制蒙版
                drawMengban()
                ctx.restore()
            }
            function drawMoon(Seconds,milliseconds) {
                ctx.save()
                // 月球旋转(10s一周)
                // 一秒: Math.PI * 2 / 10
                //一毫秒:Math.PI * 2 / 10 / 1000
                ctx.rotate(Math.PI * 2 / 10 * Seconds + Math.PI * 2 / 10 / 1000 * milliseconds)
                ctx.translate(30,0)
                ctx.drawImage(moon,-3.5,-3.5)
                ctx.restore()
            }
            function drawMengban() {
                ctx.save()
                ctx.fillStyle = 'rgba(0,0,0,0.3)'
                ctx.fillRect(0,-15,40,30)
                ctx.restore()
                
            }
        
        }

        
    </script>

在这里插入图片描述

时钟动画

<body>
  <div class="clock">
    <canvas id="tutorial" width="300" height="300px">
      你的浏览器不兼容Canvas,请升级您的浏览器!
    </canvas>
  </div>
  <script>
    window.onload = function() {
      let canvasEl = document.getElementById('tutorial')
      if(!canvasEl.getContext){
        return
      }
      let ctx = canvasEl.getContext('2d') // 2d | webgl
      requestAnimationFrame(draw)

      /**
       1秒钟会回调 61次
      */
      function draw() {
        ctx.clearRect(0, 0, 300, 300)
        ctx.save()
        let time = new Date()
        let hours = time.getHours()
        let minute = time.getMinutes()
        let second = time.getSeconds()

        // 1.绘制背景(白色圆)
        drawBg()
        // 2.绘制的数字
        drawNumbers()
        // 3.绘制时针
        drawHours(hours, minute, second)
        // 3.绘制分针
        drawMinute(minute, second)
        // 4.绘制秒针
        drawSecond(second)
        // 5.绘制圆心
        drawCircle()
        // 6.画圆上的时针的刻度
        drawHoursTick()
        // 7.画圆上的分针的刻度
        drawMinuteTick()
        ctx.restore()
        requestAnimationFrame(draw)
      }

      function drawBg(){
        ctx.save()
        ctx.translate(150, 150)
        ctx.fillStyle ='white'
        ctx.beginPath()
        ctx.arc(0,0, 130, 0, Math.PI * 2)
        ctx.fill()
        ctx.restore()
      }

      function drawNumbers(){
        ctx.save()
        ctx.translate(150, 150)

        // 开始画 3 数字
        ctx.font = "30px fangsong"
        ctx.textBaseline = 'middle'
        ctx.textAlign = 'center'

        let numbers = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2]
        for(let i =0; i<numbers.length; i++){
          // i = 3 , 0 
          // i = 4 , 占 1份, Math.PI * 2 / 12 * i  
          let x = Math.cos(Math.PI * 2 / 12 * i) * 100
          let y = Math.sin(Math.PI * 2 / 12 * i) * 100
          ctx.fillText(numbers[i], x, y)
        }
        ctx.restore()
      }
      function drawHours(hours, minute, second) {
        ctx.save()
        ctx.translate(150, 150) // 坐标轴原点在园的中心

        // Math.PI * 2 一整个圆
        // Math.PI * 2 / 12  1h
        // Math.PI * 2 / 12 / 60  1min
        // Math.PI * 2 / 12 / 60 / 60  1secon

        // 1h + 1min + 1 second = 弧度?
        ctx.rotate(
          Math.PI * 2 / 12 * hours +
          Math.PI * 2 / 12 / 60 * minute +
          Math.PI * 2 / 12 / 60 / 60 * second 
        )
        ctx.lineWidth = 5
        ctx.lineCap = 'round'
        ctx.beginPath()
        ctx.moveTo(0,0)
        ctx.lineTo(0, -50)
        ctx.stroke()
        ctx.restore()
      }

      function drawMinute(minute, second) {
        ctx.save()
        ctx.translate(150, 150) // 坐标轴原点在园的中心

        // Math.PI * 2 一整个圆
        // Math.PI * 2 / 60  1min
        // Math.PI * 2 / 60 / 60  1sec

        // 59min + 59 second = 弧度?
        ctx.rotate(
          Math.PI * 2 / 60 * minute +
          Math.PI * 2 / 60 / 60 * second 
        )
        ctx.lineWidth = 3
        ctx.lineCap = 'round'
        ctx.beginPath()
        ctx.moveTo(0,0)
        ctx.lineTo(0, -70)
        ctx.stroke()
        ctx.restore()
      }

      function drawSecond(second) {
        ctx.save()
        ctx.translate(150, 150) // 坐标轴原点在园的中心

        // Math.PI * 2 一整个圆
        // Math.PI * 2 / 60   1sec

        // 1 second = 弧度?
        ctx.rotate(
          Math.PI * 2 / 60 * second 
        )
        ctx.strokeStyle = 'red'
        ctx.lineWidth = 2
        ctx.lineCap = 'round'
        ctx.beginPath()
        ctx.moveTo(0,0)
        ctx.lineTo(0, -80)
        ctx.stroke()
        ctx.restore()
      }

      function drawCircle() {
        ctx.save()
        ctx.translate(150, 150)

        ctx.beginPath()
        ctx.arc(0, 0, 8, 0, Math.PI*2)
        ctx.fill()

        ctx.fillStyle = 'gray'
        ctx.beginPath()
        ctx.arc(0, 0, 5, 0, Math.PI*2)
        ctx.fill()

        ctx.restore()
      }

      function drawHoursTick() {
        ctx.save()
        ctx.translate(150, 150)

        for(let j = 0; j< 12 ; j ++){
          ctx.rotate(Math.PI * 2 / 12 )
          ctx.lineWidth = 3
          ctx.beginPath()
          ctx.moveTo(0, -130)
          ctx.lineTo(0, -122)
          ctx.stroke()
        }

        ctx.restore()
      }

      function drawMinuteTick() {
        ctx.save()
        ctx.translate(150, 150)
        for(let j = 0; j< 60 ; j ++){
          ctx.rotate(Math.PI * 2 / 60 )
          ctx.lineWidth = 1
          ctx.beginPath()
          ctx.moveTo(0, -130)
          ctx.lineTo(0, -125)
          ctx.stroke()
        }
        ctx.restore()
      }
    }
  </script>
</body>

在这里插入图片描述

参考

链接: mdn

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值