记录--手摸手带你撸一个拖拽效果

news2025/1/18 11:09:00

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

前言

最近看见一个拖拽效果的视频(抖音:艾恩小灰灰),看好多人评论说跟着敲也没效果,还有就是作者也不回复大家提出的一些疑问,本着知其然必要知其所以然的心理,我把实现效果研究了一遍,并且了解了其实现原理,这里给大家复盘其原理,学到就是赚到

准备

这里我们要用到字体图标,所以我们从iconfont阿里图标库直接引入

  • 找到需要的图标,添加进项目
  • 找到图标所在的项目,点击查看链接
  • 复制地址,或者点击地址复制跳转后地址链接

<link rel="stylesheet" href="https://at.alicdn.com/t/c/font_2579455_c6xlnvkj0j.cssspm=a313x.7781069.1998910419.53&file=font_2579455_c6xlnvkj0j.css">

创建所需要结构

把我们需要结构先写出来

  • draggable:让盒子可以进行拖拽
  • style="--color:#e63e31"--color让盒子背景色根据--color显示(与下方css样式相联系)
 <div class="list">
        <div class="list-item" draggable="true" style="--color:#e63e31">
            <i class="iconfont icon-shuangyuzuo constellation"></i>
            <span class="list-item-title">双鱼座</span>
        </div>
        <div class="list-item" draggable="true" style="--color:#70d265">
            <i class="iconfont icon-shuipingzuo constellation"></i>
            <span class="list-item-title">水平座</span>
        </div>
        <div class="list-item" draggable="true" style="--color:#f0e941">
            <i class="iconfont icon-mojiezuo constellation"></i>
            <span class="list-item-title">摩羯座</span>
        </div>
        <div class="list-item" draggable="true" style="--color:#da8218">
            <i class="iconfont icon-chunvzuo constellation"></i>
            <span class="list-item-title">处女座</span>
        </div>
        <div class="list-item" draggable="true" style="--color:#7ff0ec">
            <i class="iconfont icon-shizizuo constellation"></i>
            <span class="list-item-title">狮子座</span>
        </div>
    </div>

编写样式

这里直接采用flex对盒子进行排版布局

  • background-color: var(--color);var(--color)是或者自定义属性的颜色
body{
  background-color: #000;
}
.list{
  width: 300px;
  height: 360px;
  /* padding: 20px 0; */
  margin: 100px auto 0;
  display: flex;
  flex-direction: column;
  justify-content: space-around;
}
.list-item{
  width: 100%;
  display: flex;
  align-items: center;
  padding: 0 16px;
  border-radius: 10px;
  /* margin-bottom: 20px; */
background-color: var(--color);
}
.constellation{
  line-height: 2.5em;
  font-size: 20px;
  color: #fff;
}
.list-item-img{
  width: 30px;
  height: 30px;
}
.list-item-title{
  margin-left: 20px;
  color: #fff;
}
// 移动动画class
.list-item.moving{
background-color: transparent;
border: 2px dashed #ccc;
}

js编写拖拽效果

首先获取需要用到的元素

// 获取整个list
const list = document.querySelector('.list')
// 获取每一个盒子
const item = document.querySelectorAll('.list-item')

开始拖动的时候需要加上移动的类,并且设置移动效果

// 开始拖动
    list.ondragstart = e => {
        source_node = e.target
        recode(item)
        setTimeout(() => {
        // 拖拽时样式
            e.target.classList.add('moving')
        }, 0)
        // 设置拖动效果
        e.dataTransfer.effectAllowed = 'move'
    }

拖拽中需要判断是从上往下还是从下往上,根据拖拽元素和放入元素的索引进行比对,从而对拖拽元素进行插入节点操作

注意: 在码上掘金从上往下的时候会出现bug,在浏览器不会,我个人觉得应该是是码上掘金的问题

 // 拖拽放入有效目标触发
    list.ondragenter = e => {
        e.preventDefault()
        console.log(e.target.id, list)
        if (e.target === list || e.target === source_node) {
            return false
        }
        const childer = Array.from(list.children)
        const sourceIndex = childer.indexOf(source_node)
        const targetIndex = childer.indexOf(e.target)
        // console.log(sourceIndex, targetIndex)
        if (sourceIndex < targetIndex) {
            // 从下往上拖动
            list.insertBefore(source_node, e.target.nextElementSibling)
        } else {
            // 从上往下拖动
                list.insertBefore(source_node, e.target)
        }
        // 动画效果函数
        last([e.target, source_node])
    }

