Vue3 image zoom in and out, drag and drop function (custom command)

Effect

Please add a picture description

Custom drag and drop commands

vDrag.js reference source https://github.com/sunzsh
export const initVDrag = (Vue) => {
    
    
  Vue.directive("drag", (el) => {
    
    
    const oDiv = el // 当前元素
    const minTop = oDiv.getAttribute("drag-min-top")
    const ifMoveSizeArea = 20
    oDiv.onmousedown = (e) => {
    
    
      let target = oDiv
      while (
        window.getComputedStyle(target).position !== "absolute" &&
        target !== document.body
      ) {
    
    
        target = target.parentElement
      }

      document.onselectstart = () => {
    
    
        return false
      }
      if (!target.getAttribute("init_x")) {
    
    
        target.setAttribute("init_x", target.offsetLeft)
        target.setAttribute("init_y", target.offsetTop)
      }

      const initX = parseInt(target.getAttribute("init_x"))
      const initY = parseInt(target.getAttribute("init_y"))

      // 鼠标按下,计算当前元素距离可视区的距离
      const disX = e.clientX - target.offsetLeft
      const disY = e.clientY - target.offsetTop
      document.onmousemove = (e) => {
    
    
        // 通过事件委托,计算移动的距离
        // 因为浏览器里并不能直接取到并且使用clientX、clientY,所以使用事件委托在内部做完赋值
        const l = e.clientX - disX
        const t = e.clientY - disY
        // 计算移动当前元素的位置,并且给该元素样式中的left和top值赋值
        target.style.left = l + "px"
        // target.style.top = (t < minTop ? minTop : t) + "px"
        target.style.top = t + "px"
        if (
          Math.abs(l - initX) > ifMoveSizeArea ||
          Math.abs(t - initY) > ifMoveSizeArea
        ) {
    
    
          target.setAttribute("dragged", "")
        } else {
    
    
          target.removeAttribute("dragged")
        }
      }
      document.onmouseup = (e) => {
    
    
        document.onmousemove = null
        document.onmouseup = null
        document.onselectstart = null
      }
      // return false不加的话可能导致黏连,拖到一个地方时div粘在鼠标上不下来,相当于onmouseup失效
      return false
    }
  })
}

use
//main.ts
import {
    
     createApp } from "vue"
import App from "./App.vue"
const app = createApp(App)
import {
    
     initVDrag } from "@/utils/xxxx/vDrag.js"
initVDrag(app)

// env.d.ts 声明一下 js文件 不然报错
declare module "@/utils/xxxx/vDrag.js"


//组件中
// img 父元素需要设置  absolute
<img
  v-drag
  oncontextmenu="return false;"
  onselectstart="return false;"
  class="select-none w-full h-full object-contain"
  src="https://picx.zhimg.com/80/v2-b97b167e582d72cd0afb78ad53a688a1_720w.webp?source=1940ef5c"
  alt=""
/>

Custom drag and drop command writing method 2 (with parameter passing and callback writing method)

vDrag.js

