SSGOI LogoSSGOI

Instagram 过渡效果

图片展开并过渡到详细视图的 Instagram 风格过渡

Instagram 过渡效果

Instagram 过渡是一种扩展型过渡(Expansion Transition),网格中的小图片会扩展到全屏。与 Pinterest 过渡类似,但离开页面保持静态无动画,只有进入页面有动画。

演示

Loading demo...

UX 原则

何时使用?

Instagram 过渡用于从以图片为中心的信息流到详细视图的过渡

根据内容关系的适用性

콘텐츠 관계적합성설명
不相关的内容没有可扩展的连接点,不适合
同级关系⚠️可考虑用于网格内图片间的导航
层级关系最适合相同内容的放大视图,如缩略图→全屏视图

主要使用场景

  • 社交媒体信息流: 从网格信息流到帖子详情的过渡
  • 照片画廊: 从小缩略图放大到大图片
  • 作品集: 从作品网格到详细视图
  • 产品列表: 从产品网格到详情页

为什么这样工作?

  1. 保持焦点: 离开页面保持不变,让用户清楚知道从哪里来
  2. 性能优化: 只对一个页面进行动画,确保流畅过渡
  3. 自然扩展: 所选图片自然地填满屏幕
  4. 保持上下文: 背景保持可见,防止方向迷失

动效设计

扩展过程(画廊 → 详情):
1. 画廊页面保持静态(无动画)
2. 详情页从所选图片位置扩展
3. 通过 clip-path 放大填满屏幕
4. 最终占据整个屏幕

收缩过程(详情 → 画廊):
1. 详情页保持静态(无动画)
2. 画廊页面在收缩到目标图片位置时出现
3. 通过不透明度和缩放动画流畅出现

基本用法

1. 配置过渡

import { Ssgoi } from '@ssgoi/react';
import { instagram } from '@ssgoi/react/view-transitions';

const config = {
  transitions: [
    {
      from: '/gallery',
      to: '/gallery/*',
      transition: instagram(),
      symmetric: true
    }
  ]
};

export default function App() {
  return (
    <Ssgoi config={config}>
      {/* 应用内容 */}
    </Ssgoi>
  );
}

2. 标记元素

为画廊和详情页使用不同的 data 属性:

// 画廊页面(3列网格)
function Gallery() {
  return (
    <div className="grid grid-cols-3 gap-1">
      {items.map(item => (
        <Link
          key={item.id}
          to={`/gallery/${item.id}`}
          className="aspect-square"
        >
          <img
            src={item.image}
            alt={item.title}
            data-instagram-gallery-key={item.id}
            className="w-full h-full object-cover"
          />
        </Link>
      ))}
    </div>
  );
}

// 详情页
function PostDetail({ item }) {
  return (
    <div className="min-h-screen">
      <img
        src={item.image}
        alt={item.title}
        data-instagram-detail-key={item.id}
        className="w-full h-auto"
      />
      <div className="p-4">
        <p className="font-semibold">{item.likes} likes</p>
        <p className="mt-2">{item.caption}</p>
      </div>
    </div>
  );
}

实践示例

1. Instagram 风格信息流

统一的3列网格:

// 3列网格
function InstagramFeed() {
  return (
    <div className="grid grid-cols-3 gap-1">
      {posts.map((post) => (
        <div
          key={post.id}
          onClick={() => navigate(`/post/${post.id}`)}
          className="relative aspect-square cursor-pointer group"
        >
          <img
            src={post.image}
            alt={post.title}
            data-instagram-gallery-key={post.id}
            className="w-full h-full object-cover"
          />
          {/* 悬停叠加层 */}
          <div className="absolute inset-0 bg-black/40 opacity-0
                          group-hover:opacity-100 transition-opacity
                          flex items-center justify-center">
            <span className="text-white font-semibold">
              ❤️ {post.likes.toLocaleString()}
            </span>
          </div>
        </div>
      ))}
    </div>
  );
}

