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

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

投げて飛んでいくドラッグ&リリース

勢いよくフリックすると慣性で飛んでいき、弱く離した場合はバネで元の位置に戻ります。WordPressのタスク完了スワイプや通知消去演出に使えます。

mobiledragjs

プレビュー

ソースコード

HTML

<div class="d08-15-10-wrap">
          <div class="d08-15-10-stage" id="d08-15-10-stage">
            <div class="d08-15-10-chip" id="d08-15-10-chip">✅ 完了</div>
          </div>
          <p class="d08-15-10-hint">フリックで放り投げる</p>
        </div>

CSS

.d08-15-10{width:100%;display:flex;align-items:center;justify-content:center}
        .d08-15-10-wrap{display:flex;flex-direction:column;align-items:center;gap:10px}
        .d08-15-10-stage{position:relative;width:220px;height:150px}
        .d08-15-10-chip{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);background:linear-gradient(135deg,#6a5cff,#8b5cf6);color:#fff;font-size:13px;font-weight:700;padding:14px 20px;border-radius:14px;cursor:grab;touch-action:none;box-shadow:0 6px 16px rgba(106,92,255,.3);white-space:nowrap}
        .d08-15-10-chip.is-drag{cursor:grabbing}
        .d08-15-10-hint{font-size:11.5px;color:#9aa0b0;margin:0}

JavaScript

(function(){
        var root=document.getElementById('item-08-15-10');if(!root)return;
        var chip=root.querySelector('#d08-15-10-chip');if(!chip)return;
        var dragging=false,sx=0,sy=0,px=0,py=0,hist=[],raf=null;
        function apply(x,y,op){chip.style.transform='translate(calc(-50% + '+x+'px), calc(-50% + '+y+'px))';chip.style.opacity=String(op);}
        chip.addEventListener('pointerdown',function(e){
          dragging=true;chip.classList.add('is-drag');chip.style.transition='';
          if(raf)cancelAnimationFrame(raf);
          sx=e.clientX-px;sy=e.clientY-py;hist=[{x:e.clientX,y:e.clientY,t:performance.now()}];
          try{chip.setPointerCapture(e.pointerId);}catch(err){}
        });
        chip.addEventListener('pointermove',function(e){
          if(!dragging)return;
          px=e.clientX-sx;py=e.clientY-sy;
          apply(px,py,1);
          hist.push({x:e.clientX,y:e.clientY,t:performance.now()});
          if(hist.length>5)hist.shift();
        });
        function springBack(){
          var x=px,y=py,vx=0,vy=0,stiff=.16,damp=.74;
          function step(){
            var fx=(0-x)*stiff,fy=(0-y)*stiff;
            vx=(vx+fx)*damp;vy=(vy+fy)*damp;
            x+=vx;y+=vy;apply(x,y,1);
            if(Math.abs(x)>.5||Math.abs(y)>.5||Math.abs(vx)>.5||Math.abs(vy)>.5){
              raf=requestAnimationFrame(step);
            }else{px=0;py=0;apply(0,0,1);}
          }
          raf=requestAnimationFrame(step);
        }
        function end(){
          if(!dragging)return;
          dragging=false;chip.classList.remove('is-drag');
          var a=hist[0],b=hist[hist.length-1];
          var dt=(b&&a)?((b.t-a.t)||16):16;
          var vx=(b&&a)?(b.x-a.x)/dt*16:0;
          var vy=(b&&a)?(b.y-a.y)/dt*16:0;
          var speed=Math.sqrt(vx*vx+vy*vy);
          if(speed>6){
            var x=px,y=py;
            function fly(){
              x+=vx;y+=vy;vx*=.95;vy*=.95;
              var op=Math.max(0,1-(Math.abs(x)+Math.abs(y))/260);
              apply(x,y,op);
              if(Math.abs(x)<300&&Math.abs(y)<220&&(Math.abs(vx)>.4||Math.abs(vy)>.4)){
                raf=requestAnimationFrame(fly);
              }else{
                setTimeout(function(){
                  chip.style.transition='transform .4s cubic-bezier(.34,1.56,.64,1),opacity .3s';
                  px=0;py=0;apply(0,0,1);
                  setTimeout(function(){chip.style.transition='';},420);
                },250);
              }
            }
            raf=requestAnimationFrame(fly);
          }else{
            springBack();
          }
        }
        chip.addEventListener('pointerup',end);
        chip.addEventListener('pointercancel',end);
      })();

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