欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue自定義指令實現(xiàn)彈窗拖拽四邊拉伸及對角線拉伸效果

 更新時間:2021年08月04日 10:40:08   作者:一個小白web  
小編最近在做一個vue前端項目,需要實現(xiàn)彈窗的拖拽,四邊拉伸及對角線拉伸,以及彈窗邊界處理功能,本文通過實例代碼給大家分享我的實現(xiàn)過程及遇到問題解決方法,感興趣的朋友一起看看吧

引言

       近期公司vue前端項目需求:實現(xiàn)彈窗的拖拽,四邊拉伸及對角線拉伸,以及彈窗邊界處理。本人使用vue的自定義指令編寫了drag.js文件分享給大家一起學(xué)習(xí),以下代碼是本人提取出來的示意demo,僅供參考。這是本人前端小白的第一篇技術(shù)分享,如有錯誤的地方,請大家批評指正!

頁面布局

<template>
  <div
    class="parameter"
    v-dialogDrag
  >
    <div class="title">標(biāo)題
      <div class="close">
        <img
          src="../assets/close.png"
          alt=""
        >
      </div>
    </div>
    <div class="content">內(nèi)容區(qū)</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; // 右對齊
    }
  }
  .content {
    display: flex;
    justify-content: center;
    align-items: center;
    height: calc(100% - 48px);
    box-sizing: border-box;
    background: #fff;
    overflow: auto;
  }
}
</style>

      頁面布局實際效果如下:

在這里插入圖片描述

drag.js文件

       可以在main.js全局引入drag.js文件,也可以單獨在彈窗組件內(nèi)部組件引入,看是否還有其他使用場景。

       項目目錄截圖

在這里插入圖片描述

     main.js全局引入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')

彈窗拖拽實現(xiàn)及邊界限制

import Vue from 'vue'
// v-dialogDrag: 彈窗拖拽+水平方向伸縮+對角線拉伸
Vue.directive('dialogDrag', {
  bind(el) {
  	// dialogHeaderEl為標(biāo)題欄,綁定mousedown事件進行拖拽
    const dialogHeaderEl = el.querySelector('.title')
   	// dragDom為指令綁定dom元素,定義變量便于區(qū)分
    const dragDom = el
    // 獲取css所有屬性兼容性寫法 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
    const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null) 
    // 定義鼠標(biāo)按下事件
	const moveDown = e => {
      // e.clientX,Y:鼠標(biāo)相對于瀏覽器可視窗口的X,Y坐標(biāo)
      // offsetTop,offsetLeft:當(dāng)前元素相對于其offsetParent元素的頂部,左邊的距離,這里title無定位偏移,故為0	
      const disX = e.clientX - dialogHeaderEl.offsetLeft // 元素相對位置
      const disY = e.clientY - dialogHeaderEl.offsetTop // 元素相對位置

      const screenWidth = document.documentElement.clientWidth || document.body.clientWidth // 頁面可視區(qū)寬度,兼容寫法
      const screenHeight = document.documentElement.clientHeight || document.body.clientHeight // 頁面可視區(qū)高度,兼容寫法

      const dragDomWidth = dragDom.offsetWidth // 對話框?qū)挾?
      const dragDomheight = dragDom.offsetHeight // 對話框高度

      const minDragDomLeft = dragDom.offsetLeft // 對話框邊界最小left值
      const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth // 對話框邊界最大left值

      const minDragDomTop = dragDom.offsetTop // 對話框邊界最小Top值
      const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight // 對話框邊界最大Top值
      // 獲取到的值帶px 正則匹配替換
      let styL = sty.left

      // 為兼容ie
      if (styL === 'auto') styL = '0px'
      let styT = sty.top

      // 注意在ie中 第一次獲取到的值為組件自帶50% 移動之后賦值為px
      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) {
        // 通過事件委托,計算移動的距離
        let left = e.clientX - disX
        let top = e.clientY - disY

        // 邊界處理
        if (-(left) > minDragDomLeft) {
          left = -(minDragDomLeft)
        } else if (left > maxDragDomLeft) {
          left = maxDragDomLeft
        }

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

        // 移動當(dāng)前元素
        dragDom.style.left = `${left + styL}px`
        dragDom.style.top = `${top + styT}px`
        
	// 鼠標(biāo)抬起停止彈窗移動
      document.onmouseup = function () {
        document.onmousemove = null
        document.onmouseup = null
      }
    }
    dialogHeaderEl.onmousedown = moveDown    
  }
})

