仿IOS桌面悬浮球(支持拖拽、自动吸附、自动改变透明度与点击、兼容PC端与 ...

打印 上一主题 下一主题

主题 1047|帖子 1047|积分 3141

利用 pointerdown/pointermove/pointerup 实现仿IOS桌面悬浮球效果,支持拖拽、指定拖拽选对容器,指定拖拽安全区、自动吸附、自动改变透明度与点击,兼容PC端与移动端。
效果展示


https://code.juejin.cn/pen/7423757568268304421
代码实现

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.   <meta charset="UTF-8">
  5.   <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6.   <title>Document</title>
  7.   <style>
  8.     * {
  9.       margin: 0;
  10.       padding: 0;
  11.       box-sizing: border-box;
  12.     }
  13.     #app {
  14.       width: 40px;
  15.       height: 40px;
  16.       background-color: rgba(0, 0, 0, 0.15);
  17.       position: absolute;
  18.       left: 50px;
  19.       top: 50px;
  20.       cursor: pointer;
  21.       user-select: none;
  22.       /** 处理移动端只能小范围拖动 */
  23.       touch-action: none;
  24.       border-radius: 50%;
  25.       /** 处理移动端点击蓝色背景 */
  26.       -webkit-tap-highlight-color: transparent;
  27.     }
  28.     #app::before,
  29.     #app::after {
  30.       content: '';
  31.       display: block;
  32.       width: 120%;
  33.       height: 120%;
  34.       border-radius: 50%;
  35.       background-color: rgba(0, 0, 0, 0.15);
  36.       position: absolute;
  37.       top: 50%;
  38.       left: 50%;
  39.       transform: translate(-50%, -50%);
  40.     }
  41.     #app::after {
  42.       width: 80%;
  43.       height: 80%;
  44.     }
  45.     .parent {
  46.       width: 50vw;
  47.       height: 50vh;
  48.       background-color: #f1f1f1;
  49.     }
  50.   </style>
  51. </head>
  52. <body>
  53.   <div class="parent">
  54.     <div id="app"></div>
  55.   </div>
  56.   <script>
  57.     const initDrag = (app, options = {}) => {
  58.       if (!app) return
  59.       const {
  60.         gaps = [10, 10], // 左右间距和上下间距(安全区)
  61.         relative = 'window', // 相对容器 window | parent
  62.         autoAdsorb = true, // 是否自动吸附
  63.         autoAlpha = true, // 是否自动改变透明度
  64.         onClick // 点击事件
  65.       } = options
  66.       let isPointerDown = false
  67.       const parentRect = app.parentElement.getBoundingClientRect()
  68.       const parentWidth = parentRect.width
  69.       const parentHeight = parentRect.height
  70.       let maxLeft = 0
  71.       let maxTop = 0
  72.       if (relative === 'parent') {
  73.         maxLeft = ((parentWidth || window.innerWidth) - app.clientWidth) - gaps[0]
  74.         maxTop = ((parentHeight || window.innerHeight) - app.clientHeight) - gaps[1]
  75.       } else {
  76.         maxLeft = window.innerWidth - app.clientWidth - gaps[0]
  77.         maxTop = window.innerHeight - app.clientHeight - gaps[1]
  78.       }
  79.       let startLeft, startTop; // 记录开始位置
  80.       app.addEventListener('pointerdown', function (e) {
  81.         isPointerDown = true
  82.         app.style.transition = 'none'
  83.         app.style.opacity = 1
  84.         startLeft = e.clientX;
  85.         startTop = e.clientY;
  86.       });
  87.       app.addEventListener('pointermove', function (e) {
  88.         app.setPointerCapture(e.pointerId)
  89.         if (isPointerDown) {
  90.           const left = app.getBoundingClientRect().left
  91.           const top = app.getBoundingClientRect().top
  92.           let newLeft = e.clientX - left
  93.           let newTop = e.clientY - top
  94.           let movedLeft = newLeft + left - app.clientWidth / 2
  95.           let movedTop = newTop + top - app.clientHeight / 2
  96.           // 限制上、左移出边界(默认边界为窗口宽高)
  97.           movedLeft = Math.max(gaps[0], movedLeft)
  98.           movedTop = Math.max(gaps[0], movedTop)
  99.           // 限制下、右移出边界(默认边界为窗口宽高)
  100.           movedLeft = Math.min(movedLeft, maxLeft)
  101.           movedTop = Math.min(movedTop, maxTop)
  102.           app.style.left = movedLeft + 'px'
  103.           app.style.top = movedTop + 'px'
  104.         }
  105.       });
  106.       // 自动降低透明度
  107.       let autoAlphaTimer = null
  108.       const handleAutoAlpha = () => {
  109.         autoAlphaTimer && clearTimeout(autoAlphaTimer)
  110.         autoAlphaTimer = setTimeout(() => {
  111.           app.style.opacity = 0.7
  112.         }, 1000)
  113.       }
  114.       // 自动吸附
  115.       let autoAdsorbTimer = null
  116.       const handleAutoAdsorb = () => {
  117.         autoAdsorbTimer && clearTimeout(autoAdsorbTimer)
  118.         autoAdsorbTimer = setTimeout(() => {
  119.           const left = app.getBoundingClientRect().left
  120.           const movedLeft = left > maxLeft / 2 ? maxLeft : gaps[0]
  121.           app.style.transition = 'all 300ms ease-in-out'
  122.           app.style.left = movedLeft + 'px'
  123.           autoAlpha && handleAutoAlpha()
  124.         }, 100)
  125.       }
  126.       app.addEventListener('pointerup', function (e) {
  127.         isPointerDown = false
  128.         // 判断是否为点击事件
  129.         const endX = e.clientX;
  130.         const endY = e.clientY;
  131.         const distance = Math.sqrt((endX - startLeft) ** 2 + (endY - startTop) ** 2);
  132.         // 如果移动距离小于 5 像素,则认为是点击
  133.         if (distance < 5) {
  134.           app.style.transition = 'none';
  135.           app.style.opacity = 1;
  136.           app.style.left = startLeft - app.clientWidth / 2 + 'px'
  137.           app.style.top = startTop - app.clientHeight / 2 + 'px'
  138.           onClick && onClick()
  139.         } else {
  140.           if (autoAdsorb) {
  141.             handleAutoAdsorb()
  142.           } else if (autoAlpha) {
  143.             handleAutoAlpha()
  144.           }
  145.         }
  146.       });
  147.     }
  148.     initDrag(document.getElementById('app'), {
  149.       onClick: () => {
  150.         alert('click')
  151.       }
  152.     })
  153.   </script>
  154. </body>
  155. </html>
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

勿忘初心做自己

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表