拖拽结束后把拖拽时的样式移除

// 拖放结束
    list.ondragend = e => {
        e.target.classList.remove('moving')
    }

解释方法

这里有好多没有用过或者比较少用的方法,这里给大家解释一下

  • ondragstart:当用户开始拖动一个元素或文本选择时,会触发dragstart事件
  • ondragover:当元素或文本选择被拖到有效的拖放目标上时(每几百毫秒一次),就会触发拖放事件
  • ondragenter:当被拖动的元素或文本选择进入有效的拖放目标时,会触发dragenter事件
  • ondragend: 当拖放操作结束时(通过释放鼠标按钮或点击escape键)触发dragend事件。
  • e.dataTransfer.effectAllowed:用于设置拖放时的效果,常用参数有(move,link,copy)
  • getBoundingClientRect:返回元素对于视口的信息
  • requestAnimationFrame:重绘动画
  • cancelAnimationFrame:用于取消requestAnimationFrame调用请求

所有代码

HTML

<div class="list">
  <div class="list-item" draggable="true" style="--color:#e63e31" >
    <i class="iconfont icon-shuangyuzuo constellation"></i>
    <span class="list-item-title">双鱼座</span>
  </div>
  <div class="list-item" draggable="true" style="--color:#70d265" >
    <i class="iconfont icon-shuipingzuo constellation"></i>
    <span class="list-item-title">水平座</span>
  </div>
  <div class="list-item" draggable="true" style="--color:#f0e941" >
    <i class="iconfont icon-mojiezuo constellation"></i>
    <span class="list-item-title">摩羯座</span>
  </div>
  <div class="list-item" draggable="true" style="--color:#da8218" >
    <i class="iconfont icon-chunvzuo constellation"></i>
    <span class="list-item-title">处女座</span>
  </div>
  <div class="list-item" draggable="true" style="--color:#7ff0ec" >
    <i class="iconfont icon-shizizuo constellation"></i>
    <span class="list-item-title">狮子座</span>
  </div>
</div>

JS

// 操作dom元素
    const list = document.querySelector('.list')
    const item = document.querySelectorAll('.list-item')

    // 判断当前元素
    let source_node
    // 开始拖动
    list.ondragstart = e => {
        source_node = e.target
        recode(item)
        setTimeout(() => {
            e.target.classList.add('moving')
        }, 0)
        // 设置拖动效果
        e.dataTransfer.effectAllowed = 'move'
    }
    // 拖动在有效目标
    list.ondragover = e => {
        // 防止默认情况下允许删除
        e.preventDefault()
    }
    // 拖拽放入有效目标触发
    list.ondragenter = e => {
        e.preventDefault()
        console.log(e.target.id, list)
        if (e.target === list || e.target === source_node) {
            return false
        }
        const childer = Array.from(list.children)
        const sourceIndex = childer.indexOf(source_node)
        const targetIndex = childer.indexOf(e.target)
        console.log(sourceIndex, targetIndex)
        if (sourceIndex < targetIndex) {
            // 从下往上拖动
            //  console.log(source_node,e.target.nextElementSibling)
            //  if()
            list.insertBefore(source_node, e.target.nextElementSibling)
        } else {
            // 从上往下拖动
             console.log(e.target,e.target)
            if (!e.target==null||source_node==null) {
                return
            }
                list.insertBefore(source_node, e.target)
        }
        last([e.target, source_node])
    }
    // 拖放结束
    list.ondragend = e => {
        e.target.classList.remove('moving')
    }
    // 重新计算位置
    function recode(eleAll) {
        // getBoundingClientRect 返回元素对于视口信息
        for (let i = 0; i < eleAll.length; i++) {
            const {
                top,
                left
            } = eleAll[i].getBoundingClientRect()
            eleAll[i]._top = top
            eleAll[i]._left = left
        }
    }
    // 添加移动动画效果
    function last(eleAll) {
        for (let i = 0; i < eleAll.length; i++) {
            const dom = eleAll[i]
            const {
                top,
                left
            } = dom.getBoundingClientRect()
            if (dom._left) {
                dom.style.transform = `translate3d(${dom._left-left}px,${dom._top-top}px,0px)`
                // 重绘动画
                let rafId = requestAnimationFrame(function () {
                    dom.style.transition = `transform 0.3s ease-out`
                    dom.style.transform = 'none'
                })
                dom.addEventListener('transitionend', () => {
                    dom.style.transition = 'none'
                    // 取消requestAnimationFrame调用请求
                    cancelAnimationFrame(rafId)
                })
            }
        }
    }

