js实现以鼠标为中心缩放图片

最终实现效果如下图

第一版

  • 最开始接到这个需求的时候,想的很简单,感觉不是一个很复杂的东西

  • 开始想的是用 transform-origintransform:scale 实现

  • 根据鼠标位置来改变缩放中心,滚轮滚动来改变scale大小

  • 代码如下

js 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            background:
                -webkit-linear-gradient(top, transparent 99px, #ccc 99px),
                -webkit-linear-gradient(left, transparent 99px, #ccc 99px);
            background-size: 100px 100px;
        }

        .container {
            position: relative;
            top: 0;
            left: 0;
            width: 100px;
            height: 100px;
        }

        img {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            object-fit: cover;
            transform-origin: 0% 0%;
        }
    </style>
</head>

<body>
    <div class="container">
        <img id="img" src="https://t7.baidu.com/it/u=1653814446,2847580380&fm=193&f=GIF" alt="">
    </div>
    <script>
        let s = 1
        let minL = 1
        let maxL = 20

        const img = document.getElementById('img')

        img.addEventListener('mousewheel', (e) => {
            const { x, y, deltaY } = e

            s += deltaY * -0.01
            s = Math.min(Math.max(minL, s), maxL)

            img.style.transformOrigin = `${x}px ${y}px`
            img.style.transform = `scale(${s})`

        })
    </script>
</body>

</html>
  • 乍一看,貌似这个功能就这样实现了

  • 但是,当我们在一个位置缩放后,鼠标换个位置再次缩放,图片就会瞬移,如果缩放位置在最初的盒子的位置之外,图片还可能缩放到视图之外,

  • 想不通瞬移,百度,查,查到一个老哥遇到了和我相同的问题这是他的帖子,在他文章回复里看到了瞬移的原因

  • 还有解决方案
  • 继续改代码,计算scale,translate......,

  • 算不对,老是算不对,还是算不对,不算了,百度

  • 百度出来怎么做的都有,就是有点看不懂.....,虽然有些看不懂,但还是吸收了一点经验

第二版

  • 既然做不出来,那就问问水友群的大佬吧,
  • 第二版就这样出来了,用transform:scale来控制大小,top,left控制图片位置

  • 我加了一点自己思考,让这个问题变得更加容易理解

1.每次缩放的时候可以获取鼠标位置(x,y)

2.获取鼠标位置相对于图片缩放为1(transform:scale(1))时的位置(x2,y2)

3.图片从scale=1 缩放到scale=s后,鼠标的位置(x3,y3)

4.再从缩放后的位置(x2,y2),回到(x,y)的位置,计算出图片的偏移(x4,y4)

5.设置图片位置和缩放比例

js 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            background:
                -webkit-linear-gradient(top, transparent 99px, #ccc 99px),
                -webkit-linear-gradient(left, transparent 99px, #ccc 99px);
            background-size: 100px 100px;
        }


        .container {
            position: absolute;
            top: 0;
            left: 0;
            width: 200px;
            height: 200px;
        }

        img {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            object-fit: cover;
            transform-origin: 0% 0%;
        }
    </style>
</head>

<body>
    <div class="container">
        <img id="img" src="https://t7.baidu.com/it/u=1653814446,2847580380&fm=193&f=GIF" alt="">
    </div>
    <script>
        let s = 1//缩放倍率
        let lasts = 1
        let minL = 1
        let maxL = 20

        const img = document.getElementById('img')

        // 缩放前,鼠标位置相对图片缩放为1时的位置
        const getSourePosition = (el, s, x, y) => {
            let sourceTop = +el.style.top.slice(0, -2)
            let sourceLeft = +el.style.left.slice(0, -2)

            let x2 = (x - sourceLeft) / s
            let y2 = (y - sourceTop) / s

            return { x2, y2 }
        }

        // 缩放后,图片需要的位移
        getXY = (s, x, y, x2, y2) => {
            // 缩放后的位置
            let x3 = x2 * s
            let y3 = y2 * s

            // 缩放后的位置移动到鼠标位置,需要的位移
            let x4 = x - x3
            let y4 = y - y3

            return { x4, y4 }
        }

        // 设置图片位置
        const setPosition = (el, x4, y4, s) => {
            el.style.left = `${x4}px`
            el.style.top = `${y4}px`
            el.style.transform = `scale(${s})`
        }

        img.addEventListener('mousewheel', (e) => {
            const { x, y, deltaY } = e

            s += deltaY * -0.01
            s = Math.min(Math.max(minL, s), maxL)

            // 放大
            if (deltaY < 0) {
                let { x2, y2 } = getSourePosition(img, s - 1, x, y)

                let { x4, y4 } = getXY(s, x, y, x2, y2)

                if (lasts < maxL) {
                    lasts = s
                    setPosition(img, x4, y4, s)
                }
            }
            // 缩小
            if (deltaY > 0) {
                let { x2, y2 } = getSourePosition(img, s + 1, x, y)

                let { x4, y4 } = getXY(s, x, y, x2, y2)

                if (lasts > minL) {
                    lasts = s
                    setPosition(img, x4, y4, s)
                }
            }
        })
    </script>
</body>

</html>

第三版

  • 第二版其实已经实现了我们所需要的功能

  • 但是每次缩放我们都需要去获取,设置图片的lefttop,

  • lefttop 属性的改变都会触发了布局操作,开销高。更好的解决方案是在元素上使用 translate,因为它不会触发重新布局。

js 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            background:
                -webkit-linear-gradient(top, transparent 99px, #ccc 99px),
                -webkit-linear-gradient(left, transparent 99px, #ccc 99px);
            background-size: 100px 100px;
        }


        .container {
            position: absolute;
            top: 0;
            left: 0;
            width: 200px;
            height: 200px;
        }

        img {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            object-fit: cover;
            transform-origin: 0% 0%;
            transform: translate(0px, 0px) scale(1);
        }
    </style>
</head>

<body>
    <div class="container">
        <img id="img" src="https://t7.baidu.com/it/u=1653814446,2847580380&fm=193&f=GIF" alt="">
    </div>
    <script>
        let s = 1//缩放倍率
        let lasts = 1
        let minL = 1
        let maxL = 20

        const img = document.getElementById('img')

        const getTransLate = (transform) => {

            let tx = 0, ty = 0
            if (transform === '') {
                return { tx, ty }
            } else {
                tx = transform.split(' ')[0].replaceAll('translate(', '').replaceAll('px,', '')
                ty = transform.split(' ')[1].replaceAll('px)', '')
                return { tx, ty }
            }

        }
        // 缩放前,鼠标位置相对图片缩放为1时的位置
        const getSourePosition = (el, s, x, y) => {
            let { tx, ty } = getTransLate(el.style.transform)

            let x2 = (x - tx) / s
            let y2 = (y - ty) / s

            return { x2, y2 }
        }

        // 缩放后,图片需要的位移
        getXY = (s, x, y, x2, y2) => {
            // 缩放后的位置
            let x3 = x2 * s
            let y3 = y2 * s

            // 缩放后的位置移动到鼠标位置,需要的位移
            let x4 = x - x3
            let y4 = y - y3

            return { x4, y4 }
        }

        // 设置图片位置
        const setPosition = (el, x4, y4, s) => {
            el.style.transform = `translate(${x4}px, ${y4}px) scale(${s})`
        }

        img.addEventListener('mousewheel', (e) => {
            const { x, y, deltaY } = e

            s += deltaY * -0.01
            s = Math.min(Math.max(minL, s), maxL)

            // 放大
            if (deltaY < 0) {
                let { x2, y2 } = getSourePosition(img, s - 1, x, y)

                let { x4, y4 } = getXY(s, x, y, x2, y2)

                if (lasts < maxL) {
                    lasts = s
                    setPosition(img, x4, y4, s)
                }
            }
            // 缩小
            if (deltaY > 0) {
                let { x2, y2 } = getSourePosition(img, s + 1, x, y)

                let { x4, y4 } = getXY(s, x, y, x2, y2)

                if (lasts > minL) {
                    lasts = s
                    setPosition(img, x4, y4, s)
                }
            }
        })
    </script>
</body>

</html>

总结

  • 网上大佬的实现都是直接计算缩放后的tanslate

  • 我的实现是先将鼠标位置映射到缩放为1时,再缩放s倍,再回到鼠标位置

  • 初始将图片缩放中心设为0px 0px会节省一些计算

  • transform-origin不要动

相关推荐
golitter.3 分钟前
Ajax和axios简单用法
前端·ajax·okhttp
雷特IT23 分钟前
Uncaught TypeError: 0 is not a function的解决方法
前端·javascript
长路 ㅤ   1 小时前
vite学习教程02、vite+vue2配置环境变量
前端·vite·环境变量·跨环境配置
亚里士多没有德7751 小时前
强制删除了windows自带的edge浏览器,重装不了怎么办【已解决】
前端·edge
micro2010141 小时前
Microsoft Edge 离线安装包制作或获取方法和下载地址分享
前端·edge
.生产的驴1 小时前
Electron Vue框架环境搭建 Vue3环境搭建
java·前端·vue.js·spring boot·后端·electron·ecmascript
awonw1 小时前
[前端][easyui]easyui select 默认值
前端·javascript·easyui
九圣残炎1 小时前
【Vue】vue-admin-template项目搭建
前端·vue.js·arcgis
柏箱2 小时前
使用JavaScript写一个网页端的四则运算器
前端·javascript·css
TU^2 小时前
C语言习题~day16
c语言·前端·算法