DEMO
アイコンにカーソルを近づけてください
CODE
<div class="demo-stage mdk-stage">
<div class="mdk-dock" id="mdk-dock">
<button class="mdk-item" type="button" aria-label="Finder"><span>🗂️</span></button>
<button class="mdk-item" type="button" aria-label="Mail"><span>✉️</span></button>
<button class="mdk-item" type="button" aria-label="Calendar"><span>📅</span></button>
<button class="mdk-item" type="button" aria-label="Music"><span>🎵</span></button>
<button class="mdk-item" type="button" aria-label="Camera"><span>📷</span></button>
<button class="mdk-item" type="button" aria-label="Terminal"><span>💻</span></button>
<button class="mdk-item" type="button" aria-label="Settings"><span>⚙️</span></button>
</div>
<p class="mdk-note">アイコンにカーソルを近づけてください</p>
</div>
.mdk-stage { display:flex; flex-direction:column; align-items:center; justify-content:flex-end; gap:1.4rem; padding:3rem 1rem 2.2rem; background:linear-gradient(180deg,#12121A 0%,#0B0B10 100%); min-height:280px; }
.mdk-dock {
display:flex; align-items:flex-end; gap:.5rem;
padding:.6rem .8rem; border-radius:20px;
background:rgba(255,255,255,.07); border:1px solid rgba(255,255,255,.12);
backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px);
}
.mdk-item {
--mdk-scale:1;
width:44px; height:44px; padding:0; border:none; border-radius:12px; cursor:pointer;
background:rgba(255,255,255,.08);
display:flex; align-items:center; justify-content:center;
font-size:1.3rem; line-height:1;
transform:scale(var(--mdk-scale)); transform-origin:bottom center;
transition: transform .18s cubic-bezier(.22,1,.36,1), background .2s ease;
}
.mdk-item:hover, .mdk-item:focus-visible { background:rgba(233,27,137,.22); }
.mdk-item:focus-visible { outline:2px solid #E91B89; outline-offset:3px; }
.mdk-note { font-size:.72rem; color:#6E6E7A; letter-spacing:.06em; }
@media (prefers-reduced-motion: reduce) {
.mdk-item { transition-duration:.01ms; }
}
(function(){
var dock = document.getElementById('mdk-dock');
if (!dock) return;
var items = Array.prototype.slice.call(dock.querySelectorAll('.mdk-item'));
var RANGE = 130; // 影響が及ぶ距離(px)
var MAXUP = 0.85; // 最大拡大量(1 + MAXUP 倍)
function reset(){ items.forEach(function(el){ el.style.setProperty('--mdk-scale', 1); }); }
dock.addEventListener('pointermove', function(e){
items.forEach(function(el){
var r = el.getBoundingClientRect();
var center = r.left + r.width / 2;
var d = Math.abs(e.clientX - center);
var t = Math.max(0, 1 - d / RANGE);
el.style.setProperty('--mdk-scale', (1 + MAXUP * t * t).toFixed(3));
});
});
dock.addEventListener('pointerleave', reset);
window.addEventListener('blur', reset);
})();
AI PROMPT
macOSのDockのように、カーソルとの距離に応じてアイコンが拡大するナビゲーションを作って。ドックの pointermove で、各アイコンの中心X座標とカーソルX座標の距離 d を求める。影響範囲 RANGE(130px程度)で正規化して t = max(0, 1 - d / RANGE) とし、倍率を 1 + MAXUP * t * t に設定する(t を2乗すると中心付近だけが鋭く膨らみ、実機の挙動に近づく)。倍率は CSSカスタムプロパティ(--scale)で渡し、CSS側は transform: scale(var(--scale)); transform-origin: bottom center; transition: transform .18s とする。JSでは transform を直接書かず変数だけ更新するのがポイントで、CSS側のトランジションが補間してくれるためカクつかない。pointerleave と window の blur で全アイコンを 1 に戻すこと(戻し忘れると拡大したまま固まる)。アイコンは button 要素にして aria-label を付け、:focus-visible のアウトラインを残すこと。 Acceptance criteria: works with prefers-reduced-motion (animation disabled), zero console errors, zero layout shift
AIエージェントでの実装ガイド → Claude Code でWebアニメーションを実装する
BOOKS · PR · AMAZON
※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。
学びを止めない · AMAZONサブスク
※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。
カーソルとの距離に応じてアイコンが連続的に拡大する、macOS のDockでおなじみの挙動です。Magic UI の Dock として React 実装が広まりましたが、距離の正規化と倍率の計算はごく短いコードで書けるため、バニラJSとCSSカスタムプロパティだけで再現できます。
当サイトの Magnetic Hover はカーソルに要素が引き寄せられる表現で、対象は単体です。本ページは「近傍の複数要素が距離に応じて同時に変形する」別テーマで、ナビゲーションやツールバーなど要素が並ぶ場所が対象になります。
JS側では transform を直接書かずCSSカスタムプロパティだけを更新し、補間はCSSのトランジションに任せるのが滑らかさの鍵です。また pointerleave での復帰処理を省くと、カーソルが外れた瞬間に拡大したまま固まります。
なぜ距離を2乗するのですか?
線形(t のまま)だと影響範囲の端まで緩やかに膨らみ、ドック全体がぼんやり波打ちます。2乗すると中心付近だけが急に立ち上がり、離れた場所ではほぼ変化しないため、実際のmacOSに近い「隣接1〜2個だけがはっきり膨らむ」挙動になります。もっと鋭くしたい場合は3乗、緩やかにしたい場合は sqrt を使います。
スマートフォンではどうなりますか?
pointermove が継続的に発火しないため、拡大は起こらず通常のアイコン列として表示されます。レイアウトは崩れないので実害はありません。タッチ環境でも押した感触を出したい場合は :active に scale を当てるか、@media (hover: hover) でホバー環境限定の装飾として扱ってください。
コードは無料で使えますか?
はい。Motion LabのコードスニペットとAIプロンプトは、クレジット表記なしで自由に使えます。コピーしてそのままプロジェクトに利用できます。