mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Finish up Animator docs first pass
This commit is contained in:
29
docs/examples/guide/animator/animation04.py
Normal file
29
docs/examples/guide/animator/animation04.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from rich.console import RenderableType
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.reactive import reactive
|
||||
from textual.widget import Widget
|
||||
|
||||
|
||||
class ValueBox(Widget):
|
||||
value = reactive(0.0)
|
||||
|
||||
def render(self) -> RenderableType:
|
||||
return str(self.value)
|
||||
|
||||
|
||||
class AnimationApp(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
self.box = ValueBox()
|
||||
self.box.styles.background = "red"
|
||||
self.box.styles.color = "black"
|
||||
self.box.styles.padding = (1, 2)
|
||||
yield self.box
|
||||
|
||||
async def on_mount(self):
|
||||
self.box.animate("value", value=100.0, duration=100.0, easing="linear")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = AnimationApp()
|
||||
app.run()
|
||||
@@ -1,11 +1,11 @@
|
||||
# Animator
|
||||
|
||||
Textual ships with an easy-to-use system which lets you add animation to your application.
|
||||
To get a feel for what animation looks like in Textual and try out different easing functions, run `textual easing` in your terminal.
|
||||
To get a feel for what animation looks like in Textual, run `textual easing` from the command line.
|
||||
|
||||
!!! note
|
||||
|
||||
The easing preview requires the `dev` extras (using `pip install textual[dev]`).
|
||||
The `textual easing` preview requires the `dev` extras to be installed (using `pip install textual[dev]`).
|
||||
|
||||
## Animating styles
|
||||
|
||||
@@ -23,9 +23,8 @@ The app below contains a single `Static` widget which is immediately animated to
|
||||
--8<-- "docs/examples/guide/animator/animation01.py"
|
||||
```
|
||||
|
||||
Internally, the animator deals with updating the value of the `opacity` attribute on the `styles` object.
|
||||
In a single line, we've achieved a fading animation:
|
||||
|
||||
Internally, the animator repeatedly updates the value of the `opacity` attribute on the `styles` object.
|
||||
With a single line of code, we've achieved a fading animation:
|
||||
|
||||
=== "After 0s"
|
||||
|
||||
@@ -43,28 +42,56 @@ In a single line, we've achieved a fading animation:
|
||||
```
|
||||
|
||||
Remember, when the value of a property on the `styles` object gets updated, Textual automatically updates the display.
|
||||
This means there's no additional code required to trigger a display update.
|
||||
This means there's no additional code required to trigger a display update - the animation just works.
|
||||
|
||||
In the example above we specified a `duration` of two seconds, but you can alternatively pass in a `speed` value.
|
||||
|
||||
## Animating other attributes
|
||||
## The `Animatable` protocol
|
||||
|
||||
You can animate non-style attributes on widgets too.
|
||||
This could be used to drive more complex animations involving styles, or to keep animations in sync with each other.
|
||||
You can animate `float` values and any type which implements the `Animatable` protocol.
|
||||
|
||||
To implement the `Animatable` protocol, add a `def blend(self: T, destination: T, factor: float) -> T` method to the class.
|
||||
The `blend` method should return a new object which represents `self` blended with `destination` by a factor of `factor`.
|
||||
The animator will repeatedly call this method to retrieve the current value to display for the current.
|
||||
|
||||
An example of an object which implements this protocol is [Color][textual.color.Color].
|
||||
It follows that you can use `animate` to animate from one `Color` to another.
|
||||
|
||||
## Animating widget attributes
|
||||
|
||||
You can animate non-`style` attributes on widgets too, assuming they implement `Animatable`.
|
||||
Again, the animation system will take care of updating the attribute on the widget as time progresses.
|
||||
|
||||
If the attribute being animated is [reactive](./reactivity.md), Textual can handle the refreshing of the display each time the animator updates the value.
|
||||
If the attribute being animated is [reactive](./reactivity.md), Textual can refresh the display each time the animator updates the value.
|
||||
|
||||
## Animating arbitrary values
|
||||
The example below shows a simple incrementing timer that counts from 0 to 100 over 100 seconds.
|
||||
|
||||
Sometimes, you'll want to animate a value that isn't directly accessible as an attribute on a widget.
|
||||
For example, perhaps the value to be animated is nested inside some object structure, and you don't want to restructure your code to make it a top-level attribute.
|
||||
=== "animation04.py"
|
||||
|
||||
In these cases, you can make use of an "unbound" animator.
|
||||
These are animators which aren't pre-emptively associated with an object.
|
||||
They let you pass in an object, _and_ the name of the attribute you wish to animate on it.
|
||||
```python
|
||||
--8<-- "docs/examples/guide/animator/animation04.py"
|
||||
```
|
||||
|
||||
=== "Output"
|
||||
|
||||
```{.textual path="docs/examples/guide/animator/animation04.py"}
|
||||
```
|
||||
|
||||
Since `value` is reactive, the display is automatically updated each time the animator modifies it.
|
||||
|
||||
## Animating Python object attributes
|
||||
|
||||
Sometimes you'll want to animate a value that exists inside a plain old Python object.
|
||||
|
||||
In these cases, you can make use of the "unbound" animator.
|
||||
An unbound animator is an animator which isn't pre-emptively associated with (bound to) an object.
|
||||
|
||||
Unbound animators let you pass the name of the attribute you wish to animate, _and_ the object that attribute exists on.
|
||||
This is unlike the animators discussed above, which are already _bound_ to the object they were retrieved from.
|
||||
|
||||
You can retrieve the unbound animator from the `App` instance via `App.animator`, and call the `animate` method on it.
|
||||
This method is the same as the one described earlier, except the first argument is the object containing the attribute.
|
||||
|
||||
## Easing functions
|
||||
|
||||
Easing functions control the "look and feel" of an animation.
|
||||
@@ -72,6 +99,15 @@ The easing function determines the journey a value takes on its way to the targe
|
||||
Perhaps the value will be transformed linearly, moving towards the target at a constant rate.
|
||||
Or maybe it'll start off slow, then accelerate towards the final value as the animation progresses.
|
||||
|
||||
Easing functions take a single input representing the time, and output a "factor".
|
||||
This factor is what gets passed to the `blend` method in the `Animatable` protocol.
|
||||
|
||||
!!! warning
|
||||
|
||||
The factor output by the easing function will usually remain between 0 and 1.
|
||||
However, some easing functions (such as `in_out_elastic`) will produce values slightly below 0 and slightly above 1.
|
||||
Because of this, any implementation of `blend` should support values outwith the range 0 to 1.
|
||||
|
||||
Textual supports the easing functions listed on this [very helpful page](https://easings.net/).
|
||||
In order to use them, you'll need to write them as `snake_case` and remove the `ease` at the start.
|
||||
To use `easeInOutSine`, for example, you'll write `in_out_sine`.
|
||||
|
||||
Reference in New Issue
Block a user