手写图片拖拽、鼠标点位缩放

简介: 如题,无关技术背景,什么vue、react自己想用就用,这就是js加一点点css实现,意思就是可以任意迁。

如题,无关技术背景,什么vuereact自己想用就用,这就是js加一点点css实现,意思就是可以任意迁移。

效果如下:

代码片段

加载图片

在加载图片之前我们是需要准备一个容器来存放图片的,图片只能在这个容器里面拖动、缩放,下面就是一个基本的架子,这种代码就没必要解读了吧:

<html>
<head>
    <title>Document</title>
    <style>
        html,
        body {
    
            margin: 0;
            padding: 0;
        }

        body {
    
            background: #3c8079;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        #imgContainer {
    
            width: 500px;
            height: 500px;
            background: #fff;
            position: relative;
            overflow: hidden;
        }

        /* 容器下的图片一些单独的样式 */
        #imgContainer img {
    
            position: absolute;
            transform-origin: left top;
        }
    </style>
</head>
<body>
<div id="imgContainer"></div>
</body>
</html>

架子完成就是js部分了,这里img肯定是动态加载的,所以创建img标签,设置一点点属性,然后插入容器中就可以了,代码如下:

const img = document.createElement('img');
img.src = 'https://p26-passport.byteacctimg.com/img/user-avatar/6a646066de6c1376525669efb70295e9~300x300.image';
// 禁止图片拖动
img.draggable = false

img.onload = () => {
   
    const imgContainer = document.getElementById('imgContainer');
    imgContainer.append(img);
}

可以看到上面的代码插入的部分写到了imgonload事件中了,因为要计算图片的宽高,初始要缩放到容器中完全看见,太小了需要居中,还有宽高比例的问题,还有时候图片太大了,加载出来之后会设置缩放,会有闪动的情况,下面就是处理处理这些:

img.onload = () => {
   
    const imgContainer = document.getElementById('imgContainer');

    // 获取容器的宽高
    const containerWidth = imgContainer.clientWidth;
    const containerHeight = imgContainer.clientHeight;

    // 获取图片的宽高
    const imgWidth = img.width;
    const imgHeight = img.height;

    // 获取缩放值
    const ratio = getRatio([containerWidth, containerHeight], [imgWidth, imgHeight]);

    // 设置缩放
    img.style.transform = `scale(${ratio}, ${ratio})`;

    // 根据缩放值设置图片的位置,这样能让图片居中显示
    img.style.left = (containerWidth * 0.5 - (imgWidth * 0.5 * ratio)) + 'px';
    img.style.top = (containerHeight * 0.5 - (imgHeight * 0.5 * ratio)) + 'px';

    imgContainer.append(img);
}

/**
 * 获取图片和容器的比率
 * @param containerSize{Number[]}   容器宽高
 * @param imgSize{Number[]}         图片宽高
 */
function getRatio(containerSize, imgSize) {
   
    const [containerWidth, containerHeight] = containerSize;
    const [imgWidth, imgHeight] = imgSize;

    // 如果图片小于容器就不缩放
    if (containerWidth > imgWidth && containerHeight > imgHeight) {
   
        return 1;
    }

    // 图片宽度更大就返回宽度的比率,否则就高度的
    if (imgWidth > imgHeight) {
   
        return containerWidth / imgWidth;
    } else {
   
        return containerHeight / imgHeight;
    }
}

处理图片拖动

拖动很简单,大概的流程如下:

  1. 监听鼠标按下事件。
  2. 获取当前鼠标的坐标
  3. 监听鼠标移动事件。
  4. 获取鼠标移动之后的坐标。
  5. 拖动元素移动。
  6. 监听鼠标松开事件。
  7. 鼠标松开,移除鼠标移动事件和松开事件。
    流程都弄清楚就直接开干,上代码:

    // 1. 监听鼠标按下事件,这个放在容器上面,因为图片会移动,不一定每次都点击在图片上
    const imgContainer = document.getElementById('imgContainer');
    imgContainer.addEventListener('mousedown', (e) => {
         
     // 记录图片初始位置
     let posX = parseInt(img.style.left) || 0;
     let posY = parseInt(img.style.top) || 0;
    
     // 2. 获取当前鼠标的坐标
     let oldX = e.x - posX;
     let oldY = e.y - posY;
    
     // 3. 监听鼠标移动事件
     const mousemove = (e) => {
         
         // 4. 获取鼠标移动之后的坐标
         const {
         x, y} = e;
    
         // 计算拖动距离
         posX = x - oldX;
         posY = y - oldY;
    
         // 5. 拖动元素移动
         img.style.left = posX + 'px';
         img.style.top = posY + 'px';
     };
     // 注册在 document 上面就离开这个元素也能拖动
     document.addEventListener('mousemove', mousemove);
    
     // 6. 监听鼠标松开事件
     const mouseup = (e) => {
         
         // 7. 鼠标松开,移除鼠标移动事件和松开事件
         document.removeEventListener('mousemove', mousemove);
         document.removeEventListener('mouseup', mouseup)
     };
     // 注册在 document 上面在别的地方松开也会停止拖动事件的监听
     document.addEventListener('mouseup', mouseup);
    });
    

处理鼠标缩放

鼠标缩放要比拖动要难一些,还是先理一下流程:

  1. 监听鼠标滚轮滚动事件。
  2. 往上滚动,放大图片。
  3. 往下滚动,缩小图片。

流程就三步,感觉比拖动简单很多,不多废话,上代码:

