基于javascript的拖拽类封装详解
程序员文章站
2023-12-16 11:42:28
效果图如下
github地址如下:
使用方法
引入js和对应的css
import drag from '../../static/dragger.j...
效果图如下
github地址如下:
使用方法
引入js和对应的css
import drag from '../../static/dragger.js' import './assets/css/dragger.css'
之后,实例化
new drag({ id: 'box-dragger', showangle: true, isscale: false, showborder: false }) new drag({ id: 'box-dragger2', canzoom: false, canrotate: false }) new drag({ id: 'img-box', showangle: true, showposition: true }) new drag({ id: 'test' })
具体实现(封装细节)
功能细节整理:
- 旋转
- 缩放
- 平移
技术难点:
- 旋转时要注意盒子每一个点的位置发生了变化
- 针对拖拽后的盒子的left和top都有变化,计算其left和top时需将其按照中心轴旋转摆正,再进行计算
- 当且仅有一个盒子是被选中的盒子,点击哪个选中哪个。(当前页面多个实例化drag对象时,如何保证操作互不影响)
- 实现的两种不同方式:
可以选中某元素,直接给该元素内部加上操作的点
有一个pannel,选中某元素时,将这个pannel定位到该元素的位置上
这两种方式都实现过一次,第一种比较简单,但是第一种,不好控制选中某个元素才让操作点展示。
如何封装:
考虑如何让用户快速上手使用,可参考的点:
- 用户需要传入什么必须的参数
- 暴露给用户什么可设置的参数和方法
实现过程:
可配置参数
字段 | 说明 | 是否必填 | 默认值 |
---|---|---|---|
id | 目标元素id | 是 | 无 |
container | 父容器id | 否 | body |
canrotate | 是否可以旋转 | 否 | true |
canzoom | 是否可以缩放 | 否 | true |
canpull | 是否可以拉升 | 否 | true |
canmove | 是否可以平移 | 否 | true |
showangle | 展示角度 | 否 | false |
showposition | 展示位置 | 否 | false |
isscale | 是否等比例缩放 | 否 | true |
showborder | 是否展示pannel的border | 否 | false |
属性
- canrotate
- canzoom
- canpull
- canmove
- showangle
- isscale
- id
- container
- targetobj
- panneldom 操作divdom
- ...
具体看图:
代码解说
初始化参数
初始化目标dom对象的位置:记录其:
- left平距左
- top
- width
- height
- angle
- rightbottompoint 目标dom对象右下坐标
- righttoppoint 目标dom对象右上坐标
- lefttoppoint 目标dom对象左上坐标
- leftbottompoint 目标dom对象左下坐标
- leftmiddlepoint 目标dom对象左中坐标
- rightmiddlepoint 目标dom对象右中坐标
- topmiddlepoint 目标dom对象上中坐标
- bottommiddlepoint 目标dom对象下中坐标
- centerpos 目标dom对象中心点坐标
初始化pannel结构
当前的父容器中只有一个pannel结构,每次实例化对象时,会判断一下如果当前这个父容器里已经存在id为pannel的结构,就将其子节点清空,按照当前实例化对象传进来的属性重新渲染pannel子结构。如果没有id为pannel的结构,就创建。
初始化事件
- 给panneldom和targetobj绑定mousedown事件
- 给document绑定mousemove和mouseup事件
initevent () { document.addeventlistener('mousemove', e => { e.preventdefault && e.preventdefault() this.movechange(e, this.targetobj) }) document.addeventlistener('mouseup', e => { this.moveleave(this.targetobj) }) if (this.canmove) { // 外层给this.panneldom添加mousedown事件,是在所有实例化结束后,panneldom被展示在最后一个实例化对象上,鼠标按下它时,触发moveinit事件 this.panneldom.onmousedown = e => { e.stoppropagation() this.moveinit(9, e, this.targetobj) } this.targetobj.onmousedown = e => { e.stoppropagation() this.moveinit(9, e, this.targetobj) this.initpannel() // 在点击其他未被选中元素时,pannel定位到该元素上,重写panneldom事件,因为此时的this.panneldom已经根据新的目标元素被重写 this.panneldom.onmousedown= e => { this.moveinit(9, e, this.targetobj) } } } }
dom操作
旋转操作
鼠标按下时,记录当前鼠标位置距离box中心位置的y/x的反正切函数a1。
this.mouseinit = { x: math.floor(e.clientx), y: math.floor(e.clienty) } this.preradian = math.atan2(this.mouseinit.y - this.centerpos.y, this.mouseinit.x - this.centerpos.x)
鼠标移动时,记录再次计算鼠标位置距离box中心位置的y/x的反正切函数a2。
this.rotatecurrent = { x: math.floor(e.clientx), y: math.floor(e.clienty) } this.curradian = math.atan2(this.rotatecurrent.y - this.centerpos.y, this.rotatecurrent.x - this.centerpos.x)
求a2-a1,求出移动的弧度
this.tranformradian = this.curradian - this.preradian
求出最后box的旋转角度,this.getrotate(target)是js中获取某dom元素的旋转角度的方法(粘贴过来的,亲测好使)
this.angle = this.getrotate(target) + math.round(this.tranformradian * 180 / math.pi) this.preradian = this.curradian //鼠标移动的每一下都计算这个角度,所以每一下移动前的弧度值都上一次移动后的弧度值
计算旋转后box每个点的坐标,根据余弦公式,传入:旋转前每点坐标,旋转中心坐标和旋转角度
let disangle = this.angle - this.initangle this.rightbottompoint = this.getrotatedpoint(this.initrightbottompoint, this.centerpos, disangle) this.righttoppoint = this.getrotatedpoint(this.initrighttoppoint, this.centerpos, disangle) this.lefttoppoint = this.getrotatedpoint(this.initlefttoppoint, this.centerpos, disangle) this.leftbottompoint = this.getrotatedpoint(this.initleftbottompoint, this.centerpos, disangle) this.leftmiddlepoint = this.getrotatedpoint(this.initleftmiddlepoint, this.centerpos, disangle) this.rightmiddlepoint = this.getrotatedpoint(this.initrightmiddlepoint, this.centerpos, disangle) this.topmiddlepoint = this.getrotatedpoint(this.inittopmiddlepoint, this.centerpos, disangle) this.bottommiddlepoint = this.getrotatedpoint(this.initbottommiddlepoint, this.centerpos, disangle)
沿着一个方向拉升操作。
沿着一个角缩放操作。 这两个操作,主要参考了一个大佬的拖拽思想实现的
优化,mousemove事件添加节流函数
function throttle(fn, interval) { let canrun = true; return function () { if (!canrun) return; canrun = false; settimeout(() => { fn.apply(this, arguments); canrun = true; }, interval); }; } let that = this document.addeventlistener('mousemove', throttle(function (e) { e.preventdefault && e.preventdefault() that.movechange(e, that.targetobj) }, 10))
以上所述是小编给大家介绍的基于javascript的拖拽类封装详解整合,希望对大家有所帮助
推荐阅读
-
基于java Files类和Paths类的用法(详解)
-
基于Android中Webview使用自定义的javascript进行回调的问题详解
-
详解JavaScript原生封装ajax请求和Jquery中的ajax请求
-
PHP验证类的封装与使用方法详解
-
基于javascript的拖拽类封装详解
-
JavaScript使用面向对象实现的拖拽功能详解
-
Python面向对象程序设计中类的定义、实例化、封装及私有变量/方法详解
-
javascript 封装Date日期类实例详解
-
基于Android中Webview使用自定义的javascript进行回调的问题详解
-
详解Android开发技巧之PagerAdapter实现类的封装