Tween¶
animate any property over time with easing.
▶ Run in browserTags: 2d animation tween
tween(obj, prop, target, duration, easing=...) drives an attribute from its
current value to target over duration seconds. It works on any attribute
that interpolates: a float, a Vec2, a Vec3 or a Quat. A colour is a plain
tuple and holds until the end, so fade one with
Tween.colour(node, target, duration). Here each row owns a progress Property
that runs from 0 to 1 and back, and the row draws its marker that far along its
track, so the layout follows the window while the tween stays in plain numbers.
Every marker covers the same distance in the same two seconds; only the easing
curve differs.
The
easingmodule shapes the motion:easing.ease_linearhas none,ease_in_*starts slow,ease_out_*ends slow andease_in_out_*does both. Any function from 0..1 to 0..1 works as an easing.repeat=2, repeat_mode="yoyo"plays out and then back, mirroring the curve on the return leg."restart"snaps back instead and"incremental"keeps going.A tween is a coroutine: hand it to
self.start_coroutine()and the node runs it every frame until it finishes or the node leaves the tree. Inside your own coroutine,yield from tween(...)waits for it, so tweens sequence and loop with ordinary Python.
Source¶
1# /// simvx
2# tags = ["2d", "animation", "tween"]
3# web = { width = 800, height = 600, responsive = true }
4# ///
5"""Tween: animate any property over time with easing.
6
7`tween(obj, prop, target, duration, easing=...)` drives an attribute from its
8current value to `target` over `duration` seconds. It works on any attribute
9that interpolates: a float, a `Vec2`, a `Vec3` or a `Quat`. A colour is a plain
10tuple and holds until the end, so fade one with
11`Tween.colour(node, target, duration)`. Here each row owns a `progress` Property
12that runs from 0 to 1 and back, and the row draws its marker that far along its
13track, so the layout follows the window while the tween stays in plain numbers.
14Every marker covers the same distance in the same two seconds; only the easing
15curve differs.
16
17- The `easing` module shapes the motion: `easing.ease_linear` has none,
18 `ease_in_*` starts slow, `ease_out_*` ends slow and `ease_in_out_*` does both.
19 Any function from 0..1 to 0..1 works as an easing.
20- `repeat=2, repeat_mode="yoyo"` plays out and then back, mirroring the curve on
21 the return leg. `"restart"` snaps back instead and `"incremental"` keeps going.
22- A tween is a coroutine: hand it to `self.start_coroutine()` and the node runs
23 it every frame until it finishes or the node leaves the tree. Inside your own
24 coroutine, `yield from tween(...)` waits for it, so tweens sequence and loop
25 with ordinary Python.
26"""
27
28from simvx.core import Node2D, Property, easing, tween
29from simvx.graphics import App
30
31CURVES = [
32 easing.ease_linear,
33 easing.ease_in_quad,
34 easing.ease_out_quad,
35 easing.ease_in_cubic,
36 easing.ease_out_cubic,
37 easing.ease_in_out_cubic,
38]
39LABEL_WIDTH = 200
40
41
42def ping_pong(lane, curve):
43 """Out and back over two seconds each way, forever."""
44 while True:
45 yield from tween(lane, "progress", 1.0, duration=2.0, easing=curve, repeat=2, repeat_mode="yoyo")
46
47
48class Lane(Node2D):
49 """One row: the easing's name, a track, and a marker `progress` of the way along it."""
50
51 progress = Property(0.0)
52
53 def on_draw(self, renderer):
54 end = self.viewport_size.x - 40
55 x = LABEL_WIDTH + (end - LABEL_WIDTH) * self.progress
56 renderer.text(self.name, (20, -8), fill=(0.7, 0.7, 0.7))
57 renderer.line((LABEL_WIDTH, 0), (end, 0), stroke=(0.3, 0.3, 0.35))
58 renderer.circle((x, 0), 14, fill=(0.4, 0.8, 1.0, 1.0))
59
60
61class TweenDemo(Node2D):
62 def on_ready(self):
63 for curve in CURVES:
64 lane = self.add_child(Lane(name=curve.__name__))
65 self.start_coroutine(ping_pong(lane, curve))
66
67 def on_update(self, dt):
68 height = self.viewport_size.y
69 spacing = (height - 60) / len(CURVES)
70 for i, lane in enumerate(self.children):
71 lane.position = (0, 60 + spacing * (i + 0.5))
72
73 def on_draw(self, renderer):
74 renderer.text("Tween: easing curves", (20, 20), fill=(1, 1, 1), scale=2)
75
76
77if __name__ == "__main__":
78 App(title="Tween", width=800, height=600).run(TweenDemo())