自定义类似于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解析获取JSON数据
Apr 08 jQuery
jquery网页加载进度条的实现
Jun 01 jQuery
jQuery.form.js的使用详解
Jun 14 jQuery
javascript+jQuery实现360开机时间显示效果
Nov 03 jQuery
jquery实现企业定位式导航效果
Jan 01 jQuery
jQuery md5加密插件jQuery.md5.js用法示例
Aug 24 jQuery
js jquery 获取某一元素到浏览器顶端的距离实现方法
Sep 05 jQuery
jQuery实现的模仿雨滴下落动画效果
Dec 11 jQuery
jQuery实现条件搜索查询、实时取值及升降序排序的方法分析
May 04 jQuery
JQuery特殊效果和链式调用操作示例
May 13 jQuery
jQuery+css实现的点击图片放大缩小预览功能示例【图片预览 查看大图】
May 29 jQuery
如何在vue 中引入使用jquery
Nov 10 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
laravel项目利用twemproxy部署redis集群的完整步骤
2018/05/11 PHP
PHP面向对象程序设计内置标准类,普通数据类型转为对象类型示例
2019/06/12 PHP
学习YUI.Ext第五日--做拖放Darg&amp;Drop
2007/03/10 Javascript
判断对象是否Window的实现代码
2012/01/10 Javascript
调试Javascript代码(浏览器F12及VS中debugger关键字)
2013/01/25 Javascript
解决JS中乘法的浮点错误的方法
2014/01/03 Javascript
node.js中的events.EventEmitter.listenerCount方法使用说明
2014/12/08 Javascript
js实现上一页下一页的效果【附代码】
2016/03/10 Javascript
第一次接触JS require.js模块化工具
2016/04/17 Javascript
jQuery自制提示框tooltip改进版
2016/08/01 Javascript
深入理解JavaScript创建对象的多种方式以及优缺点
2017/06/01 Javascript
超级简易的JS计算器实例讲解(实现加减乘除)
2017/08/08 Javascript
vue组件实现文字居中对齐的方法
2017/08/23 Javascript
django使用channels2.x实现实时通讯
2018/11/28 Javascript
js逆向解密之网络爬虫
2019/05/30 Javascript
使用apifm-wxapi快速开发小程序过程详解
2019/08/05 Javascript
解决Idea、WebStorm下使用Vue cli脚手架项目无法使用Webpack别名的问题
2019/10/11 Javascript
vue 组件内获取actions的response方式
2019/11/08 Javascript
mapboxgl实现带箭头轨迹线的代码
2021/01/04 Javascript
python实现的简单文本类游戏实例
2015/04/28 Python
python3 实现的人人影视网站自动签到
2016/06/19 Python
Python3.5局部变量与全局变量作用域实例分析
2019/04/30 Python
Python中面向对象你应该知道的一下知识
2019/07/10 Python
django使用admin站点上传图片的实例
2019/07/28 Python
Python 基于FIR实现Hilbert滤波器求信号包络详解
2020/02/26 Python
Python基础类继承重写实现原理解析
2020/04/03 Python
Python HTMLTestRunner可视化报告实现过程解析
2020/04/10 Python
国际商务系学生个人的自我评价
2013/11/26 职场文书
行政管理毕业生自荐信
2014/02/24 职场文书
2014年租房协议书范本
2014/10/30 职场文书
党员个人承诺书
2015/04/27 职场文书
企业党支部工作总结2015
2015/05/21 职场文书
房产遗嘱范本
2015/08/06 职场文书
python获取淘宝服务器时间的代码示例
2021/04/22 Python
css中z-index: 0和z-index: auto的区别
2021/08/23 HTML / CSS
JMeter对MySQL数据库进行压力测试的实现步骤
2022/01/22 MySQL