スワイプアクション Swipe Actions
リスト項目をスワイプして表示する操作ボタン。メールやチャットアプリで定番のインタラクション。
ライブデモ
田中太郎
プロジェクトの進捗について
来週のミーティングまでに資料を準備お願いします...
佐藤花子
デザインレビューのお願い
添付のデザイン案についてご意見いただけますか...
← 左右にドラッグしてアクションを表示 →
ソースコード
<div class="demo-swipe-list">
<div class="demo-swipe-item">
<div class="demo-swipe-actions-behind demo-swipe-actions-behind--left">
<button class="demo-swipe-action-btn demo-swipe-archive">アーカイブ</button>
</div>
<div class="demo-swipe-actions-behind demo-swipe-actions-behind--right">
<button class="demo-swipe-action-btn demo-swipe-flag">フラグ</button>
<button class="demo-swipe-action-btn demo-swipe-delete">削除</button>
</div>
<div class="demo-swipe-content" data-swipe>
<div class="demo-swipe-sender">田中太郎</div>
<div class="demo-swipe-subject">プロジェクトの進捗について</div>
<div class="demo-swipe-preview">来週のミーティングまでに資料を準備お願いします...</div>
</div>
</div>
<div class="demo-swipe-item">
<div class="demo-swipe-actions-behind demo-swipe-actions-behind--left">
<button class="demo-swipe-action-btn demo-swipe-archive">アーカイブ</button>
</div>
<div class="demo-swipe-actions-behind demo-swipe-actions-behind--right">
<button class="demo-swipe-action-btn demo-swipe-flag">フラグ</button>
<button class="demo-swipe-action-btn demo-swipe-delete">削除</button>
</div>
<div class="demo-swipe-content" data-swipe>
<div class="demo-swipe-sender">佐藤花子</div>
<div class="demo-swipe-subject">デザインレビューのお願い</div>
<div class="demo-swipe-preview">添付のデザイン案についてご意見いただけますか...</div>
</div>
</div>
<p class="demo-swipe-hint">← 左右にドラッグしてアクションを表示 →</p>
</div>.demo-swipe-list { border: 1px solid #e5e7eb; border-radius: 10px; overflow: hidden; }
.demo-swipe-item { position: relative; border-bottom: 1px solid #e5e7eb; overflow: hidden; }
.demo-swipe-item:last-of-type { border-bottom: none; }
.demo-swipe-actions-behind {
position: absolute; top: 0; bottom: 0; display: flex; align-items: stretch;
}
.demo-swipe-actions-behind--left { left: 0; }
.demo-swipe-actions-behind--right { right: 0; }
.demo-swipe-action-btn {
border: none; color: #fff; font-size: 12px; font-weight: 600;
padding: 0 20px; cursor: pointer; display: flex; align-items: center;
}
.demo-swipe-archive { background: var(--ui-primary); }
.demo-swipe-flag { background: #f59e0b; }
.demo-swipe-delete { background: #ef4444; }
.demo-swipe-content {
position: relative; z-index: 2;
padding: 12px 16px; background: var(--ui-bg-alt, #fff);
transition: transform 0.3s ease;
cursor: grab; user-select: none; -webkit-user-select: none;
}
.demo-swipe-content:active { cursor: grabbing; }
.demo-swipe-sender { font-size: 14px; font-weight: 600; color: var(--ui-text, #111); }
.demo-swipe-subject { font-size: 13px; color: var(--ui-text-sub, #444); margin-top: 2px; }
.demo-swipe-preview { font-size: 12px; color: var(--ui-text-muted, #999); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.demo-swipe-hint { text-align: center; font-size: 12px; color: var(--ui-text-muted, #aaa); padding: 10px; margin: 0; }(function(){
var THRESHOLD = 60, MAX_LEFT = 130, MAX_RIGHT = 80;
document.querySelectorAll('[data-swipe]').forEach(function(el){
var startX=0, dx=0, dragging=false, snapped=0;
function start(x){ startX=x-snapped; dragging=true; el.style.transition='none'; }
function move(x){
if(!dragging) return;
dx = x - startX;
dx = Math.max(-MAX_LEFT, Math.min(MAX_RIGHT, dx));
el.style.transform='translateX('+dx+'px)';
}
function end(){
if(!dragging) return;
dragging=false;
el.style.transition='transform 0.3s ease';
if(dx < -THRESHOLD){ snapped=-MAX_LEFT; }
else if(dx > THRESHOLD){ snapped=MAX_RIGHT; }
else { snapped=0; }
el.style.transform='translateX('+snapped+'px)';
}
el.addEventListener('mousedown',function(e){e.preventDefault();start(e.clientX);});
el.addEventListener('touchstart',function(e){start(e.touches[0].clientX);},{passive:true});
document.addEventListener('mousemove',function(e){move(e.clientX);});
document.addEventListener('touchmove',function(e){move(e.touches[0].clientX);},{passive:true});
document.addEventListener('mouseup',end);
document.addEventListener('touchend',end);
});
// アクションボタンのクリック処理
document.querySelectorAll('.demo-swipe-action-btn').forEach(function(btn){
btn.addEventListener('click', function(){
var item = btn.closest('.demo-swipe-item');
if(!item) return;
if(btn.classList.contains('demo-swipe-delete')){
item.style.transition='opacity 0.3s, max-height 0.3s';
item.style.opacity='0';
item.style.maxHeight='0';
item.style.overflow='hidden';
setTimeout(function(){ item.remove(); }, 300);
} else if(btn.classList.contains('demo-swipe-archive')){
var content = item.querySelector('[data-swipe]');
if(content){ content.style.background='#e0f2fe'; content.style.transform='translateX(0)'; }
setTimeout(function(){ item.style.opacity='0.5'; }, 200);
} else if(btn.classList.contains('demo-swipe-flag')){
var sender = item.querySelector('.demo-swipe-sender');
if(sender) sender.style.color='var(--ui-primary)';
var content2 = item.querySelector('[data-swipe]');
if(content2) content2.style.transform='translateX(0)';
}
});
});
})();AIプロンプト
Basic
スワイプアクションをHTML/CSS/JSで作ってください。リスト項目を左右にスワイプすると、削除やアーカイブのボタンが表示されます。
Custom
以下の仕様でスワイプアクションを実装してください。 - メール一覧風のリストUI - 左スワイプで「フラグ」「削除」ボタンを表示 - 右スワイプで「アーカイブ」ボタンを表示 - スワイプ量に応じた弾性アニメーション - プライマリカラー: #2563eb - タッチ・マウス両対応
Advanced
本格的なスワイプアクションを実装してください。 - PointerEvents APIで統一的なタッチ/マウス処理 - スワイプ速度の検知(velocity-based threshold) - 一定以上スワイプしたら自動で全開 - 全開状態で自動実行(確認ダイアログ付き) - 他の項目をスワイプしたら前の項目を閉じる - 物理ベースのバウンスアニメーション - requestAnimationFrameでの滑らかな描画