// 1. 监听鼠标滚轮滚动事件
let zoom = 0; // 设置极值
img.addEventListener('wheel', (evt) => {
   
    // 获取图片的缩放数据
    const transform = img.style.transform;
    const scale = /scale(-?\d+(.\d+)?,\s?-?\d+?(.\d+)?)/g.exec(transform);
    if (!scale) return;

    // 理论上两个值都是一样的,这里还是写两个,可以对宽高设置不同比例
    const regExp = /-?\d+(.\d+)?/g;
    let scaleXVal = Number(regExp.exec(scale[0])[0]);
    let scaleYVal = Number(regExp.exec(scale[0])[0]);

    const radix = 0.1;
    if (evt.deltaY > 0) {
   
        // 2. 鼠往下滚动

        if (zoom <= 0) return;
        zoom--;

        scaleXVal -= radix;
        scaleYVal -= radix;
    } else {
   
        // 3. 鼠标往上滚动

        if (zoom >= 20) return;
        zoom++;

        scaleXVal += radix;
        scaleYVal += radix;
    }

    img.style.transform = `scale(${scaleXVal}, ${scaleYVal})`;
})

上面的代码已经完成了缩放,但是缩放是从左上进行的,我们的目标是沿着鼠标的点进行缩放的。

处理鼠标缩放,根据鼠标坐标点

这里的思路其实很简单(其实自己想了很久),假如图片大小是100px,放大0.1倍之后,图片就是110px,图片是左上角缩放的,那么如果鼠标是在右下角,鼠标的坐标相当于图片来说,放大之前是x: 100 y:100,放大之后是:x: 110 y: 110,这个时候只需要让图片向左上偏移10px,也就是100 * 0.1

同理:
如果鼠标在左下角:缩放前坐标:x: 0 y: 100,缩放后坐标:x: 0 y: 110;
如果鼠标在左中间:缩放前坐标:x: 0 y: 500,缩放后坐标:x: 0 y: 55;

通过上面的规律可得出这样的公式:

(鼠标基于图片的x坐标 / (图片的宽度 * 之前的缩放值)) * (图片的宽度 * 缩放基数)

上面的是宽度,高度也一样,修改后的代码如下:

// 1. 监听鼠标滚轮滚动事件
let zoom = 0; // 设置极值
img.addEventListener('wheel', (evt) => {
   
    // 获取图片的缩放数据
    const transform = img.style.transform;
    const scale = /scale(-?\d+(.\d+)?,\s?-?\d+?(.\d+)?)/g.exec(transform);
    if (!scale) return;

    // 理论上两个值都是一样的,这里还是写两个,可以对宽高设置不同比例
    const regExp = /-?\d+(.\d+)?/g;
    let scaleXVal = Number(regExp.exec(scale[0])[0]);
    let scaleYVal = Number(regExp.exec(scale[0])[0]);

    // 获取缩放前鼠标基于图片的位置
    const ratioX = evt.layerX / (img.width * scaleXVal);
    const ratioY = evt.layerY / (img.height * scaleXVal);

    // 缩放比例
    const radix = 0.1;

    // 计算缩放了多少
    const scaleX = img.width * radix;
    const scaleY = img.height * radix;

     // 获取图片的定位
    const left = parseInt(img.style.left);
    const top = parseInt(img.style.top);

    if (evt.deltaY > 0) {
   
        // 2. 鼠往下滚动

        if (zoom <= 0) return;
        zoom--;

        scaleXVal -= radix;
        scaleYVal -= radix;

        // 缩小偏移是以增加的方式
        img.style.left = (left + ratioX * scaleX) + 'px';
        img.style.top = (top + ratioY * scaleY) + 'px';
    } else {
   
        // 3. 鼠标往上滚动

        if (zoom >= 20) return;
        zoom++;

        scaleXVal += radix;
        scaleYVal += radix;

        // 缩小偏移是以减少的方式
        img.style.left = (left - ratioX * scaleX) + 'px';
        img.style.top = (top - ratioY * scaleY) + 'px';
    }

    // 设置缩放
    img.style.transform = `scale(${scaleXVal}, ${scaleYVal})`;
})
目录
相关文章
|
6月前
|
前端开发 JavaScript
拖拽旋转相册
拖拽旋转相册
|
1月前
ThreeJs添加拖动辅助线
这篇文章介绍了在Three.js中使用TransformControls组件来添加拖动辅助线,实现对3D模型在不同轴向上进行直观的拖动和平移操作。
50 0
|
1月前
ThreeJs的场景实现鼠标拖动旋转控制
这篇文章介绍了如何在Three.js中实现通过鼠标拖动来旋转场景中的模型,并提供了实现这一功能的代码示例。
53 0
|
6月前
|
索引
【sgPhotoPlayer】自定义组件:图片预览,支持点击放大、缩小、旋转图片
【sgPhotoPlayer】自定义组件:图片预览,支持点击放大、缩小、旋转图片
|
6月前
Echarts图表设置x轴y轴均随滚轮滚动缩+放 区域缩放
Echarts图表设置x轴y轴均随滚轮滚动缩+放 区域缩放
550 0
|
Web App开发 移动开发 前端开发
移动端图片操作(二)——预览、旋转、合成
在上一节中已经提到了预览,预览可以通过data: URL格式或URL对象。
移动端图片操作(二)——预览、旋转、合成
|
6月前
|
测试技术 定位技术
【sgDragMoveTile】自定义组件:拖拽瓦片图、地图、大图,滚动条对应同步滚动
【sgDragMoveTile】自定义组件:拖拽瓦片图、地图、大图,滚动条对应同步滚动
图片和文件预览组件(部分源码),可拖动,缩小,放大。 #41
图片和文件预览组件(部分源码),可拖动,缩小,放大。 #41
146 0
|
Windows 容器
一款简单的缩放拖拽图片控件
本文介绍一个针对 .NET 桌面应用程序的独立图片缩放拖拽显示控件 [SQPhoto](https://www.nuget.org/packages/SQPhoto/)。
192 0
一款简单的缩放拖拽图片控件