canvas-_03 动画相关

动画相关

制作动画的基本步骤

  • 清理画布:ctx.clearRect(0,0,canvas.width,canvas.height)

  • 保存 canvas 上下文对象的状态:ctx.save()

  • 绘制动画图形:…

  • 恢复 canvas 上下文对象的状态:ctx.restore()
    在这里插入图片描述

驱动动画的方法

  1. setTimeOut(fn,time) 和setInterval(fn,time)
    优点:使用方便,动画的时间间隔可以自定义。
    缺点:隐藏浏览器标签后,会依旧运行,造成资源浪费。与浏览器刷新频率不同步。
  2. requestAnimationFrame(fn)
    优点:性能更优良。隐藏浏览器标签后,便不会运行。与浏览器刷新频率同步。
    缺点:动画的时间间隔无法自定义

速度和加速度

  • 速度:描述物体运动快慢和运动方向的物理量

  • 加速度:描述速度变化的量

匀速运动
obj.x+=vx;
obj.y+=vy;

在这里插入图片描述

加速运动:
vx += ax;
vy += ay;
obj.x+=vx;
obj.y+=vy;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w5KOaOMK-1597720708134)(/home/aoshisen/.config/Typora/typora-user-images/image-20200818085859518.png)]

请求动画帧里的时间差

请求动画帧里的时间差是参差不齐的。比如第一次用16 毫秒驱动了动画,下一次可能是20毫秒,下下次可能是17 毫秒。

常见的误区:

比如:超人第一秒跑了100 米,第二秒内也跑了一百米,第三秒内跑了一百米,但是这并不是真正的匀速运动,虽然这样算出来的平均速率是100M/s。

但是定义匀速运动与否的单位是平均速度(区别与匀速率)正确的匀速运动应该是超人一直以100M/s的速度运动,第一秒跑了一百米,第二秒跑了一百米 。。。。。。

弹性运动

从小球落地的事件中解析弹性运动

初始数据
速度:vy=0
加速度(重力):ay=0.01
弹力:bounce=0.8

动画帧中:
vy+=ay
ball.y+=vy

碰撞地面时:
vy*=-bounce
vx*=-bounce

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QoVCV0r6-1597720708137)(/home/aoshisen/.config/Typora/typora-user-images/image-20200818093856803.png)]

补间动画

补间动画是在两个关键帧之间,以某种算法自动计算物体运动的插值,从而形成一种过度效果。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BSRrCMYf-1597720708139)(/home/aoshisen/.config/Typora/typora-user-images/image-20200818094113550.png)]

用tween.js 做补间

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lQxUmr1x-1597720708140)(/home/aoshisen/.config/Typora/typora-user-images/image-20200818094217766.png)]

颜色补间动画

tween.js 并没有提供颜色的过度方法,所以我把d3 的color 插件拼装到了tween.js 中去,形成了一个SupTween.js,使用之前需要引入

用户交互

添加交互

canvas 图形没有监听事件的方法。
比如用鼠标选择图形时,我们只能用canvas 画布监听事件,获取鼠标或触摸点在canvas 中的位置,再基于图形在canvas 中的位置和形状,判断鼠标在canvas中的点位是否在图形中。

获取canvas 中鼠标位置的方法

canvas.addEventListener('mousedown', getPos);
function getPos(event){
    const {clientX,clientY}=event;
    const {left,top}=canvas.getBoundingClientRect();
    const [x,y]=[clientX-left,clientY-top];
    console.log(x,y);
}

扩展-获取触摸点点位的方法

canvas.addEventListener('mousedown', getPos);
function getPos(event){
    const {pageX, pageY}=event.changedTouches[0];
    const {left,top}=canvas.getBoundingClientRect();
    const [x,y]=[pageX -left, pageY -top];
    console.log(x,y);
}

isPointInPath(x,y) 选择图形

isPointInPath(x,y) 是canvas 2d中的内置方法,它可以判断一个点位是否在路径中。
isPointInPath(x,y) 面向的对象是路径,所以对文字、fillRect()、strokeRect()不好使。
回顾一下路径的基本概念:在我们使用canvas 的getContext(‘2d’) 方法获取canvas 上下文对象ctx 的时候, ctx上便挂载了一个空的路径集合。在ctx.beginPath() 之后,所绘制的所有路径都会被添加到这个路径集合里,isPointInPath(x,y) 方法判断的就是x、y 点是否在这个路径集合的所有路径里。这个路径可以不用画出来,只要路径集合里有路径即可。注意,在下一次ctx.beginPath() 时,路径集合会被置空。

示例:用三个点画了一条折线,没有将其闭合,然后对其进行选择。

const ctx=canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(50,50);
ctx.lineTo(450,50);
ctx.lineTo(250,200);
console.log(ctx.isPointInPath(250,100)); //true
ctx.stroke();

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iZzCyXwK-1597720708141)(/home/aoshisen/.config/Typora/typora-user-images/image-20200818110848697.png)]

总结:

动画是像素在时间中的舞蹈。无论多么炫酷的动画,它都是在改变屏幕中像素的位置和色彩。
动画可以应用于canvas 图形的位置、旋转、缩放和样式。
动画的制作方式有两种:逐帧动画和补间动画。
逐帧动画只会考虑下一帧。
补间动画在动画的开始就已经知道了动画的结尾,只是动画的过程会有各自的精彩。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个简单的示例代码,展示了如何使用 tkinter 的 canvas 和 PIL 库实现小人走路动画。在这个示例中,我们使用了三个不同的图片,分别表示小人的不同动作。 ```python from tkinter import * from PIL import Image, ImageTk class WalkingMan: def __init__(self, master): self.master = master self.canvas = Canvas(master, width=500, height=500) self.canvas.pack() # 加载小人图片 self.images = [ Image.open("walk1.png"), Image.open("walk2.png"), Image.open("walk3.png") ] self.image_tk = [ImageTk.PhotoImage(image) for image in self.images] # 在画布上创建小人 self.man = self.canvas.create_image(50, 50, image=self.image_tk[0]) # 设置定时器,每隔一段时间更新小人的位置和动作 self.current_image = 0 self.dx = 2 self.master.after(100, self.update) def update(self): # 更新小人的位置和动作 self.canvas.move(self.man, self.dx, 0) self.current_image = (self.current_image + 1) % 3 self.canvas.itemconfig(self.man, image=self.image_tk[self.current_image]) # 如果小人碰到画布边缘,改变移动方向 x1, y1, x2, y2 = self.canvas.bbox(self.man) if x2 > 500 or x1 < 0: self.dx = -self.dx # 继续更新 self.master.after(100, self.update) root = Tk() app = WalkingMan(root) root.mainloop() ``` 在这个示例中,我们创建了一个 `WalkingMan` 类,它包含一个 `Canvas` 对象,用于显示小人的动画。在 `__init__()` 方法中,我们加载了三个不同的小人图片,并在画布上创建了一个小人对象。然后,我们设置了一个定时器,每隔一段时间更新小人的位置和动作。在 `update()` 方法中,我们首先移动小人的位置,然后更新小人的动作。如果小人碰到了画布的边缘,我们会改变它的移动方向。最后,我们使用 `after()` 方法来设置下一次更新的时间,以便我们可以持续不断地更新小人的动画。 如果你想要实现更复杂的动画效果,你可能需要使用更多的图片,以及更复杂的逻辑来控制小人的移动和动作。不过,这个示例代码应该能够帮助你入门 tkinter canvas 和 PIL 库的基本用法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值