十二、面向对象实战之封装拖拽对象【上】

简介: 前面几篇文章,我跟大家分享了JavaScript的一些基础知识,这篇文章,将会进入第一个实战环节:利用前面几章的所涉及到的知识,封装一个拖拽对象。为了能够帮助大家了解更多的方式与进行对比,我会使用三种不同的方式来实现拖拽。•不封装对象直接实现;•利用原生JavaScript封装拖拽对象;•通过扩展jQuery来实现拖拽对象。本文的例子会放置于codepen.io[1]中,供大家在阅读时直接查看。如果对于codepen不了解的同学,可以花点时间稍微了解一下。

前面几篇文章,我跟大家分享了JavaScript的一些基础知识,这篇文章,将会进入第一个实战环节:利用前面几章的所涉及到的知识,封装一个拖拽对象。为了能够帮助大家了解更多的方式与进行对比,我会使用三种不同的方式来实现拖拽。


不封装对象直接实现;


利用原生JavaScript封装拖拽对象;


通过扩展jQuery来实现拖拽对象。


本文的例子会放置于codepen.io[1]中,供大家在阅读时直接查看。如果对于codepen不了解的同学,可以花点时间稍微了解一下。


拖拽的实现过程会涉及到非常多的实用小知识,因此为了巩固我自己的知识积累,也为了大家能够学到更多的知识,我会尽量详细的将一些细节分享出来,相信大家认真阅读之后,一定能学到一些东西。


1、如何让一个DOM元素动起来


我们常常会通过修改元素的top,left,translate来其的位置发生改变。在下面的例子中,每点击一次按钮,对应的元素就会移动5px。大家可点击查看。


点击查看一个让元素动起来的小例子[2]


由于修改一个元素top/left值会引起页面重绘,而translate不会,因此从性能优化上来判断,我们会优先使用translate属性。


2、如何获取当前浏览器支持的transform兼容写法


transform是css3的属性,当我们使用它时就不得不面对兼容性的问题。不同版本浏览器的兼容写法大致有如下几种:


['transform', 'webkitTransform', 'MozTransform', 'msTransform', 'OTransform']


因此我们需要判断当前浏览器环境支持的transform属性是哪一种,方法如下:


// 获取当前浏览器支持的transform兼容写法
function getTransform() {
  var transform = '',
    divStyle = document.createElement('div').style,
    // 可能涉及到的几种兼容性写法,通过循环找出浏览器识别的那一个
    transformArr = ['transform', 'webkitTransform', 'MozTransform', 'msTransform', 'OTransform'],
    i = 0,
    len = transformArr.length;
  for (; i < len; i++) {
    if (transformArr[i] in divStyle) {
      // 找到之后立即返回,结束函数
      return transform = transformArr[i];
    }
  }
  // 如果没有找到,就直接返回空字符串
  return transform;
}


该方法用于获取浏览器支持的transform属性。如果返回的为空字符串,则表示当前浏览器并不支持transform,这个时候我们就需要使用left,top值来改变元素的位置。如果支持,就改变transform的值。


3、 如何获取元素的初始位置


我们首先需要获取到目标元素的初始位置,因此这里需要一个专门用来获取元素样式的功能函数。


但是获取元素样式在IE浏览器与其他浏览器有一些不同,我们需要一个兼容性的写法。


function getStyle(elem, property) {
  // ie通过currentStyle来获取元素的样式,其他浏览器通过getComputedStyle来获取
  return document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(elem, false)[property] : elem.currentStyle[property];
}


有了这个方法之后,就可以开始动手写获取目标元素初始位置的方法了。


function getTargetPos(elem) {
  var pos = { x: 0, y: 0 };
  var transform = getTransform();
  if (transform) {
    var transformValue = getStyle(elem, transform);
    if (transformValue == 'none') {
      elem.style[transform] = 'translate(0, 0)';
      return pos;
    } else {
      var temp = transformValue.match(/-?\d+/g);
      return pos = {
        x: parseInt(temp[4].trim()),
        y: parseInt(temp[5].trim())
      }
    }
  } else {
    if (getStyle(elem, 'position') == 'static') {
      elem.style.position = 'relative';
      return pos;
    } else {
      var x = parseInt(getStyle(elem, 'left') ? getStyle(elem, 'left') : 0);
      var y = parseInt(getStyle(elem, 'top') ? getStyle(elem, 'top') : 0);
      return pos = {
        x: x,
        y: y
      }
    }
  }
}


