Motion · Interação

Glass pill dictation typing

On black, a glass pill pops in at 1.25× then eases to size, an accent light sweeping inside; the cursor leads, placeholder text types in as the glow dims with typing progress, ending as a neutral dark glass bar.

1920×108016:930 fps2.17s · 65 quadrosremotionLicença: Apache-2.0#shotcraft#remotion#interaction

Fonte: video-shotcraft (Vincent Wei) ↗ · Apache-2.0 · Composição Remotion (React/TSX) — vídeo de prévia, sem player no navegador

When to use

The entrance of a voice/AI input box; an interaction cue for "talk to it"; a quiet transitional beat between high-energy segments.

Intention

Tell a small story with "the light dims as input arrives": while idle it glows (waiting for you to speak), and the moment you start talking it hands the glow over to the text. In 1.7s only three things happen — pop in, type, light fades. Any extra motion (bounce, color change, icon animation) would break this beat's function as a rest.

Duration & energy

  • Duration: About 1.7s (50f@30fps)
  • Energy: Low (the quietest beat in the whole film, only the light receding)

Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)

意图

用"光随输入熄灭"讲一件很小的事:待命时它在发光(等你说话),你一开口 它就把光让给文字。1.7s 里只做三件事——弹出、打字、光退。任何额外动作 (弹跳、色变、图标动画)都会让这一拍不再是休止符。

动效核心

  • 胶囊高 46px、圆角 23px、padding: 0 8px 0 16px,定宽

PW = 文本实测宽 + 168px(≈整句宽度的 2 倍),不随打字伸缩

  • 出场 scale 1.25→1,seg(0→0.22, outCubic)(≈0.36s);

opacity 在前 0.025 就跳满——先"在了"再落位

  • 内嵌强调色光是一层 linear-gradient(90deg, α0 → α.35 @42% → α.95 @100%)

的左暗右亮渐层(光在胶囊内部,不是外部光晕)

  • 打字:光标 t≈0.025 先行出现,字符 seg(0.06→0.73) 匀速铺完 18 字符,

尾段保持;光标 0.75→0.8 撤掉

  • 光熄灭 g = 1 − seg(0.08→0.76, inOutQuad),与打字进度同窗口;

g 同时驱动三层阴影收敛:内描边 0.16+0.1g、内顶光 0.03+0.04g、外泛光 26g px / 0.28g

  • 右端 30px 描边圆角方块内 5 根竖条声波,基准高 [13,7,10,6,9]

(左高右低),叠 ±1.6·sin(t·18 + i·1.7) 极轻呼吸

参数表

| 参数 | 典型值 | 调节手感 | |------|--------|----------| | 胶囊定宽 | 文本实测宽 + 168px(≈句宽 2 倍) | 定宽是关键:随字伸缩会读作 chip 而不是输入条 | | 出场缩放 | scale 1.25→1,t 0→0.22 outCubic | 1.25 倍是"从观众面前落回去"的量;1.5 倍就成了 pop 弹窗 | | opacity 时序 | 前 0.025 跳满(早于缩放落位) | 同步淡入会让它像"渐渐出现",失掉那一下轻微的实感 | | 内嵌光 | 90deg、0 → .35@42% → .95@100% | 光必须在胶囊内部;改成外 glow 就成了"待通知"而不是"待输入" | | 打字 | 18 字符,t 0.06→0.73 匀速 | 匀速 ≈ 3f/字符;加 ease 就读作"有人在犹豫" | | 光熄灭 | 1 → 0,t 0.08→0.76 inOutQuad | 与打字同窗口是全片唯一的因果:光让位给文字 | | 阴影收敛 | 内描边 0.16+0.1g / 内顶光 0.03+0.04g / 外泛光 26g px | 三层都挂在同一个 g 上,收尾自然落到"中性深色玻璃条" | | 声波竖条 | 5 根、基准 [13,7,10,6,9]、±1.6px、频率 18 | 幅度到 4px 就成了"正在录音"的活跃态,与"待命"矛盾 | | 光标 | t≈0.025 先行、0.75→0.8 撤 | 光标先于文字出现,是"输入框已就绪"的信号 |

已知坑

  • 定宽靠一个 visibility:hidden 的测量节点算 offsetWidth,字体必须

与正式文本完全一致(400 21px + letter-spacing:.3px), 差一档就宽度不对

  • 声波竖条是持续呼吸(不受 t 分段控制),所以首尾帧的高度不同——

用作 loop 素材时要把频率 18 调成 2π 的整数倍

  • 打字进度 floor(seg(…)*len + 1e-6),那个 epsilon 是防止末字符在

t=0.73 整点因浮点误差不出现,别删

  • ACCENT_RGB(默认 146,126,212 紫)内嵌光与外泛光共用,

换项目品牌色只改这一个变量

  • 占位句 "Speak or type here"(18 字符);换句要保持字符数接近,

否则匀速打字的节奏和定宽比例都要重调