// 帖子详情
function PostView({ post }) {
  return (
    <div className="min-h-screen bg-black">
      <img
        src={post.image}
        alt={post.title}
        data-instagram-detail-key={post.id}
        className="w-full h-auto"
      />

      {/* 帖子详情 */}
      <div className="bg-white p-4">
        <div className="flex items-center gap-4 mb-4">
          <button className="flex items-center gap-2">
            <HeartIcon className="w-6 h-6" />
            <span>{post.likes}</span>
          </button>
          <button>
            <CommentIcon className="w-6 h-6" />
          </button>
          <button>
            <ShareIcon className="w-6 h-6" />
          </button>
        </div>

        <p className="text-sm">
          <strong>{post.author}</strong> {post.caption}
        </p>

        <p className="text-xs text-gray-500 mt-2">
          {post.timestamp}
        </p>
      </div>
    </div>
  );
}

2. 照片作品集

从作品网格到全屏:

// 作品集网格
function PortfolioGrid() {
  return (
    <main className="container mx-auto p-4">
      <div className="grid grid-cols-3 gap-4">
        {works.map((work) => (
          <article
            key={work.id}
            onClick={() => navigate(`/work/${work.id}`)}
            className="relative aspect-square rounded-lg overflow-hidden
                       cursor-pointer hover:scale-105 transition-transform"
          >
            <img
              src={work.thumbnail}
              alt={work.title}
              data-instagram-gallery-key={work.id}
              className="w-full h-full object-cover"
            />
            <div className="absolute bottom-0 left-0 right-0 p-3
                            bg-gradient-to-t from-black/80 to-transparent">
              <h3 className="text-white font-semibold text-sm">
                {work.title}
              </h3>
              <p className="text-white/80 text-xs">{work.category}</p>
            </div>
          </article>
        ))}
      </div>
    </main>
  );
}

// 作品详情
function WorkDetail({ work }) {
  return (
    <div className="min-h-screen bg-gray-900">
      <div className="max-w-4xl mx-auto">
        <img
          src={work.image}
          alt={work.title}
          data-instagram-detail-key={work.id}
          className="w-full h-auto"
        />

        <div className="p-6 text-white">
          <h1 className="text-3xl font-bold mb-2">{work.title}</h1>
          <p className="text-gray-400 mb-4">{work.category}</p>
          <p className="text-gray-300 leading-relaxed">
            {work.description}
          </p>

          <div className="mt-6 flex gap-4">
            <span className="text-sm text-gray-400">
              {work.date}
            </span>
            <span className="text-sm text-gray-400">
              {work.client}
            </span>
          </div>
        </div>
      </div>
    </div>
  );
}

自定义

弹簧配置

调整动画的弹性和速度:

instagram({
  spring: {
    stiffness: 150,  // 越低越平滑
    damping: 20      // 越高越快稳定
  }
})

注意事项

区分键属性

  • 画廊: data-instagram-gallery-key
  • 详情: data-instagram-detail-key
  • 必须使用不同的属性名

布局考虑事项

  • 画廊应使用统一网格(推荐3列)
  • 详情页需要足够的内边距
  • 图片应保持宽高比

与 Pinterest 过渡的区别

  • Pinterest: 两个页面都有动画
  • Instagram: 只有进入页面有动画
  • 性能: Instagram 更轻量更快
  • 用途: Instagram 最适合统一网格

无障碍访问

  • 支持 ESC 键关闭详情页
  • 增强的键盘导航
  • 减少动效设置时立即过渡

最佳实践

✅ 应该做的

  • 用于统一的3列网格
  • 应用于以图片为中心的内容
  • 提供清晰的关闭按钮
  • 使用移动优化的布局

❌ 不应该做的

  • 不适合不规则网格
  • 避免用于以文字为主的内容
  • 不要同时扩展太多元素
  • 在慢速网络上使用大图片要小心