canvas动画及案例

news2024/11/23 11:21:37

介绍

由于我们是用 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/366407.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot项目打包部署到阿里云服务器、通过Maven插件制作Docker镜像、部署项目容器、配置生产环境

制作通用模块jar包 通用模块不是运行的&#xff0c;而且要被其他模块引入的&#xff0c;所以该模块不能采用springboot打包方式制作jar包&#xff0c;否则其他模块无法引入通用模块。 1、修改通用模块&#xff0c;设置模块为非Springboot项目 <?xml version"1.0&qu…

致盛咨询携手亚马逊云科技进一步开拓中国市场

作为医疗保健领域的咨询公司&#xff0c;ZS需要保证服务可靠性、敏捷性和安全性的同时&#xff0c;获得经济效益。亚马逊云科技丰富的云服务产品简化了ZS基础架构的搭建&#xff0c;为ZS节省了大量的人力与资金成本。同时&#xff0c;缩短了ZS扩展基础设施的周转时间&#xff0…

LeetCode Cookbook 哈希表(collections.Counter()和collections.defaultdict())

好久不更了&#xff0c;这次一鼓作气&#xff0c;学完它&#xff01; 文章目录LeetCode Cookbook 哈希表30. 串联所有单词的子串36. 有效的数独&#xff08;很不错的循环题目&#xff09;49. 字母异位词分组290. 单词规律447. 回旋镖的数量575. 分糖果594. 最长和谐子序列599. …

Read book Netty in action(Chapter VI)--ByteBuf

序言 之前学习了传输&#xff0c;通过前面的学习我们都知道&#xff0c;网络数据的基本单位是字节。JDK中提供了ByteBuffer作为字节的容器&#xff0c;但是过于繁琐复杂&#xff0c;Netty中提供了ByteBuf作为替代品。学习一下。 API Netty的数据处理API通过两个组件暴露 ---…

STM32开发(15)----芯片内部温度传感器

芯片内部温度传感器前言一、什么是内部温度传感器&#xff1f;二、实验过程1.STM32CubeMX配置2.代码实现3.实验结果总结前言 本章介绍STM32芯片温度传感器的使用方法和获取方法。 一、什么是内部温度传感器&#xff1f; STM32 有一个内部的温度传感器&#xff0c;可以用来测…

竣达技术 | 巡检触摸屏配合电池柜,电池安全放首位!

机房蓄电池常见的故障 1.机房电池着火和爆炸 目前在数据机房蓄电池爆炸着火事故频发&#xff0c;导致业主损失严重。一般机房电池是由于其中一节电池裂化后未妥善管理&#xff0c;电池急剧恶化导致爆炸着火。由于电池是串联及并联在使用&#xff0c;只要一节着火燃烧整片瞬间…

RK3399平台开发系列讲解(LED子系统篇)LED子系统详解

🚀返回专栏总目录 文章目录 一、设备树编写二、LED子系统2.1、用户态2.2、内核驱动三、驱动代码3.1、平台设备驱动的注册3.2、平台设备驱动的probe四、使用方法沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将详细介绍LED子系统。 一、设备树编写 节点属性添加…

免费领取丨精算与金融建模行业解决方案白皮书,不要错过!

一、我国精算行业现状 精算学是对人类社会所面临的各种风险及其他客观事务进行量化分析和处理的一门科学。在保险、金融、投资和各类风险管理等许多领域得到广泛应用&#xff0c;尤其在保险和社会保障领域&#xff0c;已成为不可或缺的科学和技术&#xff0c;以保险公司为例&a…

直播间的2个小感悟

我是卢松松&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 在线人数固定 最近直播间出现了很多新面孔&#xff0c;有的是偶然刷到的&#xff0c;有的是关注互联网找到的。而直播间的人数一直没什么变化&#xff0c;卢松松在抖音直播较少&#xff0c;主播间…

mysql 8.0.22安装

mysql8.0.22安装1. 配置my.ini2. 添加环境变量3. 安装mysql3.1 mysql初始化3.2 安装mysql服务3.3 启动mysql服务4. 连接数据库修改连接数据库的密码前提&#xff1a;已经从官网下载mysql8.0.22安装包并解压&#xff08;下载地址&#xff1a;https://dev.mysql.com/downloads/in…

