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

news2024/11/27 20:39:36

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

效果展示

在这里插入图片描述
https://code.juejin.cn/pen/7423757568268304421

代码实现

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    #app {
      width: 40px;
      height: 40px;
      background-color: rgba(0, 0, 0, 0.15);
      position: absolute;
      left: 50px;
      top: 50px;
      cursor: pointer;
      user-select: none;
      /** 处理移动端只能小范围拖动 */
      touch-action: none;
      border-radius: 50%;
      /** 处理移动端点击蓝色背景 */
      -webkit-tap-highlight-color: transparent;
    }

    #app::before,
    #app::after {
      content: '';
      display: block;
      width: 120%;
      height: 120%;
      border-radius: 50%;
      background-color: rgba(0, 0, 0, 0.15);
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
    }

    #app::after {
      width: 80%;
      height: 80%;
    }

    .parent {
      width: 50vw;
      height: 50vh;
      background-color: #f1f1f1;
    }
  </style>
</head>

<body>
  <div class="parent">
    <div id="app"></div>
  </div>

  <script>
    const initDrag = (app, options = {}) => {
      if (!app) return

      const {
        gaps = [10, 10], // 左右间距和上下间距(安全区)
        relative = 'window', // 相对容器 window | parent
        autoAdsorb = true, // 是否自动吸附
        autoAlpha = true, // 是否自动改变透明度
        onClick // 点击事件
      } = options

      let isPointerDown = false
      const parentRect = app.parentElement.getBoundingClientRect()
      const parentWidth = parentRect.width
      const parentHeight = parentRect.height
      let maxLeft = 0
      let maxTop = 0
      if (relative === 'parent') {
        maxLeft = ((parentWidth || window.innerWidth) - app.clientWidth) - gaps[0]
        maxTop = ((parentHeight || window.innerHeight) - app.clientHeight) - gaps[1]
      } else {
        maxLeft = window.innerWidth - app.clientWidth - gaps[0]
        maxTop = window.innerHeight - app.clientHeight - gaps[1]
      }
      let startLeft, startTop; // 记录开始位置

      app.addEventListener('pointerdown', function (e) {
        isPointerDown = true
        app.style.transition = 'none'
        app.style.opacity = 1

        startLeft = e.clientX;
        startTop = e.clientY;
      });

      app.addEventListener('pointermove', function (e) {
        app.setPointerCapture(e.pointerId)
        if (isPointerDown) {
          const left = app.getBoundingClientRect().left
          const top = app.getBoundingClientRect().top
          let newLeft = e.clientX - left
          let newTop = e.clientY - top

          let movedLeft = newLeft + left - app.clientWidth / 2
          let movedTop = newTop + top - app.clientHeight / 2

          // 限制上、左移出边界(默认边界为窗口宽高)
          movedLeft = Math.max(gaps[0], movedLeft)
          movedTop = Math.max(gaps[0], movedTop)

          // 限制下、右移出边界(默认边界为窗口宽高)
          movedLeft = Math.min(movedLeft, maxLeft)
          movedTop = Math.min(movedTop, maxTop)

          app.style.left = movedLeft + 'px'
          app.style.top = movedTop + 'px'
        }
      });

      // 自动降低透明度
      let autoAlphaTimer = null
      const handleAutoAlpha = () => {
        autoAlphaTimer && clearTimeout(autoAlphaTimer)
        autoAlphaTimer = setTimeout(() => {
          app.style.opacity = 0.7
        }, 1000)
      }

      // 自动吸附
      let autoAdsorbTimer = null
      const handleAutoAdsorb = () => {
        autoAdsorbTimer && clearTimeout(autoAdsorbTimer)
        autoAdsorbTimer = setTimeout(() => {
          const left = app.getBoundingClientRect().left
          const movedLeft = left > maxLeft / 2 ? maxLeft : gaps[0]
          app.style.transition = 'all 300ms ease-in-out'
          app.style.left = movedLeft + 'px'
          autoAlpha && handleAutoAlpha()
        }, 100)
      }

      app.addEventListener('pointerup', function (e) {
        isPointerDown = false

        // 判断是否为点击事件
        const endX = e.clientX;
        const endY = e.clientY;
        const distance = Math.sqrt((endX - startLeft) ** 2 + (endY - startTop) ** 2);

        // 如果移动距离小于 5 像素,则认为是点击
        if (distance < 5) {
          app.style.transition = 'none';
          app.style.opacity = 1;
          app.style.left = startLeft - app.clientWidth / 2 + 'px'
          app.style.top = startTop - app.clientHeight / 2 + 'px'

          onClick && onClick()
        } else {
          if (autoAdsorb) {
            handleAutoAdsorb()
          } else if (autoAlpha) {
            handleAutoAlpha()
          }
        }
      });
    }

    initDrag(document.getElementById('app'), {
      onClick: () => {
        alert('click')
      }
    })
  </script>