// html 使用
// binding.arg 传参
// x x轴相对当前位置偏移量
// y y轴相对当前位置偏移量
{
    
    
  /* <img src="" v-drag:[initDrag]="dragHandle" alt="" /> 
const initDrag = reactive({ x: 0, y: 0 }) */
}
export const initVDrag = (Vue) => {
    
    
  Vue.directive("drag", {
    
    
    mounted(el, binding) {
    
    
      const oDiv = el // 当前元素
      const minTop = oDiv.getAttribute("drag-min-top")
      const ifMoveSizeArea = 20
      const {
    
     x = 0, y = 0 } = binding.arg || {
    
    }
      const initOffsetLeft = el.offsetLeft || 0
      const initoffsetTop = el.offsetTop || 0
      initOffset(el, binding, x, y, initOffsetLeft, initoffsetTop)
      // 初始化 x y 偏移量 数据
      oDiv.onmousedown = (e) => {
    
    
        let target = oDiv
        while (
          window.getComputedStyle(target).position !== "absolute" &&
          target !== document.body
        ) {
    
    
          target = target.parentElement
        }

        document.onselectstart = () => {
    
    
          return false
        }
        // if (!target.getAttribute("init_x")) {
    
    
        //   target.setAttribute("init_x", target.offsetLeft)
        //   target.setAttribute("init_y", target.offsetTop)
        // }

        // const initX = parseInt(target.getAttribute("init_x"))
        // const initY = parseInt(target.getAttribute("init_y"))

        const initX = parseInt(target.offsetLeft)
        const initY = parseInt(target.offsetTop)
        // 鼠标按下,计算当前元素距离可视区的距离
        const disX = e.clientX - target.offsetLeft
        const disY = e.clientY - target.offsetTop
        document.onmousemove = (e) => {
    
    
          // 通过事件委托,计算移动的距离
          // 因为浏览器里并不能直接取到并且使用clientX、clientY,所以使用事件委托在内部做完赋值
          const l = e.clientX - disX
          const t = e.clientY - disY
          // 计算移动当前元素的位置,并且给该元素样式中的left和top值赋值
          target.style.left = l + "px"
          // target.style.top = (t < minTop ? minTop : t) + "px"
          target.style.top = t + "px"
          // 拖拽改变回调函数
          dragCallBack(target, binding, l, t, initOffsetLeft, initoffsetTop)
          if (
            Math.abs(l - initX) > ifMoveSizeArea ||
            Math.abs(t - initY) > ifMoveSizeArea
          ) {
    
    
            target.setAttribute("dragged", "")
          } else {
    
    
            target.removeAttribute("dragged")
          }
        }
        document.onmouseup = (e) => {
    
    
          document.onmousemove = null
          document.onmouseup = null
          document.onselectstart = null
        }
        // return false不加的话可能导致黏连,拖到一个地方时div粘在鼠标上不下来,相当于onmouseup失效
        return false
      }
    },
  })
}
// 拖拽改变回调
const dragCallBack = (el, binding, x, y, initOffsetLeft, initoffsetTop) => {
    
    
  const dragHandle = binding.value || null
  if (dragHandle instanceof Function) {
    
    
    dragHandle({
    
    
      el,
      x,
      y,
      initOffsetLeft,
      initoffsetTop,
      setOffset: (x, y) => {
    
    
        setOffset(el, binding, x, y, initOffsetLeft, initoffsetTop)
      },
      binding,
    })
  }
}
// 设置偏移量
const setOffset = (target, binding, x, y, initOffsetLeft, initoffsetTop) => {
    
    
  // 计算移动当前元素的位置,并且给该元素样式中的left和top值赋值
  target.style.left = x + "px"
  target.style.top = y + "px"
  dragCallBack(target, binding, x, y, initOffsetLeft, initoffsetTop)
}
const initOffset = (target, binding, x, y, initOffsetLeft, initoffsetTop) => {
    
    
  const initX = (target.offsetLeft || 0) * 1 + x * 1
  const initY = (target.offsetTop || 0) * 1 + y * 1
  setOffset(target, binding, initX, initY, initOffsetLeft, initoffsetTop)
}

use
//main.ts
import {
    
     createApp } from "vue"
import App from "./App.vue"
const app = createApp(App)
import {
    
     initVDrag } from "@/utils/xxxx/vDrag.js"
initVDrag(app)

// env.d.ts 声明一下 js文件 不然报错
declare module "@/utils/xxxx/vDrag.js"



//组件中
<script setup lang="ts">
//初始化拖拽数据
const initDrag = reactive({
    
    
  x: 0, //x轴偏移量  单位px
  y: 0, //y轴偏移量  单位px
})
// 拖拽元素 信息
const dragInfo = ref({
    
    } as any)
// 拖拽事件回调
const dragHandle = (options: any) => {
    
    
  dragInfo.value = options
}
</script>
 //写法一 v-drag:[initDrag]="dragHandle"
 //写法二 v-drag:[initDrag]  
 //写法三 v-drag
// img 父元素需要设置  absolute
<img
  v-drag
  oncontextmenu="return false;"
  onselectstart="return false;"
  class="select-none w-full h-full object-contain"
  src="https://picx.zhimg.com/80/v2-b97b167e582d72cd0afb78ad53a688a1_720w.webp?source=1940ef5c"
  alt=""
/>

custom scaling command

vWheelScale.js

According to the needs of the project, I added dynamic parameters and callback functions to the instructions without modifying them by myself.

