css元素可拖动,使⽤css-transform实现更好的拖拽功能
拖拽功能是⽬前⽹页上⼀种⾮常常见的功能,例如“登录弹窗”的拖拽。本⽂将使⽤transform来实现这⼀功能。
⼀、拖拽的⽤户⾏为分析与原理解析
⼆、代码实现
三、总结
本⽂所涉及的案例可能会⽤到的⼀些必备的知识点:
1、JavaScript中的DOM2级事件绑定
2、正则的编写与匹配
3、获取元素计算后的样式的相关API
4、⿏标坐标的位置获取
5、ES6的模板字符串语法
6、另外,为了能够顺利使⽤到transform,读者可能还需要对CSS3的⼀些样式规则有些了解
因此,如果读者对以上这些知识点的了解还有⽋缺,可以在在此之前捎带预习⼀下。
另外,本⽂配套的这个案例虽然采⽤的webpack构建运⾏,但核⼼代码与之⽆关。
如果读者不熟悉webpack的构建⽅式,也不⽤担⼼会看不懂代码。
⽂章内容难度:☆复制代码
⼀、拖拽的⽤户⾏为分析与原理解析如果读者熟悉了这个过程并也熟知了其中的原理,可以忽略此部分
拖拽的整个过程⼤致可以使⽤此图来描述:
元素的上边距离页⾯顶部的距离值(以下简称“上边距离”)从Y(a)变成了Y(b),“左边距离”从X(a)变成了X(b),也即完成了元素的移动。
在整个的变化过程中,有这样的⼀个隐藏信息:⿏标相对于元素的坐标(distX, distY)在整个移动过程中是没有发⽣变化的,⽤图上的关系即可以表⽰为:cX(b) - X(b) = cX(a) - X(a) = distX,cY(b) - Y(b) = cY(a) - Y(a) = distY。那么,在整个移动过程中,元素的“上边距离”= ⿏标移动中任意时刻的Y坐标 - distY,“左边距离”= ⿏标移动中任意时刻的X坐标 - distX。那么怎么求出distX和distY呢?
我们在按下⿏标的那⼀刻,浏览器就会告诉我们⿏标的坐标(cX, cY),同时,我们也可以求出⽬标元素的“上边距离”(Y)和“左边距
离”(X),这样distX = cX - X,distY = cY - Y。
⼆、代码实现
1.初始化⼯作
按照第⼀部分的分析,我们需要在按下⿏标的那⼀刻获取元素的“上边距离”和“左边距离”。在传统的采⽤【position: absolute】定位的实现⽅式中,这⼀步我们可以通过DOM的【offsetTop】和【offsetLeft】来分别获取它们的值。但既然我们采⽤transform的⽅式来实现,就不再使⽤这两个属性
了。
我们⾸先设置元素的⼀些关键样式(部分UI样式已忽略):
.drag-box-translate3d{
transform: translate3d(0, 0, 1px);
-moz-transform: translate3d(0, 0, 1px);
-webkit-transform: translate3d(0, 0, 1px);
will-change: transform;
-moz-will-change: transform;
-webkit-will-change: transform;
}复制代码值得注意的是,我们采⽤translate3d的属性值并设置了z轴的值为1px,这样做的⽬的是强制浏览器使⽤GPU加速,从⽽获得更加流畅的体验。
判断浏览器是否启⽤GPU加速,可以在定位到该元素之后,查看元素的计算后的样式:transform的值是matrix还是matrix3d,显⽰为后者时,即表⽰已开启GPU加速。
如果我们使⽤【position: absolute】来实现,那么初始位置的X(a)和Y(a)分别以left和top的值来分别指定,但采⽤transform来实现时,我们就可以使⽤translateX和translateY来分别指定X(a)和Y(a)。在上
⾯的CSS设置中,X(a)和Y(a)就被分别设置为0和0。
我们需要在代码中获取该元素的transform的计算后的值,代码如下:
export function getStyle(el,
attr){
if( ComputedStyle !== 'undefined' ){
ComputedStyle(el, null)[attr]
}else if(typeof el.currentStyle !== 'undefiend' ){
return el.currentStyle[attr]
}
return ''
}复制代码
2.绑定mousedown事件并获取distX和distY
我们准备⼀个独⽴的⽂件drag.matrix.js来编写我们的代码,⽤来实现模块化的编程。
我们⾸先定义⼀个模块内的全局变量⽤来承载需要绑定拖拽功能的元素。/* 定义元素变量 */
let ELEMENT = null复制代码
再定义⼀个模块内的全局对象⽤来存储计算⽤到的各个距离与尺⼨数据。/* 定义距离尺⼨的存储池 */ let E_SIZER = {}
复制代码
mousedown事件的回调函数如下:/**
* mousedown事件
* @param {MouseEvent} evte ⿏标事件对象
* @returns {undefined}
**/
function bindMouseDownEvent(evte){
// 阻⽌冒泡
evte.stopPropagation()
// 阻⽌默认事件
evte.preventDefault()
// 解析matrix的正则
let matrix3dReg = /^matrix3d\((?:[-\d.]+,\s*){12}([-\d.]+),\s*([-\d.]+)(?:,\s*[-\d.]+){2}\)/, matrixReg = /^matrix\((?:[-\d.]+,\s*){4}([-\d.]+),\s*([-\d.]+)\)$/
// 获取解析后的transform样式属性值(计算后的样式)
let matrix3dSourceValue = Style(
evte.target,
'transform'
)
// 使⽤正则解析matrix
let matrix3dArrValue =
matrix3dSourceValue.match( matrix3dReg ) || matrix3dSourceValue.match( matrixReg ) // 记录⿏标点击时的坐标
E_SIZER['clientX'] = evte.clientX
E_SIZER['clientY'] = evte.clientY
css鼠标点击样式// 记录matrix解析后的translateX & translateY的值
E_SIZER['targetX'] = matrix3dArrValue[1]
E_SIZER['targetY'] = matrix3dArrValue[2]
// 计算坐标边界巨⿅
E_SIZER['distX'] = E_SIZER['clientX'] - E_SIZER['targetX']
E_SIZER['distY'] = E_SIZER['clientY'] - E_SIZER['targetY']
// 绑定mousemove事件
document.addEventListener('mousemove', bindMouseMoveEvent, false)
} 复制代码
被设置了transform属性值为translate3d的元素,浏览器会将这个样式的属性值计算为matrix3d(...)的矩阵。
那么怎么获取到translateX和translateY的值呢?
这⾥提供两个正则,⽤来解析matrix或matrix3d的值并得到translateX和translateY的值:
/^matrix3d\((?:[-\d.]+,\s*){12}([-\d.]+),\s*([-\d.]+)(?:,\s*[-\d.]+){2}\)/
/^matrix\((?:[-\d.]+,\s*){4}([-\d.]+),\s*([-\d.]+)\)$/复制代码
这两个正则可以直接使⽤,例如:
有了以上的分析和知识储备,我们就可以在⿏标按下的那⼀刻,获取到元素的初始X(a)和Y(a)的值了,也即上述的【bindMouseDownEvent】函数。
3.绑定mousemove事件移动元素
mouseover事件的回调函数如下:/**
* mousemove事件
* @param {MouseEvent} evte ⿏标事件对象
* @returns {undefined}
**/
function bindMouseMoveEvent(evte){
evte.stopPropagation()
evte.preventDefault()
let moveX = evte.clientX - E_SIZER['distX']
let moveY = evte.clientY - E_SIZER['distY']
// 写⼊style
ansform =
Transform =
ELEMENT.style.webkitTransform =
`translate3d(${moveX}px, ${moveY}px, 1px)`
} 复制代码
如果读者对本⽂第⼀部分的分析理解了的话,对于这⼀段函数应该会⽐较容易理解了。我们只要将⿏标在移动中的坐标值“转换”到元素的⾝上,即可完成对元素的实时移动了。
我们需要将【bindMouseMoveEvent】绑定到document上,因为在快速移动过程中,⿏标实际上会移出元素,如果直接将该回调函数绑定到元素上,可能会导致移动过程异常终⽌。
4.绑定mouseup解除功能
mouseup事件的回调函数如下:/**
* mouseup事件
* @param {MouseEvent} evte ⿏标事件对象
* @returns {undefined}
**/
function bindMouseUpEvent(evte){
evte.stopPropagation()
evte.preventDefault()
} 复制代码
我们需要将绑定到document上的mousemove回调事件函数移除。
5.初始化事件绑定/**
* 绑定事件
* @param {MouseEvent} evte ⿏标事件对象
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论