§7.3 3D 动画#
约 720 个字 126 行代码 预计阅读时间 13 分钟
一、3D 对象 API#
3D 对象有许许多多方法供我们调用。
1.1 基本 3D 对象的 API#
基本 3D 对象的 API 名称都是一样的,它们都是继承自类 _Object3D
,都可以进行平移、旋转和缩放。
1.1.1 平移#
平移使用方法 translate
,平移没有什么特别,就是移动对象在空间中的位置。
1.1.2 旋转#
旋转使用方法 rotate
,旋转有两个重载,可以旋转绕某一点进行旋转,也可以旋转绕某一条线进行旋转。 两者的参数略有不同,默认情况下是绕原点进行旋转。
1.1.3 缩放#
缩放使用方法 scale
,缩放没有重载,但是它有一个参数非常有意思,即缩放参照点。将这个点设为原点,那么对象将会以原点为参照进行大小缩放,这样它的大小和位置都会发生改变。但如果我们将其设为改对象的几何中心,则对象只有大小会发生变化,位置不会有改动。
1.1.4 几何中心#
此 API 名为 center
,调用它不会有任何变化,但它返回该对象的几何中心。可以配合其他 API 来进行使用。
1.2 复杂 3D 对象的 API#
其实复杂的 3D 对象本质上就是基本 3D 对象的组合体而已,因此它们在拥有基本 3D 对象的 API 的前提下,还有其他的方法。内置的两种复杂几何体都是继承于类 Geometry
,它们本身没有额外的方法,但是 Geometry
相比于基本 3D 对象,增加了一个 append
方法,可以将更多的基本 3D 对象添加到一个几何体实例中去。
二、通过动画类实现 3D 动画#
动画类指的就是上一章的 Animation
,通过它我们可以很方便地实现一些简单乃至复杂的动画。
有了 3D 对象的 API 之后,我们就可以通过动画类来实现各种各样~花里胡哨~的效果!Animation
的使用方法已在上一章讲述,这里不再赘述。
三、通过 after 方法实现简单动画#
除了可以使用 Animation
类之外,我们还可以使用 tkinter
模块中的功能来实现一些简单的功能。
tkinter
模块中的控件都有一个名为 after
的方法,可以让我们快速做出比较简单的动画,实际上,Animation
的底层也是调用的 after
,只不过进行了一些简单的封装。
下面是一个简单的示例(如果下面的动图很卡,说明你网页加载比较慢,并非 tkintertools
的问题,耐心等待就好):
是不是眼花缭乱了?
源代码
import math
import tkintertools as tkt
from tkintertools import tools_3d as t3d
root = tkt.Tk('3D Text', 1280, 720)
space = t3d.Space(root, 1280, 720, 0, 0, bg='black', keep=False)
r = 300
O = t3d.Point(space, [0, 0, 0], fill='white', size=3)
X = t3d.Line(space, [0, 0, 0], [1, 0, 0], fill='')
Y = t3d.Line(space, [0, 0, 0], [0, 1, 0], fill='')
Z = t3d.Line(space, [0, 0, 0], [0, 0, 1], fill='')
ring = {'x': [], 'y': [], 'z': []} # type: dict[str, list[t3d.Text3D]]
line = {'x': [], 'y': [], 'z': []} # type: dict[str, list[t3d.Text3D]]
for i in range(26):
t = chr(65+i)
φ = i/26 * math.tau
c1 = r * math.sin(φ)
c2 = r * math.cos(φ)
ring['x'].append(t3d.Text3D(space, [0, c1, c2], text=t, fill='#FF0000'))
ring['y'].append(t3d.Text3D(space, [c1, 0, c2], text=t, fill='#00FF00'))
ring['z'].append(t3d.Text3D(space, [c1, c2, 0], text=t, fill='#0000FF'))
for i in range(10):
t = str(i)
c = (i+1) * 600/11 - r
line['x'].append(t3d.Text3D(space, [c, 0, 0], text=t, fill='#00FFFF'))
line['y'].append(t3d.Text3D(space, [0, c, 0], text=t, fill='#FF00FF'))
line['z'].append(t3d.Text3D(space, [0, 0, c], text=t, fill='#FFFF00'))
def animation():
for obj3D in ring['x']:
obj3D.rotate(0.05, axis=X.coordinates)
for obj3D in ring['y']:
obj3D.rotate(0.05, axis=Y.coordinates)
for obj3D in ring['z']:
obj3D.rotate(0.05, axis=Z.coordinates)
for obj3D in line['x']:
obj3D.rotate(-0.05, axis=Y.coordinates)
for obj3D in line['y']:
obj3D.rotate(-0.05, axis=Z.coordinates)
for obj3D in line['z']:
obj3D.rotate(-0.05, axis=X.coordinates)
for obj3D in space.items_3d():
obj3D.rotate(0, -0.01, 0.01, center=O.center())
obj3D.update()
root.after(10, animation)
animation()
root.mainloop()
或许我们可以用它做一个 3D 的时钟?
源代码
import math
import time
import tkintertools as tkt
from tkintertools import tools_3d as t3d
root = tkt.Tk('3D Clock', 1280, 720)
space = t3d.Space(root, 1280, 720, 0, 0, bg='black', keep=False)
O = t3d.Point(space, [0, 0, 0], fill='white', size=3)
X = t3d.Line(space, [0, 0, 0], [1, 0, 0], fill='')
Y = t3d.Line(space, [0, 0, 0], [0, 1, 0], fill='')
Z = t3d.Line(space, [0, 0, 0], [0, 0, 1], fill='')
h = t3d.Line(space, [0, 0, 0], [0, 0, 100], width=6, fill='#FF0000')
m = t3d.Line(space, [0, 0, 0], [0, 0, 150], width=4, fill='#00FF00')
s = t3d.Line(space, [0, 0, 0], [0, 0, 250], width=2, fill='#0000FF')
fill = []
fill += tkt.color(['#FF0000', '#00FF00'], seqlength=20)
fill += tkt.color(['#00FF00', '#0000FF'], seqlength=20)
fill += tkt.color(['#0000FF', '#FF0000'], seqlength=20)
r = 300
for i, c in enumerate(fill):
t = f'{(i // 5):02d}' if i % 5 == 0 else '●'
if t == '00':
t = 12
size = 30 if i % 5 == 0 else 16
φ = (15 - i) / 60 * math.tau
x = r * math.cos(φ)
y = r * math.sin(φ)
t3d.Text3D(space, [0, x, y], text=t, fill=c, font=(tkt.FONT, -size))
time_ = list(map(int, time.strftime('%H %M %S', time.localtime()).split()))
h_ = time_[0] % 12
m_ = time_[1]
s_ = time_[2]
h.rotate((60 * ((60 * h_) + m_) + s_) * -
math.tau / 60 / 60 / 12, axis=X.coordinates)
m.rotate(((60 * m_) + s_) * -math.tau / 60 / 60, axis=X.coordinates)
s.rotate(s_ * -math.tau / 60, axis=X.coordinates)
def animation(speed: int, t: int = 60*((60*h_) + m_) + s_) -> None:
"""动画"""
s.rotate(-math.tau / 60, axis=X.coordinates)
space.itemconfigure(s.item, fill=fill[(t % 60)])
s.update()
m.rotate(-math.tau / 60 / 60, axis=X.coordinates)
space.itemconfigure(m.item, fill=fill[((t // 60) % 60)])
m.update()
h.rotate(-math.tau / 60 / 60 / 12, axis=X.coordinates)
space.itemconfigure(h.item, fill=fill[((t // 60 // 12) % 60)])
h.update()
space.after(1000 // speed, animation, speed, t + 1)
def start() -> None:
"""校正时间"""
while not math.isclose(time.time(), int(time.time())):
continue
animation(1) # 参数为时间流逝速度
start()
root.mainloop()
欢迎在评论区留下你的想法
下面没有更多评论啦😆