Web 挂载、动态列表与横向滚动
自 0.1.0-rc.8 起可用。 本次新增功能对应 #334、#336 和 #337。实验性 /web 运行时可独立使用或与 Vue 集成,组件无需维护第二套动画引擎。
Vue 与 Nuxt:等待真实挂载
Vue 的 mounted 不保证元素已进入 document。Nuxt out-in 和 Suspense 可能先在分离的容器中挂载页面。createViewportEnterDirective() 观察实际连接,再创建视口入场动画;不使用固定延迟或逐帧轮询。每个应用或 SSR 请求创建一个指令,并将其用于普通 HTML 元素。
import { createViewportEnterDirective } from '@rafaelaugusto/jolt/web/vue'
export default defineNuxtPlugin((nuxt) => {
nuxt.vueApp.directive(
'enter',
createViewportEnterDirective({
viewport: {
keyframes: { opacity: [0, 1], y: [24, 0] },
transition: { duration: 350 },
},
}),
)
})<template><section v-enter>Content</section></template>工厂在 SSR 中安全,不输出隐藏内容的样式。runtime 可借用宿主上下文;config 配置自有上下文,viewport 提供默认选项。指令绑定覆盖默认值;更改配方时替换选项对象。卸载会取消等待,断开连接仅清理该入场效果。KeepAlive 重新创建未完成或重复入场,保留已完成的一次性历史;更换绑定重置历史。支持已知 ShadowRoot 及其宿主;移动到未知的封闭 shadow 树不在此适配器合同内。
useWebMotion().use() 和进入过渡同样等待实际插入。引用替换、停用、取消和 scope 销毁都会取消等待。命令式 animate()、set() 和 runtime.viewportEnter() 仍要求已挂载目标。异步数据应在同步 scope 回调之前准备。
如果页面过渡只改变透明度,而异步内容会改变页面高度,请像 Nuxt 示例一样为页面设置固定的 transform-origin: 0 0。默认的百分比原点会随尺寸变化而移动。如果变换需要随尺寸调整的中心点,请使用 runtime 的 observeGeometry();等待元素插入文档并不代表接管外部几何变化。
打包后的 Nuxt 4.6.3/Vue 3.5.44 消费者在三个浏览器验证 SSR、历史返回、延迟插入、KeepAlive 和减少动态效果。out-in 中打断异步路由时,即使仅使用 CSS 过渡,Nuxt 也可能停留在空占位符;此适配器不修复该路由限制。Vue 取消和资源清理有独立测试。参见验证报告。
回收 DOM 时保留数据身份
animateItems() 管理动态行入场,不实现虚拟化或 FLIP 重排。唯一 key 可以是字符串、有限数值或对象身份。DOM 目标也必须唯一并挂载于运行时 document。更新先验证整个集合,再替换所有权。Vue 中在 nextTick() 后或 post-flush watcher 中调用 update()。
const rows = runtime.animateItems([{ key: item.id, target: rowElement }], 'fadeInUp', {
root: scroller,
once: true,
duration: 400,
})
// After the renderer has committed the next DOM rows:
rows.update(nextItems.map((item) => ({ key: item.id, target: elements.get(item.id)! })))
rows.forget(item.id)
rows.suspend()
rows.resume()
rows.dispose()默认 once: false 允许再次入场。once: true 的历史属于数据 key:同一 DOM 的新 key 重新入场;已见 key 的新 DOM 立即达到终点。对象使用弱引用历史;基本类型 key 保留到 forget(key)、forget() 或销毁。数据从缓存移除时清理旧 key。同一 key/元素重排保留播放,移除只停止该行入场。每行独立进入,因此拒绝 stagger;固定组错峰使用 viewportEnter(),FLIP 使用 animateLayout()。
pending、completed、suspended、disposed 描述控制器。空集合可随后添加行。一个 observer 组服务整个集合,一次性入场完成后断开观察。无 IntersectionObserver、减少动态效果或禁用时,内容仍可读。scope 接管控制器,暂停与销毁保留新的应用通道和独立 press。
LTR 与 RTL 横向滚动
const offset = runtime.scrollValue(rail, { axis: 'x' })
const progress = offset.to([0, 600], [0, 1])
const link = runtime.scrollLinked(
rail,
indicator,
{ scaleX: [0.02, 1] },
{
axis: 'x',
range: [0, 600],
execution: 'auto',
},
)
// After application geometry changes:
offset.refresh()
link.refresh()
// On teardown:
link.dispose()
progress.dispose()
offset.dispose()默认轴仍为 y。axis: x 从逻辑起点返回归一化 CSS 像素:LTR 从左,RTL 从右,向另一端递增。负 RTL scrollLeft 与弹性越界会归一化并钳制。document 和元素具有相同合同。方向、class/style、内容、大小和字体变化会使几何失效;其他变化后调用 refresh()。
合适的播放使用所选轴的 ScrollTimeline。execution: scheduler 强制共享调度器回退;测试对比 LTR/RTL 终点和反向滚动。合成、部分替换、减少动态效果及源移除沿用所有权规则。静止时不调度帧。所选轴上的反向 flex 和竖排 writing-mode 在获取所有权前明确拒绝。不承诺 CSS 像素、原生 DIP 和原生 RTL 约定完全相同。