设计模式学习笔记 - 外观模式

设计模式学习笔记 - 外观模式一、影院管理问题二、传统方式解决影院管理问题三、外观模式介绍1、基本介绍2、原理类图四、外观模式解决影院管理问题五、外观模式在MyBatis框架应用的源码分析六、外观模式的注意事项和细节一、影院管理问题 组建一个家庭影院&#xff1a;DVD 播放…

ur3+robotiq ft sensor+robotiq 2f 140+realsense d435i配置rviz,gazebo仿真环境

ur3robotiq ft sensorrobotiq 2f 140realsense d435i配置rviz&#xff0c;gazebo仿真环境 搭建环境&#xff1a; ubuntu: 20.04 ros: Nonetic sensor: robotiq_ft300 gripper: robotiq_2f_140_gripper UR: UR3 reasense&#xff1a; D435i 通过下面几篇博客配置好了ur3、力传…

带你轻松实现通讯录(C语言版)

文章目录前言通讯录初始化通讯录运行的基本框架和菜单增添联系人删除联系人查找联系人修改联系人信息展示通讯录通讯录联系人个数排序通讯录文件操作储存通讯录信息销毁通讯录整体代码Contacts.hContacts.ctest.c写在最后前言 学习C语言的小伙伴&#xff0c;相信都要经历实现通…

Web网页测试全流程解析论Web自动化测试

1、功能测试 web网页测试中的功能测试&#xff0c;主要测试网页中的所有链接、数据库连接、用于在网页中提交或获取用户信息的表单、Cookie 测试等。 &#xff08;1&#xff09;查看所有链接&#xff1a; 测试从所有页面到被测特定域的传出链接。 测试所有内部链接。 测试链…

开学准备哪些电容笔?ipad触控笔推荐平价

在现代&#xff0c;数码产品的发展受到高技术的驱动。不管是在工作上&#xff0c;还是在学习上&#xff0c;大的显示屏可以使图像更加清晰。Ipad将成为我们日常生活中不可或缺的一部分&#xff0c;无论现在或将来。如果ipad配上一款方便操作的电容笔&#xff0c;将极大地提高我…

Unity性能优化:如何优化Drawcall

前言 降低游戏的Drawcall&#xff0c;是渲染优化很重要的手段&#xff0c;接下来从以下4个方面来分析如何降低DrawCall: 对惹&#xff0c;这里有一个游戏开发交流小组&#xff0c;希望大家可以点击进来一起交流一下开发经验呀 降低Drawcall的意义是什么?如何查看游戏的Drawca…

C++继承、构造函数和析构函数

构造函数 与 析构函数 构造函数代表一个对象的生成&#xff0c;主要作用是初始化类的成员变量&#xff0c;可以被重载 如果没有显式构造函数&#xff0c;则实例化对象时&#xff0c;系统会自动生成一个无参的构造函数 构造函数的名称与类名相同 析构函数代表的是一个对象的销…

初识Python——“Python”

各位CSDN的uu们你们好呀&#xff0c;今天进入到了我们的新专栏噢&#xff0c;Python是小雅兰的专业课&#xff0c;那么现在&#xff0c;就让我们进入Python的世界吧 计算机基础概念 什么是计算机&#xff1f; 什么是编程&#xff1f; 编程语言有哪些&#xff1f; Python背景知…

MySQL的安装(详解)

文章目录前言一、yum方式安装1、下载并安装MySQL2、 启动MySQL数据库3、查看MySQL初始密码4、登录数据库5、修改MySQL默认密码6、授予root用户远程管理权限7、输入exit退出数据库二、rpm安装方式1、检查2、卸载mariadb3、安装4、启动5、密码总结前言 本教程为Linux下安装mysql的…

若依配置教程(九)若依前后端分离版部署到服务器Nginx(Windows版)

搭建若依环境 要部署到服务器上&#xff0c;首先要在本地运行若依系统 文章目录搭建若依环境后端部署1.在application.yml中修改后台端口&#xff0c;这里默认是8080。2.在application-druid.yml中修改正式环境数据库。3.后端打包部署前端部署下载安装NginxNginx代理配置启动N…