プルトゥリフレッシュ Pull to Refresh
画面を下に引っ張ってコンテンツを更新するパターン。モバイルアプリの標準的な更新操作。
ライブデモ
引っ張って更新
フィード項目 1 — 10分前
フィード項目 2 — 30分前
フィード項目 3 — 1時間前
フィード項目 4 — 2時間前
フィード項目 5 — 3時間前
コンテンツを下にドラッグして更新
ソースコード
<div class="demo-ptr-container">
<div class="demo-ptr-indicator">
<svg class="demo-ptr-spinner" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M21 12a9 9 0 11-6.219-8.56"/></svg>
<span class="demo-ptr-text">引っ張って更新</span>
</div>
<div class="demo-ptr-content">
<div class="demo-ptr-item"><span class="demo-ptr-dot"></span>フィード項目 1 — 10分前</div>
<div class="demo-ptr-item"><span class="demo-ptr-dot"></span>フィード項目 2 — 30分前</div>
<div class="demo-ptr-item"><span class="demo-ptr-dot"></span>フィード項目 3 — 1時間前</div>
<div class="demo-ptr-item"><span class="demo-ptr-dot"></span>フィード項目 4 — 2時間前</div>
<div class="demo-ptr-item"><span class="demo-ptr-dot"></span>フィード項目 5 — 3時間前</div>
</div>
<p class="demo-ptr-hint">コンテンツを下にドラッグして更新</p>
</div>.demo-ptr-container {
position: relative; height: 260px; overflow-y: auto; overflow-x: hidden;
border: 1px solid #e5e7eb; border-radius: 10px; background: #fff;
}
.demo-ptr-indicator {
display: flex; flex-direction: column; align-items: center; gap: 6px;
padding: 16px; margin-top: -60px; transition: margin-top 0.3s;
color: var(--ui-primary);
}
.demo-ptr-spinner {
transition: transform 0.2s; stroke: var(--ui-primary);
}
.demo-ptr-indicator.refreshing .demo-ptr-spinner {
animation: demo-ptr-spin 0.8s linear infinite;
}
@keyframes demo-ptr-spin { to { transform: rotate(360deg); } }
.demo-ptr-text { font-size: 12px; color: #999; }
.demo-ptr-content { padding: 0 12px 12px; }
.demo-ptr-item {
display: flex; align-items: center; gap: 10px;
padding: 12px; border-bottom: 1px solid #f3f4f6;
font-size: 13px; color: #444;
}
.demo-ptr-dot {
width: 8px; height: 8px; border-radius: 50%;
background: var(--ui-primary); flex-shrink: 0;
}
.demo-ptr-hint { text-align: center; font-size: 12px; color: #aaa; padding: 8px; margin: 0; }(function(){
var container = document.querySelector('.demo-ptr-container');
var indicator = document.querySelector('.demo-ptr-indicator');
var text = document.querySelector('.demo-ptr-text');
var startY = 0, pulling = false;
container.addEventListener('touchstart', function(e){
if(container.scrollTop === 0){ startY = e.touches[0].clientY; pulling = true; }
}, {passive:true});
container.addEventListener('mousedown', function(e){
if(container.scrollTop === 0){ startY = e.clientY; pulling = true; }
});
function onMove(y){
if(!pulling) return;
var dist = Math.max(0, Math.min(80, (y - startY) * 0.5));
indicator.style.marginTop = (-60 + dist) + 'px';
if(dist > 50) text.textContent = '離して更新';
else text.textContent = '引っ張って更新';
}
container.addEventListener('touchmove', function(e){ onMove(e.touches[0].clientY); }, {passive:true});
container.addEventListener('mousemove', function(e){ if(pulling) onMove(e.clientY); });
function onEnd(){
if(!pulling) return;
pulling = false;
var mt = parseInt(indicator.style.marginTop) || -60;
if(mt > -10){
indicator.style.marginTop = '0px';
indicator.classList.add('refreshing');
text.textContent = '更新中...';
setTimeout(function(){
indicator.classList.remove('refreshing');
indicator.style.marginTop = '-60px';
text.textContent = '引っ張って更新';
}, 1500);
} else {
indicator.style.marginTop = '-60px';
}
}
container.addEventListener('touchend', onEnd);
container.addEventListener('mouseup', onEnd);
container.addEventListener('mouseleave', onEnd);
})();AIプロンプト
Basic
プルトゥリフレッシュをHTML/CSS/JSで作ってください。リストを下に引っ張ると更新インジケーターが表示され、離すとリフレッシュアニメーションが再生されます。
Custom
以下の仕様でプルトゥリフレッシュを実装してください。 - 引っ張り量に応じたスピナー回転アニメーション - 閾値を超えると「離して更新」テキストに切り替え - 更新中はスピナーが回転し続ける - 更新完了後にスムーズに元に戻る - プライマリカラー: #2563eb - タッチ・マウス両対応
Advanced
パフォーマンス最適化されたプルトゥリフレッシュを実装してください。 - PointerEvents APIでタッチ/マウスを統一処理 - requestAnimationFrameでの滑らかな描画 - CSS transformのみ使用(リフロー回避) - overscroll-behavior: containで標準のブラウザ更新を無効化 - 物理ベースのバウンスアニメーション - 更新中のキャンセル機能 - Service Worker連携でオフライン時のフォールバック