44
55import asyncio
66from time import time
7+ from tracemalloc import start
78from typing import Callable
89
910from dataclasses import dataclass
1011
1112from ._timer import Timer
1213from ._types import MessageTarget
13- from .message_pump import MessagePump
14-
15- EasingFunction = Callable [[float ], float ]
1614
1715
18- LinearEasing = lambda value : value
19-
16+ EasingFunction = Callable [[float ], float ]
2017
21- def InOutCubitEasing (x : float ) -> float :
22- return 4 * x * x * x if x < 0.5 else 1 - pow (- 2 * x + 2 , 3 ) / 2
18+ # https://easings.net/
19+ EASING = {
20+ "none" : lambda x : 1.0 ,
21+ "round" : lambda x : 0.0 if x < 0.5 else 1.0 ,
22+ "linear" : lambda x : x ,
23+ "in_cubic" : lambda x : x * x * x ,
24+ "in_out_cubic" : lambda x : 4 * x * x * x if x < 0.5 else 1 - pow (- 2 * x + 2 , 3 ) / 2 ,
25+ "out_cubic" : lambda x : 1 - pow (1 - x , 3 ),
26+ }
2327
2428
2529log = logging .getLogger ("rich" )
@@ -35,11 +39,26 @@ class Animation:
3539 end_value : float
3640 easing_function : EasingFunction
3741
38- def __call__ (self , obj : object , time : float ) -> bool :
39- progress = min (1.0 , (time - self .start_time ) / self .duration )
40- eased_progress = self .easing_function (progress )
41- value = self .start_value + (self .end_value - self .start_value ) * eased_progress
42- setattr (obj , self .attribute , value )
42+ def __call__ (self , time : float ) -> bool :
43+
44+ if self .duration == 0 :
45+ value = self .end_value
46+ else :
47+ progress = min (1.0 , (time - self .start_time ) / self .duration )
48+ if self .end_value > self .start_value :
49+ eased_progress = self .easing_function (progress )
50+ value = (
51+ self .start_value
52+ + (self .end_value - self .start_value ) * eased_progress
53+ )
54+ else :
55+ eased_progress = 1 - self .easing_function (progress )
56+ value = (
57+ self .end_value
58+ + (self .start_value - self .end_value ) * eased_progress
59+ )
60+
61+ setattr (self .obj , self .attribute , value )
4362 return value == self .end_value
4463
4564
@@ -53,22 +72,25 @@ def __call__(
5372 attribute : str ,
5473 value : float ,
5574 * ,
56- duration : float = 1 ,
57- easing : EasingFunction = InOutCubitEasing ,
75+ duration : float | None = None ,
76+ speed : float | None = None ,
77+ easing : EasingFunction | str = "in_out_cubic" ,
5878 ) -> None :
79+ easing_function = EASING [easing ] if isinstance (easing , str ) else easing
5980 self ._animator .animate (
6081 self ._obj ,
6182 attribute = attribute ,
6283 value = value ,
6384 duration = duration ,
64- easing = easing ,
85+ speed = speed ,
86+ easing = easing_function ,
6587 )
6688
6789
6890class Animator :
69- def __init__ (self , target : MessageTarget ) -> None :
91+ def __init__ (self , target : MessageTarget , frames_per_second : int = 30 ) -> None :
7092 self ._animations : dict [tuple [object , str ], Animation ] = {}
71- self ._timer : Timer = Timer (target , 1 / 30 , target , callback = self )
93+ self ._timer = Timer (target , 1 / frames_per_second , target , callback = self )
7294
7395 async def start (self ) -> None :
7496 asyncio .get_event_loop ().create_task (self ._timer .run ())
@@ -85,22 +107,33 @@ def animate(
85107 attribute : str ,
86108 value : float ,
87109 * ,
88- duration : float = 1 ,
89- easing : EasingFunction = InOutCubitEasing ,
110+ duration : float | None = None ,
111+ speed : float | None = None ,
112+ easing : EasingFunction = EASING ["in_out_cubic" ],
90113 ) -> None :
91- start_value = getattr ( obj , attribute )
114+
92115 start_time = time ()
93116
117+ animation_key = (obj , attribute )
118+ if animation_key in self ._animations :
119+ self ._animations [animation_key ](start_time )
120+
121+ start_value = getattr (obj , attribute )
122+ if duration is not None :
123+ animation_duration = duration
124+ else :
125+ animation_duration = abs (value - start_value ) / (speed or 50 )
126+
94127 animation = Animation (
95128 obj ,
96129 attribute = attribute ,
97130 start_time = start_time ,
98- duration = duration ,
131+ duration = animation_duration ,
99132 start_value = start_value ,
100133 end_value = value ,
101134 easing_function = easing ,
102135 )
103- self ._animations [( obj , attribute ) ] = animation
136+ self ._animations [animation_key ] = animation
104137 self ._timer .resume ()
105138
106139 async def __call__ (self ) -> None :
@@ -111,6 +144,5 @@ async def __call__(self) -> None:
111144 animation_keys = list (self ._animations .keys ())
112145 for animation_key in animation_keys :
113146 animation = self ._animations [animation_key ]
114- obj , _attribute = animation_key
115- if animation (obj , animation_time ):
147+ if animation (animation_time ):
116148 del self ._animations [animation_key ]
0 commit comments