backend/gsap API
包 Luna-Flow/geometry3d/backend/gsap 把前端的 DrawList 画成 SVG 多边形,并用 GSAP 时间轴驱动动画。三角形按由远到近排序(画家算法),写入 <svg> 元素中可复用的 <polygon> 节点。GsapPlayer 封装了一个 GSAP 时间轴,它以当前时间回调 MoonBit。该包只支持 js 目标,使用 moonbit-community/rabbita/dom,并要求 GSAP 3 位于 globalThis.gsap。
import {
"Luna-Flow/geometry3d/frontend",
"Luna-Flow/geometry3d/backend/gsap",
"moonbit-community/rabbita/dom",
}
supported_targets = "js"
GSAP 设计解释了排序方式及其局限;GSAP 教程搭建了一个播放器页面。
配置
GsapSvgColor
GsapSvgColor 是 RGB 颜色,各通道取值于 。
pub struct GsapSvgColor {
red : Int
green : Int
blue : Int
}
GsapSvgColor::rgb
GsapSvgColor::rgb(r, g, b) 构造一个颜色,并把每个通道截断到 。
pub fn GsapSvgColor::rgb(Int, Int, Int) -> Self
GsapSvgRenderConfig
GsapSvgRenderConfig 包含 SVG 尺寸、背景色、前景色和明暗级数。
pub struct GsapSvgRenderConfig {
width : Int
height : Int
background_color : GsapSvgColor
foreground_color : GsapSvgColor
shade_levels : Int
}
这些字段在包外是只读的。每次渲染调用都会规范化配置:非正的尺寸变为 640 × 480,颜色通道被截断,shade_levels 被截断到 。
GsapSvgRenderConfig::default, GsapSvgRenderConfig::sized
GsapSvgRenderConfig::default() 为 640 × 480,GsapSvgRenderConfig::sized(w, h) 使用给定尺寸(非正值变为 640 或 480)。两者都使用背景色 rgb(7, 12, 22)、前景色 rgb(112, 226, 255) 和 256 个明暗级别。
pub fn GsapSvgRenderConfig::default() -> Self
pub fn GsapSvgRenderConfig::sized(Int, Int) -> Self
test "svg config" {
let config = @gsap.GsapSvgRenderConfig::sized(-1, 360)
debug_inspect((config.width, config.height), content="(640, 360)")
let fg = config.foreground_color
debug_inspect([fg.red, fg.green, fg.blue], content="[112, 226, 255]")
}
渲染
render_draw_list
render_draw_list(svg, list, config) 更新 <svg> 元素,使其显示一个绘制列表。
pub fn render_draw_list(@dom.Element, @frontend.DrawList, GsapSvgRenderConfig) -> Unit
它按三个顶点的平均深度对三角形排序,最远的在前,深度相同时保持列表顺序。它设置元素的 width、height、viewBox(0 0 W H)、role="img" 和 data-geometry3d-backend="gsap-svg" 属性。它确保该元素的直接子节点中有背景 <rect data-gsap-svg-background> 和分组 <g data-gsap-svg-triangles>(第一次时会替换所有子节点)。然后把分组增减到每个三角形对应一个 <polygon>,并写入每个多边形的 points 和 fill。填充色是前景色乘以量化后的亮度 ,其中 ,与 Canvas 后端相同。多边形在多次调用之间复用,因此重复渲染不会重新创建 DOM 节点。
render_scene
render_scene(svg, scene, view, config) 依次运行 @frontend.build_draw_list 和 render_draw_list。
pub fn render_scene(@dom.Element, @frontend.Scene, @frontend.RenderView, GsapSvgRenderConfig) -> Unit
fn draw_on(svg : @dom.Element, angle : Double) -> Unit {
let view = @frontend.RenderView::perspective(
@view.Camera3::default(4.5),
@view.PerspectiveProjection::new(@view.Viewport::new(640, 480), 400.0),
)
let scene = @frontend.Scene::single(
@core.cube_mesh(1.0),
@core.Transform3::rotation(0.4, angle, 0.0),
@frontend.Light::default(),
)
@gsap.render_scene(svg, scene, view, @gsap.GsapSvgRenderConfig::sized(640, 480))
}
播放
GsapTimeline
GsapTimeline 是指向 JavaScript GSAP 时间轴对象的不透明句柄。
type GsapTimeline
GsapPlayer
GsapPlayer 是一个默认暂停、时长固定的 GSAP 时间轴,它把当前时间报告给 MoonBit 回调。
pub struct GsapPlayer {
timeline : GsapTimeline
duration_seconds : Double
}
GsapPlayer::new
GsapPlayer::new(duration, on_frame, repeat?) 创建一个暂停的时间轴,它以线性缓动把时钟从 补间到 duration 秒,并在每次更新时调用 on_frame(t)。它还会立即调用一次 on_frame(0.0)。
pub fn GsapPlayer::new(Double, (Double) -> Unit, repeat? : Int) -> Self
非正的时长变为 1 秒。repeat 是 GSAP 的重复次数:-1(默认)表示无限重复,0 表示只播放一次。如果 globalThis.gsap.timeline 不可用,它会抛出 JavaScript 错误而中止。
GsapPlayer::play, GsapPlayer::pause, GsapPlayer::reverse, GsapPlayer::restart, GsapPlayer::kill
这些方法转发到时间轴的 play()、pause()、reverse()、restart() 和 kill()。
pub fn GsapPlayer::play(Self) -> Unit
pub fn GsapPlayer::pause(Self) -> Unit
pub fn GsapPlayer::reverse(Self) -> Unit
pub fn GsapPlayer::restart(Self) -> Unit
pub fn GsapPlayer::kill(Self) -> Unit
调用 kill 之后不得再使用该播放器。
GsapPlayer::seek, GsapPlayer::time
player.seek(t) 把播放头移到 t 秒,并截断到 ;player.time() 返回播放头的局部时间。
pub fn GsapPlayer::seek(Self, Double) -> Unit
pub fn GsapPlayer::time(Self) -> Double
GsapPlayer::progress, GsapPlayer::set_progress
player.progress() 以单次迭代的比例返回播放头位置;player.set_progress(p) 设置该位置,并把 p 截断到 。
pub fn GsapPlayer::progress(Self) -> Double
pub fn GsapPlayer::set_progress(Self, Double) -> Unit
GsapPlayer::time_scale, GsapPlayer::set_time_scale
player.time_scale() 返回播放速度因子;player.set_time_scale(k) 设置该因子,非正的 k 会被替换为 1.0。
pub fn GsapPlayer::time_scale(Self) -> Double
pub fn GsapPlayer::set_time_scale(Self, Double) -> Unit
要倒放,请使用 reverse,而不是负的速度因子。
GsapPlayer::set_repeat
player.set_repeat(n) 设置重复次数,小于 -1 的值会被提升为 -1(无限重复)。
pub fn GsapPlayer::set_repeat(Self, Int) -> Unit
GsapPlayer::is_paused
player.is_paused() 返回时间轴是否处于暂停状态。
pub fn GsapPlayer::is_paused(Self) -> Bool
fn start_player(svg : @dom.Element) -> @gsap.GsapPlayer {
let player = @gsap.GsapPlayer::new(8.0, fn(t) { draw_on(svg, t * 0.8) }, repeat=-1)
player.set_time_scale(0.5)
player.play()
player
}