# Lens Bloom：可直接交给其他 Agent 的复现提示词

独立实现 Lens Bloom：My Photoshoot 大标题分裂，两侧之间绽放10张照片，随后收拢成照片堆，再逐张向下翻阅。只使用一个progress标量驱动布局。读取本目录plates.json，保持roman/name/desc/src和顺序。不得使用旧JS或iframe；拆出布局纯函数和输入/渲染控制器，GSAP只推动初始目标进度。

## 视觉与尺寸

全屏禁止默认滚动，stage touch-action:none；背景#ece8e0，65%10%处叠#faf9f7→透明75%的径向渐变。正文Geist，标题Playfair Display，辅助Geist Mono，文字#1c1b16。My和Photoshoot分成两个绝对定位节点，top50%,left0，400 clamp(3.6rem,12.5vw,13.5rem)/1、字距−.03em、transform-origin左中，z5，nowrap。W<640两词隐藏。

deck全屏z10、perspective1400px。卡片图片cover、圆角8px、底#1b1916、shadow0 22px 36px −16px #0007；W<640时卡高=H、宽=W，其他卡高=.66H、宽=.72卡高；实际CSS宽高round，布局数学保留浮点数。spacing=min(.46卡宽+.014W,.32W/9)。所有卡片绝对left/top0；transform以中心坐标后接translate(−50%,−50%)定位。

caption left clamp(24px,7vw,100px),bottom clamp(70px,12vh,130px),z20。罗马编号500衬线clamp(1.7rem,2.7vw,2.4rem),opacity.55；标题上间距6、clamp(1.7rem,3.4vw,3rem)/1.1、字距−.02em；介绍上10,max34ch,13px/1.6,opacity.62；计数上14,10px等宽/.26em/opacity.45。底部中央Scroll or drag为10px等宽/.26em，底clamp(16px,3vh,30px)，上有1×22px竖线、间距10；CSS以1.9s ease-in-out循环上下4px、透明度.4→.72→.4。hud与progress轨迹存在但视觉隐藏。

## 三阶段布局

设N10，i0…9，姿态[x,y,z,s,o,zi]。smooth(t)=t²(3−2t)，所有clamp默认[0,1]。

Spread(i)=[(i−4.5)spacing,0,0,.46,1,300−12i]。
Stack(i,focus)：rel=i−focus。rel<0时gone=min(−rel,2)，姿态[0,.36goneH,40gone,1+.04gone,clamp(1.1−1.6gone),500]；否则back=min(rel,3.2)，姿态[0,−back×.055H,−80back,1−.075back,rel>3.4?0:1,300−round(12rel)]。

- progress<1：bloom=smooth(clamp((progress−.06−.05i)/.3))；基于Spread，scale乘(.7+.3bloom)，y加(1−bloom)×.03H，opacity=bloom。
- 1≤progress<2：t=smooth(clamp((progress−1)/.55))；八成以前的展开行向Stack(i,0)插值所有6分量，zi取round。若目标Stack透明度<.5，opacity强制clamp(1−2.4t)。到1.55已基本收拢。
- progress≥2：Stack(i,clamp(progress−2,0,9))；经过的卡向下离场，不在循环末尾跳回首张。

## 进度、标题与拖影

target/progress/lastProgress初始为W<640?2:0。桌面入场创建GSAP 2.8s power1.inOut tween，每次onUpdate以tween.progress()（线性时间进度）赋给target，值0→1。不要误用被ease处理过的属性值；实际最终停在Bloom，不倒放到标题。resize进入手机且target<2时target=progress=2。

每帧progress+=(target−progress)×.09，|target−progress|<.0004时精确到target。speed=|progress−lastProgress|，再更新lastProgress。progress>2.05时streak=clamp(15speed)，其他0。每卡拖影opacity=.8×streak×卡opacity；拖影为纵向渐变：上白.14，40%…60%透明，下黑.24。

标题split=smooth(clamp(progress))，halfDeck=9spacing/2+卡宽×.46/2，gap=mix(0,halfDeck+.03W−20,split)，wordScale=mix(1,.33,split)，fade=1−clamp((progress−1)/.55)。My的x=W/2−gap−自身offsetWidth×wordScale−20；Photoshoot的x=W/2+gap+20。两者transform=translateY(−50%) translate(x px,0) scale(wordScale)，x1位、scale/fade3位。

每卡translate3d(W/2+x,H/2+y,z) translate(−50%,−50%) scale(s)，位置1位、scale/opacity3位、zIndex=zi。caption显现browse=clamp((progress−1.55)/.45)，opacity=browse，translateY=(1−browse)16px。cue内联opacity=.4(1−browse)，保留原CSS循环。隐藏progressFill scaleX=progress/11保留4位；stageName按progress<.9/1.9为Title/Bloom/Deck。当前索引=clamp(round(progress<2?0:progress−2),0,9)，变化时填caption（罗马编号加括号）。

## 输入与验收

滚轮target+=deltaY×.0011并限制[0,11]，阻止默认。按下保存lastY并捕获指针；拖动每次target−=dy×.0032并clamp；松开/cancel结束。右/下键target+1，左/上−1，范围同上。使用grab/grabbing光标。

在1440×1000和390×844比较入场3600ms、右键收拢/翻阅、滚轮700、向上拖动后的所有卡位/透明度/拖影与标题布局。手机直接首张满屏，桌面先展示两侧标题与小卡展开行。验证caption顺序、滚动上限、无溢出和控制台异常。清理事件、rAF与入场tween。验证 `rewrite/scripts/check-lens-bloom.mjs`；证据 `artifacts/rewrite-validation/lens-bloom/`。

## 本地图片与中英文（2026-09-21 更新）

本轮已接入本地公开图片和中英双语。复现时同时遵循 [共享规范](../../shared/README.md)：沿用当前本地图片路径，使用共享词典与语言偏好（`zh` / `en`），覆盖动态内容、辅助标签和 Canvas 文字；不要恢复旧的远程图片链接。中文拆字标题应先翻译再拆分，并保持原有动画机制。图片来源与替换映射在共享规范中列出。
