移動端可拖拽效果
阿新 • • 發佈:2018-11-08
<!doctype html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no"> <title>移動端可拖拽效果</title> <style> .ball { position: fixed; line-height: 2.75rem; width: 2.75rem; height: 2.75rem; padding: .5rem; text-align: center; border-radius: 99px; color: #fff; /*border: 1rem solid transparent;*/ background-color: #00bc12; background-clip: padding-box; text-decoration: none; top: 13em; } </style> </head> <body> <div style="height: 2000px;">拖拽效果測試,PC瀏覽器訪問,需開啟控制檯進入移動模式。</div> <a href="https://blog.csdn.net/qq_38881495" id="ball" class="ball">拖我</a> <script> /** * @Inertia.js * @author zhangxinxu * @version * @Created: 18-11-07 * @description 拖動元素,並且具有慣性和邊緣反彈效果 */ (function (global, factory) { if (typeof define === 'function' && (define.amd || define.cmd)) { define(factory); } else { global.Inertia = factory(); } }(this, function () { 'use strict'; var Inertia = function (ele, options) { var defaults = { // 是否吸附邊緣 edge: true }; var params = {}; options = options || {}; for (var key in defaults) { if (typeof options[key] !== 'undefined') { params[key] = options[key]; } else { params[key] = defaults[key]; } } var data = { distanceX: 0, distanceY: 0 }; var win = window; // 瀏覽器窗體尺寸 var winWidth = win.innerWidth; var winHeight = win.innerHeight; if (!ele) { return; } // 設定transform座標等方法 var fnTranslate = function (x, y) { x = Math.round(1000 * x) / 1000; y = Math.round(1000 * y) / 1000; ele.style.webkitTransform = 'translate(' + [x + 'px', y + 'px'].join(',') + ')'; ele.style.transform = 'translate3d(' + [x + 'px', y + 'px', 0].join(',') + ')'; }; var strStoreDistance = ''; // 居然有android機子不支援localStorage if (ele.id && win.localStorage && (strStoreDistance = localStorage['Inertia_' + ele.id])) { var arrStoreDistance = strStoreDistance.split(','); ele.distanceX = +arrStoreDistance[0]; ele.distanceY = +arrStoreDistance[1]; fnTranslate(ele.distanceX, ele.distanceY); } // 顯示拖拽元素 ele.style.visibility = 'visible'; // 如果元素在螢幕之外,位置使用初始值 var initBound = ele.getBoundingClientRect(); if (initBound.left < -0.5 * initBound.width || initBound.top < -0.5 * initBound.height || initBound.right > winWidth + 0.5 * initBound.width || initBound.bottom > winHeight + 0.5 * initBound.height ) { ele.distanceX = 0; ele.distanceY = 0; fnTranslate(0, 0); } ele.addEventListener('touchstart', function (event) { // if (data.inertiaing) { // return; // } var events = event.touches[0] || event; data.posX = events.pageX; data.posY = events.pageY; data.touching = true; if (ele.distanceX) { data.distanceX = ele.distanceX; } if (ele.distanceY) { data.distanceY = ele.distanceY; } // 元素的位置資料 data.bound = ele.getBoundingClientRect(); data.timerready = true; }); // easeOutBounce演算法 /* * t: current time(當前時間); * b: beginning value(初始值); * c: change in value(變化量); * d: duration(持續時間)。 **/ var easeOutBounce = function (t, b, c, d) { if ((t /= d) < (1 / 2.75)) { return c * (7.5625 * t * t) + b; } else if (t < (2 / 2.75)) { return c * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75) + b; } else if (t < (2.5 / 2.75)) { return c * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375) + b; } else { return c * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375) + b; } }; document.addEventListener('touchmove', function (event) { if (data.touching !== true) { return; } // 當移動開始的時候開始記錄時間 if (data.timerready == true) { data.timerstart = +new Date(); data.timerready = false; } event.preventDefault(); var events = event.touches[0] || event; data.nowX = events.pageX; data.nowY = events.pageY; var distanceX = data.nowX - data.posX, distanceY = data.nowY - data.posY; // 此時元素的位置 var absLeft = data.bound.left + distanceX, absTop = data.bound.top + distanceY, absRight = absLeft + data.bound.width, absBottom = absTop + data.bound.height; // 邊緣檢測 if (absLeft < 0) { distanceX = distanceX - absLeft; } if (absTop < 0) { distanceY = distanceY - absTop; } if (absRight > winWidth) { distanceX = distanceX - (absRight - winWidth); } if (absBottom > winHeight) { distanceY = distanceY - (absBottom - winHeight); } // 元素位置跟隨 var x = data.distanceX + distanceX, y = data.distanceY + distanceY; fnTranslate(x, y); // 快取移動位置 ele.distanceX = x; ele.distanceY = y; }, { // fix #3 #5 passive: false }); document.addEventListener('touchend', function () { if (data.touching === false) { // fix iOS fixed bug return; } data.touching = false; // 計算速度 data.timerend = +new Date(); if (!data.nowX || !data.nowY) { return; } // 移動的水平和垂直距離 var distanceX = data.nowX - data.posX, distanceY = data.nowY - data.posY; if (Math.abs(distanceX) < 5 && Math.abs(distanceY) < 5) { return; } // 距離和時間 var distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY), time = data.timerend - data.timerstart; // 速度,每一個自然重新整理此時移動的距離 var speed = distance / time * 16.666; // 經測試,2~60多px不等 // 設定衰減速率 // 數值越小,衰減越快 var rate = Math.min(10, speed); // 開始慣性緩動 data.inertiaing = true; // 反彈的引數 var reverseX = 1, reverseY = 1; // 速度計演算法 var step = function () { if (data.touching == true) { data.inertiaing = false; return; } speed = speed - speed / rate; // 根據運動角度,分配給x, y方向 var moveX = reverseX * speed * distanceX / distance, moveY = reverseY * speed * distanceY / distance; // 此時元素的各個數值 var bound = ele.getBoundingClientRect(); if (moveX < 0 && bound.left + moveX < 0) { moveX = 0 - bound.left; // 碰觸邊緣方向反轉 reverseX = reverseX * -1; } else if (moveX > 0 && bound.right + moveX > winWidth) { moveX = winWidth - bound.right; reverseX = reverseX * -1; } if (moveY < 0 && bound.top + moveY < 0) { moveY = -1 * bound.top; reverseY = -1 * reverseY; } else if (moveY > 0 && bound.bottom + moveY > winHeight) { moveY = winHeight - bound.bottom; reverseY = -1 * reverseY; } var x = ele.distanceX + moveX, y = ele.distanceY + moveY; // 位置變化 fnTranslate(x, y); ele.distanceX = x; ele.distanceY = y; if (speed < 0.1) { speed = 0; if (params.edge == false) { data.inertiaing = false; if (win.localStorage) { localStorage['Inertia_' + ele.id] = [x, y].join(); } } else { // 邊緣吸附 edge(); } } else { requestAnimationFrame(step); } }; var edge = function () { // 時間 var start = 0, during = 25; // 初始值和變化量 var init = ele.distanceX, y = ele.distanceY, change = 0; // 判斷元素現在在哪個半區 var bound = ele.getBoundingClientRect(); if (bound.left + bound.width / 2 < winWidth / 2) { change = -1 * bound.left; } else { change = winWidth - bound.right; } var run = function () { // 如果使用者觸控元素,停止繼續動畫 if (data.touching == true) { data.inertiaing = false; return; } start++; var x = easeOutBounce(start, init, change, during); fnTranslate(x, y); if (start < during) { requestAnimationFrame(run); } else { ele.distanceX = x; ele.distanceY = y; data.inertiaing = false; if (win.localStorage) { localStorage['Inertia_' + ele.id] = [x, y].join(); } } }; run(); }; step(); }); }; return Inertia; })); </script> <script> new Inertia(document.getElementById('ball')); </script> </body> </html>