自定义类似于jQuery UI Selectable 的Vue指令v-selectable


Posted in jQuery onAugust 23, 2017

话不多说,先看效果。

自定义类似于jQuery UI Selectable 的Vue指令v-selectable

其实就是一个可以按住鼠标进行一个区域内条目选择的功能,相信用过Jquery UI 的都知道这是selectable的功能,然而我们如果用Vue开发的话没有类似的插件,当然你仍然可以把jquery的拿过来直接用,但是我又不想引入jquery 和 jquery UI在我的项目中,于是我就自己尝试着实现类似的功能。

要实现这个功能分两步。第一步是实现鼠标选择区域的功能,第步部是把这个区域内被选择的item添加一个active的类。

先看如何实现按住鼠标画虚线框,思路是先把容器元素的定位改为relative 然后判断当鼠标按下(mousedown)的时候,进行记住这个点击点的位置(e.layerX , e.layerY),然后鼠标移动(mousemove)的时候,实时的监测鼠标的位置(e.layerX , e.layerY),有了这两个位置就可以动态的创建一个div,它的定位为absolute,然后把它添加的容器框里,并且每次清空前一个框就可以了。为什么是用e.layerX e.layerY呢,

layerX layerY

         如果元素的position样式不是默认的static,我们说这个元素具有定位属性。

         在当前触发鼠标事件的元素和它的祖先元素中找到最近的具有定位属性的元素,计算鼠标与其的偏移值,以找到元素的border的左上角的外交点作为相对点。如果找不到具有定位属性的元素,那么就相对于当前页面计算偏移,此时等同于pageY。按照这个思路完成以下代码:

export default (Vue, options = {}) =>{
  const listener = (ele, binding) =>{
    let reactArea = {
      startX: 0,
      startY: 0,
      endX: 0,
      endY: 0
    }
    //是否一直按下鼠标
    let isMouseDown = false
    let areaSelect = {}
    //将元素定位改为relative
    ele.style.position = 'relative'
    ele.addEventListener('mousedown', function(e) {
      reactArea.startX = e.layerX;
      reactArea.startY = e.layerY;
      isMouseDown = true
    })
    ele.addEventListener('mousemove', function(e) {
      if(isMouseDown){
        let preArea = ele.getElementsByClassName('v-selected-area')
        if(preArea.length){
          ele.removeChild(preArea[0])
        }
        reactArea.endX = e.layerX
        reactArea.endY = e.layerY
        let leftValue = 0
        let topValue = 0
        let widthValue = Math.abs(reactArea.startX - reactArea.endX)
        let heightValue = Math.abs(reactArea.startY - reactArea.endY)
        if(reactArea.startX >= reactArea.endX){
          leftValue = reactArea.endX
        }else{
          leftValue = reactArea.startX
        }
        if(reactArea.startY > reactArea.endY ){
          topValue = reactArea.endY
        }else{
          topValue = reactArea.startY
        }
        //判断同时有宽高才开始画虚线框
        if(reactArea.startX != reactArea.endX && reactArea.startY !=reactArea.endY){
          areaSelect = document.createElement('div')
          areaSelect.classList.add("v-selected-area")
          areaSelect.style.position = "absolute";
          areaSelect.style.left = leftValue + 'px'
          areaSelect.style.top = topValue + 'px'
          areaSelect.style.width = widthValue + 'px'
          areaSelect.style.height = heightValue + 'px'
          areaSelect.style.border = "1px dashed grey"
          ele.append(areaSelect)
        }
      }
    })
    ele.addEventListener('mouseup', function(e) {
      isMouseDown = false
      //每次鼠标点击完了areaSelect
      if(areaSelect && areaSelect.childNodes && ele.contains(areaSelect)){
        ele.removeChild(areaSelect)
      }
      areaSelect = null
    })
  }
   Vue.directive('selectable',{
    inserted:listener,
    updated:listener
  })
}

这个时就可以实现画虚线框的效果

自定义类似于jQuery UI Selectable 的Vue指令v-selectable

下一步是如何把每个item置为选中状态。思路是遍历这个容器ul 的所有子元素li ,然后判断每个li是否在选中的框内部。然后看每个元素的offsetLeft 和 offsetTop 计算元素相对于父元素的位置,然后通过getBoundingClientRect().height 和 getBoundingClientRect().width 确定子元素的宽高。这些就可以计算出元素的位置和大小了,然后如何判断这个元素是否在选择区域内呢?我的规则是这个元素的四个角位置有任何一个在选择区域内或者选择区域就在这个区域的内部,就算是这个元素被选中了(这个判断方式感觉不是很完美)。按照这个思路,继续完成我们的代码:

