Ranim logo

Ranim 是一个使用 Rust 编写的程序化动画引擎,受 manim 和 JAnim 启发。

  • 矢量图形基于二阶贝塞尔曲线表示,使用 SDF 渲染

  • 使用 wgpu,兼容多种后端图形 API

  • 可编译到 WASM,支持 Web 平台

Preview App hello_ranim 示例输出

scenes.rs(共享场景与项目装配)

use std::f64::consts::PI;

use ranim::{
    anims::{creation::WritingAnim, fading::FadingAnim, morph::MorphAnim},
    color::palettes::manim,
    items::vitem::{
        VItem,
        geometry::{Circle, Square},
    },
    prelude::*,
    utils::rate_functions::smooth,
};

#[scene]
#[wasm_demo_doc]
#[output(dir = "./output/hello_ranim")]
fn hello_ranim(r: &mut RanimScene) {
    let mut square = Square::new(2.0);
    square.set_color(manim::BLUE_C);

    let circle: VItem = VItem::from(Circle::new(2.0)).with(|circle| {
        circle.set_color(manim::RED_C).rotate_on_z(PI / 4.0 - PI);
    });

    let mut vitem = VItem::from(square.clone());

    let seq = seq![
        square.fade_in().with_rate_func(smooth),
        vitem.morph_to(circle).with_rate_func(smooth),
        vitem.show(),
        vitem.clone().unwrite().with_rate_func(smooth),
        vitem.write().with_rate_func(smooth),
        vitem.fade_out().with_rate_func(smooth),
    ];
    r.play(
        CameraFrame::default()
            .show()
            .with_duration(seq.cursor_sec()),
    );
    r.play(seq);

    r.insert_time_mark(3.7, TimeMark::Capture("preview.png".to_string()));
}

#[allow(unused)]
fn hello_ranim_chained(r: &mut RanimScene) {
    let square = Square::new(2.0).with(|square| {
        square.set_color(manim::BLUE_C);
    });

    let mut content = seq![square.clone().fade_in().with_rate_func(smooth)];

    let circle: VItem = Circle::new(2.0)
        .with(|circle| {
            circle.set_color(manim::RED_C);
        })
        .into();

    let mut vitem = VItem::from(square);
    content
        .push(vitem.morph_to(circle).with_rate_func(smooth))
        .hold(1.0)
        .extend(seq![
            vitem.clone().unwrite().with_rate_func(smooth),
            vitem.write().with_rate_func(smooth),
            vitem.fade_out().with_rate_func(smooth),
        ]);

    r.play(
        CameraFrame::default()
            .show()
            .with_duration(content.cursor_sec()),
    );
    r.play(content);

    r.insert_time_mark(3.7, TimeMark::Capture("preview.png".to_string()));
}

/// Register the scenes provided by this example.
pub struct ScenesPlugin;

impl ranim::Plugin for ScenesPlugin {
    fn build(&self, app: &mut bevy_app::App) {
        app.add_scenes([hello_ranim::scene()]);
    }
}

main.rs(平台入口)

mod scenes;

#[cfg(not(target_arch = "wasm32"))]
fn main() {
    #[cfg(feature = "cmd-render")]
    {
        use ranim::prelude::*;
        let mut app = App::new();
        app.add_plugins(scenes::ScenesPlugin);
        run_cli(app.build());
    }
    #[cfg(not(feature = "cmd-render"))]
    compile_error!("native examples require the `cmd-render` feature (enabled by default)");
}

// The WASM module is driven by JavaScript through app().
#[cfg(target_arch = "wasm32")]
fn main() {}

#[cfg(target_arch = "wasm32")]
#[wasm_bindgen::prelude::wasm_bindgen]
pub fn app() -> ranim::wasm::WasmApp {
    use ranim::prelude::*;

    let mut app = App::new();
    app.add_plugins(scenes::ScenesPlugin);
    ranim::wasm::WasmApp::new(app.build())
}