CSS

body{
  background-color: #000;
}
.list{
  width: 300px;
  height: 360px;
  /* padding: 20px 0; */
  margin: 100px auto 0;
  display: flex;
  flex-direction: column;
  justify-content: space-around;
}
.list-item{
  width: 100%;
  display: flex;
  align-items: center;
  padding: 0 16px;
  border-radius: 10px;
  /* margin-bottom: 20px; */
background-color: var(--color);
}
.constellation{
  line-height: 2.5em;
  font-size: 20px;
  color: #fff;
}
.list-item-img{
  width: 30px;
  height: 30px;
}
.list-item-title{
  margin-left: 20px;
  color: #fff;
}
.list-item.moving{
background-color: transparent;
border: 2px dashed #ccc;
}

本文转载于:

https://juejin.cn/post/7171269067729272868

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 

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

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

相关文章

栈帧之局部变量表(Local Variables)解读

局部变量表也被称之为局部变量数组或本地变量表 定义为一个数字数组&#xff0c;主要用于存储方法参数和定义在方法体内的局部变量&#xff0c;这些数据类型包括各类基本数据类型、对象引用&#xff08;reference&#xff09;&#xff0c;以及returnAddress类型。由于局部变量表…

2023最新版本RabbitMQ下载安装教程

一、RabbitMQ简介 RabbitMQ 是一个由 Erlang 语言开发的 AMQP 的开源实现。主要用于在进程、应用程序和服务器之间交换数据&#xff0c;可以通过插件支持进行扩展&#xff0c;支持许多协议&#xff0c;并提供高性能、可靠性、集群和高可用队列。 AMQP &#xff1a;Advanced Me…

2023年疫情开放,国内程序员薪资涨了还是跌了?大数据告诉你答案

自从疫情开放&#xff0c;国内各个行业都开始有复苏的迹象&#xff0c;尤其是旅游行业更是空前暴涨&#xff0c;那么互联网行业如何&#xff1f; 有人说今年好找工作多了&#xff0c;有人说依然是内卷得一塌糊涂&#xff0c;那么今年开春以来&#xff0c;各个岗位的程序员工资…

第十四届蓝桥杯模拟赛(第三期)试题与题解 C++

目录 一、填空题 &#xff08;一&#xff09;最小的十六进制(答案&#xff1a;2730) &#xff08;二&#xff09;Excel的列(答案&#xff1a;BYT) &#xff08;三&#xff09;相等日期(答案&#xff1a;70910) &#xff08;四&#xff09;多少种取法(答案&#xff1a;189)…

20 客户端服务订阅的事件机制剖析

Nacos客户端服务订阅的事件机制剖析 我们已经分析了Nacos客户端订阅的核心流程&#xff1a;Nacos客户端通过一个定时任务&#xff0c;每6秒从注册中心获取实例列表&#xff0c;当发现实例发生变化时&#xff0c;发布变更事件&#xff0c;订阅者进行业务处理&#xff0c;然后更…

超长文解析Linux块设备驱动编写方法

1.前提知识 一个块驱动提供对块存储设备&#xff08;比如 SD 卡、EMMC、NAND Flash、Nor Flash、SPI Flash、机械硬盘、固态硬盘等&#xff09;以固定大小&#xff08;块的大小由内核决定&#xff0c;常常是 4096 字节 &#xff09;的块为基本单位&#xff0c;进行随机的存取。…

【项目实战】使用Feign服务间相互调用,其实OpenFeign也没有想象中那么难嘛

一、Feign介绍 openfeign是一个java的http客户端,用来简化http调用 二、Feign架构(来自官方) Feign由五大部分组成, 由于刚开始接触 feign ,比较关注的 clients 跟 encoders/decoders 三、OKHTTP与Feign之间的关系 在Feign中,Client是一个非常重要的组件,Feign最终…

Altium Designer19 #学习笔记# | 基础应用技巧汇总

全文目录一.元件符号库二.元件封装库1.AD09 集成元件库/封装库三.电路原理图1. 巧用查找"相似对象功能"1.1 查找相同元件1.2. 查找相同文本1.3. 查找相同网络 &#xff1a;E - S - C四.PCB原理图【AD PCB模式下的常用快捷键】PCB视图放大/缩小PCB视图左/右移动PCB切换…

《第一行代码》 第十章:服务

一&#xff0c;在子线程中更新UI 1&#xff0c;新建项目&#xff0c;修改布局代码 <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"&g…

Bluetooth

