实现拖拽

实现拖拽的原理很简单:

  1. 鼠标在domEl上按下,拖拽开始,此时记录鼠标按下的位置
  2. 鼠标在window上移动时,计算当前的鼠标位置和按下时的初始位置,将坐标相减得到差值,将这个差值加到现有的 topleft 属性上

因此我绑定四个事件:

  1. domEl上绑定mousedown事件,用于记录当鼠标点下的位置,和将代表拖拽中的isdragging置为true:
function onMousedown(e) {this.dragOrign.x = e.pageXthis.dragOrign.y = e.pageYthis.isdragging = true
}//为了onMousedown方法可以使用this而使用了bind
this.bindMousedown = onMousedown.bind(this)//之所以这样写是为了方便注销这个事件
this.domEl.addEventListener("mousedown",this.bindMousedown)
  1. window上绑定mousemove事件,用于更新domEl的位置和this.config的配置,之所以不绑定在domEl上是因为在移动鼠标的时候,经常会不小心将鼠标移出当前正在拖动的元素,体验不是很好
function onMousemove(e) {if(this.isdragging){const moveX = e.pageX - this.dragOrign.xconst moveY = e.pageY - this.dragOrign.ylet newX = this.x.num + moveXlet newY = this.y.num + moveY//domEl是否可以拖拽出父元素if(!this.dragOutable){const maxX = this.parentElWidth - this.width.numconst maxY = this.parentElHeight - this.height.numconst minX = 0const minY = 0if(newX < minX) {newX = minX}if(newX > maxX) {newX = maxX}if(newY < minY) {newY = minY}if(newY > maxY) {newY = maxY}}this.x.num = newXthis.y.num = newYthis.dragOrign.x = e.pageXthis.dragOrign.y = e.pageYthis._setStyle()this._updatePositionConfig()}
}/**
* 更新config的width和height,x,y参数
*/
this._updatePositionConfig = function() {this.config.x = getSizeText(this.x,this.parentElWidth)this.config.y = getSizeText(this.y,this.parentElHeight)this.config.width = getSizeText(this.width,this.parentElWidth)this.config.height = getSizeText(this.height,this.parentElHeight)
}
/**
* 设置元素样式
*/
this._setStyle = function() {this.domEl.style.position = 'absolute'this.domEl.style.left = this.x.num + 'px'this.domEl.style.top = this.y.num + 'px'this.domEl.style.width = this.width.num + 'px'this.domEl.style.height = this.height.num + 'px'
}this.bindMouseMove = onMousemove.bind(this)
window.addEventListener("mousemove",this.bindMouseMove)
  1. window上绑定mouseleave事件,移出window之后就不能再拖拽
function onMouseleave(e) {if(this.isdragging) {this.isdragging = false}
}
this.bindMouseLeave = onMouseleave.bind(this)
window.addEventListener("mouseleave",this.bindMouseLeave,false)
  1. window上绑定mouseup事件,鼠标松开后也不能再拖拽
function onMouseup(e) {this.isdragging = false
}
this.bindMouseUp = onMouseup.bind(this)
window.addEventListener("mouseup",this.bindMouseUp)

销毁整个实例时需要注销掉这几个事件:

function removeDragMethods() {this.domEl.removeEventListener("mousedown",this.bindMousedown)window.removeEventListener("mousemove",this.bindMouseMove)window.removeEventListener("onMouseleave",this.bindMouseLeave)window.removeEventListener("mouseup",this.bindMouseUp)
}//Accelerator的destroy方法做一下修改
destroy() {clearInterval(this.watchParentInterval)removeDragMethods(this)this.watchParentInterval = nullconst index = Accelerator._instanceList.findIndex((instance) => { return this.id === instance.id })Accelerator._instanceList.splice(index,1)
}

代码分割

拖拽的几个事件一加代码可读性立马就变差了,由于我需要的仅仅只是两个操作,注册拖拽相关事件,和注销拖拽相关事件,所以我可以将拖拽的方法都抽出去,仅仅暴露两个注册和注销的方法出来:
创建src/accelerator/drag.js:

export function onMousedown(e) {//...
}export function onMousemove(e) {//...
}export function onMouseleave(e) {//...
}export function onMouseup(e) {//...
}export function setDragMethods(_this) {if(_this.dragable){_this.bindMousedown = onMousedown.bind(_this)_this.domEl.addEventListener("mousedown",_this.bindMousedown)_this.bindMouseMove = onMousemove.bind(_this)window.addEventListener("mousemove",_this.bindMouseMove)_this.bindMouseLeave = onMouseleave.bind(_this)window.addEventListener("mouseleave",_this.bindMouseLeave,false)_this.bindMouseUp = onMouseup.bind(_this)window.addEventListener("mouseup",_this.bindMouseUp)}
}export function removeDragMethods(_this) {_this.domEl.removeEventListener("mousedown",_this.bindMousedown)window.removeEventListener("mousemove",_this.bindMouseMove)window.removeEventListener("onMouseleave",_this.bindMouseLeave)window.removeEventListener("mouseup",_this.bindMouseUp)
}

定义在Accelerator上的静态属性和方法也有点碍眼,新建src/accelerator/registerStatic.js

export function registerStaticMethod (Accelerator) {Accelerator.ID = 1Accelerator.x = 0;Accelerator.y = 0;Accelerator.width = '100px'Accelerator.height = '100px'Accelerator.autoCount = falseAccelerator.dragable = trueAccelerator.dragOutable = trueAccelerator._instanceList = []/*** * @param {*} config 设置Accelerator的静态属性*/Accelerator.setStaticConfig = function (config){Accelerator.x = config.x || Accelerator.xAccelerator.y = config.y || Accelerator.yAccelerator.width = config.width || Accelerator.widthAccelerator.height = config.height || Accelerator.heightAccelerator.autoCount = config.autoCount || Accelerator.autoCountAccelerator.dragable = config.dragable || Accelerator.dragableAccelerator.dragOutable = config.dragOutable || Accelerator.dragOutable}/*** 销毁所有Accelerator实例*/Accelerator.destroyAll = function() {for(let i = 0;i < Accelerator._instanceList.length; i++){const instance = Accelerator._instanceList[i]instance.destroy()i--}}
}

然后在src/accelerator/index.js中调用:

import { registerStaticMethod } from './registerStatic'
import {setDragMethods,removeDragMethods
} from './drag'class Accelerator {/*** * @param {*} domEl dom元素,必传* @param {*} config  配置项*/constructor(domEl,config = {}){//...this._init()}/*** 初始化元素的大小和位置,并且刷新Accelerator上的静态参数*/_init(){//...//设置拖拽setDragMethods(this)}//...destroy() {//...removeDragMethods(this)}
}registerStaticMethod(Accelerator)window.Accelerator = Accelerator
export default Accelerator

attr()方法更改属性值

一般实例化传入的配置都是可以在后面改动的,改配置的同时页面上渲染的也应该实时改变。

先设计一下attr()方法:
1.有两个参数attrNameattrValue,attrName是要更改的属性名,attrValue是属性值,使用方法类似下面:

const domEl = document.createElement('div')
const Ac = new accelerator(domEl)
Ac.attr('x','10%')

2.如果attrValue没有传的话就返回这个属性绑定的值:

Ac.attr('x') //'10%'

3.如果attrName传入的是一个Object对象,就将这个对象的值更新到this.config上,例如:

Ac.attr({x:'20%',y:'20%'
})

逻辑整理完毕,代码如下:

/**
*
* @param { string | object } } attrName 属性名 或 object类型的属性及属性值
* @param {*} attrValue 属性值
*/
attr(attrName, attrValue = ''){//先判断attrName的类型const type = typeof(attrName)const orignDragable = this.dragableif(type === 'string'){//字符串的话就验证第二个attrValue的值if(attrValue || attrValue === false) {//不为空就重新设置一下这个值if(attrName!='id') {this.config[attrName] = attrValue}} else {//attrValue为空就返回attrName这个参数的值return this.config[attrName]}}else if(type === 'object'){//attrName为Object//设置this.config//不允许改变idif(attrName.id) {delete attrName.id}this.config = {...this.config,...attrName}}//重新计算参数值this._computedConfig(this.config)//重新设置位置this._setStyle()//其他控制方面的参数变化if(this.dragable != orignDragable) {if(this.dragable){setDragMethods(this)}else{removeDragMethods(this)}}
}

示例

最后写个例子验证一下,老规矩不放代码,看一下效果:

手把手教写拖拽布局插件(拖拽功能篇)相关推荐

  1. 手把手教写拖拽布局插件(辅助线功能篇)

    辅助线功能大概的思路是当同一个父元素中的Accelerator实例超过两个时,移动到两个实例的坐标相同时显示出辅助线,原本这个功能应该是和吸附一起使用的,不过我打算先写辅助线,因为没有写吸附,所以辅助 ...

  2. 手把手教你开发photoshop面板插件(附demo和工具)

    手把手教你开发photoshop面板插件(附demo和工具) 一.前言 二.插件演示 三.目录文件介绍 3.1 插件安装 3.2 开启ps开发模式 3.3 插件文件介绍 3.4 manifest 文件 ...

  3. 手把手教你用JAVA实现“声音复刻”功能(复刻你的声音)标贝科技

    手把手教你用JAVA实现"声音复刻"功能(复刻你的声音)标贝科技 前言 什么是声音复刻? 使用少量的用户声音,短时间内快速为用户量身打造个人定制音色 一.内容太长不愿意看,直接使用 ...

  4. 手把手教大家搭建微信公众号查题功能

    手把手教大家搭建微信公众号查题功能 本平台优点: 多题库查题.独立后台.响应速度快.全网平台可查.功能最全! 1.想要给自己的公众号获得查题接口,只需要两步! 2.题库: 题库:题库后台(点击跳转) ...

  5. 巴菲特与搭档查理芒格手把手教你如何读财报,唯一一篇百看不厌炒股最实用文章

    巴菲特与搭档查理芒格手把手教你如何读财报,唯一一篇百看不厌炒股最实用文章 来源投资快报 巴菲特和芒格是老乡,非常有缘分的是芒格和巴菲特爷爷的杂货铺只有六个街区,甚至小时候芒格还在杂货铺打过工,不过那个 ...

  6. css布局方式_手把手教你CSS Flex布局「真香」

    作者:EcbJS 转发链接:https://blog.csdn.net/EcbJS/article/details/106466757 前言   之前做项目,关于布局方面没怎么深入研究过,好多页面都是 ...

  7. 手把手教您安装WordPress博客系统初装篇

    WordPress安装初衷: 今天受朋友所托付,负责帮忙搞一个周易八字预测网,域名是(www.zy8z.com) 域名注册好一段时间了,也备案通过了,就差程序了,跟朋友小聊天一会儿,感觉也就是弄一个简 ...

  8. 手把手教你撸最新Youtube视频 拖拽动画效果

    前言 又到了金三银四的季节了,忙的人特别忙,面试啊,加班啊,闲的人也是特别闲吧,就比如我,天天划水,闲的写文章,做动画,同时呢各种新技术在不断的涌进,推动者软件行业的发展,不要焦虑,不要着急,学好本分 ...

  9. android 拖拽布局,Android拖拽、回弹布局

    这一次拆解的是今日头条的关注页面:点击关注的头像会弹出一个文章列表.在边界拖拽会出现关闭提示.这次同时实现了Android端和IOS端的效果. 先讲解Android端的实现吧,毕竟我是个Android ...

最新文章

  1. [android] setOnTouchEvent 设置返回值为true 和 false的区别
  2. Linux 设备 eth0 似乎不存在, 初始化操作将被延迟
  3. web前端分享:性能优化之文档碎片处理
  4. 使用 VSCode 开发 uniapp
  5. 单机 搭建kafka集群 本地_Kafka单机环境搭建简记
  6. android 存储方式简书,Android的多种数据存储方式
  7. 16位和32位微处理器(4)——Pentium的寄存器及相关机制
  8. WOFF字体的Mime类型?
  9. 2019Java视频教程-玩转oracle
  10. cs229吴恩达机器学习课件
  11. 集成隔离电源的隔离式RS-485/RS-422收发器
  12. 计算机信息技术学ps吗,小学信息技术photoshop教案.docx
  13. 差分进化算法DE优化BPNN
  14. 平面几何----笛沙格定理及其应用
  15. 配置Appium会话---capability配置信息
  16. vt功能对计算机有影响吗,win7怎么开启vt模拟器?电脑开vt有什么坏处?
  17. 计算机图形学大会和学术刊物编辑
  18. 少儿培训python
  19. python全栈工程师薪水_Python工程师薪资待遇是多少?老男孩Python周末班
  20. 计算机组成原理setb,计算机组成原理与汇编语言4

热门文章

  1. OD笔试题-空汽水瓶可以换汽水
  2. ffmpeg将ogg和wav格式转化为mp3
  3. 一步一步学Silverlight 2系列(21):如何在Silverlight中调用JavaScriptjavascript
  4. 【福大/计院】转专业
  5. 优雅编程之这样使用枚举和注解,你就“正常”了(二十九)
  6. 小渔夫 | 月销2亿,融资1亿,这家内衣企业有点东西
  7. 滴滴出行技术副总裁赖春波:每天发现几十万异常订单,仅有几起为真!
  8. 什么是跨职能流程图? Cross-Functional / Swimlane Flowchart
  9. GaussDB常用命令
  10. Linux运维精华面试题