Bootstrap
博客内容搜索
Kaysama's Blog

已知矩形,坐标(x,y),宽width,高height,当前放大倍数为n。此时光标位置(a,b),光标相对矩形的位置(a-x,b-y),在该位置对矩形放大到原尺寸的m倍

设矩形的缩放中心在矩形左上角,则放大后:

1
2
x = a - (a - x) / n * m
y = b - (b - y) / n * m

原理:先把图片左上角移到鼠标位置,然后x轴往左偏移鼠标相对矩形的位移,因为该位移已经被缩放过,m是相对于原图的倍数,所以要先除n恢复到原图,再乘m获得最新缩放下的偏移;

另一种理解方式:

1
2
x = a + (x - a) / n * m
y = b + (y - b) / n * m

原理:光标位置(a, b)的点固定,缩放前点(x, y)与光标的距离是(x - a, y - b),因为(a, b)是原点,所以缩放后的点与光标的距离是((x - a) / n * m, (y - b) / n * m),可得最终坐标

源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8"/>
<title>缩放</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<style>
* {
padding: 0;
margin: 0;
outline: 0;
overflow: hidden;
}

html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
}

#ele {
position: absolute;
left: 0;
top: 0;
transform-origin: 0 0;
transform: translate(0, 0) scale(1);
user-select: none;
pointer-events: none;
}
</style>
</head>
<body>
<img src="img.jpg" id="ele">
<script>
(function () {
let $ele = document.getElementById('ele')
const $body = document.body

const onImgLoad = () => {
let scale = 1
let translateX = 0
let translateY = 0

let targetScale = 1
let targetTranslateX = 0
let targetTranslateY = 0

let start = {x: 0, y: 0}
const mouse = {x: 0, y: 0}
let moving = false
let mouseDown = false
let scaling = false

$ele.style.width = $ele.width + 'px'
$ele.style.height = $ele.height + 'px'

targetTranslateX = translateX = ($body.clientWidth - $ele.width) * 0.5
targetTranslateY = translateY = ($body.clientHeight - $ele.height) * 0.5

$body.onmousedown = e => {
start = {x: e.pageX - translateX, y: e.pageY - translateY}
mouseDown = true
moving = true
scaling = false
}
$body.onmousemove = e => {
if (mouseDown) {
targetTranslateX = e.pageX - start.x
targetTranslateY = e.pageY - start.y
}
}
$body.onmouseup = e => {
mouseDown = false
}
$body.onwheel = function (e) {
scaling = true
moving = false
const delta = e.wheelDelta ? e.wheelDelta : -e.deltaY
delta > 0 ? (targetScale = scale * 1.4) : (targetScale = scale / 1.4)
mouse.x = e.pageX
mouse.y = e.pageY
}

const setScale = (oldScale, newScale) => {
translateX = mouse.x + (translateX - mouse.x) / oldScale * newScale
translateY = mouse.y + (translateY - mouse.y) / oldScale * newScale
scale = newScale
}

let raf
const loop = () => {
if (moving) {
translateX += (targetTranslateX - translateX) * 0.1
translateY += (targetTranslateY - translateY) * 0.1
}
if (scaling) {
setScale(scale, scale + (targetScale - scale) * 0.1)
}
$ele.style.transform = 'translate(' + translateX + 'px, ' + translateY + 'px) scale(' + scale + ')'
raf = requestAnimationFrame(loop)
}

raf = requestAnimationFrame(loop)
}
$ele.complete ? onImgLoad() : $ele.onload = onImgLoad
})()
</script>
</body>
</html>

效果预览:

完整代码戳这里

在线演示1在线演示2