export default (Vue, options = {}) =>{
 const listener = (ele, binding) =>{
 let reactArea = {
  startX: 0,
  startY: 0,
  endX: 0,
  endY: 0
 }
 //是否一直按下鼠标
 let isMouseDown = false
 let areaSelect = {}
 //将元素定位改为relative
 ele.style.position = 'relative'
 ele.addEventListener('mousedown', function(e) {
  reactArea.startX = e.layerX;
  reactArea.startY = e.layerY;
  isMouseDown = true
 })
 ele.addEventListener('mousemove', function(e) {
  if(isMouseDown){
   let preArea = ele.getElementsByClassName('v-selected-area')
  if(preArea.length){
   ele.removeChild(preArea[0])
  }
  reactArea.endX = e.layerX
  reactArea.endY = e.layerY
  let leftValue = 0
  let topValue = 0
  let widthValue = Math.abs(reactArea.startX - reactArea.endX)
  let heightValue = Math.abs(reactArea.startY - reactArea.endY)
  if(reactArea.startX >= reactArea.endX){
   leftValue = reactArea.endX
  }else{
   leftValue = reactArea.startX
  }
  if(reactArea.startY > reactArea.endY ){
   topValue = reactArea.endY
  }else{
   topValue = reactArea.startY
  }
  //判断同时有宽高才开始画虚线框
  if(reactArea.startX != reactArea.endX && reactArea.startY !=reactArea.endY){
   areaSelect = document.createElement('div')
   areaSelect.classList.add("v-selected-area")
   areaSelect.style.position = "absolute";
   areaSelect.style.left = leftValue + 'px'
   areaSelect.style.top = topValue + 'px'
   areaSelect.style.width = widthValue + 'px'
   areaSelect.style.height = heightValue + 'px'
   areaSelect.style.border = "1px dashed grey"
   ele.append(areaSelect)
  }
  let children = ele.getElementsByTagName('li')
  for(let i =0 ; i < children.length ; i ++ ){
   let childrenHeight = children[i].getBoundingClientRect().height
   let childrenWidth = children[i].getBoundingClientRect().width
   //每个li元素的位置
   let offsetLeft = children[i].offsetLeft
   let offsetTop = children[i].offsetTop
   //每个li元素的宽高
   let endPositionH = childrenHeight + offsetTop
   let endPositionW = childrenWidth + offsetLeft
   //五个条件满足一个就可以判断被选择
   //一是右下角在选择区域内
   let require1 = endPositionH > topValue && endPositionW > leftValue && endPositionH < topValue + heightValue && endPositionW < leftValue + widthValue
   //二是左上角在选择区域内
   let require2 = offsetTop > topValue && offsetLeft > leftValue && offsetTop < topValue + heightValue && offsetLeft < leftValue + widthValue
   //三是右上角在选择区域内
   let require3 = offsetTop > topValue && offsetLeft + childrenWidth > leftValue && offsetTop < topValue + heightValue && offsetLeft + childrenWidth< leftValue + widthValue
   //四是左下角在选择区域内
   let require4 = offsetTop + childrenHeight > topValue && offsetLeft > leftValue && offsetTop + childrenHeight < topValue + heightValue && offsetLeft < leftValue + widthValue
   //五选择区域在元素体内
   let require5 = offsetTop < topValue && offsetLeft < leftValue && offsetTop + childrenHeight > topValue + heightValue && offsetLeft + childrenWidth > leftValue + widthValue
   if(require1 || require2 || require3 || require4 || require5){
   children[i].classList.add('active')
   }else{
   children[i].classList.remove('active')
   }
  }
  }
 })
 ele.addEventListener('mouseup', function(e) {
  isMouseDown = false
  if(areaSelect && areaSelect.childNodes && ele.contains(areaSelect)){
  ele.removeChild(areaSelect)
  }
  areaSelect = null
 })
 }
 Vue.directive('selectable',{
 inserted:listener,
 updated:listener
 })
}

完成之后再看看如何使用,html 结构:

<ul v-selectable >
<li class="square">
 item1

</li>

<li class="oval">
 item2

</li>

<li class="triangle">
 item3

</li>

<li class="triangle-topleft">
 item4

</li>

<li class="curvedarrow">
 item5

</li>

<li class="triangle-topleft">
 item6

</li>
</ul>

注意ul的这个v-selectable就是我们自定义的指令,但是使用之前必须 Vue.use

import Vue from 'vue'
import Selectable from '@/components/vue-selectable/vue-selectable.js' //这个修改为你的js路径
Vue.use(Selectable);

再给我们的ul li 加点样式,注意我们的被选择项会被添加一个active的class,通过这个来改变选中项样式

<style scoped>
 ul{
 margin: 40px 40px 40px 40px;
 border: 1px solid red;
 width: 300px;
 padding-bottom: 20px;
 }
 ul li {
 width: 200px;
 height: 30px;
 list-style: none;
 border: 1px solid black;
 margin-left: 10px;
 margin-top: 30px;
 text-align: center;
 line-height: 30px;
 user-select:none;
 }
 ul li.active{
 background-color: red;
 }