参考实现

demos/interaction/glass-pill-dictation-typing/ (GlassPillDictationTyping.tsx)

Reference implementation (Remotion)

  • demos/interaction/glass-pill-dictation-typing/GlassPillDictationTyping.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/interaction/glass-pill-dictation-typing/GlassPillDictationTyping.tsx

The component imports shared fixtures from demos/_fixtures and helpers from assets/lib in the repo; SKILL.md there documents the render workflow.

Tudo nesta página também é legível por máquina. Aponte seu agente para o JSON ou adicione o servidor MCP do gimgs e peça para buscar templates de motion.

  1. Baixe o JSON — contém a receita do plano (timing, easing, tabela de parâmetros, armadilhas) e o código TSX completo.
  2. Clone o repositório video-shotcraft; o componente importa fixtures de demos/_fixtures e helpers de assets/lib.
  3. Registre o componente como <Composition> em um projeto Remotion e renderize com `npx remotion render`, ou reimplemente a receita no seu próprio engine.
curl -s https://gimgs.net/motion/glass-pill-dictation-typing.json
git clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/interaction/glass-pill-dictation-typing/GlassPillDictationTyping.tsx
npx remotion render <CompositionId> out.mp4
claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("glass-pill-dictation-typing")

Prompt

Use the video-shotcraft shot recipe "Glass pill dictation typing" (Remotion composition, Apache-2.0) published on gimgs.net.
Recipe JSON (prompt = timing/easing/parameter table/pitfalls, files = TSX source): https://gimgs.net/motion/glass-pill-dictation-typing.json
Raw TSX: https://gimgs.net/motion/glass-pill-dictation-typing/template.html
Repository: https://github.com/Vincentwei1021/video-shotcraft  (component: demos/interaction/glass-pill-dictation-typing/GlassPillDictationTyping.tsx; imports demos/_fixtures and assets/lib)
Spec: 1920x1080, 30 fps, 2.17s

1. Read the JSON and follow its "prompt" section (when to use, intention, parameter table, known pitfalls).
2. Adapt it for: <describe what you want>.
3. Either clone the repo, register the component as a <Composition> in a Remotion project and run npx remotion render <CompositionId> out.mp4 — or re-implement the recipe in your own engine (HTML/WAAPI works: keep the same timing and easing).

JSON · Markdown · llms.txt

5,384 chars · Apache-2.0Abrir template bruto ↗
// ===== demos/interaction/glass-pill-dictation-typing/GlassPillDictationTyping.tsx =====
// glass-pill-dictation-typing — Glass Pill Dictation 玻璃胶囊听写(motion-lab 定稿转原生 Remotion)
// 纯黑底上一条定宽玻璃胶囊:整条以约 1.25 倍略大弹出后缓落到位,胶囊内部自左暗到右亮
// 铺一层强调色光(ACCENT 变量,默认紫);光标先行出现,随后打字出现占位句
// 「Speak or type here」,光随打字进度渐渐熄灭,收尾成中性深色玻璃条;右端是描边圆角
// 方块里的竖条声波图标——全片最安静的一拍。
// 设计坐标 480×270(DesignStage 等比放大),参数表数值以此坐标系标定。
import React, { useLayoutEffect, useRef, useState } from 'react';
import { DesignStage, E, lerp, seg, useT } from '../../_fixtures/Motion';

export const GLASS_PILL_DICTATION_TYPING_DURATION = 50; // 1650ms @30fps

// 模板强调色:实际使用时按项目品牌色替换这一个变量(内嵌光 + 外泛光共用)
const ACCENT_RGB = '146,126,212';
const UI = '-apple-system,system-ui,"Segoe UI",sans-serif';
const PH = 46; // 胶囊高度
const ICON = 30; // 右端声波图标方块尺寸
const BASE = [13, 7, 10, 6, 9]; // 竖条基准高度(左高右低的听写图标)
const TEXT = 'Speak or type here'; // 占位句,长度贴近原片(19→18 字符),打字节奏不变

