CSS3 最新特性 - CSS View Transitions
章节标题
CSS View Transitions 详解
核心知识点讲解
1. CSS View Transitions 的基本概念
CSS View Transitions(视图过渡)是 CSS View Transitions Module Level 1 规范中的一个重要特性,它允许开发者在不同页面状态之间创建平滑的过渡动画。通过捕获页面元素的前后状态并在它们之间创建过渡效果,View Transitions 为用户提供了更加流畅、直观的界面体验。
核心思想:
- 捕获阶段(Capture Phase):捕获页面元素在状态变化前后的视觉表现
- 过渡阶段(Transition Phase):在捕获的前后状态之间创建平滑的过渡动画
- 完成阶段(Completion Phase):过渡完成后,显示新的页面状态
优势:
- 提供更加流畅、专业的页面状态过渡效果
- 减少用户在页面切换时的认知负荷
- 支持单页应用和多页应用的页面过渡
- 简化复杂动画的实现,减少 JavaScript 依赖
- 支持细粒度的元素过渡控制
2. CSS View Transitions 的主要 API
2.1 document.startViewTransition() 方法
document.startViewTransition() 是启动视图过渡的主要方法,它接受一个回调函数,该回调函数用于执行导致页面状态变化的操作。
// 基本语法
const transition = document.startViewTransition(callback);
// 示例:切换主题
function toggleTheme() {
document.startViewTransition(() => {
document.documentElement.classList.toggle('dark-theme');
});
}
// 示例:切换页面内容
function navigateTo(pageId) {
document.startViewTransition(() => {
// 隐藏所有页面
document.querySelectorAll('.page').forEach(page => {
page.classList.add('hidden');
});
// 显示目标页面
document.getElementById(pageId).classList.remove('hidden');
});
}返回值:
- 返回一个
ViewTransition对象,包含过渡的状态和方法 - 可以使用
transition.finishedPromise 来监听过渡完成
2.2 ViewTransition 对象
ViewTransition 对象提供了控制和监听过渡的方法和属性:
// ViewTransition 对象的属性和方法
const transition = document.startViewTransition(callback);
// 过渡完成的 Promise
transition.finished.then(() => {
console.log('Transition completed');
});
// 跳过过渡
transition.skipTransition();
// 过渡的当前状态
console.log(transition.ready); // Promise,过渡准备就绪时解析
console.log(transition.domUpdates); // Promise,DOM 更新完成时解析2.3 CSS 伪元素
View Transitions 使用一系列 CSS 伪元素来控制过渡效果:
| 伪元素 | 描述 |
|---|---|
::view-transition |
过渡的根容器 |
::view-transition-group(root) |
根元素的过渡组 |
::view-transition-image-pair(root) |
根元素的前后状态图像对 |
::view-transition-old(root) |
根元素的旧状态图像 |
::view-transition-new(root) |
根元素的新状态图像 |
::view-transition-group(name) |
命名元素的过渡组 |
::view-transition-image-pair(name) |
命名元素的前后状态图像对 |
::view-transition-old(name) |
命名元素的旧状态图像 |
::view-transition-new(name) |
命名元素的新状态图像 |
示例:
/* 自定义根元素过渡 */
::view-transition-old(root) {
animation: fade-out 0.5s ease-in-out;
}
::view-transition-new(root) {
animation: fade-in 0.5s ease-in-out;
}
/* 自定义命名元素过渡 */
::view-transition-old(header) {
animation: slide-out 0.5s ease-in-out;
}
::view-transition-new(header) {
animation: slide-in 0.5s ease-in-out;
}
/* 定义动画 */
@keyframes fade-out {
from { opacity: 1; }
to { opacity: 0; }
}
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slide-out {
from { transform: translateX(0); }
to { transform: translateX(-100%); }
}
@keyframes slide-in {
from { transform: translateX(100%); }
to { transform: translateX(0); }
}2.4 view-transition-name 属性
view-transition-name 属性用于为元素指定一个过渡名称,以便在过渡过程中单独控制该元素的动画效果。
/* 基本语法 */
.view-transition-name: none | <custom-ident>;
/* 示例 */
/* 为 header 元素指定过渡名称 */
header {
view-transition-name: header;
}
/* 为 logo 元素指定过渡名称 */
.logo {
view-transition-name: logo;
}
/* 禁用元素的过渡 */
.sidebar {
view-transition-name: none;
}注意事项:
- 每个过渡名称在文档中必须是唯一的
- 使用
none可以禁用元素的过渡效果 - 只有指定了过渡名称的元素才会在过渡过程中被单独处理
3. CSS View Transitions 的语法和用法
基本用法示例:
// 1. 基本页面切换
function switchContent() {
document.startViewTransition(() => {
document.getElementById('content1').classList.add('hidden');
document.getElementById('content2').classList.remove('hidden');
});
}
// 2. 监听过渡完成
async function navigateWithTransition() {
const transition = document.startViewTransition(() => {
// 执行页面切换操作
updatePageContent();
});
// 等待过渡完成
await transition.finished;
console.log('Navigation completed with transition');
}
// 3. 结合 CSS 自定义过渡
function toggleThemeWithTransition() {
document.startViewTransition(() => {
document.documentElement.classList.toggle('dark');
});
}CSS 配置示例:
/* 1. 基本过渡样式 */
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 0.6s;
animation-timing-function: ease-in-out;
}
::view-transition-old(root) {
animation-name: fade-out;
}
::view-transition-new(root) {
animation-name: fade-in;
}
/* 2. 自定义元素过渡 */
.header {
view-transition-name: header;
}
.logo {
view-transition-name: logo;
}
::view-transition-old(header) {
animation: slide-out-left 0.5s ease-in-out;
}
::view-transition-new(header) {
animation: slide-in-right 0.5s ease-in-out;
}
::view-transition-old(logo),
::view-transition-new(logo) {
animation-duration: 0.8s;
animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}
::view-transition-old(logo) {
animation-name: scale-out;
}
::view-transition-new(logo) {
animation-name: scale-in;
}
/* 3. 定义动画 */
@keyframes fade-out {
from { opacity: 1; }
to { opacity: 0; }
}
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slide-out-left {
from { transform: translateX(0); opacity: 1; }
to { transform: translateX(-100%); opacity: 0; }
}
@keyframes slide-in-right {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
@keyframes scale-out {
from { transform: scale(1); opacity: 1; }
to { transform: scale(0.5); opacity: 0; }
}
@keyframes scale-in {
from { transform: scale(1.5); opacity: 0; }
to { transform: scale(1); opacity: 1; }
}
/* 4. 响应式过渡 */
@media (prefers-reduced-motion: reduce) {
::view-transition-old(root),
::view-transition-new(root) {
animation: none;
}
}4. CSS View Transitions 的浏览器兼容性
支持情况:
- Chrome 111+ (自 2023 年 3 月起)
- Edge 111+ (自 2023 年 3 月起)
- Safari 16.4+ (自 2023 年 3 月起,部分支持)
- Firefox:正在开发中
兼容性处理:
对于不支持 View Transitions 的浏览器,可以使用特性检测并提供降级方案:
// 检测浏览器是否支持 View Transitions
function supportsViewTransitions() {
return 'startViewTransition' in document;
}
// 使用 View Transitions 或降级方案
function navigateTo(pageId) {
if (supportsViewTransitions()) {
// 使用 View Transitions
document.startViewTransition(() => {
updatePage(pageId);
});
} else {
// 降级方案:直接更新页面
updatePage(pageId);
}
}使用 CSS @supports 检测:
/* 基础样式 */
.content {
transition: opacity 0.3s ease;
}
/* 支持 View Transitions 的浏览器 */
@supports (view-transition-name: none) {
.content {
transition: none;
}
::view-transition-old(root),
::view-transition-new(root) {
animation: fade 0.6s ease-in-out;
}
@keyframes fade {
from { opacity: 0; }
to { opacity: 1; }
}
}实用案例分析
案例一:单页应用导航
需求:创建一个单页应用,使用 View Transitions 实现页面之间的平滑过渡效果。
HTML 结构:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>单页应用导航示例</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<!-- 导航栏 -->
<nav class="navbar">
<ul>
<li><a href="#" data-page="home">首页</a></li>
<li><a href="#" data-page="about">关于我们</a></li>
<li><a href="#" data-page="services">服务</a></li>
<li><a href="#" data-page="contact">联系我们</a></li>
</ul>
</nav>
<!-- 页面内容 -->
<main class="main-content">
<section id="home" class="page active">
<h1>欢迎来到首页</h1>
<p>这是单页应用的首页内容</p>
<div class="feature-card">
<h2>特色服务</h2>
<p>我们提供高质量的服务</p>
</div>
</section>
<section id="about" class="page">
<h1>关于我们</h1>
<p>我们是一家专注于提供优质服务的公司</p>
<div class="team-section">
<h2>我们的团队</h2>
<p>由专业人士组成的团队</p>
</div>
</section>
<section id="services" class="page">
<h1>我们的服务</h1>
<p>我们提供多种专业服务</p>
<div class="service-list">
<h2>服务列表</h2>
<ul>
<li>服务 1</li>
<li>服务 2</li>
<li>服务 3</li>
</ul>
</div>
</section>
<section id="contact" class="page">
<h1>联系我们</h1>
<p>如有任何问题,请随时联系我们</p>
<div class="contact-form">
<h2>联系表单</h2>
<form>
<input type="text" placeholder="姓名">
<input type="email" placeholder="邮箱">
<textarea placeholder="消息"></textarea>
<button type="submit">提交</button>
</form>
</div>
</section>
</main>
<script src="script.js"></script>
</body>
</html>CSS 样式:
/* 基础样式 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
}
/* 导航栏样式 */
.navbar {
background-color: #333;
color: white;
padding: 1rem;
position: sticky;
top: 0;
z-index: 100;
view-transition-name: navbar;
}
.navbar ul {
display: flex;
list-style: none;
gap: 1rem;
}
.navbar a {
color: white;
text-decoration: none;
padding: 0.5rem 1rem;
border-radius: 4px;
transition: background-color 0.3s ease;
}
.navbar a:hover {
background-color: rgba(255, 255, 255, 0.1);
}
/* 页面内容样式 */
.main-content {
padding: 2rem;
}
.page {
display: none;
min-height: 80vh;
padding: 2rem;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.page.active {
display: block;
}
.page h1 {
margin-bottom: 1rem;
color: #333;
view-transition-name: page-title;
}
.page p {
margin-bottom: 2rem;
color: #666;
}
/* 特色卡片样式 */
.feature-card,
.team-section,
.service-list,
.contact-form {
background-color: #f5f5f5;
padding: 1.5rem;
border-radius: 8px;
margin-top: 2rem;
}
/* 联系表单样式 */
.contact-form form {
display: flex;
flex-direction: column;
gap: 1rem;
margin-top: 1rem;
}
.contact-form input,
.contact-form textarea {
padding: 0.8rem;
border: 1px solid #ddd;
border-radius: 4px;
}
.contact-form button {
padding: 0.8rem;
background-color: #333;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.contact-form button:hover {
background-color: #555;
}
/* View Transitions 样式 */
@supports (view-transition-name: none) {
/* 导航栏过渡 */
::view-transition-old(navbar),
::view-transition-new(navbar) {
animation: none;
mix-blend-mode: normal;
}
/* 页面标题过渡 */
::view-transition-old(page-title) {
animation: slide-out 0.6s ease-in-out;
}
::view-transition-new(page-title) {
animation: slide-in 0.6s ease-in-out;
}
/* 根元素过渡 */
::view-transition-old(root) {
animation: fade-out 0.6s ease-in-out;
}
::view-transition-new(root) {
animation: fade-in 0.6s ease-in-out;
}
}
/* 动画定义 */
@keyframes fade-out {
from { opacity: 1; }
to { opacity: 0; }
}
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slide-out {
from { transform: translateX(0); opacity: 1; }
to { transform: translateX(-100%); opacity: 0; }
}
@keyframes slide-in {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
/* 响应式设计 */
@media (max-width: 768px) {
.navbar ul {
flex-direction: column;
gap: 0.5rem;
}
.main-content {
padding: 1rem;
}
.page {
padding: 1rem;
}
}JavaScript 控制:
// 导航控制逻辑
const navLinks = document.querySelectorAll('.navbar a');
const pages = document.querySelectorAll('.page');
// 为导航链接添加点击事件监听器
navLinks.forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
const targetPage = link.getAttribute('data-page');
navigateToPage(targetPage);
});
});
// 页面导航函数
function navigateToPage(pageId) {
// 检查浏览器是否支持 View Transitions
if ('startViewTransition' in document) {
// 使用 View Transitions
document.startViewTransition(() => {
updateActivePage(pageId);
});
} else {
// 降级方案:直接更新页面
updateActivePage(pageId);
}
}
// 更新活动页面
function updateActivePage(pageId) {
// 隐藏所有页面
pages.forEach(page => {
page.classList.remove('active');
});
// 显示目标页面
document.getElementById(pageId).classList.add('active');
// 更新导航链接状态
navLinks.forEach(link => {
link.style.fontWeight = link.getAttribute('data-page') === pageId ? 'bold' : 'normal';
});
}
// 初始化:设置首页为活动页面
updateActivePage('home');效果:
- 点击导航链接时,页面之间会有平滑的过渡动画
- 页面标题会有滑入滑出效果
- 整个页面内容会有淡入淡出效果
- 导航栏保持固定,不参与过渡
- 对于不支持 View Transitions 的浏览器,会降级为直接页面切换
案例二:主题切换
需求:创建一个主题切换功能,使用 View Transitions 实现主题切换时的平滑过渡效果。
HTML 结构:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>主题切换示例</title>
<link rel="stylesheet" href="theme-toggle.css">
</head>
<body>
<div class="container">
<header class="header">
<h1>主题切换示例</h1>
<button id="theme-toggle" class="theme-toggle">切换主题</button>
</header>
<main class="content">
<section class="card">
<h2>欢迎使用主题切换</h2>
<p>点击上方的按钮切换明/暗主题,体验平滑的过渡效果。</p>
</section>
<section class="card">
<h2>功能特性</h2>
<ul>
<li>平滑的主题过渡动画</li>
<li>响应式设计</li>
<li>可访问性支持</li>
<li>减少认知负荷</li>
</ul>
</section>
</main>
<footer class="footer">
<p>© 2023 主题切换示例</p>
</footer>
</div>
<script src="theme-toggle.js"></script>
</body>
</html>CSS 样式:
/* 基础样式 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
line-height: 1.6;
transition: background-color 0.3s ease, color 0.3s ease;
}
/* 容器样式 */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
/* 头部样式 */
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
padding-bottom: 1rem;
border-bottom: 1px solid #ddd;
view-transition-name: header;
}
.header h1 {
color: #333;
view-transition-name: site-title;
}
/* 主题切换按钮 */
.theme-toggle {
padding: 0.8rem 1.5rem;
background-color: #333;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s ease;
view-transition-name: theme-button;
}
.theme-toggle:hover {
background-color: #555;
}
/* 内容样式 */
.content {
margin-bottom: 2rem;
}
/* 卡片样式 */
.card {
background-color: white;
padding: 2rem;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
margin-bottom: 2rem;
view-transition-name: card;
}
.card h2 {
margin-bottom: 1rem;
color: #333;
}
.card p {
margin-bottom: 1.5rem;
color: #666;
}
.card ul {
list-style: none;
}
.card li {
margin-bottom: 0.5rem;
color: #666;
padding-left: 1.5rem;
position: relative;
}
.card li::before {
content: "•";
position: absolute;
left: 0;
color: #333;
font-weight: bold;
}
/* 页脚样式 */
.footer {
padding-top: 1rem;
border-top: 1px solid #ddd;
text-align: center;
color: #666;
}
/* 暗色主题 */
body.dark {
background-color: #1a1a1a;
color: #e0e0e0;
}
body.dark .header {
border-bottom-color: #333;
}
body.dark .header h1 {
color: #e0e0e0;
}
body.dark .theme-toggle {
background-color: #e0e0e0;
color: #1a1a1a;
}
body.dark .theme-toggle:hover {
background-color: #f0f0f0;
}
body.dark .card {
background-color: #2d2d2d;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
body.dark .card h2 {
color: #e0e0e0;
}
body.dark .card p,
body.dark .card li {
color: #b0b0b0;
}
body.dark .card li::before {
color: #e0e0e0;
}
body.dark .footer {
border-top-color: #333;
color: #b0b0b0;
}
/* View Transitions 样式 */
@supports (view-transition-name: none) {
/* 头部过渡 */
::view-transition-old(header),
::view-transition-new(header) {
animation: none;
mix-blend-mode: normal;
}
/* 网站标题过渡 */
::view-transition-old(site-title),
::view-transition-new(site-title) {
animation: fade 0.6s ease-in-out;
}
/* 主题按钮过渡 */
::view-transition-old(theme-button),
::view-transition-new(theme-button) {
animation: scale 0.6s ease-in-out;
mix-blend-mode: normal;
}
/* 卡片过渡 */
::view-transition-old(card),
::view-transition-new(card) {
animation: slide-fade 0.8s ease-in-out;
}
/* 根元素过渡 */
::view-transition-old(root) {
animation: fade-out 0.6s ease-in-out;
}
::view-transition-new(root) {
animation: fade-in 0.6s ease-in-out;
}
}
/* 动画定义 */
@keyframes fade {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes scale {
from { transform: scale(0.8); opacity: 0; }
to { transform: scale(1); opacity: 1; }
}
@keyframes slide-fade {
from { transform: translateY(20px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
@keyframes fade-out {
from { opacity: 1; }
to { opacity: 0; }
}
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
/* 响应式设计 */
@media (max-width: 768px) {
.container {
padding: 1rem;
}
.header {
flex-direction: column;
align-items: flex-start;
gap: 1rem;
}
.card {
padding: 1.5rem;
}
}JavaScript 控制:
// 主题切换逻辑
const themeToggle = document.getElementById('theme-toggle');
const body = document.body;
// 检查本地存储中的主题偏好
const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'dark' || (!savedTheme && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
body.classList.add('dark');
updateButtonText();
}
// 为主题切换按钮添加点击事件监听器
themeToggle.addEventListener('click', () => {
// 检查浏览器是否支持 View Transitions
if ('startViewTransition' in document) {
// 使用 View Transitions
document.startViewTransition(() => {
toggleTheme();
});
} else {
// 降级方案:直接切换主题
toggleTheme();
}
});
// 切换主题函数
function toggleTheme() {
// 切换主题类
body.classList.toggle('dark');
// 保存主题偏好到本地存储
const currentTheme = body.classList.contains('dark') ? 'dark' : 'light';
localStorage.setItem('theme', currentTheme);
// 更新按钮文本
updateButtonText();
}
// 更新按钮文本
function updateButtonText() {
themeToggle.textContent = body.classList.contains('dark') ? '切换到亮色主题' : '切换到暗色主题';
}效果:
- 点击主题切换按钮时,页面会在亮色和暗色主题之间平滑过渡
- 不同元素会有不同的过渡效果:标题淡入淡出,按钮缩放,卡片滑入淡出
- 主题偏好会保存到本地存储,刷新页面后保持不变
- 支持系统主题偏好检测
- 对于不支持 View Transitions 的浏览器,会降级为直接主题切换
案例三:图片库筛选
需求:创建一个图片库,使用 View Transitions 实现筛选分类时的平滑过渡效果。
HTML 结构:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>图片库筛选示例</title>
<link rel="stylesheet" href="gallery.css">
</head>
<body>
<div class="container">
<header class="gallery-header">
<h1>图片库</h1>
<div class="filter-buttons">
<button data-filter="all" class="filter-btn active">全部</button>
<button data-filter="nature" class="filter-btn">自然</button>
<button data-filter="city" class="filter-btn">城市</button>
<button data-filter="portrait" class="filter-btn">人像</button>
</div>
</header>
<main class="gallery">
<div class="gallery-item nature">
<img src="https://via.placeholder.com/300x200/FF5733/FFFFFF?text=Nature+1" alt="自然景观 1">
<div class="gallery-caption">
<h3>自然景观 1</h3>
<p>美丽的自然风景</p>
</div>
</div>
<div class="gallery-item city">
<img src="https://via.placeholder.com/300x200/33FF57/FFFFFF?text=City+1" alt="城市景观 1">
<div class="gallery-caption">
<h3>城市景观 1</h3>
<p>繁华的城市风光</p>
</div>
</div>
<div class="gallery-item portrait">
<img src="https://via.placeholder.com/300x200/3357FF/FFFFFF?text=Portrait+1" alt="人像 1">
<div class="gallery-caption">
<h3>人像 1</h3>
<p>人物肖像摄影</p>
</div>
</div>
<div class="gallery-item nature">
<img src="https://via.placeholder.com/300x200/FF33F5/FFFFFF?text=Nature+2" alt="自然景观 2">
<div class="gallery-caption">
<h3>自然景观 2</h3>
<p>壮观的自然景象</p>
</div>
</div>
<div class="gallery-item city">
<img src="https://via.placeholder.com/300x200/33FFF5/FFFFFF?text=City+2" alt="城市景观 2">
<div class="gallery-caption">
<h3>城市景观 2</h3>
<p>现代城市建筑</p>
</div>
</div>
<div class="gallery-item portrait">
<img src="https://via.placeholder.com/300x200/F5FF33/FFFFFF?text=Portrait+2" alt="人像 2">
<div class="gallery-caption">
<h3>人像 2</h3>
<p>艺术人像摄影</p>
</div>
</div>
</main>
</div>
<script src="gallery.js"></script>
</body>
</html>CSS 样式:
/* 基础样式 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
line-height: 1.6;
background-color: #f5f5f5;
color: #333;
}
/* 容器样式 */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
/* 头部样式 */
.gallery-header {
margin-bottom: 2rem;
text-align: center;
}
.gallery-header h1 {
margin-bottom: 1.5rem;
color: #333;
view-transition-name: gallery-title;
}
/* 筛选按钮容器 */
.filter-buttons {
display: flex;
justify-content: center;
gap: 1rem;
margin-bottom: 2rem;
}
/* 筛选按钮样式 */
.filter-btn {
padding: 0.8rem 1.5rem;
background-color: #333;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s ease;
view-transition-name: filter-button;
}
.filter-btn:hover {
background-color: #555;
}
.filter-btn.active {
background-color: #007bff;
}
/* 画廊样式 */
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 2rem;
}
/* 画廊项样式 */
.gallery-item {
background-color: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
view-transition-name: gallery-item;
}
.gallery-item:hover {
transform: translateY(-5px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
/* 画廊图片样式 */
.gallery-item img {
width: 100%;
height: 200px;
object-fit: cover;
display: block;
}
/* 画廊标题样式 */
.gallery-caption {
padding: 1.5rem;
}
.gallery-caption h3 {
margin-bottom: 0.5rem;
color: #333;
}
.gallery-caption p {
color: #666;
font-size: 0.9rem;
}
/* 隐藏的画廊项 */
.gallery-item.hidden {
display: none;
}
/* View Transitions 样式 */
@supports (view-transition-name: none) {
/* 画廊标题过渡 */
::view-transition-old(gallery-title),
::view-transition-new(gallery-title) {
animation: fade 0.6s ease-in-out;
}
/* 筛选按钮过渡 */
::view-transition-old(filter-button),
::view-transition-new(filter-button) {
animation: scale 0.6s ease-in-out;
mix-blend-mode: normal;
}
/* 画廊项过渡 */
::view-transition-old(gallery-item),
::view-transition-new(gallery-item) {
animation: slide-fade 0.8s ease-in-out;
}
/* 根元素过渡 */
::view-transition-old(root) {
animation: fade-out 0.6s ease-in-out;
}
::view-transition-new(root) {
animation: fade-in 0.6s ease-in-out;
}
}
/* 动画定义 */
@keyframes fade {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes scale {
from { transform: scale(0.8); opacity: 0; }
to { transform: scale(1); opacity: 1; }
}
@keyframes slide-fade {
from { transform: translateY(20px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
@keyframes fade-out {
from { opacity: 1; }
to { opacity: 0; }
}
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
/* 响应式设计 */
@media (max-width: 768px) {
.container {
padding: 1rem;
}
.filter-buttons {
flex-wrap: wrap;
gap: 0.5rem;
}
.filter-btn {
padding: 0.6rem 1.2rem;
font-size: 0.9rem;
}
.gallery {
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1.5rem;
}
.gallery-item img {
height: 180px;
}
.gallery-caption {
padding: 1.2rem;
}
}
@media (max-width: 480px) {
.gallery {
grid-template-columns: 1fr;
}
.gallery-item img {
height: 200px;
}
}JavaScript 控制:
// 画廊筛选逻辑
const filterButtons = document.querySelectorAll('.filter-btn');
const galleryItems = document.querySelectorAll('.gallery-item');
// 为筛选按钮添加点击事件监听器
filterButtons.forEach(button => {
button.addEventListener('click', () => {
const filter = button.getAttribute('data-filter');
// 检查浏览器是否支持 View Transitions
if ('startViewTransition' in document) {
// 使用 View Transitions
document.startViewTransition(() => {
applyFilter(filter);
updateActiveButton(button);
});
} else {
// 降级方案:直接应用筛选
applyFilter(filter);
updateActiveButton(button);
}
});
});
// 应用筛选
function applyFilter(filter) {
galleryItems.forEach(item => {
if (filter === 'all' || item.classList.contains(filter)) {
item.classList.remove('hidden');
} else {
item.classList.add('hidden');
}
});
}
// 更新活动按钮
function updateActiveButton(activeButton) {
filterButtons.forEach(button => {
button.classList.remove('active');
});
activeButton.classList.add('active');
}效果:
- 点击筛选按钮时,画廊会根据选择的分类筛选图片,并使用平滑的过渡动画
- 不同元素会有不同的过渡效果:标题淡入淡出,按钮缩放,图片项滑入淡出
- 支持响应式设计,在不同屏幕尺寸下调整布局
- 对于不支持 View Transitions 的浏览器,会降级为直接筛选
知识总结
CSS View Transitions 的核心价值:
- 提供更加流畅、专业的页面状态过渡效果
- 减少用户在页面切换时的认知负荷
- 支持单页应用和多页应用的页面过渡
- 简化复杂动画的实现,减少 JavaScript 依赖
- 支持细粒度的元素过渡控制
CSS View Transitions 的主要 API:
document.startViewTransition():启动视图过渡的方法ViewTransition对象:控制和监听过渡的状态和方法- CSS 伪元素:
::view-transition-*系列伪元素 view-transition-name属性:为元素指定过渡名称
CSS View Transitions 的语法特点:
- 使用 JavaScript 启动过渡,CSS 控制过渡效果
- 支持为不同元素定义不同的过渡效果
- 可以通过 CSS 动画自定义过渡效果
- 支持监听过渡的各个阶段
浏览器兼容性:
- 现代浏览器(Chrome、Edge)已支持
- Safari 16.4+ 部分支持
- Firefox 正在开发中
- 可以使用特性检测提供降级方案
实际应用场景:
- 单页应用导航
- 主题切换
- 内容筛选和排序
- 表单状态变化
- 模态框的显示和隐藏
- 数据可视化的更新
学习建议
实践练习:
- 创建一个使用 View Transitions 的单页应用导航
- 实现一个带有平滑过渡效果的主题切换功能
- 构建一个使用 View Transitions 的图片画廊筛选系统
- 尝试为表单提交和验证添加过渡效果
深入学习:
- 研究 CSS View Transitions Module Level 1 规范的完整内容
- 探索 View Transitions 与其他 CSS 特性的结合使用
- 学习如何优化 View Transitions 的性能
- 了解 View Transitions 的未来发展方向和新特性
应用拓展:
- 在实际项目中使用 View Transitions 提升用户体验
- 创建可重用的 View Transitions 组件
- 探索 View Transitions 在不同类型网站中的应用
- 研究如何使用 View Transitions 实现更复杂的动画效果
通过本教程的学习,相信你已经掌握了 CSS View Transitions 的基本概念和使用方法。在实际项目中,合理运用这一强大的 CSS 特性,可以创建更加流畅、专业的用户界面,提升网站的整体用户体验。