// html 使用
// maxScale 缩放最大倍速  默认5
// minScale 最小倍速 默认0.5
{
    
    
  /* <img src="" v-wheelScale:[initWheelScale]="wheelScaleChange" alt="" /> 
const initWheelScale = reactive({ maxScale: 5, minScale: 0.5 }) */
}
export const initVWheelScale = (Vue) => {
    
    
  let oldScale = null //记录 上一次 scale 值
  Vue.directive("wheelScale", (el, binding) => {
    
    
    if (el) {
    
    
      el.onwheel = (e) => {
    
    
        const {
    
     maxScale = 5, minScale = 0.5 } = binding.arg || {
    
    }
        const cssVarName = "--scale"
        let _scale = el.style.getPropertyValue(cssVarName) || 1
        if (e.wheelDelta > 0) {
    
    
          _scale = _scale * 1 + 0.1
        } else {
    
    
          _scale = _scale * 1 - 0.1
        }
        // 现在缩放范围
        if (_scale > maxScale) {
    
    
          _scale = maxScale
        } else if (_scale < minScale) {
    
    
          _scale = minScale
        }
        // 设置 --scale 变量 缩放比例
        const setVarScale = (el, cssVarName) => {
    
    
          let cssText = el.style.cssText
          let cssTextList = cssText.split(";")
          let isExist = false
          let isExistIndex = -1
          for (let index = 0; index < cssTextList.length; index++) {
    
    
            const element = cssTextList[index]
            if (element.includes(cssVarName + ":")) {
    
    
              isExist = true
              isExistIndex = index
              break
            }
          }
          if (isExist) {
    
    
            cssTextList[isExistIndex] = `--scale: ${
      
      _scale}`
          } else {
    
    
            cssTextList.push(`--scale: ${
      
      _scale}`)
            //   el.setAttribute("style", `--scale: ${_scale}`)
          }
          cssText = cssTextList.join(";")
          el.style.cssText = cssText
        }
        // 设置 style.transform
        const setTransformCss = (el, cssVarName) => {
    
    
          let transformCssString = el.style.transform
          let regScaleGlobal = /scale\(.*?[ )]*[)]+[ ]*/g //匹配 Scale属性 全局
          if (regScaleGlobal.test(transformCssString)) {
    
    
            transformCssString = transformCssString.replace(
              regScaleGlobal,
              ` scale(var(${
      
      cssVarName})) `
            )
          } else {
    
    
            transformCssString += " " + `scale(var(${
      
      cssVarName}))`
          }
          el.style.transform = transformCssString
        }
        setVarScale(el, cssVarName)
        setTransformCss(el, cssVarName)
        // 缩放改变回调函数
        const wheelScaleChange = binding.value || null

        if (wheelScaleChange instanceof Function && _scale != oldScale) {
    
    
          oldScale = _scale
          wheelScaleChange({
    
    
            cssVarName,
            currentScale: _scale,
            maxScale,
            minScale,
          })
        }
      }
    }
  })
}
use
//main.ts
import {
    
     createApp } from "vue"
import App from "./App.vue"
const app = createApp(App)
import {
    
     initVWheelScale} from "@/utils/xxxx/vWheelScale.js"
initVWheelScale(app)
// env.d.ts 声明一下 js文件 不然报错
declare module "@/utils/xxxx/vWheelScale.js"

//组件中
<script setup lang="ts">
const initWheelScale = reactive({
    
     maxScale: 5, minScale: 0.5 })
const wheelScaleChange = (el: any) => {
    
    
  console.log("wheelScaleChange el", el)
}
</script>
 //写法一 v-wheelScale:[initWheelScale]="wheelScaleChange"
 //写法二 v-wheelScale:[initWheelScale]  
 //写法三 v-wheelScale
  <img
    v-drag
    v-wheelScale:[initWheelScale]="wheelScaleChange"
    oncontextmenu="return false;"
    onselectstart="return false;"
    class="select-none w-full h-full object-contain"
    src="https://picx.zhimg.com/80/v2-b97b167e582d72cd0afb78ad53a688a1_720w.webp?source=1940ef5c"
    alt=""
  />
 

Custom scaling command writing method 2 (with parameter passing and callback writing method)