鼠標(biāo)指針懸停樣式

       彈窗并沒有設(shè)置cursor:move懸停樣式,因為參考的是瀏覽器拖拽實際效果,如果想設(shè)置move,需要增加邊界判斷條件。

       判斷鼠標(biāo)懸浮指針類型中x > left + width - 5,其中5為自己設(shè)置的可拉伸區(qū)域,因為需求中彈窗不可設(shè)置邊框和padding,所以無實際可拖拽元素,故手動設(shè)置5px(可根據(jù)需求自行更改)。

鼠標(biāo)指針懸停更多樣式請參考MDN

 // 定義鼠標(biāo)懸停樣式
    const CURSORTYPE = {
      top: 'n-resize',
      bottom: 's-resize',
      left: 'w-resize',
      right: 'e-resize',
      // right_top寫法是便于后面代碼數(shù)據(jù)處理
      right_top: 'ne-resize', 
      left_top: 'nw-resize',
      left_bottom: 'sw-resize',
      right_bottom: 'se-resize',
      default: 'default',
    };

    // 判斷鼠標(biāo)懸浮指針類型
    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'
    }

四邊拉伸及對角線拉伸

       在做對角線拉伸過程中思路出現(xiàn)一點偏差,我發(fā)現(xiàn)瀏覽器窗口對角線拉伸可以X軸方向拉伸,Y方向拉伸,斜邊拉伸,故分三種情況判斷,可是這樣做出來實際彈窗效果只能拉伸一點點,不滿足拉伸需求。經(jīng)過思考后發(fā)現(xiàn),實際對角線拉伸為X,Y軸疊加和,參考矢量疊加。

       因為對角線拉伸為X軸和Y軸的疊加,故考慮將四邊拉伸封裝函數(shù),對角線拉伸直接調(diào)用相應(yīng)的X,Y軸,減少代碼量。傳遞數(shù)據(jù)的時候因為對角線拉伸需要傳遞兩個值,而四邊拉伸只需要傳遞一個值,所以需要對數(shù)據(jù)進行包裝。例如:右側(cè)拉伸傳遞數(shù)據(jù)['right', null],而右下角傳遞數(shù)據(jù)['right', 'bottom']

  // 判斷邊界條件
    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實際 = left + marginLeft 計算時需要將marginLeft減掉
          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實際 = top + marginTop 計算時需要將marginTop減掉
          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 })
      // 判斷是否為彈窗頭部
      if (x > left &&
        x < left + width &&
        y > top + 5 &&
        y < top + dialogHeaderEl.clientHeight) {
        // dialogHeaderEl.onmousedown = moveDown
      } else {
        document.onmousemove = function (e) {
          // 移動時禁用默認(rèn)事件
          e.preventDefault()
          let endX = e.clientX
          let endY = e.clientY
          let diffX = endX - x
          let diffY = endY - y
          let arr
          // 將type轉(zhuǎn)換為數(shù)組格式,簡化代碼判斷調(diào)用即可
          if (type.indexOf('_') == -1) {
            arr = [type, '']
          } else {
            arr = type.split('_')
          }
          boundaryLimit({ left, top, width, height, diffX, diffY, screenHeight, screenWidth, arr })
        }
        // 拉伸結(jié)束
        document.onmouseup = function () {
          document.onmousemove = null

          document.onmouseup = null
        }
      }
    }    

拉伸干涉

       因為彈窗設(shè)置了overflow: auto,故拉伸過程勢必會產(chǎn)生右側(cè),底部滾動條,在實際拉伸的時候滾動條會和拉伸區(qū)域干涉。解決方案為:在彈窗右側(cè)和底部外部增加空div條,實際拉伸區(qū)域為空div即可解決。(空div條寬高為5px,與之前設(shè)置的拉伸區(qū)域一致)

在這里插入圖片描述

<template>
  <div
    class="parameter"
    v-dialogDrag
  >
    <div class="title">標(biāo)題
      <div class="close">
        <img
          src="../assets/close.png"
          alt=""
        >
      </div>
    </div>
    <div class="content">內(nèi)容區(qū)</div>
    <div class="rightBlank">123</div>
    <div class="bottomBlank">456</div>
  </div>
</template>

更改后頁面效果為

在這里插入圖片描述

附項目倉庫地址

到此這篇關(guān)于Vue自定義指令實現(xiàn)彈窗拖拽,四邊拉伸及對角線拉伸的文章就介紹到這了,更多相關(guān)vue自定義指令彈窗拖拽內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論