← 動的パーツギャラリーへ戻る

パーツ番号 08-15-05 ・ JavaScript使用

慣性で滑るドラッグリスト

チップを勢いよく振って離すと慣性で滑り、端では跳ね返って止まります。WordPressの日付選択バーやタグの横スクロールUIに応用できます。

scrolljs

プレビュー

ソースコード

HTML

<div class="d08-15-05-track" id="d08-15-05-track">
          <div class="d08-15-05-row" id="d08-15-05-row">
            <div class="d08-15-05-chip">Mon</div>
            <div class="d08-15-05-chip">Tue</div>
            <div class="d08-15-05-chip">Wed</div>
            <div class="d08-15-05-chip">Thu</div>
            <div class="d08-15-05-chip">Fri</div>
            <div class="d08-15-05-chip">Sat</div>
            <div class="d08-15-05-chip">Sun</div>
          </div>
        </div>

CSS

.d08-15-05{width:100%;display:flex;align-items:center;justify-content:center}
        .d08-15-05-track{position:relative;width:100%;max-width:250px;overflow:hidden;touch-action:none}
        .d08-15-05-row{display:flex;gap:10px;width:max-content;cursor:grab}
        .d08-15-05-row.is-drag{cursor:grabbing}
        .d08-15-05-chip{flex:0 0 auto;background:#fff;border:1.5px solid #e2e2e8;border-radius:10px;padding:12px 16px;font-size:12.5px;font-weight:700;color:#2a2d3a;box-shadow:0 1px 3px rgba(0,0,0,.06);user-select:none}

JavaScript

(function(){
        var root=document.getElementById('item-08-15-05');if(!root)return;
        var track=root.querySelector('#d08-15-05-track'),row=root.querySelector('#d08-15-05-row');
        if(!track||!row)return;
        var dragging=false,startX=0,startPos=0,pos=0,minPos=0,raf=null,hist=[];
        function bounds(){minPos=Math.min(0,track.clientWidth-row.scrollWidth);}
        function apply(p){row.style.transform='translateX('+p+'px)';}
        row.addEventListener('pointerdown',function(e){
          dragging=true;row.classList.add('is-drag');
          if(raf)cancelAnimationFrame(raf);
          bounds();startX=e.clientX;startPos=pos;hist=[{x:e.clientX,t:performance.now()}];
          try{row.setPointerCapture(e.pointerId);}catch(err){}
        });
        row.addEventListener('pointermove',function(e){
          if(!dragging)return;
          var dx=e.clientX-startX;
          var raw=startPos+dx;
          if(raw>0)raw=raw*.35;
          else if(raw<minPos)raw=minPos+(raw-minPos)*.35;
          pos=raw;apply(pos);
          hist.push({x:e.clientX,t:performance.now()});
          if(hist.length>5)hist.shift();
        });
        function springTo(target){
          var x=pos,v=0,stiff=.2,damp=.72;
          function step(){
            var f=(target-x)*stiff;v=(v+f)*damp;x+=v;pos=x;apply(x);
            if(Math.abs(target-x)>.5||Math.abs(v)>.5){raf=requestAnimationFrame(step);}
            else{pos=target;apply(target);}
          }
          raf=requestAnimationFrame(step);
        }
        function end(){
          if(!dragging)return;
          dragging=false;row.classList.remove('is-drag');
          if(pos>0){springTo(0);return;}
          if(pos<minPos){springTo(minPos);return;}
          var a=hist[0],b=hist[hist.length-1];
          var dt=(b&&a)?(b.t-a.t)||16:16;
          var v=b&&a?(b.x-a.x)/dt*16:0;
          var x=pos,raf2=null;
          function momentum(){
            x+=v;v*=.94;pos=x;
            if(x>0){springTo(0);return;}
            if(x<minPos){springTo(minPos);return;}
            apply(x);
            if(Math.abs(v)>.4){raf=requestAnimationFrame(momentum);}
          }
          if(Math.abs(v)>.4){raf=requestAnimationFrame(momentum);}
        }
        row.addEventListener('pointerup',end);
        row.addEventListener('pointercancel',end);
      })();

クラス名 .d08-15-05 は本ライブラリ内での識別用です。複数のパーツを同じページで使う場合は、重複しないよう名前を変更してください。