export const GlassPillDictationTyping: React.FC = () => {
  const t = useT();

  // 定宽:原片胶囊宽度约为整句文本宽度的 2 倍,不随词伸缩——挂载时实测一次整句宽度
  const measRef = useRef<HTMLDivElement>(null);
  const [textW, setTextW] = useState(180); // 兜底估算值,useLayoutEffect 实测后覆盖(先于首帧绘制)
  useLayoutEffect(() => {
    if (measRef.current) setTextW(measRef.current.offsetWidth);
  }, []);
  const PW = Math.round(textW + 168);

  // 出场:整体略大(~1.25x)快速浮现,约 0.45s 内缓落到位
  const s = lerp(seg(t, 0, 0.22, E.outCubic), 1.25, 1);
  // 打字:caret 先行(~t0.03),字符 t0.06→0.73 匀速铺完,尾段保持
  const n = Math.floor(seg(t, 0.06, 0.73) * TEXT.length + 1e-6);
  const caretO = seg(t, 0.025, 0.045) * (1 - seg(t, 0.75, 0.8));
  // 强调色光随打字进度渐熄;同步收掉描边亮度的一点富余
  const g = 1 - seg(t, 0.08, 0.76, E.inOutQuad);

  return (
    <DesignStage bg="#000">
      <div
        style={{
          position: 'absolute',
          inset: 0,
          background: '#000',
          overflow: 'hidden',
          // 暗部校色:原样片 x264 把外泛光的暗尾(1~2 级灰)压成纯 0,本渲染的
          // 泛光尾巴因此比样片亮一档、拖得更远。contrast(1.008) 等价于黑端减 ~1 级
          // (255*0.008/2≈1)、中间调不动,按对照帧实测校准(f1 0.9205→0.9844)。
          filter: 'contrast(1.008)',
        }}
      >
        {/* 居中走 translateX:PW 为奇数时圆心在 .5 半像素上,left/flex 会被布局
            取整偏 0.5 设计像素;transform 矩阵不吸附,能与原片圆心逐像素对齐 */}
        <div
          style={{
            position: 'absolute',
            left: 0,
            top: (270 - PH) / 2,
            height: PH,
            width: PW,
            borderRadius: PH / 2,
            boxSizing: 'border-box',
            display: 'flex',
            alignItems: 'center',
            padding: '0 8px 0 16px',
            overflow: 'hidden',
            background: '#0d0d13',
            willChange: 'transform,opacity',
            opacity: seg(t, 0, 0.025),
            transform: `translateX(${(480 - PW) / 2}px) scale(${s})`,
            boxShadow: `inset 0 0 0 1px rgba(255,255,255,${0.16 + 0.1 * g}), inset 0 14px 22px rgba(255,255,255,${0.03 + 0.04 * g}), 0 0 ${26 * g}px rgba(${ACCENT_RGB},${0.28 * g})`,
          }}
        >
          {/* 内嵌强调色光:左暗右亮的渐层,随打字进度熄灭(原片光在胶囊内部,无外部光晕) */}
          <div
            style={{
              position: 'absolute',
              inset: 0,
              pointerEvents: 'none',
              opacity: g,
              background: `linear-gradient(90deg,rgba(${ACCENT_RGB},0) 0%,rgba(${ACCENT_RGB},.35) 42%,rgba(${ACCENT_RGB},.95) 100%)`,
            }}
          />
          <div
            style={{
              position: 'relative',
              font: `400 21px ${UI}`,
              color: '#f4f4f5',
              whiteSpace: 'pre',
              letterSpacing: 0.3,
              flex: 'none',
            }}
          >
            {TEXT.slice(0, n)}
          </div>
          <div
            style={{
              position: 'relative',
              width: 2,
              height: 23,
              borderRadius: 1,
              background: '#eaeaec',
              marginLeft: 2,
              flex: 'none',
              opacity: caretO,
            }}
          />
          <div
            style={{
              position: 'relative',
              marginLeft: 'auto',
              width: ICON,
              height: ICON,
              borderRadius: 9,
              boxSizing: 'border-box',
              border: '1.5px solid rgba(255,255,255,.5)',
              background: 'rgba(255,255,255,.05)',
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center',
              flex: 'none',
            }}
          >
            {/* 声波竖条轻微呼吸(原片几乎静止,仅微动) */}
            <div style={{ display: 'flex', alignItems: 'center', gap: 2.2, height: '100%' }}>
              {BASE.map((h, i) => (
                <div
                  key={i}
                  style={{
                    width: 1.5,
                    borderRadius: 1.5,
                    background: '#ececee',
                    height: h + 1.6 * Math.sin(t * 18 + i * 1.7),
                  }}
                />
              ))}
            </div>
          </div>
        </div>
        {/* 隐藏测量条:与正文同字体同字距,量一次整句宽度以定胶囊定宽 */}
        <div
          ref={measRef}
          style={{
            position: 'absolute',
            visibility: 'hidden',
            whiteSpace: 'pre',
            font: `400 21px ${UI}`,
            letterSpacing: 0.3,
            top: -999,
          }}
        >
          {TEXT}
        </div>
      </div>
    </DesignStage>
  );
};


  1. Read the recipe
    Prompt = when to use, intention, parameter table with typical values and how each one feels, known pitfalls. Treat values as calibrated starting points.
  2. Get the code
    tool: git
    git clone https://github.com/Vincentwei1021/video-shotcraft && cd video-shotcraft && npm install
  3. Register + render
    tool: remotion
    npx remotion render <CompositionId> out.mp4 # component: demos/interaction/glass-pill-dictation-typing/GlassPillDictationTyping.tsx
  4. Or re-implement
    Port the timing/easing from the recipe to HTML + WAAPI (gimgs motion contract) or any engine; keep the same segment windows and easing.
paramtypedefaultdescription
—

Mais nesta categoria