自定义类似于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 相关文章推荐
JavaScript使用链式方法封装jQuery中CSS()方法示例
Apr 07 jQuery
jQuery Tree Multiselect使用详解
May 02 jQuery
基于jquery实现多级菜单效果
Jul 25 jQuery
深入研究jQuery图片懒加载 lazyload.js使用方法
Aug 16 jQuery
jQuery菜单实例(全选,反选,取消)
Aug 28 jQuery
详解jQuery中的isPlainObject()使用方法
Feb 27 jQuery
jQuery实现新闻播报滚动及淡入淡出效果示例
Mar 23 jQuery
jQuery实现百度图片移入移出内容提示框上下左右移动的效果
Jun 05 jQuery
jQuery实现的老虎机跑动效果示例
Dec 29 jQuery
jQuery实现适用于移动端的跑马灯抽奖特效示例
Jan 18 jQuery
jQuery事件blur()方法的使用实例讲解
Mar 30 jQuery
jQuery实现动态加载(按需加载)javascript文件的方法分析
May 31 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安装扩展mcrypt以及相关依赖项(PHP安装PECL扩展的方法)
2013/07/05 PHP
php多维数组去掉重复值示例分享
2014/03/02 PHP
php实现httpclient类示例
2014/04/08 PHP
ThinkPHP3.1之D方法实例详解
2014/06/20 PHP
php图片上传类 附调用方法
2016/05/15 PHP
$()JS小技巧
2007/07/21 Javascript
JavaScript 判断指定字符串是否为有效数字
2010/05/11 Javascript
function foo的原型与prototype属性解惑
2010/11/19 Javascript
js解析与序列化json数据(二)序列化探讨
2013/02/01 Javascript
中文路径导致unitpngfix.js不正常的解决方法
2013/06/26 Javascript
js编写trim()函数及正则表达式的运用
2013/10/24 Javascript
JavaScript使表单中的内容显示在屏幕上的方法
2015/06/29 Javascript
JavaScript判断对象是否为数组
2015/12/22 Javascript
Web纯前端“旭日图”实现元素周期表
2017/03/10 Javascript
微信小程序表单验证WxValidate的使用
2019/11/27 Javascript
八种Vue组件间通讯方式合集(推荐)
2020/08/18 Javascript
JavaScript封装单向链表的示例代码
2020/09/17 Javascript
python读写ini文件示例(python读写文件)
2014/03/25 Python
理解Python中的With语句
2015/02/02 Python
Python多线程、异步+多进程爬虫实现代码
2016/02/17 Python
在python下使用tensorflow判断是否存在文件夹的实例
2019/06/10 Python
python实现知乎高颜值图片爬取
2019/08/12 Python
Python实现TCP探测目标服务路由轨迹的原理与方法详解
2019/09/04 Python
python的json中方法及jsonpath模块用法分析
2019/12/06 Python
python打包多类型文件的操作方法
2020/09/21 Python
HTML5自定义视频播放器源码
2020/01/06 HTML / CSS
ghd澳大利亚官方网站:英国最受欢迎的美发工具品牌
2018/05/21 全球购物
迪斯尼假期(欧洲、中东及非洲):Disney Holidays EMEA
2021/02/15 全球购物
招商经理岗位职责
2013/11/16 职场文书
中式结婚主持词
2014/03/14 职场文书
大学毕业寄语大全
2014/04/10 职场文书
三字经教学反思
2014/04/26 职场文书
同步小康驻村工作简报
2015/07/20 职场文书
决心书格式范文
2015/09/23 职场文书
MySQL之PXC集群搭建的方法步骤
2021/05/25 MySQL
python基础之文件操作
2021/10/24 Python