</style>

这样就可以达到开头的效果了。实际上代码运行过程中还是有许多小bug的,本文只是提供了一个简单的思路和代码,更多功能可以自己修改代码进行添加。如果不明白这个自定义指令为什么是这样的写法,可以参考我的另一篇文章自定义懒加载图片插件v-lazyload。

总结

以上所述是小编给大家介绍的自定义类似于jQuery UI Selectable 的Vue指令v-selectable,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

jQuery 相关文章推荐
jQuery树插件zTree使用方法详解
May 02 jQuery
jQuery.Ajax()的data参数类型详解
Jul 23 jQuery
深入研究jQuery图片懒加载 lazyload.js使用方法
Aug 16 jQuery
JavaScript实现离开页面前提示功能【附jQuery实现方法】
Sep 26 jQuery
jQuery ajax调用webservice注意事项
Oct 08 jQuery
jquery在启动页面时,自动加载数据的实例
Jan 22 jQuery
jQuery与vue实现拖动验证码功能
Jan 30 jQuery
jQuery实现的点击标题文字切换字体效果示例【测试可用】
Apr 26 jQuery
jQuery插件实现弹性运动完整示例
Jul 07 jQuery
jQuery实现获取及设置CSS样式操作详解
Sep 05 jQuery
jQuery序列化form表单数据为JSON对象的实现方法
Sep 20 jQuery
如何用webpack4.0撸单页/多页脚手架 (jquery, react, vue, typescript)
Jun 18 jQuery
jquery.rotate.js实现可选抽奖次数和中奖内容的转盘抽奖代码
Aug 23 #jQuery
简单实现jQuery上传图片显示预览功能
Jun 29 #jQuery
关于JS与jQuery中的文档加载问题
Aug 22 #jQuery
jQuery插件DataTables分页开发心得体会
Aug 22 #jQuery
jQuery实现广告条滚动效果
Aug 22 #jQuery
基于jQuery的表单填充实例
Aug 22 #jQuery
使用jQuery实现简单的tab框实例
Aug 22 #jQuery
You might like
教你如何用php实现LOL数据远程获取
2014/06/10 PHP
ThinkPHP3.1新特性之多数据库操作更加完善
2014/06/19 PHP
ThinkPHP采用GET方式获取中文参数查询无结果的解决方法
2014/06/26 PHP
分享最受欢迎的5款PHP框架
2014/11/27 PHP
浅谈PHP中的
2016/04/23 PHP
PHP之图片上传类实例代码(加了缩略图)
2016/06/30 PHP
PHP如何实现订单的延时处理详解
2017/12/30 PHP
JavaScript经典效果集锦
2010/07/06 Javascript
js getBoundingClientRect() 来获取页面元素的位置
2010/11/25 Javascript
javascript 闭包疑问
2010/12/30 Javascript
简单的Jquery全选功能
2013/11/07 Javascript
js中字符串编码函数escape()、encodeURI()、encodeURIComponent()区别详解
2016/04/01 Javascript
深入理解JavaScript 函数
2016/06/06 Javascript
Jquery ajax请求导出Excel表格的实现代码
2016/06/08 Javascript
简单的js表格操作
2016/09/24 Javascript
JavaScript 函数节流详解及方法总结
2017/02/09 Javascript
微信小程序之获取当前位置经纬度以及地图显示详解
2017/05/09 Javascript
webpack打包nodejs项目的方法
2018/09/26 NodeJs
angular中的post请求处理示例详解
2020/06/30 Javascript
Vue实现穿梭框效果
2020/09/30 Javascript
Vue实现购物小球抛物线的方法实例
2020/11/22 Vue.js
使用Python编写一个最基础的代码解释器的要点解析
2016/07/12 Python
小米5s微信跳一跳小程序python源码
2018/01/08 Python
python 产生token及token验证的方法
2018/12/26 Python
Python魔法方法 容器部方法详解
2020/01/02 Python
浏览器实现移动端高性能css3动画(开启gpu加速)
2013/12/23 HTML / CSS
Bibloo罗马尼亚网站:女装、男装、童装及鞋子和配饰
2019/07/20 全球购物
电大毕业个人生自我鉴定
2014/03/26 职场文书
我的老师教学反思
2014/05/01 职场文书
小学学校门卫岗位职责
2014/08/03 职场文书
交通事故案件代理词
2015/05/23 职场文书
2015国庆节感想
2015/08/04 职场文书
公司车队管理制度
2015/08/04 职场文书
医疗纠纷调解协议书
2015/08/06 职场文书
2016暑期师德培训心得体会
2016/01/09 职场文书
Nginx图片服务器配置之后图片访问404的问题解决
2022/03/21 Servers