输入 · 10
指针坐标
Pointer position · pointer把指针变成局部像素和相对中心的坐标。
悬停倾斜需要知道指针在卡片的哪一侧。直接用屏幕像素,卡片缩小后同一相对位置会产生不同的角度;把局部位置换成 −1…1,再随时间阻尼,大小和刷新率变化都不会破坏手感。按下状态也要独立记录,才能给触摸与鼠标一致的反馈。
公式
nx = 2·(clientX − left)/width − 1 ny = 2·(clientY − top)/height − 1 angleX ← damp(angleX, −ny·maxAngle, λ, dt)
参数
| 本页默认 | 成熟实现 | |
|---|---|---|
| nx, ny | −1…1 | —随元素尺寸换算,不使用窗口尺寸。 |
| λ | 10 /s | —指针事件更新目标,帧循环平滑显示。 |
怎样做好
- 每次事件读取元素边界,把
clientX/Y转成局部坐标;窗口坐标不能直接用来驱动卡片。www-motion/rewrite/liquid-type/app.js:4 - 按下和移动是两种信号:按下可立刻点亮边框,倾斜仍可平滑跟随。
- 触摸操作用 Pointer Events 与 pointer capture 保持手势连贯;取消事件也要清掉按下状态。
在我们的案例里
68 个案例至少使用了指针位置或按下信号。liquid-type 把鼠标坐标换成舞台局部坐标来驱动字形;很多画廊再把按下状态交给拖拽判定。
出现在 68 个案例
01Momentum Slider02Drag Gallery03Formation04Cardwall05Riffle06Revolver07Rotunda08Iris09Lens Bloom10Frieze11Aperture12Folio13Cinema Reel14Stream Showcase15Hardback16Picasso Ecommerce17Assembly18Fathom
+50 更多
19Emulsion20Portfolio Photographer21Reverie22Boutique23Nova24Alive25Shape Trail26Membrane27Liquid Type28Filament29Pixels30Cipher31Console32Cutout33Crema34Aster35Spring36Benchmark37Amour38Fluid Gallery40Atelier41Desk 9542Isle43Pantheon44Atlas45Aurora46Rooster Rush47Overworld48Moments49Burst50Dimension51Morph52Meow53Relay54Lumen55Serene56Fleur57Atrium58Skyward59Helix60Crate61Prism62Flashlight63Viewfinder64Sand65Arrange66Tunnel67Reel68Type69Loupe
参考实现 · basics.js
/**
* Live pointer state relative to an element: pixels, plus -1..1 centred coordinates that
* survive resizes. Reads happen in events; consumers read the object in their frame loop.
*/
export function trackPointer(el) {
const s = { x: 0, y: 0, nx: 0, ny: 0, inside: false, down: false, type: 'mouse' };
const set = e => {
const r = el.getBoundingClientRect();
s.x = e.clientX - r.left; s.y = e.clientY - r.top;
s.nx = (s.x / r.width) * 2 - 1; s.ny = (s.y / r.height) * 2 - 1; s.type = e.pointerType;
};
const on = (t, f) => el.addEventListener(t, f);
on('pointerenter', e => { s.inside = true; set(e); });
on('pointermove', set);
on('pointerleave', () => { s.inside = false; });
on('pointerdown', e => { s.down = true; set(e); });
on('pointerup', () => { s.down = false; });
on('pointercancel', () => { s.down = false; s.inside = false; });
return s;
}