写博客是为了和过去以及未来的自己对话
“对任何渴望进步的人来说,写博客/文章/回答对自己的成长帮助都是巨大的。坚持写作的那一刻起,就已经开始受益。”

实现一个可缩放并支持滚动的图片容器

在这篇博客中,我将介绍如何实现一个简单的交互功能:在一个 div 中放置一张图片,通过鼠标拖动操作实现图片缩放,并且在图片放大后,滚动条能够调整位置以查看被放大的部分。

以下是具体实现的代码和步骤。


功能概述

  1. 鼠标交互:当按下鼠标时记录初始位置;松开鼠标时,根据鼠标的移动方向和距离来决定图片的缩放系数。
  2. 缩放中心:缩放以鼠标按下位置为基准。
  3. 滚动支持:图片放大后,容器的滚动条会调整位置,保证缩放基准点的位置始终保持相对稳定。
    为什么这里要通过移动滚动条的位置,而不是直接通过计算鼠标按下点的横纵轴比例使用transformOrigin属性变换图片,是因为scale放大图片的时候,如果不是以(0,0)作为基点,那么上下左右都可能超出容器,而滚动条只有在右边和下边超出容器的时候才会出现,而且初始滚动条位置是0的位置,那么就会导致图片的左上部分可能出现被裁剪的情况。(还有一种方式是直接通过修改图片的宽高,这时候因为是图片的实际尺寸变化了,所以滚动条是可以显示全部图片的,这种相比transform的性能开销更大,因为浏览器需要重新计算布局)

HTML 代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Image Zoom with Scroll</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }

        .container {
            position: relative;
            width: 1200px;
            height: auto;
            overflow: auto;
            background: #f0f0f0;
            cursor: grab;
        }

        img {
            display: block;
            transform-origin: center;
            transition: transform 0.1s ease;
        }
    </style>
</head>

<body>
    <div class="container" id="zoomContainer">
        <img src="C:\Users\27643\Pictures\Screenshots\スクリーンショット 2024-05-14 014320.png" alt="Zoomable Image"
            id="zoomImage" draggable="false">
    </div>
    <script>
        const container = document.getElementById('zoomContainer');
        const image = document.getElementById('zoomImage');

        let scale = 1;
        let isMouseDown = false;
        let startX, startY;
        let endX, endY;

        image.addEventListener('mousedown', (e) => {
            e.preventDefault();
            isMouseDown = true;
            startX = e.clientX - container.offsetLeft;
            startY = e.clientY - container.offsetTop;
            console.log("start: " + startX + "  " + startY);
        });

        image.addEventListener('mousemove', (e) => {
            if (!isMouseDown) return;

            endX = e.clientX - container.offsetLeft;
            endY = e.clientY - container.offsetTop;
            console.log("end: " + endX + "  " + endY);
        });

        image.addEventListener('mouseup', () => {
            if (!isMouseDown) return;
            isMouseDown = false;
            if (startX === undefined || startY === undefined || endX === undefined || endY === undefined) {
                return;
            }
            const deltaX = endX - startX;
            const deltaY = endY - startY;
            const movementMagnitude = Math.sqrt(deltaX ** 2 + deltaY ** 2);
            const movementDirection = deltaX + deltaY; // Positive: zoom in, Negative: zoom out

            const zoomFactor = 1 + (movementDirection > 0 ? 1 : -1) * movementMagnitude * 0.001;
            const newScale = Math.min(10, Math.max(0.3, scale * zoomFactor));
            console.log(newScale);

            const cursorX = startX + container.scrollLeft;
            const cursorY = startY + container.scrollTop;

            const cursorRatioX = cursorX / (image.offsetWidth * scale);
            const cursorRatioY = cursorY / (image.offsetHeight * scale);

            scale = newScale;

            const newCursorX = cursorRatioX * (image.offsetWidth * scale);
            const newCursorY = cursorRatioY * (image.offsetHeight * scale);

            image.style.transform = `scale(${scale})`;
            image.style.transformOrigin = "0 0";

            container.scrollLeft = newCursorX - startX;
            container.scrollTop = newCursorY - startY;
            startX = undefined;
            startY = undefined;
            endX = undefined;
            endY = undefined;
        });

        container.addEventListener('mouseleave', () => {
            isMouseDown = false;
        });
    </script>
</body>

</html>

实现细节

样式部分

容器 .container

设置 position: relativeoverflow: auto,保证图片可以超出容器并通过滚动条查看。

使用固定宽度和自适应高度。

图片样式

通过 transform-origin: center 控制缩放的基准点。

transition: transform 0.1s ease 用于增加平滑的缩放过渡效果。

脚本部分

鼠标事件监听

mousedown:记录鼠标按下时的坐标。

mousemove:跟踪鼠标移动(可选,仅用于调试)。

mouseup:计算鼠标的位移,决定缩放方向和缩放比例。

缩放基准点

使用 transform-origin 属性,使图片基于左上顶点变换,从而能够让滚动条显示完整图片

计算容器滚动条的位置,保证缩放后鼠标位置相对于容器保持不变。

滚动条调整

根据鼠标点击点与图片的相对位置,调整滚动条的 scrollLeft scrollTop,保证用户能查看到缩放后的区域。

缩放逻辑

计算鼠标移动的方向和距离:

const deltaX = endX - startX;
const deltaY = endY - startY;
const movementMagnitude = Math.sqrt(deltaX ** 2 + deltaY ** 2);
const movementDirection = deltaX + deltaY;

根据 movementDirection 决定是放大还是缩小。

限制缩放比例在合理范围内(0.3 到 10 倍):

const newScale = Math.min(10, Math.max(0.3, scale * zoomFactor));

总结

这段代码实现了图片的基于鼠标交互的缩放功能,并且通过合理调整滚动条位置,让用户能够查看被放大图片的细节区域。如果你有类似的需求,可以直接复用或根据自己的场景进一步优化!

posted on 2025-01-29 00:52  sxb_sunday  阅读(178)  评论(0)    收藏  举报