コマ送りステップアニメ
CSSのsteps()だけで、なめらかさを意図的に捨てた12fps風のコマ送り質感を作ります。商品紹介をストップモーション動画風に見せたいときや、操作感をコミカルにしたい場面に。3匹が飛び飛びに横断します。
ライブデモ
使用例(お題: カフェ MOON BREW)
この技法を「カフェ MOON BREW」というテーマのダミーサイトで実際に使った例です。
HTML
<!-- MOON BREW: 焙煎したて。コーヒーアイテムがコマ送りで横切る店頭サイン -->
<div class="stage" aria-label="コーヒーアイテムがコマ送りで横断する店頭演出">
<div class="ground" aria-hidden="true"></div>
<div class="walker w1" aria-hidden="true">
<svg class="critter" viewBox="0 0 60 60" width="60" height="60">
<path d="M18 22 L42 22 L38 54 L22 54 Z" fill="#C97B4A"/>
<rect x="15" y="17" width="30" height="7" rx="3" fill="#6F4E37"/>
<rect x="23" y="31" width="14" height="9" rx="2" fill="#F4ECDD"/>
<path d="M26 14 q4 -5 0 -10 M34 14 q4 -5 0 -10" stroke="#cdb89b" stroke-width="2" fill="none" stroke-linecap="round"/>
</svg>
<span class="shadow"></span>
</div>
<div class="walker w2" aria-hidden="true">
<svg class="critter" viewBox="0 0 60 60" width="60" height="60">
<ellipse cx="30" cy="32" rx="17" ry="22" fill="#4A2C14"/>
<path d="M30 12 Q22 32 30 52" stroke="#2c1810" stroke-width="3" fill="none"/>
</svg>
<span class="shadow"></span>
</div>
<div class="walker w3" aria-hidden="true">
<svg class="critter" viewBox="0 0 60 60" width="60" height="60">
<path d="M10 42 Q14 24 30 24 Q46 24 50 42 Q40 50 30 46 Q20 50 10 42 Z" fill="#E0A95F"/>
<path d="M22 31 v11 M30 28 v17 M38 31 v11" stroke="#b9803f" stroke-width="2"/>
</svg>
<span class="shadow"></span>
</div>
<p class="cap" aria-hidden="true">MOON BREW · ROASTED DAILY</p>
</div>
CSS
* { box-sizing: border-box; }
html, body { margin: 0; width: 100%; min-height: 100%; background: #F4ECDD; }
.stage {
position: relative;
width: 100%;
height: 100vh;
min-height: 260px;
max-height: 100%;
overflow: hidden;
background: #F4ECDD;
font-family: Georgia, "Times New Roman", serif;
animation: jitter .45s steps(2) infinite;
}
.ground {
position: absolute; left: 0; right: 0; bottom: 34px;
height: 2px; background: #6F4E37; opacity: .85;
}
.walker {
position: absolute; left: 0;
will-change: transform;
animation: cross 3.2s steps(14) infinite;
}
.w1 { bottom: 120px; }
.w2 { bottom: 64px; animation-delay: -1.1s; }
.w3 { bottom: 38px; animation-delay: -2.2s; }
.critter {
display: block;
transform-origin: 50% 80%;
animation: spin .5s steps(2) infinite alternate;
}
.shadow {
position: absolute; left: 50%; bottom: -6px;
width: 42px; height: 7px; margin-left: -21px;
background: rgba(111,78,55,.2);
border-radius: 50%;
transform-origin: center;
animation: foot .5s steps(2) infinite alternate;
}
.cap {
position: absolute; right: 18px; bottom: 8px; margin: 0;
font-size: 12px; letter-spacing: .22em; font-weight: 700;
color: rgba(111,78,55,.55);
}
@keyframes cross { from { transform: translateX(-80px); } to { transform: translateX(calc(100vw + 80px)); } }
@keyframes spin { from { transform: rotate(-8deg); } to { transform: rotate(8deg); } }
@keyframes foot { from { transform: scaleX(.9); } to { transform: scaleX(1.05); } }
@keyframes jitter { 0% { transform: translate(-1px, 0) rotate(-.6deg); } 100% { transform: translate(1px, 1px) rotate(.6deg); } }
JavaScript
// MOON BREW のコマ送りも CSS の steps() のみで完結(JS不要)。
// 14コマ/3.2s の「飛び」がコマ撮りらしさの正解値。
実装ガイド
使いどころ
商品紹介やストップモーション動画風の見せ方、コミカルな導入演出に。なめらかさをあえて捨てた飛び飛びの動きが、手作り感やレトロ感を与えます。
実装時の注意点
動きの核はCSS animationのsteps(N)です。横移動・自転・接地影・全体の手ブレを別々の短いstepsアニメで重ね、フレームレートの違いから有機的なズレを作ります。JSは不要。コマ数を増やすと普通の滑らかアニメに見えるので、分割数は意図的に少なく(例 14コマ/3.2s)保ちます。
対応ブラウザ
CSS animationのsteps()は全モダンブラウザ(Chrome・Edge・Firefox・Safari)で安定動作し、特殊機能に依存しないため追加要件は少なめです。prefers-reduced-motionで動きを止める/弱める配慮を入れるとよく、具体的な対応バージョンはターゲット環境で確認してください。
よくある失敗
stepsの分割数を増やしすぎる、linear以外のイージングを混ぜると「コマ送り感」が消えます。複数要素の周期を完全に揃えると機械的になり、わずかにズラすと生きます。背景に溶ける淡色だと動きが見えにくいので前景とのコントラストを確保しましょう。
応用例
SVGをブランドキャラに差し替えてフレーム枚数や周期でテンポを調整、背景に紙テクスチャを敷く、効果音と同期させる(音はサイト方針次第)などに発展できます。
コード
HTML
<!-- コマ送りステップアニメ:steps() だけで作る12fps風のカクカク横断 -->
<div class="stage" aria-label="コマ送りで画面を横断する3匹のキャラクター">
<div class="ground" aria-hidden="true"></div>
<div class="walker w1" aria-hidden="true">
<svg class="critter" viewBox="0 0 60 60" width="60" height="60">
<ellipse cx="30" cy="38" rx="18" ry="15" fill="#FF5C5C"/>
<circle cx="30" cy="20" r="11" fill="#FF5C5C"/>
<circle cx="26" cy="19" r="2.3" fill="#1A1A1A"/>
<circle cx="34" cy="19" r="2.3" fill="#1A1A1A"/>
<path d="M18 12 L23 3 M42 12 L37 3" stroke="#1A1A1A" stroke-width="2" stroke-linecap="round"/>
</svg>
<span class="shadow"></span>
</div>
<div class="walker w2" aria-hidden="true">
<svg class="critter" viewBox="0 0 60 60" width="60" height="60">
<path d="M14 40 Q30 24 46 40 L42 50 Q30 56 18 50 Z" fill="#FFC83D"/>
<circle cx="30" cy="34" r="10" fill="#FFC83D"/>
<circle cx="27" cy="33" r="2.2" fill="#1A1A1A"/>
<circle cx="33" cy="33" r="2.2" fill="#1A1A1A"/>
<path d="M30 32 l6 -4" stroke="#1A1A1A" stroke-width="2" stroke-linecap="round"/>
</svg>
<span class="shadow"></span>
</div>
<div class="walker w3" aria-hidden="true">
<svg class="critter" viewBox="0 0 60 60" width="60" height="60">
<rect x="12" y="26" width="36" height="26" rx="10" fill="#4ED1A1"/>
<circle cx="22" cy="36" r="2.3" fill="#1A1A1A"/>
<circle cx="38" cy="36" r="2.3" fill="#1A1A1A"/>
<path d="M16 24 v-6 M44 24 v-6" stroke="#1A1A1A" stroke-width="2" stroke-linecap="round"/>
</svg>
<span class="shadow"></span>
</div>
<p class="cap" aria-hidden="true">STOP MOTION</p>
</div>
CSS
:root{
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
--ease-inout: cubic-bezier(0.65, 0, 0.35, 1);
}
* { box-sizing: border-box; }
html, body { margin: 0; width: 100%; min-height: 100%; background: #F7F5F0; }
.stage {
position: relative;
width: 100%;
height: 100vh;
min-height: 260px;
max-height: 100%;
overflow: hidden;
background: #F7F5F0;
font-family: "Arial Black", system-ui, sans-serif;
/* ステージ全体を微妙に揺らしてコマ撮りの手ブレ感を出す */
animation: jitter .45s steps(2) infinite;
}
/* 地面(高さ2pxの線) */
.ground {
position: absolute; left: 0; right: 0; bottom: 34px;
height: 2px; background: #1A1A1A; opacity: .8;
}
/* 横断するキャラ。steps(14) でカクカク移動 */
.walker {
position: absolute; left: 0;
will-change: transform;
animation: cross 3.2s steps(14) infinite;
}
.w1 { bottom: 120px; }
.w2 { bottom: 64px; animation-delay: -1.1s; }
.w3 { bottom: 38px; animation-delay: -2.2s; }
/* 子のSVGだけを自転(steps(2)でパタパタ) */
.critter {
display: block;
transform-origin: 50% 80%;
animation: spin .5s steps(2) infinite alternate;
}
/* 接地影:steps(2)で踏ん張る */
.shadow {
position: absolute; left: 50%; bottom: -6px;
width: 42px; height: 7px; margin-left: -21px;
background: rgba(26,26,26,.18);
border-radius: 50%;
transform-origin: center;
animation: foot .5s steps(2) infinite alternate;
}
.cap {
position: absolute; right: 18px; bottom: 8px; margin: 0;
font-size: 12px; letter-spacing: .28em; font-weight: 900;
color: rgba(26,26,26,.28);
}
@keyframes cross {
from { transform: translateX(-80px); }
to { transform: translateX(calc(100vw + 80px)); }
}
@keyframes spin {
from { transform: rotate(-8deg); }
to { transform: rotate(8deg); }
}
@keyframes foot {
from { transform: scaleX(.9); }
to { transform: scaleX(1.05); }
}
@keyframes jitter {
0% { transform: translate(-1px, 0) rotate(-.6deg); }
100% { transform: translate(1px, 1px) rotate(.6deg); }
}
JavaScript
// コマ送りステップアニメは CSS の steps() のみで完結する(JS不要)。
// steps() のジャンプ数を増やすと普通の滑らかアニメに見えてしまうため、
// 14コマ/3.2s(≒4.4fps相当)の「飛び」がこの演出の正解値。
🤖 AIエージェント用プロンプト
このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「コマ送りステップアニメ」の効果を追加してください。
# 追加してほしい効果
コマ送りステップアニメ(アニメーション & トランジション)
CSSのsteps()だけで、なめらかさを意図的に捨てた12fps風のコマ送り質感を作ります。商品紹介をストップモーション動画風に見せたいときや、操作感をコミカルにしたい場面に。3匹が飛び飛びに横断します。
# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】
# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- コマ送りステップアニメ:steps() だけで作る12fps風のカクカク横断 -->
<div class="stage" aria-label="コマ送りで画面を横断する3匹のキャラクター">
<div class="ground" aria-hidden="true"></div>
<div class="walker w1" aria-hidden="true">
<svg class="critter" viewBox="0 0 60 60" width="60" height="60">
<ellipse cx="30" cy="38" rx="18" ry="15" fill="#FF5C5C"/>
<circle cx="30" cy="20" r="11" fill="#FF5C5C"/>
<circle cx="26" cy="19" r="2.3" fill="#1A1A1A"/>
<circle cx="34" cy="19" r="2.3" fill="#1A1A1A"/>
<path d="M18 12 L23 3 M42 12 L37 3" stroke="#1A1A1A" stroke-width="2" stroke-linecap="round"/>
</svg>
<span class="shadow"></span>
</div>
<div class="walker w2" aria-hidden="true">
<svg class="critter" viewBox="0 0 60 60" width="60" height="60">
<path d="M14 40 Q30 24 46 40 L42 50 Q30 56 18 50 Z" fill="#FFC83D"/>
<circle cx="30" cy="34" r="10" fill="#FFC83D"/>
<circle cx="27" cy="33" r="2.2" fill="#1A1A1A"/>
<circle cx="33" cy="33" r="2.2" fill="#1A1A1A"/>
<path d="M30 32 l6 -4" stroke="#1A1A1A" stroke-width="2" stroke-linecap="round"/>
</svg>
<span class="shadow"></span>
</div>
<div class="walker w3" aria-hidden="true">
<svg class="critter" viewBox="0 0 60 60" width="60" height="60">
<rect x="12" y="26" width="36" height="26" rx="10" fill="#4ED1A1"/>
<circle cx="22" cy="36" r="2.3" fill="#1A1A1A"/>
<circle cx="38" cy="36" r="2.3" fill="#1A1A1A"/>
<path d="M16 24 v-6 M44 24 v-6" stroke="#1A1A1A" stroke-width="2" stroke-linecap="round"/>
</svg>
<span class="shadow"></span>
</div>
<p class="cap" aria-hidden="true">STOP MOTION</p>
</div>
【CSS】
:root{
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
--ease-inout: cubic-bezier(0.65, 0, 0.35, 1);
}
* { box-sizing: border-box; }
html, body { margin: 0; width: 100%; min-height: 100%; background: #F7F5F0; }
.stage {
position: relative;
width: 100%;
height: 100vh;
min-height: 260px;
max-height: 100%;
overflow: hidden;
background: #F7F5F0;
font-family: "Arial Black", system-ui, sans-serif;
/* ステージ全体を微妙に揺らしてコマ撮りの手ブレ感を出す */
animation: jitter .45s steps(2) infinite;
}
/* 地面(高さ2pxの線) */
.ground {
position: absolute; left: 0; right: 0; bottom: 34px;
height: 2px; background: #1A1A1A; opacity: .8;
}
/* 横断するキャラ。steps(14) でカクカク移動 */
.walker {
position: absolute; left: 0;
will-change: transform;
animation: cross 3.2s steps(14) infinite;
}
.w1 { bottom: 120px; }
.w2 { bottom: 64px; animation-delay: -1.1s; }
.w3 { bottom: 38px; animation-delay: -2.2s; }
/* 子のSVGだけを自転(steps(2)でパタパタ) */
.critter {
display: block;
transform-origin: 50% 80%;
animation: spin .5s steps(2) infinite alternate;
}
/* 接地影:steps(2)で踏ん張る */
.shadow {
position: absolute; left: 50%; bottom: -6px;
width: 42px; height: 7px; margin-left: -21px;
background: rgba(26,26,26,.18);
border-radius: 50%;
transform-origin: center;
animation: foot .5s steps(2) infinite alternate;
}
.cap {
position: absolute; right: 18px; bottom: 8px; margin: 0;
font-size: 12px; letter-spacing: .28em; font-weight: 900;
color: rgba(26,26,26,.28);
}
@keyframes cross {
from { transform: translateX(-80px); }
to { transform: translateX(calc(100vw + 80px)); }
}
@keyframes spin {
from { transform: rotate(-8deg); }
to { transform: rotate(8deg); }
}
@keyframes foot {
from { transform: scaleX(.9); }
to { transform: scaleX(1.05); }
}
@keyframes jitter {
0% { transform: translate(-1px, 0) rotate(-.6deg); }
100% { transform: translate(1px, 1px) rotate(.6deg); }
}
【JavaScript】
// コマ送りステップアニメは CSS の steps() のみで完結する(JS不要)。
// steps() のジャンプ数を増やすと普通の滑らかアニメに見えてしまうため、
// 14コマ/3.2s(≒4.4fps相当)の「飛び」がこの演出の正解値。
# 外部ライブラリ
なし(追加ライブラリ不要)
# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。