Home  >  Article  >  Web Front-end  >  Detailed explanation of custom Vue instruction v-selectable similar to jQuery UI Selectable

Detailed explanation of custom Vue instruction v-selectable similar to jQuery UI Selectable

小云云
小云云Original
2018-05-17 16:37:032500browse

This article mainly introduces the relevant information about customizing the Vue instruction v-selectable similar to jQuery UI Selectable. Friends in need can refer to it. I hope it can help everyone.

Without further ado, let’s look at the effect first.

In fact, it is a function that allows you to hold down the mouse to select items in an area. I believe everyone who has used Jquery UI knows that this is a selectable function. However, if we use Vue There is no similar plug-in for development. Of course, you can still use jquery directly, but I don’t want to introduce jquery and jquery UI into my project, so I tried to implement similar functions myself.

There are two steps to achieve this function. The first step is to implement the function of mouse selection area, and the second step is to add an active class to the selected item in this area.

Let’s first look at how to draw a dotted frame by holding down the mouse. The idea is to first change the positioning of the container element to relative and then determine when the mouse is pressed (mousedown) and remember the position of the click point (e .layerX, e.layerY), and then monitor the mouse position (e.layerX, e.layerY) in real time when the mouse moves (mousemove). With these two positions, you can dynamically create a p and its positioning For absolute, then add it to the container box, and clear the previous box each time. Why use e.layerX e.layerY?

layerX layerY

If the position style of the element is not the default static, we say this Elements have positioning attributes.

Find the nearest element with positioning attribute among the element that currently triggers the mouse event and its ancestor element, calculate the offset value of the mouse to it, and find the diplomatic point of the upper left corner of the element's border as the relative point . If an element with a positioning attribute is not found, the offset is calculated relative to the current page, which is equivalent to pageY. Complete the following code according to this idea:

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('p')
          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
  })
}

At this time, you can achieve the effect of drawing a dotted box

The next step is how to set each item to be selected state. The idea is to traverse all the child elements li of this container ul, and then determine whether each li is inside the selected box. Then look at the offsetLeft and offsetTop of each element to calculate the element's position relative to the parent element, and then determine the width and height of the child element through getBoundingClientRect().height and getBoundingClientRect().width. These can calculate the position and size of the element, and then how to determine whether the element is within the selection area? My rule is that if any of the four corners of this element is within the selection area or the selection area is inside this area, the element is considered selected (this judgment method does not feel perfect). Following this idea, continue to complete our code:

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('p')
   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(&#39;active&#39;)
   }else{
   children[i].classList.remove(&#39;active&#39;)
   }
  }
  }
 })
 ele.addEventListener(&#39;mouseup&#39;, function(e) {
  isMouseDown = false
  if(areaSelect && areaSelect.childNodes && ele.contains(areaSelect)){
  ele.removeChild(areaSelect)
  }
  areaSelect = null
 })
 }
 Vue.directive(&#39;selectable&#39;,{
 inserted:listener,
 updated:listener
 })
}

After completion, let’s see how to use it, html structure:

<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>

Note that the v-selectable of ul is our custom instruction, but Before using it, you must Vue.use

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

Add some style to our ul li. Note that our selected item will be added with an active class. Use this to change the style of the selected item

<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>

This will achieve the initial effect. In fact, there are still many small bugs in the running process of the code. This article only provides a simple idea and code. You can modify the code and add more functions by yourself. If you don’t understand why this custom instruction is written like this, you can refer to my other article on customizing the lazy loading image plug-in v-lazyload.

Related recommendations:

Yii Framework Development Tutorial Zii Component-Selectable Example_PHP Tutorial

##Yii Framework Development Tutorial Zii Component-Selectable Example

selectable

The above is the detailed content of Detailed explanation of custom Vue instruction v-selectable similar to jQuery UI Selectable. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn