Vue custom instructions to achieve pop-up window drag four-side stretching and diagonal stretching effect

Vue custom instructions to achieve pop-up window drag four-side stretching and diagonal stretching effect

introduction

The company's recent Vue front-end project requirements: realize the dragging of pop-up windows, four-side stretching and diagonal stretching, as well as pop-up window boundary processing. I used vue's custom instructions to write the drag.js file to share with everyone to learn together. The following code is a schematic demo I extracted for reference only. This is my first technical sharing as a front-end newbie. If there are any mistakes, please criticize and correct me!

Page Layout

<template>
  <div
    class="parameter"
    v-dialogDrag
  >
    <div class="title">Title<div class="close">
        <img
          src="../assets/close.png"
          alt=""
        >
      </div>
    </div>
    <div class="content">Content area</div>
  </div>
</template>
<style lang="less">
.parameter {
  height: 569px;
  width: 960px;
  position: absolute;
  left: 50%;
  top: 50%;
  margin-left: calc(-960px / 2);
  margin-top: calc(-569px / 2);
  z-index: 999;
  background: #fff;
  box-sizing: border-box;
  box-shadow: 0px 12px 32px 0px rgba(0, 0, 0, 0.08);
  border-radius: 2px;
  .title {
    display: flex;
    font-size: 16px;
    height: 48px;
    line-height: 48px;
    background: #f5f5f5;
    box-sizing: border-box;
    box-shadow: inset 0px -1px 0px rgba(0, 0, 0, 0.12);
    border-radius: 2px 2px 0px 0px;
    padding: 0 20px;
    z-index: 99;
    font-size: 16px;
    font-weight: 500;
    color: rgba(0, 0, 0, 0.85);
    .close {
      img {
        width: 10px;
      }
      margin-left: auto; // right alignment}
  }
  .content {
    display: flex;
    justify-content: center;
    align-items: center;
    height: calc(100% - 48px);
    box-sizing: border-box;
    background: #fff;
    overflow:auto;
  }
}
</style>

The actual effect of the page layout is as follows:

insert image description here

drag.js file

You can import the drag.js file globally in main.js, or you can import it separately in the pop-up component to see if there are other usage scenarios.

Project directory screenshot

insert image description here

main.js globally imports drag.js

import Vue from 'vue'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue'
import '../drag.js'

Vue.config.productionTip = false
Vue.use(ElementUI);

new Vue({
  render: h => h(App),
}).$mount('#app')

Pop-up window drag implementation and boundary restrictions

import Vue from 'vue'
// v-dialogDrag: pop-up window drag + horizontal stretch + diagonal stretch Vue.directive('dialogDrag', {
  bind(el) {
  	// dialogHeaderEl is the title bar, bind the mousedown event for dragging const dialogHeaderEl = el.querySelector('.title')
   	// dragDom is the dom element bound to the instruction. Define variables to facilitate distinction const dragDom = el
    // Get all CSS attributes compatibility writing method ie dom element.currentStyle firefox google window.getComputedStyle(dom element, null);
    const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null) 
    // Define mouse press event const moveDown = e => {
      // e.clientX, Y: X, Y coordinates of the mouse relative to the browser's visible window // offsetTop, offsetLeft: The distance of the current element relative to the top and left of its offsetParent element. Here, title has no positioning offset, so it is 0	
      : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : :

      // For compatibility with ie
      if (styL === 'auto') styL = '0px'
      let styT = sty.top

      // Note that in IE, the first value obtained is the component's own 50% and the value is assigned to px after moving
      if (sty.left.includes('%')) {
        styL = +document.body.clientWidth * (+styL.replace(/%/g, '') / 100)
        styT = +document.body.clientHeight * (+styT.replace(/%/g, '') / 100)
      } else {
        styL = +styL.replace(/\px/g, '')
        styT = +styT.replace(/\px/g, '')
      }   

      document.onmousemove = function (e) {
        // Calculate the moving distance through event delegation let left = e.clientX - disX
        let top = e.clientY - disY

        // Boundary processing if (-(left) > minDragDomLeft) {
          left = -(minDragDomLeft)
        } else if (left > maxDragDomLeft) {
          left = maxDragDomLeft
        }

        if (-(top) > minDragDomTop) {
          top = -(minDragDomTop)
        } else if (top > maxDragDomTop) {
          top = maxDragDomTop
        }

        // Move the current element dragDom.style.left = `${left + styL}px`
        dragDom.style.top = `${top + styT}px`
        
	// Stop the pop-up window from moving when the mouse is raised document.onmouseup = function () {
        document.onmousemove = null
        document.onmouseup = null
      }
    }
    dialogHeaderEl.onmousedown = moveDown    
  }
})

Mouse pointer hover style

The pop-up window does not set the cursor: move hover style because it refers to the actual effect of browser dragging. If you want to set move, you need to add boundary judgment conditions.

Determine the mouse hover pointer type, x > left + width - 5 , where 5 is the stretchable area you set. Because the pop-up window cannot set borders and padding, there is no actual draggable element, so manually set 5px (can be changed according to needs).

For more styles of mouse pointer hover, please refer to MDN

 // Define mouse hover style const CURSORTYPE = {
      top: 'n-resize',
      bottom: 's-resize',
      left: 'w-resize',
      right: 'e-resize',
      // right_top is written to facilitate data processing in the following code right_top: 'ne-resize', 
      left_top: 'nw-resize',
      left_bottom: 'sw-resize',
      right_bottom: 'se-resize',
      default: 'default',
    };

    // Determine the mouse hover pointer type const checkType = obj => {
      const { x, y, left, top, width, height } = obj
      let type
      if (x > left + width - 5 && el.scrollTop + y <= top + height - 5 && top + 5 <= y) {
        type = 'right'
      }
      else if (left + 5 > x && el.scrollTop + y <= top + height - 5 && top + 5 <= y) {
        type = 'left'
      } else if (el.scrollTop + y > top + height - 5 && x <= left + width - 5 && left + 5 <= x) {
        type = 'bottom'
      } else if (top + 5 > y && x <= left + width - 5 && left + 5 <= x) {
        type = 'top'
      } else if (x > left + width - 5 && el.scrollTop + y > top + height - 5) {
        type = 'right_bottom'
      } else if (left + 5 > x && el.scrollTop + y > top + height - 5) {
        type = 'left_bottom'
      } else if (top + 5 > y && x > left + width - 5) {
        type = 'right_top'
      } else if (top + 5 > y && left + 5 > x) {
        type = 'left_top'
      }
      return type || 'default'
    }

Four-side stretching and diagonal stretching

There was a slight deviation in my thinking during the diagonal stretching process. I found that the browser window can be stretched diagonally in the X-axis direction, the Y-axis direction, and the bevel edge, so I divided it into three situations for judgment. However, the actual pop-up window effect can only be stretched a little bit, which does not meet the stretching requirements. After thinking about it, I found that the actual diagonal stretching is the superposition of the X and Y axes and the reference vector.

Because diagonal stretching is the superposition of the X-axis and the Y-axis, we consider encapsulating the four-side stretching function and directly calling the corresponding X and Y axes for diagonal stretching to reduce the amount of code. When passing data, the data needs to be packaged because diagonal stretching requires two values ​​to be passed, while four-sided stretching only requires one value. For example: the right side passes the data ['right', null] , while the bottom right corner passes the data ['right', 'bottom']

  // Determine boundary conditions const boundaryLimit = obj => {
      const { left, top, width, height, diffX, diffY, screenHeight, screenWidth, arr } = obj
      if (arr[0] == 'right' || arr[1] == 'right') {
        if (width + diffX > screenWidth - left) {
          dragDom.style.width = screenWidth - left + 'px'
        } else {
          dragDom.style.width = width + diffX + 'px'
        }
      }
      if (arr[0] == 'left' || arr[1] == 'left') {
        if (width - diffX > width + left) {
          dragDom.style.width = width + left + 'px'
          dragDom.style.left = - parseInt(sty.marginLeft) + 'px'
        } else {
          dragDom.style.width = width - diffX + 'px'
          // left actually = left + marginLeft. When calculating, marginLeft needs to be subtracted. dragDom.style.left = left + diffX - parseInt(sty.marginLeft) + 'px'
        }
      }
      if (arr[0] == 'top' || arr[1] == 'top') {
        if (height - diffY > height + top) {
          dragDom.style.height = height + top + 'px'
          dragDom.style.top = - parseInt(sty.marginTop) + 'px'
        } else {
          dragDom.style.height = height - diffY + 'px'
          // top actually = top + marginTop. MarginTop needs to be subtracted when calculating. dragDom.style.top = top + diffY - parseInt(sty.marginTop) + 'px'
        }
      }
      if (arr[0] == 'bottom' || arr[1] == 'bottom') {
        if (height + diffY > screenHeight - top) {
          dragDom.style.height = screenHeight - top
        } else {
          dragDom.style.height = height + diffY + 'px'
        }
      }
    }
    dragDom.onmousedown = e => {
      const x = e.clientX
      const y = e.clientY
      const width = dragDom.clientWidth
      const height = dragDom.clientHeight
      const left = dragDom.offsetLeft
      const top = dragDom.offsetTop
      const screenWidth = document.documentElement.clientWidth || document.body.clientWidth
      const screenHeight = document.documentElement.clientHeight || document.body.clientHeight
      // dragDom.style.userSelect = 'none'
      let type = checkType({ x, y, left, top, width, height })
      // Determine if it is a pop-up window header if (x > left &&
        x < left + width &&
        y > top + 5 &&
        y < top + dialogHeaderEl.clientHeight) {
        // dialogHeaderEl.onmousedown = moveDown
      } else {
        document.onmousemove = function (e) {
          // Disable default events when moving e.preventDefault()
          let endX = e.clientX
          let endY = e.clientY
          let diffX = endX - x
          let diffY = endY - y
          let arr
          // Convert type to array format to simplify code judgment and call if (type.indexOf('_') == -1) {
            arr = [type, '']
          } else {
            arr = type.split('_')
          }
          boundaryLimit({ left, top, width, height, diffX, diffY, screenHeight, screenWidth, arr })
        }
        // Stretch ends document.onmouseup = function () {
          document.onmousemove = null

          document.onmouseup = null
        }
      }
    }

Stretch Interference

Because the pop-up window is set overflow: auto , the stretching process will inevitably produce scroll bars on the right and bottom. During the actual stretching, the scroll bars will interfere with the stretching area. The solution is to add an empty div bar on the right and bottom of the pop-up window, and the actual stretching area is an empty div. (The width and height of the empty div bar are 5px, which is consistent with the stretch area set previously)

insert image description here

<template>
  <div
    class="parameter"
    v-dialogDrag
  >
    <div class="title">Title<div class="close">
        <img
          src="../assets/close.png"
          alt=""
        >
      </div>
    </div>
    <div class="content">Content area</div>
    <div class="rightBlank">123</div>
    <div class="bottomBlank">456</div>
  </div>
</template>

The page effect after the change is

insert image description here

Attached project warehouse address

This is the end of this article about Vue custom instructions to implement pop-up window dragging, four-side stretching and diagonal stretching. For more related vue custom instruction pop-up window dragging content, please search 123WORDPRESS.COM's previous articles or continue to browse the following related articles. I hope everyone will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • VUE implements Studio management background to change window size by dragging and dropping the mouse
  • VUE implements a pop-up component that can be dragged at will
  • Vue suspended draggable floating button example code
  • Vue implements a visual drag page editor
  • Implementing drag and drop function based on Vue
  • Realizing drag effect based on Vue
  • Vue implements div drag and drop
  • Vue implements drag and drop
  • Vue draggable realizes the drag function from left to right
  • Vue implements drag window function

<<:  Detailed installation instructions for the cloud server pagoda panel

>>:  Detailed explanation of MySQL sql99 syntax inner join and non-equivalent join

Recommend

Detailed explanation of object literals in JS

Table of contents Preface 1. Set the prototype on...

What to do if you forget the initial password when installing MySQL on Mac

Forgetting the password is a headache. What shoul...

Detailed explanation of Vue custom instructions

Table of contents Vue custom directive Custom dir...

Detailed explanation of how to gracefully delete a large table in MySQL

Preface To delete a table, the command that comes...

How to remove the underline of a hyperlink using three simple examples

To remove the underline of a hyperlink, you need t...

How to use time as a judgment condition in MySQL

Background: During the development process, we of...

Summary of basic operations for MySQL beginners

Library Operations Query 1.SHOW DATABASE; ----Que...

Solving problems encountered when importing and exporting Mysql

background Since I converted all my tasks to Dock...

CSS margin overlap and how to prevent it

The vertically adjacent edges of two or more bloc...

Detailed installation tutorial for MySQL zip archive version (5.7.19)

1. Download the zip archive version from the offi...

How many ports can a Linux server open at most?

Table of contents Port-related concepts: Relation...