hero x -60→80 · orbs ∓40 / ∓24 · 700–1100ms
Counter-motion
手前の要素が右へ動くとき、背景の ambient レイヤーを逆方向にわずかにずらして奥行きを出す。before / after の比較デモ。
Without counter-motion
Hero card
→ 右へ移動
With counter-motion ✓
Hero card
→ 右へ移動
/**
* Counter-motion — hero と ambient を逆方向に動かし視差効果を出す比較デモ。
* 移植元: motion-playground app/_sections/CounterMotion.tsx(dark 変換 M-B-2)
*/
import { motion } from 'motion/react';
import { easings } from '../../../lib/motion-tokens/playground';
import { useLabReplay } from './use-lab-replay';
function Scene({
withCounter,
playKey,
}: {
withCounter: boolean;
playKey: number;
}) {
return (
<div className="relative h-56 overflow-hidden rounded-lg bg-[var(--bg-surface)]">
{/* Ambient layer */}
<motion.div
key={`bg-${playKey}`}
initial={{ x: 0, opacity: 0 }}
animate={{ x: withCounter ? -40 : 40, opacity: 1 }}
transition={{ duration: 0.9, ease: easings.gentle }}
className="pointer-events-none absolute -top-12 -right-20 h-72 w-72 rounded-full bg-indigo-400/20 blur-3xl"
/>
<motion.div
key={`bg2-${playKey}`}
initial={{ x: 0, opacity: 0 }}
animate={{ x: withCounter ? -24 : 24, opacity: 1 }}
transition={{ duration: 1.1, ease: easings.gentle }}
className="pointer-events-none absolute -bottom-12 -left-12 h-56 w-56 rounded-full bg-amber-400/20 blur-3xl"
/>
{/* Hero card */}
<motion.div
key={`hero-${playKey}`}
initial={{ x: -60, opacity: 0 }}
animate={{ x: 80, opacity: 1 }}
transition={{ duration: 0.7, ease: easings.md3Emphasized }}
className="absolute top-1/2 left-0 -translate-y-1/2 rounded-lg bg-[var(--bg-elevated)] px-5 py-3 ring-1 ring-[var(--border-strong)]"
style={{ boxShadow: '0 8px 24px rgba(0, 0, 0, 0.35)' }}
>
<p className="text-sm font-medium text-[var(--text-primary)]">
Hero card
</p>
<p className="text-xs text-[var(--text-muted)]">→ 右へ移動</p>
</motion.div>
</div>
);
}
export default function CounterMotion() {
const { rootRef, playKey } = useLabReplay();
return (
<div ref={rootRef} data-lab-island className="w-full p-4 lg:p-6">
<div className="grid gap-6 sm:grid-cols-2">
<div>
<p className="mb-3 text-xs font-medium uppercase tracking-wide text-[var(--text-muted)]">
Without counter-motion
</p>
<Scene withCounter={false} playKey={playKey} />
</div>
<div>
<p className="mb-3 text-xs font-medium uppercase tracking-wide text-[var(--text-muted)]">
With counter-motion ✓
</p>
<Scene withCounter={true} playKey={playKey} />
</div>
</div>
</div>
);
} Implement counter-motion (parallax depth) with motion/react:
- two scenes side by side: with / without counter-motion
- hero card: x -60 -> 80, opacity 0 -> 1, 700ms, cubic-bezier(0.05, 0.7, 0.1, 1)
- ambient orb A: x to -40 (counter) or +40 (control), 900ms, cubic-bezier(0.4, 0, 0.2, 1)
- ambient orb B: x to -24 or +24, 1100ms, same ease
- rule: background layers drift opposite the hero at ~20-30% of its speed
- orbs use large blur + ~20% opacity; replay by re-keying all animated elements