效果如下:

代码如下:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>商品图片切换展示 - 极客定制</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
            background: #f5f5f5;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            padding: 40px 20px;
        }

        .product-gallery {
            display: flex;
            gap: 20px;
            background: #fff;
            padding: 30px;
            border-radius: 16px;
            box-shadow: 0 10px 40px rgba(0,0,0,0.08);
            max-width: 900px;
            width: 100%;
            position: relative;
        }

        /* 左侧缩略图列表 */
        .thumbnail-list {
            display: flex;
            flex-direction: column;
            gap: 12px;
            width: 80px;
            flex-shrink: 0;
            max-height: 500px;
            overflow-y: auto;
            overflow-x: hidden;
            padding-right: 4px;
        }

        .thumbnail-list::-webkit-scrollbar {
            width: 4px;
        }
        .thumbnail-list::-webkit-scrollbar-track {
            background: transparent;
        }
        .thumbnail-list::-webkit-scrollbar-thumb {
            background: #ddd;
            border-radius: 4px;
        }
        .thumbnail-list::-webkit-scrollbar-thumb:hover {
            background: #bbb;
        }
        .thumbnail-list {
            scrollbar-width: thin;
            scrollbar-color: #ddd transparent;
        }

        .thumb-item {
            width: 76px;
            height: 76px;
            border-radius: 8px;
            overflow: hidden;
            cursor: pointer;
            border: 2px solid transparent;
            transition: all 0.3s ease;
            position: relative;
            background: #f8f8f8;
            flex-shrink: 0;
        }

        .thumb-item img {
            width: 100%;
            height: 100%;
            object-fit: cover;
            transition: transform 0.3s ease;
        }

        .thumb-item:hover img {
            transform: scale(1.05);
        }

        .thumb-item.active {
            border-color: #ff6b35;
            box-shadow: 0 0 0 3px rgba(255, 107, 53, 0.15);
        }

        .thumb-item.active::after {
            content: '';
            position: absolute;
            inset: 0;
            border-radius: 6px;
            border: 2px solid #ff6b35;
            pointer-events: none;
        }

        /* 右侧大图区域 */
        .main-image-wrapper {
            flex: 1;
            position: relative;
            border-radius: 12px;
            min-height: 500px;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        .main-image-container {
            position: relative;
            width: 100%;
            height: 100%;
            min-height: 500px;
            display: flex;
            align-items: center;
            justify-content: center;
            overflow: hidden;
            cursor: crosshair;
            border-radius: 12px;
            background: #f8f8f8;
        }

        .main-image-container img {
            max-width: 100%;
            max-height: 500px;
            object-fit: contain;
            transition: opacity 0.3s ease, transform 0.3s ease;
            opacity: 1;
        }

        .main-image-container img.fade-out {
            opacity: 0;
            transform: scale(0.98);
        }

        /* 放大镜 — 正方形 */
        .zoom-lens {
            position: absolute;
            width: 140px;
            height: 140px;
            border: 2px solid rgba(255, 107, 53, 0.9);
            background: rgba(255, 255, 255, 0.15);
            pointer-events: none;
            opacity: 0;
            transition: opacity 0.2s ease;
            box-shadow: 0 0 15px rgba(0,0,0,0.15), inset 0 0 20px rgba(255,255,255,0.3);
            display: none;
            z-index: 20;
            left: 0;
            top: 0;
        }

        /* 放大结果窗口 — 正方形 */
        .zoom-result {
            position: absolute;
            top: 30px;
            left: calc(100% + 20px);
            width: 400px;
            height: 400px;
            border-radius: 12px;
            overflow: hidden;
            border: 1px solid #eee;
            background: #fff;
            opacity: 0;
            pointer-events: none;
            transition: opacity 0.3s ease;
            box-shadow: 0 10px 40px rgba(0,0,0,0.15);
            z-index: 100;
        }

        .zoom-result.visible {
            opacity: 1;
        }

        .zoom-result img {
            position: absolute;
            top: 0;
            left: 0;
            object-fit: cover;
            transition: none;
        }

        /* 图片指示器 */
        .image-indicator {
            position: absolute;
            bottom: 15px;
            left: 50%;
            transform: translateX(-50%);
            display: flex;
            gap: 6px;
            z-index: 10;
        }

        .dot {
            width: 8px;
            height: 8px;
            border-radius: 50%;
            background: rgba(0,0,0,0.2);
            transition: all 0.3s ease;
            cursor: pointer;
        }

        .dot.active {
            background: #ff6b35;
            width: 20px;
            border-radius: 4px;
        }

        /* 提示文字 */
        .zoom-hint {
            position: absolute;
            bottom: 40px;
            left: 50%;
            transform: translateX(-50%);
            background: rgba(0,0,0,0.6);
            color: #fff;
            padding: 6px 14px;
            border-radius: 20px;
            font-size: 12px;
            pointer-events: none;
            opacity: 0.8;
            transition: opacity 0.3s ease;
            z-index: 15;
        }

        .main-image-container:hover .zoom-hint {
            opacity: 0;
        }

        /* 响应式 */
        @media (max-width: 768px) {
            .product-gallery {
                flex-direction: column-reverse;
                align-items: center;
            }
            .thumbnail-list {
                flex-direction: row;
                width: 100%;
                max-height: none;
                overflow-x: auto;
                overflow-y: hidden;
                padding-right: 0;
                padding-bottom: 4px;
                justify-content: flex-start;
            }
            .thumbnail-list::-webkit-scrollbar {
                height: 4px;
            }
            .thumb-item {
                width: 76px;
                height: 76px;
            }
            .main-image-wrapper {
                min-height: 350px;
            }
            .main-image-container {
                min-height: 350px;
            }
            .zoom-result {
                display: none !important;
            }
            .zoom-lens {
                display: none !important;
            }
        }
    </style>
<base target="_blank">
</head>
<body>

    <div class="product-gallery" id="productGallery">
        <!-- 左侧缩略图 — 只需在这里写图片 -->
        <div class="thumbnail-list" id="thumbnailList">
            <div class="thumb-item active" data-main="https://images.unsplash.com/photo-1521572163474-6864f6400bc2?w=800&h=800&fit=crop" data-zoom="https://images.unsplash.com/photo-1521572163474-6864f6400bc2?w=1600&h=1600&fit=crop">
                <img src="https://images.unsplash.com/photo-1521572163474-6864f6400bc2?w=200&h=200&fit=crop" alt="商品图1">
            </div>
            <div class="thumb-item" data-main="https://images.unsplash.com/photo-1581655353564-dfba12320e24?w=800&h=800&fit=crop" data-zoom="https://images.unsplash.com/photo-1581655353564-dfba12320e24?w=1600&h=1600&fit=crop">
                <img src="https://images.unsplash.com/photo-1581655353564-dfba12320e24?w=200&h=200&fit=crop" alt="商品图2">
            </div>
            <div class="thumb-item" data-main="https://images.unsplash.com/photo-1576566588028-4147f3842f27?w=800&h=800&fit=crop" data-zoom="https://images.unsplash.com/photo-1576566588028-4147f3842f27?w=1600&h=1600&fit=crop">
                <img src="https://images.unsplash.com/photo-1576566588028-4147f3842f27?w=200&h=200&fit=crop" alt="商品图3">
            </div>
            <div class="thumb-item" data-main="https://images.unsplash.com/photo-1618354691373-d851c5c3a990?w=800&h=800&fit=crop" data-zoom="https://images.unsplash.com/photo-1618354691373-d851c5c3a990?w=1600&h=1600&fit=crop">
                <img src="https://images.unsplash.com/photo-1618354691373-d851c5c3a990?w=200&h=200&fit=crop" alt="商品图4">
            </div>
            <div class="thumb-item" data-main="https://images.unsplash.com/photo-1503341504253-dff4815485f1?w=800&h=800&fit=crop" data-zoom="https://images.unsplash.com/photo-1503341504253-dff4815485f1?w=1600&h=1600&fit=crop">
                <img src="https://images.unsplash.com/photo-1503341504253-dff4815485f1?w=200&h=200&fit=crop" alt="商品图5">
            </div>
            <div class="thumb-item" data-main="https://images.unsplash.com/photo-1562157873-818bc0726f68?w=800&h=800&fit=crop" data-zoom="https://images.unsplash.com/photo-1562157873-818bc0726f68?w=1600&h=1600&fit=crop">
                <img src="https://images.unsplash.com/photo-1562157873-818bc0726f68?w=200&h=200&fit=crop" alt="商品图6">
            </div>
            <div class="thumb-item" data-main="https://images.unsplash.com/photo-1556821840-3a63f95609a7?w=800&h=800&fit=crop" data-zoom="https://images.unsplash.com/photo-1556821840-3a63f95609a7?w=1600&h=1600&fit=crop">
                <img src="https://images.unsplash.com/photo-1556821840-3a63f95609a7?w=200&h=200&fit=crop" alt="商品图7">
            </div>
            <div class="thumb-item" data-main="https://images.unsplash.com/photo-1583743814966-8936f5b7be1a?w=800&h=800&fit=crop" data-zoom="https://images.unsplash.com/photo-1583743814966-8936f5b7be1a?w=1600&h=1600&fit=crop">
                <img src="https://images.unsplash.com/photo-1583743814966-8936f5b7be1a?w=200&h=200&fit=crop" alt="商品图8">
            </div>
            <div class="thumb-item" data-main="https://images.unsplash.com/photo-1596755094514-f87e34085b2c?w=800&h=800&fit=crop" data-zoom="https://images.unsplash.com/photo-1596755094514-f87e34085b2c?w=1600&h=1600&fit=crop">
                <img src="https://images.unsplash.com/photo-1596755094514-f87e34085b2c?w=200&h=200&fit=crop" alt="商品图9">
            </div>
            <div class="thumb-item" data-main="https://images.unsplash.com/photo-1620799140408-edc6dcb6d633?w=800&h=800&fit=crop" data-zoom="https://images.unsplash.com/photo-1620799140408-edc6dcb6d633?w=1600&h=1600&fit=crop">
                <img src="https://images.unsplash.com/photo-1620799140408-edc6dcb6d633?w=200&h=200&fit=crop" alt="商品图10">
            </div>
        </div>

        <!-- 右侧大图 -->
        <div class="main-image-wrapper" id="mainImageWrapper">
            <div class="main-image-container" id="mainImageContainer">
                <img id="mainImage" src="" alt="主图">
                <div class="zoom-lens" id="zoomLens"></div>
                <div class="zoom-hint">鼠标悬停查看放大效果</div>
            </div>
            <div class="image-indicator" id="imageIndicator"></div>
        </div>

        <!-- 放大窗口 — 正方形 -->
        <div class="zoom-result" id="zoomResult">
            <img id="zoomResultImg" src="" alt="放大图">
        </div>
    </div>

    <script>
        let currentIndex = 0;
        let isZoomVisible = false;
        let zoomReady = false;

        const mainImage = document.getElementById('mainImage');
        const zoomResultImg = document.getElementById('zoomResultImg');
        const mainContainer = document.getElementById('mainImageContainer');
        const zoomLens = document.getElementById('zoomLens');
        const zoomResult = document.getElementById('zoomResult');
        const mainWrapper = document.getElementById('mainImageWrapper');
        const productGallery = document.getElementById('productGallery');
        const thumbnailList = document.getElementById('thumbnailList');
        const imageIndicator = document.getElementById('imageIndicator');
        const thumbItems = () => thumbnailList.querySelectorAll('.thumb-item');

        function getImagesFromDOM() {
            const items = thumbItems();
            return Array.from(items).map(item => ({
                thumb: item.querySelector('img').src,
                main: item.dataset.main,
                zoom: item.dataset.zoom
            }));
        }

        function renderDots(count) {
            imageIndicator.innerHTML = '';
            for (let i = 0; i < count; i++) {
                const dot = document.createElement('div');
                dot.className = 'dot' + (i === 0 ? ' active' : '');
                dot.addEventListener('click', () => switchImage(i));
                imageIndicator.appendChild(dot);
            }
        }

        function switchImage(index) {
            const items = thumbItems();
            const images = getImagesFromDOM();

            if (index === currentIndex) return;
            if (index < 0 || index >= images.length) return;

            currentIndex = index;
            zoomReady = false;

            items.forEach((item, i) => {
                item.classList.toggle('active', i === index);
            });

            const dots = imageIndicator.querySelectorAll('.dot');
            dots.forEach((dot, i) => {
                dot.classList.toggle('active', i === index);
            });

            mainImage.classList.add('fade-out');

            setTimeout(() => {
                mainImage.src = images[index].main;
                zoomResultImg.src = images[index].zoom;
                mainImage.classList.remove('fade-out');
                waitForImagesReady();
            }, 200);

            const activeItem = items[index];
            if (activeItem) {
                activeItem.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
            }
        }

        function bindThumbnailEvents() {
            const items = thumbItems();
            items.forEach((item, index) => {
                item.addEventListener('mouseenter', () => switchImage(index));
                item.addEventListener('click', () => switchImage(index));
            });
        }

        function positionZoomResult() {
            if (window.innerWidth <= 768) return;
            const wrapperRect = mainWrapper.getBoundingClientRect();
            const galleryRect = productGallery.getBoundingClientRect();
            zoomResult.style.top = (wrapperRect.top - galleryRect.top) + 'px';
            zoomResult.style.left = (wrapperRect.right - galleryRect.left + 20) + 'px';
        }

        // 等待主图和放大图都加载完成
        function waitForImagesReady() {
            let mainLoaded = false;
            let zoomLoaded = false;

            function check() {
                if (mainLoaded && zoomLoaded) {
                    initZoomLens();
                    zoomReady = true;
                    return;
                }
                setTimeout(check, 50);
            }

            function onMainLoad() {
                mainLoaded = true;
                mainImage.removeEventListener('load', onMainLoad);
            }
            function onZoomLoad() {
                zoomLoaded = true;
                zoomResultImg.removeEventListener('load', onZoomLoad);
            }

            if (mainImage.complete && mainImage.naturalWidth > 0) {
                mainLoaded = true;
            } else {
                mainImage.addEventListener('load', onMainLoad);
            }

            if (zoomResultImg.complete && zoomResultImg.naturalWidth > 0) {
                zoomLoaded = true;
            } else {
                zoomResultImg.addEventListener('load', onZoomLoad);
            }

            check();
        }

        // 初始化放大镜
        function initZoomLens() {
            if (!mainImage.naturalWidth || !zoomResultImg.naturalWidth) return;
            zoomResultImg.style.width = zoomResultImg.naturalWidth + 'px';
            zoomResultImg.style.height = zoomResultImg.naturalHeight + 'px';
        }

        // 检测鼠标是否在当前图片的实际显示区域内
        function isMouseInImage(e) {
            const imgRect = mainImage.getBoundingClientRect();
            return (
                e.clientX >= imgRect.left &&
                e.clientX <= imgRect.right &&
                e.clientY >= imgRect.top &&
                e.clientY <= imgRect.bottom
            );
        }

        // 显示放大镜和放大窗口
        function showZoom() {
            if (isZoomVisible) return;
            isZoomVisible = true;
            positionZoomResult();
            zoomLens.style.display = 'block';
            zoomResult.classList.add('visible');
            requestAnimationFrame(() => {
                zoomLens.style.opacity = '1';
            });
        }

        // 隐藏放大镜和放大窗口
        function hideZoom() {
            if (!isZoomVisible) return;
            isZoomVisible = false;
            zoomLens.style.opacity = '0';
            zoomResult.classList.remove('visible');
            setTimeout(() => {
                if (!isZoomVisible) zoomLens.style.display = 'none';
            }, 200);
        }

        // 更新放大镜位置
        function updateZoomPosition(e) {
            if (!zoomReady) return;

            const imgRect = mainImage.getBoundingClientRect();
            const containerRect = mainContainer.getBoundingClientRect();

            // 图片在容器中的 CSS 显示区域
            const imgLeft = imgRect.left - containerRect.left;
            const imgTop = imgRect.top - containerRect.top;
            const imgDisplayW = imgRect.width;
            const imgDisplayH = imgRect.height;

            // 放大镜尺寸固定(从 CSS 读取)
            const lensSize = 140;

            // 鼠标在容器中的位置
            const mouseX = e.clientX - containerRect.left;
            const mouseY = e.clientY - containerRect.top;

            // 放大镜左上角位置:以鼠标为中心,但整个放大镜不能超出图片 CSS 边界
            let lensLeft = mouseX - lensSize / 2;
            let lensTop = mouseY - lensSize / 2;

            // 限制放大镜在图片边界内(当鼠标到边缘,放大镜被"顶住")
            lensLeft = Math.max(imgLeft, Math.min(lensLeft, imgLeft + imgDisplayW - lensSize));
            lensTop = Math.max(imgTop, Math.min(lensTop, imgTop + imgDisplayH - lensSize));

            zoomLens.style.left = lensLeft + 'px';
            zoomLens.style.top = lensTop + 'px';

            // 放大镜在图片 CSS 显示区域内的相对比例(0~1)
            const ratioX = (lensLeft - imgLeft) / (imgDisplayW - lensSize);
            const ratioY = (lensTop - imgTop) / (imgDisplayH - lensSize);

            // 放大图偏移:按同样的比例映射到放大图 natural 尺寸
            const resultX = -ratioX * (zoomResultImg.naturalWidth - zoomResult.offsetWidth);
            const resultY = -ratioY * (zoomResultImg.naturalHeight - zoomResult.offsetHeight);

            zoomResultImg.style.left = resultX + 'px';
            zoomResultImg.style.top = resultY + 'px';
        }

        // 鼠标进入容器
        mainContainer.addEventListener('mouseenter', function(e) {
            if (window.innerWidth <= 768) return;
            if (!zoomReady) return;
            if (isMouseInImage(e)) {
                showZoom();
                updateZoomPosition(e);
            }
        });

        // 鼠标在容器内移动
        mainContainer.addEventListener('mousemove', function(e) {
            if (window.innerWidth <= 768) return;
            if (!zoomReady) return;

            if (isMouseInImage(e)) {
                showZoom();
                updateZoomPosition(e);
            } else {
                hideZoom();
            }
        });

        // 鼠标离开容器
        mainContainer.addEventListener('mouseleave', function() {
            hideZoom();
        });

        window.addEventListener('resize', function() {
            positionZoomResult();
            initZoomLens();
        });

        document.addEventListener('keydown', function(e) {
            const total = thumbItems().length;
            if (e.key === 'ArrowLeft' && currentIndex > 0) {
                switchImage(currentIndex - 1);
            } else if (e.key === 'ArrowRight' && currentIndex < total - 1) {
                switchImage(currentIndex + 1);
            }
        });

        function init() {
            const images = getImagesFromDOM();
            if (images.length === 0) return;

            renderDots(images.length);
            bindThumbnailEvents();

            mainImage.src = images[0].main;
            zoomResultImg.src = images[0].zoom;

            waitForImagesReady();
        }

        init();
    </script>
</body>
</html>