跳转到正文
jolt.
← 动效配方

运动 / iOS / NATIVESCRIPT VUE

跃入光中。

卡片沉入曲面地平线,再升入下一个场景。

来自 Auralis ↗
原生录屏 · iOSAuralis iOS 创作者录屏:动画开场、悬浮玻璃卡片、三个引导场景和页面控件。原始速度,无音频;省略最初 4.25 秒的系统主屏幕与启动转场。录制设备未说明;非性能测试。账户和服务均为示意。

动效背后的代码。

Jolt 0.1.0-rc.11

来自项目的完整源文件,固定到已核对的版本。请结合下面的依赖说明进行适配。

需要 NativeScript-Vue 3 和 Jolt RC.11。函数返回关键帧与选项,由 AuroraView 放入作用域时间线。提供 CardLayout、两个场景中更低的边界(较大的 y 值)以及卡片索引和总数。几何基于 402 点画布、中心 x=201,需按场景调整或缩放。card.view 负责转场,card.art 负责持续漂浮。保留渲染器的减少动态淡入淡出、生命周期保护和 scope 清理。函数本身不绘制玻璃、文字或粒子,请结合 aurora-view.ts 与 artwork.ts。录屏平台为 iOS。

choreography.ts
import type { AnimateOptions, Keyframes } from '@rafaelaugusto/jolt'

export interface CardLayout {
  x: number
  y: number
  width: number
  height: number
  rotation: number
  opacity: number
}

export const sceneTiming = {
  enterAt: 300,
  enterStagger: 42,
  enterDuration: 820,
  exitStagger: 26,
  exitDuration: 440,
  horizonAt: 180,
  horizonDuration: 620,
} as const

export interface CardFlight {
  keyframes: Keyframes
  options: AnimateOptions
}

/** All cards pass through the same pool of light, regardless of swipe direction. */
function poolOffset(card: CardLayout, horizon: number) {
  return {
    x: 201 - (card.x + card.width / 2),
    // Hide the whole shrunken card behind the deepest part of the curved edge.
    y: horizon + 62 - (card.y + card.height / 2),
  }
}

export function exitFlight(card: CardLayout, horizon: number, index: number, count: number): CardFlight {
  const pool = poolOffset(card, horizon)
  const spin = (index % 2 === 0 ? 1 : -1) * (24 + index * 3)
  return {
    keyframes: {
      x: [0, -pool.x * 0.08, pool.x * 0.3, pool.x],
      y: [0, -14, pool.y * 0.25, pool.y],
      scale: [1, 1.045, 0.72, 0.06],
      rotate: [card.rotation, card.rotation - spin * 0.1, card.rotation + spin * 0.4, card.rotation + spin],
      opacity: [card.opacity, card.opacity, card.opacity * 0.85, 0],
    },
    options: {
      duration: sceneTiming.exitDuration,
      delay: (count - 1 - index) * sceneTiming.exitStagger,
      times: [0, 0.2, 0.6, 1],
      ease: ['easeOut', 'easeIn', 'easeIn'],
      opacity: { times: [0, 0.5, 0.82, 1] },
    },
  }
}

export function entryFlight(card: CardLayout, horizon: number, index: number): CardFlight {
  const pool = poolOffset(card, horizon)
  const twist = (index % 2 === 0 ? -1 : 1) * (30 + index * 2)
  const overshoot = 23 + index % 3 * 6
  return {
    keyframes: {
      x: [pool.x, -pool.x * 0.07, pool.x * 0.02, 0],
      y: [pool.y, -overshoot, 7, 0],
      scale: [0.08, 1.055, 0.985, 1],
      rotate: [card.rotation + twist, card.rotation - twist * 0.17, card.rotation + twist * 0.04, card.rotation],
      opacity: [0, card.opacity, card.opacity, card.opacity],
    },
    options: {
      duration: sceneTiming.enterDuration,
      delay: sceneTiming.enterAt + index * sceneTiming.enterStagger,
      times: [0, 0.55, 0.79, 1],
      ease: ['easeOut', 'easeInOut', 'easeOut'],
      opacity: { times: [0, 0.22, 0.79, 1] },
    },
  }
}
查看完整示例 ↗

阅读完整指南 ↗