</body>

</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2204377.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(27)QPSK信号在非相关平坦莱斯(Rician)衰落信道上的误码率性能MATLAB仿真

文章目录 前言一、Rician衰落信道模型的MATLAB代码二、在非相关的平坦Rician衰落信道上传输QPSK符号模型1.MATLAB仿真代码2.仿真结果 前言 本文首先给出莱斯衰落信道的建模函数&#xff0c;然后基于该函数给出在非相关的平坦Rician衰落信道上传输QPSK数字调制符号的MATLAB仿真…

【OpenCV】基础操作学习--实现原理理解

读取和显示图像 基本操作 cv2.imread(filename , flags)&#xff1a;文件中读取图像&#xff0c;从指定路径中读取图像&#xff0c;返回一个图像数组&#xff08;NumPy数组&#xff09; filename&#xff1a;图像文件的路径flags&#xff1a;指定读取图像的方式 cv2.IMREAD_COL…

IP地址我个人的理解

作为连IP地址都没弄懂的一个网络小白&#xff0c;有时候开发以及建站会接触到IP、DNS等等概念&#xff0c;所以说有必要去查询一下相关知识内容&#xff0c;羞耻地说其实我是有一个计算机网络技术三级证书&#xff0c;但是这个啥用没有死记硬背就行了&#xff0c;许多知识点不能…

软件工程pipeline梳理

文章目录 软件工程pipeline梳理为什么需要梳理软件工程的pipeline软件工程pipeline的概念与注意点软件工程pipeline中的最大挑战rethink相关资料 软件工程pipeline梳理 为什么需要梳理软件工程的pipeline 反思自己日常工作中的认知和行为。以算法/软件工程师为代表的技术工种往…

2024-10-10 问AI: [AI面试题]激活函数在神经网络中的作用是什么?

文心一言 激活函数在神经网络中扮演着至关重要的角色。它们的主要作用包括&#xff1a; 引入非线性&#xff1a; 神经网络中的每一层通常是由线性变换&#xff08;如权重矩阵乘以输入向量再加上偏置&#xff09;构成的。如果没有激活函数&#xff0c;多层神经网络将仅仅是一个…

正点原子讲解SPI学习,驱动编程NOR FLASH实战

配置SPI传输速度时&#xff0c;需要先失能SPI,__HAL_SPI_DISABLE,然后操作SPI_CR1中的波特率设置位&#xff0c;再使能SPI, NM25Q128驱动步骤 myspi.c #include "./BSP/MYSPI/myspi.h"SPI_HandleTypeDef g_spi1_handler; /* SPI句柄 */void spi1_init(void) {g_spi…

前端基础(四十):拖放功能的实现

