bevy_tween 0.12.0

Flexible tweening plugin library for Bevy
Documentation
use std::f32::consts::TAU;

use bevy::prelude::*;
use bevy_tween::{
    combinator::{AnimationCommands, parallel, tween_exact},
    interpolate::angle_z,
    prelude::*,
    tween::TargetComponent,
};

fn main() {
    App::new()
        .add_plugins((DefaultPlugins, DefaultTweenPlugins::default()))
        .add_systems(Startup, setup)
        .run();
}

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands.spawn(Camera2d);

    let bevy_text = asset_server.load("bevy.png");
    let tween_text = asset_server.load("tween.png");
    let triangle_image = asset_server.load("big_triangle.png");
    let ease = EaseKind::ExponentialInOut;

    commands.spawn((
        Sprite {
            image: bevy_text,
            ..default()
        },
        Transform::from_xyz(-300., 0., 0.),
    ));

    commands.spawn((
        Sprite {
            image: tween_text,
            ..default()
        },
        Transform::from_xyz(340., 10., 0.),
    ));

    // colors by https://color-hex.org/color-palettes/189
    let colors = [
        Color::srgb_u8(0, 128, 191),
        Color::srgb_u8(0, 172, 223),
        Color::srgb_u8(85, 208, 255),
        Color::srgb_u8(124, 232, 255),
        Color::srgb_u8(204, 249, 255),
    ];
    let mut spawn_triangle = |color, z| {
        commands
            .spawn((
                Sprite {
                    image: triangle_image.clone(),
                    color,
                    ..default()
                },
                Transform::from_xyz(0., 0., z),
            ))
            .id()
    };
    let triangles = colors
        .iter()
        .enumerate()
        .map(|(i, color)| spawn_triangle(*color, (i + 2) as f32))
        .map(|t| t.into_target())
        .collect::<Vec<_>>();

    let secs = 12.;

    commands
        .animation()
        .repeat(Repeat::Infinitely)
        .insert(parallel((
            snap_rotate(triangles[4].clone(), secs, 7, 4., ease),
            snap_rotate(triangles[3].clone(), secs, 7, 6., ease),
            snap_rotate(triangles[2].clone(), secs, 7, 8., ease),
            snap_rotate(triangles[1].clone(), secs, 7, 10., ease),
            snap_rotate(triangles[0].clone(), secs, 7, 12., ease),
        )));
}

fn secs(secs: f32) -> Duration {
    Duration::from_secs_f32(secs)
}

fn snap_rotate(
    target: TargetComponent,
    dur: f32,
    max: usize,
    rev: f32,
    ease: EaseKind,
) -> impl FnOnce(&mut AnimationCommands, &mut Duration) {
    move |a, pos| {
        for i in 0..max {
            let max = max as f32;
            let i = i as f32;
            tween_exact(
                secs(i / max * dur)..secs((i + 1.) / max * dur),
                ease,
                target.with(angle_z(
                    rev * TAU * (max - i) / max,
                    rev * TAU * (max - i - 1.) / max,
                )),
            )(a, pos);
        }
        *pos += secs(dur)
    }
}