在拖拽过程中,我们需要不停的设置目标元素的新位置,这样它才会移动起来,因此我们需要一个设置目标元素位置的方法。


// pos = { x: 200, y: 100 }
function setTargetPos(elem, pos) {
  var transform = getTransform();
  if (transform) {
    elem.style[transform] = 'translate(' + pos.x + 'px, ' + pos.y + 'px)';
  } else {
    elem.style.left = pos.x + 'px';
    elem.style.top = pos.y + 'px';
  }
  return elem;
}


4、我们需要用到哪些事件?


在pc上的浏览器中,结合mousedown、mousemove、mouseup这三个事件可以帮助我们实现拖拽。


mousedown 鼠标按下时触发mousemove 鼠标按下后拖动时触发mouseup 鼠标松开时触发


而在移动端,分别与之对应的则是touchstart、touchmove、touchend


当我们将元素绑定这些事件时,有一个事件对象将会作为参数传递给回调函数,通过事件对象,我们可以获取到当前鼠标的精确位置,鼠标位置信息是实现拖拽的关键。


事件对象十分重要,其中包含了非常多的有用的信息,这里我就不扩展了,大家可以在函数中将事件对象打印出来查看其中的具体属性,这个方法对于记不清事件对象重要属性的童鞋非常有用。


5、拖拽的原理


当事件触发时,我们可以通过事件对象获取到鼠标的精切位置。这是实现拖拽的关键。当鼠标按下(mousedown触发)时,我们需要记住鼠标的初始位置与目标元素的初始位置,我们的目标就是实现当鼠标移动时,目标元素也跟着移动,根据常理我们可以得出如下关系:


移动后的鼠标位置 - 鼠标初始位置 = 移动后的目标元素位置 - 目标元素的初始位置


如果鼠标位置的差值我们用dis来表示,那么目标元素的位置就等于:


移动后目标元素的位置 = dis + 目标元素的初始位置


通过事件对象,我们可以精确的知道鼠标的当前位置,因此当鼠标拖动(mousemove)时,我们可以不停的计算出鼠标移动的差值,以此来求出目标元素的当前位置。这个过程,就实现了拖拽。


而在鼠标松开(mouseup)结束拖拽时,我们需要处理一些收尾工作。详情见代码。


6、 我又来推荐思维导图辅助写代码了


常常有新人朋友跑来问我,如果逻辑思维能力不强,能不能写代码做前端。我的答案是:能。因为借助思维导图,可以很轻松的弥补逻辑的短板。而且比在自己头脑中脑补逻辑更加清晰明了,不易出错。


上面第六点我介绍了原理,因此如何做就显得不是那么难了,而具体的步骤,则在下面的思维导图中明确给出,我们只需要按照这个步骤来写代码即可,试试看,一定很轻松。


微信图片_20220510231442.jpg


相关文章
|
11月前
Threejs入门进阶实战案例(6):对象的通用属性/函数
Threejs入门进阶实战案例(6):对象的通用属性/函数
43 0
|
3月前
|
设计模式 存储
二十三种设计模式全面解析-揭秘访问者模式:开启对象间灵活交互之门
二十三种设计模式全面解析-揭秘访问者模式:开启对象间灵活交互之门
|
10月前
|
设计模式 安全 C++
C++进阶 特殊类的设计
C++进阶 特殊类的设计
33 0
|
前端开发
前端学习笔记202305学习笔记第二十二天-分页组件封装和复用1
前端学习笔记202305学习笔记第二十二天-分页组件封装和复用1
36 0
|
前端开发
前端学习笔记202305学习笔记第二十二天-分页组件封装和复用2
前端学习笔记202305学习笔记第二十二天-分页组件封装和复用2
33 0
|
前端开发
前端学习笔记202305学习笔记第二十二天-表格数据方法封装
前端学习笔记202305学习笔记第二十二天-表格数据方法封装
51 0
|
前端开发
前端学习笔记202305学习笔记第二十二天-表格数据方法封装2
前端学习笔记202305学习笔记第二十二天-表格数据方法封装2
54 0
|
Java
Java面向对象(20)--接口
Java面向对象(20)--接口
93 2
|
Java
Java面向对象(4)--封装和隐藏
Java面向对象(4)--封装和隐藏
115 0
Java面向对象(4)--封装和隐藏