效果 源码 <div class"draggable-wrap"><div class"draggable-box" draggable"true" data-json{"name": "Lee"}><h1>Lee</h1><div class"drop-box" data-json{"name": &qu…

API网关之Hango

Hango 是基于云原生和服务网格技术的开源 API 网关&#xff0c;专为现代分布式系统设计&#xff0c;提供高效、安全、可扩展的流量管理解决方案。Hango 网关是基于 Envoy Proxy 构建的&#xff0c;能够处理复杂的微服务架构中流量控制、服务治理和安全需求。Hango 强调与 Kuber…

Java | Leetcode Java题解之第472题连接词

题目&#xff1a; 题解&#xff1a; class Solution {Trie trie new Trie();public List<String> findAllConcatenatedWordsInADict(String[] words) {List<String> ans new ArrayList<String>();Arrays.sort(words, (a, b) -> a.length() - b.length(…

大模型1-本地部署实现交互问答

任务 在本地部署大模型&#xff0c;调用大模型进行对话。 添加库&#xff1a; 1、Transformer Transformers 是由 Hugging Face 开发的一个开源库&#xff0c;广泛应用于自然语言处理&#xff08;NLP&#xff09;任务。其主要功能是简化了对大型预训练语言模型的加载和使用…

神经网络整体架构

文章目录 1.输入层Input2.卷积层Conv3.激活函数层(一)Sigmoid 函数(二)Tanh 函数(三)修正线性单元ReLU(四)Leaky ReLU函数(带泄露的Relu)(五)参数化ReLU 4.池化层POOL5.全连接层FC6.输出层Output 用全连接神经网络处理大尺寸图像具有三个明显的缺点&#xff1a; ①将图像展开为…

从加载到对话:使用 Transformers 本地运行量化 LLM 大模型(GPTQ AWQ)

&#xff08;无需显卡&#xff09;使用 Transformers 在本地加载具有 70 亿参数的 LLM 大语言模型&#xff0c;通过这篇文章你将学会用代码创建属于自己的 GPT。 LLM 的加载、微调和应用涉及多个方面&#xff0c;今天我们先聚焦于加载&#xff0c;本文的难点仅在于正确安装和知…

SQL第16课挑战题

1. 美国各州的缩写应始终用大写。更新所有美国地址&#xff0c;包括供应商状态&#xff08;Vendors表中的vend_state)和顾客状态&#xff08;customers表中的cust_state),使它们均为大写。 2. 第15课挑战题1要求将自己添加到customers表中&#xff0c;现在删除自己&#xff0c;…

活动预告丨第二十八期 “CCF 开源高校行”暨“木兰技术开放日”活动走进北京大学...

点击蓝字 关注我们 CCF Opensource Development Committee 开源高校行 北京大学站 在数字化转型的浪潮中&#xff0c;开源软件人才的培养是信息技术创新发展的重要根基&#xff0c;高校学子作为我国开源生态的源头活水备受重视。10月9日下午15:00-17:10 “CCF 开源高校行”暨“…

【VScode】如何使用详细步骤【笔记】、配置 C / C ++【笔记】

2024 - 10 - 10 - 笔记 - 24 作者(Author)&#xff1a;郑龙浩(仟濹) 该笔记写于 2024-07-02 摘抄到博客上的时间是 2024-10-10 VScode配置 C / C 笔记 我是看了下方链接的视频后为了方便后期复习做的笔记: B站某UP主的视频如下&#xff1a; VScode配置C/C开发环境&#xff…

科研绘图系列:R语言绘制SCI文章图2

文章目录 介绍加载R包导入数据图a图b图d系统信息介绍 文章提供了绘制图a,图b和图d的数据和代码 加载R包 library(ggplot2) library(dplyr) library(readxl) library(ggpmisc)导入数据 数据可从以下链接下载(画图所需要的所有数据): 百度网盘下载链接: https://pan.baid…

S14 瑞士轮胜者组 TES 2:0 击败 DK 晋级淘汰赛

电子竞技的赛场上&#xff0c;总有一些瞬间&#xff0c;让所有的质疑和嘲笑变得苍白无力。 今天&#xff0c;滔搏战队用自己的行动&#xff0c;再次证明了这一点。 他们不仅翻越了挡在面前的高山&#xff0c;更让世界听到了他们的故事。 这支曾被低估的队伍&#xff0c;在夏季…

使用Pytorch+Numpy+Matplotlib实现手写字体分类和图像显示

文章目录 1.引用2.内置图片数据集加载3.处理为batch类型4.设置运行设备5.查看数据6.绘图查看数据图片(1)不显示图片标签(2)打印图片标签(3)图片显示标签 7.定义卷积函数8.卷积实例化、损失函数、优化器9.训练和测试损失、正确率(1)训练(2)测试(3)循环(4)损失和正确率曲线(5)输出…

绘图技巧 | 矩形树状图(Treemap)绘图技巧分享~~

今天这篇推文&#xff0c;小编还是像往常一样交给大家绘图技巧&#xff0c;今天的主角就是-*树形矩阵图(Treemap)*。绘制树形图使用R或者Python都是可以绘制的&#xff0c;今天我们还是使用R进行绘制(Python绘制结果为交互式&#xff0c;后面统一介绍相应的库)。在R中有专门的包…

Java项目-----图形验证码登陆实现

原理: 验证码在前端显示,但是是在后端生成, 将生成的验证码存入redis,待登录时,前端提交验证码,与后端生成的验证码比较. 详细解释: 图形验证码的原理(如下图代码).前端发起获取验证码的请求后, 1 后端接收请求,生成一个键key(随机的键) 然后生成一个验证码作为map的valu…