以鼠标位置为中心进行滚动缩放

需求:在图片上进行手动标注,当目标较小时需要放大后再进行框选标注。默认状态下,图片是以左上角为中心进行缩放的,如果对图片设置了居中则是以中点为中心进行缩放的。现在我们需要实现以目标(即鼠标位置)为中心进行滚动缩放。

1.原理

以鼠标位置为中心进行缩放其实就是先进行缩放然后再进行平移操作,将缩放完成的图形平移到鼠标位置,使得缩放前后鼠标指向的图形位置不变。

那么我们最重要的就是计算出偏移量,具体怎么算不再赘述,详情点击偏移量计算原理

2.套公式

首先我们需要先确定我们的图形是以什么位置进行缩放,这点很重要,因为不同的缩放位置进行平移的时候算法不同

  • 以图片左上角缩放
javascript 复制代码
deltaX -= (1 - n) * (width / 2 - e.clientX + x1)
deltaY -= (1 - n) * (height / 2 - e.clientY + y1)
  • 以图片中点缩放
javascript 复制代码
deltaX -= (e.clientX - x1) * (n - 1)
deltaY -= (e.clientY - y1) * (n - 1)

以上两种方式只有 deltaX 和 deltaY的计算有区别,其他逻辑是一样的,即缩放再平移

3.绑定缩放事件

以下为以图片左上角缩放的例子

javascript 复制代码
<template>
    <div class="img-wrap" @mousewheel.prevent="rollImg($event)">
	    <img class="imgDiv" ref="imgDiv" :src="imgSrc" alt="" />
    <div>
</template>

<script>
const imgDiv = ref(null)
const deltaX = ref(0) // 需要平移的x轴距离
const deltaY = ref(0) // 需要平移的y轴距离
const preScale = ref(1) // 记录图片缩放前的比例
// 图片参数
const params = ref({
	zoomVal: 1, // 缩放比例
	deg: 0,
	offsetLeft: 0,
	offsetTop: 0,
})

// 以鼠标位置为中心进行缩放
function rollImg(e) {
	params.value.zoomVal += e.wheelDelta / 1200
	const n = params.value.zoomVal / preScale.value
	const { x: x1, y: y1, width, height } = imgDiv.value.getBoundingClientRect()
	deltaX.value -= (1 - n) * (width / 2 - e.clientX + x1)
	deltaY.value -= (1 - n) * (height / 2 - e.clientY + y1)
	if (params.value.zoomVal >= 0.2) {
		img.style.transform = `translate(${deltaX.value}px, ${deltaY.value}px) scale(${params.value.zoomVal})`
	} else {
		params.value.zoomVal = 0.2
		img.style.transform = `translate(${deltaX.value}px, ${deltaY.value}px) scale(${params.value.zoomVal})`
		return false
	}
	preScale.value = params.value.zoomVal
}
</script>

以图片中点缩放只需要替换deltaX 和 deltaY的计算即可,但是中点缩放有一个坑,下面详细说明

4.以图片中点缩放的坑

如果

css 复制代码
.img-wrap {
	position: relative;
	.imgDiv {
		position: absolute;
		left: 50%;
		transform: translateX(-50%);
	}
}
  • 初始状态下图片使用 left: 50% + transform: translate(-50%, 0) 实现水平居中

  • 缩放计算时使用 deltaX/deltaY 的绝对像素位移``transform: `translate({deltaX.value}px, {deltaY.value}px)```

  • 根本原因在于初始状态下的居中定位方式与缩放时的位移 计算逻辑存在冲突,首次缩放时这个偏移量未被纳入 deltaX/deltaY 计算

解决方法:

不再使用CSS的百分比和translate来实现居中,而是用`deltaX`和`deltaY`来模拟居中。这样,在缩放时,位移计算就可以正确叠加

javascript 复制代码
function resetParams(){
	const imgWrapWidth = imgWrap.value.clientWidth
	const imgDivWidth = imgDiv.value.offsetWidth
	deltaX.value = (imgWrapWidth - imgDivWidth ) / 2
	deltaY.value = 0
	preScale.value = 1
	imgDiv.value.style.left = 0
	imgDiv.value.style.top = 0
	imgDiv.value.style.transform = `translate(${deltaX.value}px, ${deltaY.value}}px)`
}
相关推荐
qb14 分钟前
vue3.5.18源码-编译-入口
前端·vue.js·架构
小桥风满袖15 分钟前
极简三分钟ES6 - 类与继承
前端·javascript
虫无涯16 分钟前
【分享】基于百度脑图,并使用Vue二次开发的用例脑图编辑器组件
前端·vue.js·编辑器
子兮曰17 分钟前
🚀99% 的前端把 reduce 用成了「高级 for 循环」—— 这 20 个骚操作让你一次看懂真正的「函数式折叠」
前端·javascript·typescript
wifi歪f18 分钟前
📦 qiankun微前端接入实战
前端·javascript·面试
小桥风满袖18 分钟前
极简三分钟ES6 - Symbol
前端·javascript
子兮曰22 分钟前
🚀Map的20个神操作,90%的开发者浪费了它的潜力!最后的致命缺陷让你少熬3天夜!
前端·javascript·ecmascript 6
NewChapter °26 分钟前
如何通过 Gitee API 上传文件到指定仓库
前端·vue.js·gitee·uni-app
练习时长两年半的Java练习生(升级中)29 分钟前
从0开始学习Java+AI知识点总结-30.前端web开发(JS+Vue+Ajax)
前端·javascript·vue.js·学习·web
vipbic1 小时前
关于Vue打包的遇到模板引擎解析的引号问题
前端·webpack