vWheelScale.js
// html 使用
// maxScale 缩放最大倍速  默认5
// minScale 最小倍速 默认0.5
// initScale 默认倍速
// cssVarName css scale 变量名
{
    
    
  /* <img src="" v-wheelScale:[initWheelScale]="wheelScaleHandle" alt="" /> 
const initWheelScale = reactive({ maxScale: 5, minScale: 0.5 }) */
}
export const initVWheelScale = (Vue) => {
    
    
  Vue.directive("wheelScale", (el, binding) => {
    
    
    const {
    
    
      maxScale = 5,
      minScale = 0.5,
      initScale = 1,
      cssVarName = "--scale",
    } = binding.arg || {
    
    }
    let currentScale = initScale || el.style.getPropertyValue(cssVarName) || 1
    setWheelScale(binding, {
    
    
      el,
      cssVarName,
      currentScale,
      minScale,
      maxScale,
    })
    if (el) {
    
    
      el.onwheel = (e) => {
    
    
        currentScale = el.style.getPropertyValue(cssVarName) || 1

        if (e.wheelDelta > 0) {
    
    
          currentScale = currentScale * 1 + 0.1
        } else {
    
    
          currentScale = currentScale * 1 - 0.1
        }
        setWheelScale(binding, {
    
    
          el,
          cssVarName,
          currentScale,
          minScale,
          maxScale,
        })
      }
    }
  })
}
// 设置 --scale 变量 缩放比例
const setVarScale = (el, cssVarName, currentScale, minScale, maxScale) => {
    
    
  // 现在缩放范围
  if (currentScale > maxScale) {
    
    
    currentScale = maxScale
  } else if (currentScale < minScale) {
    
    
    currentScale = minScale
  }
  let cssText = el.style.cssText
  let cssTextList = cssText.split(";")
  let isExist = false
  let isExistIndex = -1
  for (let index = 0; index < cssTextList.length; index++) {
    
    
    const element = cssTextList[index]
    if (element.includes(cssVarName + ":")) {
    
    
      isExist = true
      isExistIndex = index
      break
    }
  }
  if (isExist) {
    
    
    cssTextList[isExistIndex] = `--scale: ${
      
      currentScale}`
  } else {
    
    
    cssTextList.push(`--scale: ${
      
      currentScale}`)
    //   el.setAttribute("style", `--scale: ${currentScale}`)
  }
  cssText = cssTextList.join(";")
  el.style.cssText = cssText
  return currentScale
}
// 设置 style.transform
const setTransformCss = (el, cssVarName) => {
    
    
  let transformCssString = el.style.transform
  let regScaleGlobal = /scale\(.*?[ )]*[)]+[ ]*/g //匹配 Scale属性 全局
  if (regScaleGlobal.test(transformCssString)) {
    
    
    transformCssString = transformCssString.replace(
      regScaleGlobal,
      ` scale(var(${
      
      cssVarName})) `
    )
  } else {
    
    
    transformCssString += " " + `scale(var(${
      
      cssVarName}))`
  }
  el.style.transform = transformCssString
}
export const setWheelScale = (binding = {
     
     }, options) => {
    
    
  const {
    
     el, cssVarName, currentScale, minScale, maxScale } = options
  const nowScale = setVarScale(el, cssVarName, currentScale, minScale, maxScale)
  setTransformCss(el, cssVarName)
  // 缩放改变回调函数
  const wheelScaleHandle = binding.value || null
  if (wheelScaleHandle instanceof Function) {
    
    
    wheelScaleHandle({
    
    
      el,
      cssVarName,
      maxScale,
      minScale,
      currentScale: nowScale,
      setScale: (_scale) => {
    
    
        setWheelScale(binding, {
    
     ...options, currentScale: _scale })
      },
      binding,
    })
  }
}

use
//main.ts
import {
    
     createApp } from "vue"
import App from "./App.vue"
const app = createApp(App)
import {
    
     initVWheelScale} from "@/utils/xxxx/vWheelScale.js"
initVWheelScale(app)
// env.d.ts 声明一下 js文件 不然报错
declare module "@/utils/xxxx/vWheelScale.js"

//组件中
<script setup lang="ts">
const initWheelScale = reactive({
    
    
  maxScale: 5,
  minScale: 0.5,
  initScale: 1,
  cssVarName: "--scale",
})
const wheelScaleDomInfo = ref({
    
    } as any)
const wheelScaleHandle = (options: any) => {
    
    
  wheelScaleDomInfo.value = options
}


// 函数调用修改 缩放
const scaleModified = (type = "add", addNum = 0.2) => {
    
    
  if (wheelScaleDomInfo.value?.setScale) {
    
    
    let {
    
     currentScale, setScale } = wheelScaleDomInfo.value
    switch (type) {
    
    
      case "add":
        setScale(currentScale * 1 + addNum)
        break
      case "subtract":
        setScale(currentScale * 1 - addNum)
        break
    }
  }
}
//scaleModified("add") //放大
//scaleModified("subtract") //缩小

</script>
 //写法一 v-wheelScale:[initWheelScale]="wheelScaleHandle"
 //写法二 v-wheelScale:[initWheelScale]  
 //写法三 v-wheelScale
  <img
    v-drag
    v-wheelScale:[initWheelScale]="wheelScaleHandle"
    oncontextmenu="return false;"
    onselectstart="return false;"
    class="select-none w-full h-full object-contain"
    src="https://picx.zhimg.com/80/v2-b97b167e582d72cd0afb78ad53a688a1_720w.webp?source=1940ef5c"
    alt=""
  />
 

おすすめ

転載: blog.csdn.net/weixin_43245095/article/details/128468656