You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何借助共享元素实现屏幕间导航与图片平滑缩放过渡?

实现缩略图平滑放大弹窗及返回效果

核心思路

通过CSS过渡(transition)结合JavaScript动态计算元素位置/尺寸,让弹窗内的图片从缩略图的位置、大小平滑过渡到全屏状态,关闭时反向过渡回原位置。同时通过transform: scale实现缩放功能。


1. HTML结构

需要列表缩略图容器和全屏弹窗容器,弹窗内包含原图、缩放按钮、关闭按钮:

<!-- 列表缩略图 -->
<div class="thumbnail-list">
  <img 
    class="thumbnail" 
    src="thumb-1.jpg" 
    data-original="original-1.jpg"
    alt="示例图片"
  >
  <!-- 更多缩略图... -->
</div>

<!-- 全屏弹窗 -->
<div class="image-modal" id="imageModal">
  <button class="modal-close" id="modalClose">&times;</button>
  <div class="modal-controls">
    <button class="zoom-btn" id="zoomIn">+</button>
    <button class="zoom-btn" id="zoomOut">-</button>
  </div>
  <img class="modal-image" id="modalImage" src="" alt="">
</div>

2. CSS样式

重点是给弹窗图片设置过渡属性,同时控制弹窗的全屏覆盖、层级:

/* 缩略图样式 */
.thumbnail {
  width: 200px;
  height: 150px;
  object-fit: cover;
  cursor: zoom-in;
  margin: 10px;
}

/* 弹窗容器 */
.image-modal {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background: rgba(0,0,0,0.8);
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 999;
  overflow: hidden;
}

.image-modal.active {
  display: flex;
}

/* 弹窗图片 */
.modal-image {
  position: absolute;
  transition: transform 0.3s ease, opacity 0.3s ease;
  cursor: grab;
  max-width: 90vw;
  max-height: 90vh;
  opacity: 0;
}

.modal-image.animate-in {
  opacity: 1;
  transform: translate(0, 0) scale(1);
}

/* 控制按钮样式 */
.modal-close {
  position: absolute;
  top: 20px;
  right: 20px;
  color: white;
  background: transparent;
  border: none;
  font-size: 32px;
  cursor: pointer;
}

.modal-controls {
  position: absolute;
  bottom: 20px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 10px;
}

.zoom-btn {
  padding: 8px 16px;
  font-size: 20px;
  cursor: pointer;
  border: none;
  border-radius: 4px;
  background: rgba(255,255,255,0.9);
}

3. JavaScript逻辑

分四个核心功能:打开弹窗的平滑过渡、缩放控制、关闭弹窗的平滑返回、防止滚动穿透:

const modal = document.getElementById('imageModal');
const modalImg = document.getElementById('modalImage');
const closeBtn = document.getElementById('modalClose');
const zoomInBtn = document.getElementById('zoomIn');
const zoomOutBtn = document.getElementById('zoomOut');
const thumbnails = document.querySelectorAll('.thumbnail');

let currentScale = 1;
let currentThumbnail = null;
let initialRect = null;

// 打开弹窗逻辑
thumbnails.forEach(thumb => {
  thumb.addEventListener('click', function() {
    currentThumbnail = this;
    initialRect = this.getBoundingClientRect();
    currentScale = 1;

    // 设置弹窗图片初始位置和大小
    modalImg.src = this.dataset.original;
    modalImg.style.transform = `translate(${initialRect.left}px, ${initialRect.top}px) scale(${initialRect.width / modalImg.naturalWidth})`;
    modalImg.style.opacity = '0';

    // 显示弹窗后触发过渡
    modal.classList.add('active');
    // 强制重绘确保过渡生效
    void modalImg.offsetWidth;
    modalImg.classList.add('animate-in');

    // 禁止背景滚动
    document.body.style.overflow = 'hidden';
  });
});

// 缩放控制
zoomInBtn.addEventListener('click', () => {
  if (currentScale < 3) {
    currentScale += 0.2;
    modalImg.style.transform = `scale(${currentScale})`;
  }
});

zoomOutBtn.addEventListener('click', () => {
  if (currentScale > 0.5) {
    currentScale -= 0.2;
    modalImg.style.transform = `scale(${currentScale})`;
  }
});

// 关闭弹窗逻辑
closeBtn.addEventListener('click', closeModal);

function closeModal() {
  // 移除过渡类,触发反向动画
  modalImg.classList.remove('animate-in');
  // 还原到缩略图位置和大小
  modalImg.style.transform = `translate(${initialRect.left}px, ${initialRect.top}px) scale(${initialRect.width / modalImg.naturalWidth})`;

  // 等待过渡完成后隐藏弹窗
  setTimeout(() => {
    modal.classList.remove('active');
    document.body.style.overflow = '';
  }, 300); // 与CSS过渡时间一致
}

// 点击背景关闭弹窗
modal.addEventListener('click', (e) => {
  if (e.target === modal) {
    closeModal();
  }
});

关键细节说明

  • 过渡触发时机:通过void modalImg.offsetWidth强制浏览器重绘,确保初始状态被正确识别后再触发过渡动画。
  • 位置计算:使用getBoundingClientRect()获取缩略图的视口坐标,保证弹窗图片从精确位置开始过渡。
  • 缩放边界限制:设置缩放范围(0.5-3倍)避免图片过小或过大超出视口。
  • 滚动穿透处理:打开弹窗时禁用body的滚动,关闭后恢复。

内容的提问来源于stack exchange,提问作者KonYaco

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 13:01:06