自定义类似于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 append与appendTo方法比较
May 24 jQuery
jQuery事件_动力节点Java学院整理
Jul 05 jQuery
jquery插件canvaspercent.js实现百分比圆饼效果
Jul 18 jQuery
jQuery选择器之子元素过滤选择器
Sep 28 jQuery
jQuery创建及操作xml格式数据示例
May 26 jQuery
JQuery搜索框自动补全(模糊匹配)功能实现示例
Jan 08 jQuery
jQuery事件多次绑定与解绑问题实例分析
Feb 19 jQuery
jquery+css实现Tab栏切换的代码实例
May 14 jQuery
Jquery 动态添加元素并添加点击事件实现过程解析
Oct 12 jQuery
jQuery实现全选、反选和不选功能的方法详解
Dec 04 jQuery
jQuery实现简单评论功能
Aug 19 jQuery
jQuery实现鼠标拖拽登录框移动效果
Sep 13 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 中常量的知识整理
2017/04/14 PHP
Javascript实例教程(19) 使用HoTMetal(1)
2006/12/23 Javascript
Jquery 扩展方法
2010/05/06 Javascript
JavaScript在IE和Firefox浏览器下的7个差异兼容写法小结
2010/06/18 Javascript
通过继承IHttpHandle实现JS插件的组织与管理
2010/07/13 Javascript
js实现两个值相加alert出来精确到指定位
2013/09/25 Javascript
Query中click(),bind(),live(),delegate()的区别
2013/11/19 Javascript
JS实现两个大数(整数)相乘
2014/04/28 Javascript
jquery插件jSignature实现手动签名
2015/05/04 Javascript
如何利用AngularJS打造一款简单Web应用
2015/12/05 Javascript
javascript中arguments,callee,caller详解
2016/03/16 Javascript
Angularjs 动态改变title标题(兼容ios)
2016/12/29 Javascript
Node.js连接mongodb实例代码
2017/06/06 Javascript
vue教程之toast弹框全局调用示例详解
2020/08/24 Javascript
Webpack优化配置缩小文件搜索范围
2017/12/25 Javascript
webpack打包并将文件加载到指定的位置方法
2018/02/22 Javascript
弱类型语言javascript开发中的一些坑实例小结【变量、函数、数组、对象、作用域等】
2019/08/07 Javascript
JavaScript中的全局属性与方法深入解析
2020/06/14 Javascript
swiper实现导航滚动效果
2020/12/13 Javascript
[49:31]TFT vs Mski Supermajor小组赛C组 BO3 第一场 6.3
2018/06/04 DOTA
Python聊天室实例程序分享
2016/01/05 Python
Python中的列表生成式与生成器学习教程
2016/03/13 Python
python 时间戳与格式化时间的转化实现代码
2016/03/23 Python
python实现发送邮件功能
2017/07/22 Python
Django+Ajax+jQuery实现网页动态更新的实例
2018/05/28 Python
Python matplotlib学习笔记之坐标轴范围
2019/06/28 Python
wxPython+Matplotlib绘制折线图表
2019/11/19 Python
利用Tensorflow的队列多线程读取数据方式
2020/02/05 Python
荟萃全球保健品:维他购
2018/05/09 全球购物
上课迟到检讨书100字
2014/01/11 职场文书
工程质量承诺书范文
2014/03/27 职场文书
小学毕业典礼演讲稿
2014/09/09 职场文书
“5.12”护士节主持词
2015/07/04 职场文书
2016年记者节感言
2015/12/08 职场文书
就业指导讲座心得体会
2016/01/15 职场文书
环境保护宣传标语大全!
2019/06/28 职场文书