{"id":27931374,"url":"https://github.com/bradysimon/iced_anim","last_synced_at":"2025-05-07T03:15:10.475Z","repository":{"id":249082283,"uuid":"818802733","full_name":"bradysimon/iced_anim","owner":"bradysimon","description":"A crate to simplify animating Iced widgets","archived":false,"fork":false,"pushed_at":"2025-05-04T19:22:46.000Z","size":264,"stargazers_count":18,"open_issues_count":0,"forks_count":5,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-07T03:15:03.949Z","etag":null,"topics":["animation","iced","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bradysimon.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-06-22T23:07:58.000Z","updated_at":"2025-03-27T10:25:55.000Z","dependencies_parsed_at":"2024-12-26T23:18:59.804Z","dependency_job_id":"3a749e43-27db-48ae-bd7c-9e9621f66013","html_url":"https://github.com/bradysimon/iced_anim","commit_stats":{"total_commits":98,"total_committers":2,"mean_commits":49.0,"dds":"0.020408163265306145","last_synced_commit":"40d93b363a5a544b8fc32097e95d1eef906c21b8"},"previous_names":["brady-simon/iced_anim","bradysimon/iced_anim"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bradysimon%2Ficed_anim","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bradysimon%2Ficed_anim/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bradysimon%2Ficed_anim/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bradysimon%2Ficed_anim/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bradysimon","download_url":"https://codeload.github.com/bradysimon/iced_anim/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252804224,"owners_count":21806773,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["animation","iced","rust"],"created_at":"2025-05-07T03:15:09.826Z","updated_at":"2025-05-07T03:15:10.455Z","avatar_url":"https://github.com/bradysimon.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Iced Animations\n\nThis package is designed to make it easy to animate between values using \n[the Iced framework](https://github.com/iced-rs/iced).\n\n## Overview\n\nAnimations are powered by the `Animate` trait. You can pick between spring or\ntransition animations. Controlled animations using the `Animation` widget have\na few parts:\n\n- Storing an `Animated\u003cT\u003e` value in your app state\n- Including an update message, e.g. `Message::UpdateSize(iced_anim::Event\u003cT\u003e)`\n- Piping animation events to your animated value\n- Wrapping the UI you want to animate in an `Animation` widget\n\nLet's follow an example where you have a `size` in your app state and want to\nanimate changes to it:\n\n```rust\nuse iced_anim::{Animated, Event};\n\n#[derive(Default)]\nstruct State {\n    // Your animated value, wrapped in an `Animated` type.\n    size: Animated\u003cf32\u003e,\n}\n\n#[derive(Clone)]\nenum Message {\n    // A message that lets you (and animation widgets) update the size.\n    UpdateSize(Event\u003cf32\u003e),\n}\n\nimpl State {\n    fn update(\u0026mut self, message: Message) {\n        match message {\n            // Let your animated value process the event\n            Message::UpdateSize(event) =\u003e self.size.update(event),\n        }\n    }\n}\n```\n\nNext, let's add a default impl using an ease transition that sets the initial\nsize at 50px:\n\n```rust\nuse iced_anim::transition::Easing;\n\nimpl Default for State {\n    fn default() -\u003e Self {\n        Self {\n            size: Animated::transition(50.0, Easing::EASE),\n        }\n    }\n}\n```\n\nOr, if you're feeling spicy, you can use a spring animation:\n\n```rust\nuse iced_anim::spring::Motion;\n\nimpl Default for State {\n    fn default() -\u003e Self {\n        Self {\n            size: Animated::spring(50.0, Motion::BOUNCY),\n        }\n    }\n}\n```\n\nNext, you need to use your animated size in a view. You can use the `Animation`\nwidget to make your UI animate when the `size` changes. You'll also want a way\nto change the size so you can see the animation, so let's add some buttons too.\n\n```rust\nuse iced::{\n    widget::{button, column, container, row, text},\n    Color, Element, Length,\n};\nuse iced_anim::{animation::animation, transition::Easing};\n\nimpl State {\n    pub fn view(\u0026self) -\u003e Element\u003cMessage\u003e {\n        let buttons = row![\n            button(text(\"-50\")).on_press(Message::UpdateSize(Event::Target(self.size.target() - 50.0))),\n            button(text(\"+50\")).on_press(Message::UpdateSize(Event::Target(self.size.target() + 50.0))),\n        ]\n        .spacing(8);\n\n        let animated_box = animation(\n            \u0026self.size,\n            container(text(*self.size.value() as isize))\n                .style(|_| container::background(Color::from_rgb(1.0, 0.0, 0.0)))\n                .center(*self.size.value()),\n        )\n        .on_update(Message::UpdateSize);\n\n        column![buttons, animated_box]\n            .spacing(8)\n            .padding(8)\n            .width(Length::Shrink)\n            .into()\n    }\n}\n```\n\nOr, if you don't want to type of `Event::Target`, you can replace it with \n`.into()` since the `From` impl for `Event` is the `Event::Target` case.\n\n```rust\nlet buttons = row![\n    button(text(\"-50\")).on_press(Message::UpdateSize((self.size.target() - 50.0).into())),\n    button(text(\"+50\")).on_press(Message::UpdateSize((self.size.target() + 50.0).into())),\n]\n.spacing(8);\n```\n\nThe `Animation` widget works by watching your animated `size` and sending\nupdates through the message you pass to `.on_update` to trigger rebuilds.\nAnytime you set a new target value for your animation, the `Animation` widget\nwill pick up the change and emit messages to update the animated value.\n\nIn the above example, you can set a new target by emitting the message we\ncreated, e.g. `Message::UpdateSize(150.0.into())`. If you need to access the\ncurrent animated value or the target value (the value being animated towards),\nyou can use the `.value()` and `.target()` functions respectively.\n\nIf you have a very simple value you'd like to animate but don't want to store\nin your app state, then consider the `AnimationBuilder` widget.\n\n### `AnimationBuilder` widget\n\nThe `AnimationBuilder` widget lets you have uncontrolled animations that takes\nsome sort of value that implements `Animate` and a closure to build out a UI\nbased on the current interpolated value. The benefit is that you don't have to\nemit messages for very simple animations but has a couple limitations mentioned\nbelow. Typical usage might look something like this, where `self.size` is an \n`f32` in your app state:\n\n```rust\nAnimationBuilder::new(self.size, |size| {\n    container(text(size as isize))\n        .center(size)\n        .into()\n})\n.animates_layout(true)\n.animation(Easing::LINEAR.with_duration(Duration::from_millis(300)))\n```\n\n\u003e NOTE: `.animates_layout(true)` is what allows re-rendering certain properties\nlike the app layout or text. This is off by default to avoid unnecessarily\ninvalidating the app layout, but will be necessary in certain situations.\n\nThe UI will automatically re-render when `self.size` is changed. The closure\nprovides the current animated value and will be called to generate the next\nview as appropriate. This means you don't need to store an `Animated` value in\nyour app state or have a message dedicated to updating it. To see more for this\nparticular example, refer to the `animated_size` example.\n\nYou can customize the animation curve and duration with the `.animation` method\nand pass in either a `Spring` or an `Easing` instance.\n\n#### Limitations\n\nIt might not be easy/possible to pass in non-clonable content like custom\nelements to `AnimationBuilder`'s closure to due the closure being having to be\ninvoked multiple times to animate between values. Making reusable functions\nthat use this widget and also take a generic element might be difficult.\n\nNested animations also don't work if both properties are actively being \nanimated. One animation at a time will function correctly, but trying to adjust\nboth at the same time leads to the inner property skipping to the final value.\nUse the `Animation` widget if you need any of these properties.\n\n### Should I use `Animation` or `AnimationBuilder`?\n\nGenerally, if you're animating a tiny value that might not be directly within\nyour state and the element won't contain any nested animated values, then use\n`AnimationBuilder`. Otherwise, use the state-driven `Animation` to avoid the\nlimitations of widget-driven animations. Also, use `Animation` anytime you want\nyour state to contain the animated value.\n\n### Should I use a `Spring` or a `Transition`?\n\nIt's largely personal preference, but springs are typically better at \ninteractive animations because they're momentum-based and keep their existing\nspeed even when the target changes. This makes animations whose target value is\nalways changing to appear much more fluid. See the `animated_bubble` example\nfor a constantly-changing animation target.\n\nIf your animation isn't going to be constantly changing or it's something minor\nlike a color, then you can use a transition with an easing curve.\n\n## Animated widgets\n\nA subset of the standard `iced` widgets are exported under a `widgets` feature\nflag. You can use these as drop-in replacements for the existing widgets and\ncontrol the animation mode via the `.animation()` builder function on them:\n\n```rust\nuse iced::widget::text;\nuse iced_anim::widget::button;\n\nlet my_button = button(text(\"Animated button\"))\n    .on_press(Message::DoSomething);\n```\n\n## Types that implement `Animate`\n\nSeveral types implement `Animate` by default, such as `f32`, `iced::Color`,\nand `iced::Theme`, with support for others being added in the future. You can\nalso derive `Animate` on your own structs if you enable the `derive` feature\nand all inner properties already implement `Animate`:\n\n```rust\nuse iced_anim::Animate;\n\n#[derive(Animate)]\nstruct CustomRectangle {\n    width: f32,\n    height: f32,\n    color: iced::Color,\n}\n```\n\nYou can also animate multiple values at once by providing a tuple up to a\nlength of four:\n\n```rust\nAnimationBuilder::new((self.size, self.color), |(size, color)| {\n    container(text(size as isize).color(color))\n        .center(size)\n        .into()\n})\n.animates_layout(true)\n```\n\n## Controlling the animation\n\nYou can customize the animation by passing in different values to the \n`.animation` function. You can pass anything that can be converted to an\n`animated::Mode`, namely a `spring::Motion` or `transition::Easing`. There are\ndefaults like `Easing::EASE_OUT` and `Motion::BOUNCY`, but you can create your\nown as well.\n\n```rust\nuse std::time::Duration;\nuse iced::widget::{container, text};\nuse iced_anim::{AnimationBuilder, spring::Motion};\n\nAnimationBuilder::new(self.size, |size| {\n    container(text(size as isize))\n        .center(size)\n        .into()\n})\n.animates_layout(true)\n.animation(Motion { \n    damping: 0.5,\n    response: Duration::from_millis(500),\n})\n```\n\n## Examples\n\nRefer to the `examples` directory for a variety of ways to use this crate.\nYou can also run these examples locally with `cargo run --example \u003cpackage\u003e`,\ne.g. `cargo run --example animated_color`.\n\n## Breaking Changes\n\n### 0.1 -\u003e 0.2\n\n- Introduced the `Animated` API, which encompasses both springs and transitions\n- `Animation` widget now takes an `Animated` value instead of a `Spring`\n- Renamed `SpringMotion` to `Motion` under the `iced_anim::spring` module\n- Renamed `SpringEvent` to `Event` and added a new `SettleAt(T)` case\n- Moved `AnimatedState` to the top-level module instead of the `widget` module\n- Replaced `.motion()` functions with `.animation()` functions that take a new \n  `iced_anim::animated::Mode` that allows configuring springs or transitions\n- Add `lerp` function to `Animate` trait\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbradysimon%2Ficed_anim","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbradysimon%2Ficed_anim","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbradysimon%2Ficed_anim/lists"}