vue3 图片放大缩小、拖拽功能(自定义指令)
const oDiv = el // 当前元素 const minTop = oDiv . getAttribute("drag-min-top") const ifMoveSizeArea = 20 oDiv . onmousedown =(e) => {
·
效果
自定义 拖拽指令
vDrag.js 参考来源 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
}
})
}
使用
//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=""
/>
自定义 拖拽指令 写法二 (带传参及回调写法)
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)
}
使用
//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=""
/>
自定义 缩放指令
vWheelScale.js
根据项目需要 我指令加了 动态参数 及 回调函数 不需要自行修改
// 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,
})
}
}
}
})
}
使用
//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=""
/>
自定义 缩放指令 写法二(带传参及回调写法)
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,
})
}
}
使用
//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=""
/>
更多推荐
已为社区贡献14条内容
所有评论(0)