GATT简介 蓝牙分为经典蓝牙和低功耗蓝牙&#xff08;BLE&#xff09;&#xff0c;我们常用的蓝牙遥控器就是低功耗蓝牙 低功耗蓝牙&#xff08;BLE&#xff09;连接都是建立在 GATT (Generic Attribute Profile) 协议之上。 GATT全称Generic Attribute Profile&#xff08;直译…

软件测试用例篇(2)

功能测试界面测试兼容性测试安全测试易用性测试性能测试 针对有需求的案例来设计测试用例:邮箱注册&#xff0c;部分测试用例 https://zay1xofb7z6.feishu.cn/mindnotes/bmncnKD5Ak6GSZl3PRlWDgF9z3g#mindmap 一)等价类: 场景需求:姓名长度是6-200位&#xff0c;那么如何进行设…

【数据结构初阶】手撕单链表

目录一.链表概念和结构二.单链表功能的实现1.打印单链表内容2.申请单链表节点3.头插和尾插4.头删和尾删5.单链表查找6.pos位置前后插入7.pos位置删除三.链表面试题剖析一.链表概念和结构 概念&#xff1a;链表是一种物理存储结构上非连续、非顺序的存储结构&#xff0c;数据元素…

5-12 SpringCloud快速开发入门:服务消费者构建Hystrix Dashboard监控端点

服务消费者构建Hystrix Dashboard监控端点 Hystrix 仪表盘工程已经创建好了&#xff0c;现在我们需要有一个服务&#xff0c;让这个服务提供一个路径为/actuator/hystrix.stream 接口&#xff0c;然后就可以使用 Hystrix 仪表盘来对该服务进行监控了&#xff1b; 我们改造消费者…

pandas常用操作

文章目录1 认识Pandas2 pandas常用数据结构2.1 Series2.1.1 Series创建2.1.2 数据类型转换2.1.3 查看Series对象的属性2.1.4 预览数据head、tail2.1.5 通过索引获取数据2.2 DataFrame2.2.1 创建DataFrame对象2.2.2 获取行、列、值2.2.3 数据预览2.2.4 通过索引获取数据2.2.5 增…

【Redis】Redis高级客户端Lettuce详解

文章目录前提Lettuce简介连接Redis定制的连接URI语法基本使用API同步API异步API反应式API发布和订阅事务和批量命令执行Lua脚本执行高可用和分片普通主从模式哨兵模式集群模式动态命令和自定义命令高阶特性配置客户端资源使用连接池几个常见的渐进式删除例子在SpringBoot中使用…

C/C++每日一练(20230304)

目录 1. 计数质数 ☆ 2. 筛选10到1000的回文数 ☆ 3. 计算位于矩阵边缘的元素之和 ★ 1. 计数质数 统计所有小于非负整数 n 的质数的数量。 示例 1&#xff1a; 输入&#xff1a;n 10 输出&#xff1a;4 解释&#xff1a;小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7…

【HomeKit】从HomeKit架构层细化到HomeKit ADK集成

前言&#xff1a;这篇文章是对于苹果协议文件《HomeKit ADK Integration Guide - Addendum for Televisions》的学习&#xff0c;针对版本为ADK 6.0电视。描述了将HomeKit ADK的电视简介集成到目标平台中所需的步骤。 总说明 此配置文件用于控制启用Airplay的电视&#xff0c;…

高通Android 13默认切换免提功能

1、测试部反馈 由于平板本身没有听筒功能 因此考虑工厂直接切换到免提功能 2、修改路径 frameworks/av/services/audiopolicy/enginedefault/src/Engine.cpp 3、编译源码ok 拨打紧急号码 可以正常切换到免提功能 其他mtk平台可能不一样 具体以项目实际为准 相关链接 构建…

ESP32编译及运行错误记录

1、打印格式不对 一般都是因为日志中某个参数打印格式不匹配造成。 ESP_LOGI(TAG, "[APP] Free memory: %lu bytes", esp_get_free_heap_size());//将之前的%d 改为%lu 2、配置载不对 这里选择了蓝牙模块需要引入蓝牙组件才能编译通过 idf.py menuconfig Component…

项目中的MD5、盐值加密

首先介绍一下MD5&#xff0c;而项目中用的是MD5和盐值来确保密码的安全性&#xff1b; 1. md5简介 md5的全称是md5信息摘要算法&#xff08;英文&#xff1a;MD5 Message-Digest Algorithm &#xff09;&#xff0c;一种被广泛使用的密码散列函数&#xff0c;可以产生一个128位…