已知矩形,坐标(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