# 模式与指南 ## 使用 `useDeferredValue` 的可搜索网格 `useDeferredValue` 将筛选更新转变为一次过渡,从而激活 ``: ```tsx 'use client'; import { useDeferredValue, useState, ViewTransition, Suspense } from 'react'; export default function SearchableGrid({ itemsPromise }) { const [search, setSearch] = useState(''); const deferredSearch = useDeferredValue(search); return ( <> setSearch(e.currentTarget.value)} /> }> ); } ``` 在延迟列表内部,为每个条目添加 `` 会在每次按键时触发交叉淡入淡出。使用 `default="none"` 修复: ```tsx {filteredItems.map(item => ( ))} ``` ## 使用 `startTransition` 的卡片展开/折叠 在网格与详情视图之间切换,使用共享元素变形: ```tsx 'use client'; import { useState, useRef, startTransition, ViewTransition } from 'react'; export default function ItemGrid({ items }) { const [expandedId, setExpandedId] = useState(null); const scrollRef = useRef(0); return expandedId ? ( i.id === expandedId)} onClose={() => { startTransition(() => { setExpandedId(null); setTimeout(() => window.scrollTo({ behavior: 'smooth', top: scrollRef.current }), 100); }); }} /> ) : (
{items.map(item => ( { scrollRef.current = window.scrollY; startTransition(() => setExpandedId(item.id)); }} /> ))}
); } ``` ## 类型安全的过渡辅助函数 使用 `as const` 数组和派生类型防止 ID 冲突: ```tsx const transitionTypes = ['default', 'transition-to-detail', 'transition-to-list'] as const; const animationTypes = ['auto', 'none', 'animate-slide-from-left', 'animate-slide-from-right'] as const; type TransitionType = (typeof transitionTypes)[number]; type AnimationType = (typeof animationTypes)[number]; type TransitionMap = { default: AnimationType } & Partial, AnimationType>>; export function HorizontalTransition({ children, enter, exit }: { children: React.ReactNode; enter: TransitionMap; exit: TransitionMap; }) { return {children}; } ``` ## 无重挂载的交叉淡入淡出 省略 `key` 可触发更新(交叉淡入淡出),而非退出+进入。避免 Suspense 重挂载/重新请求: ```jsx ``` 当内容身份发生变化(状态重置)时使用 `key`;对于交叉淡入淡出(标签页、面板、轮播)则省略。 ## 将元素与父级动画隔离 ### 持久布局元素 持久元素(头部、导航栏、侧边栏)会被捕获到页面的过渡快照中。使用 `viewTransitionName` 修复: ```jsx ``` 然后从 `css-recipes.md` 添加持久元素隔离 CSS。对于 `backdrop-blur`/`backdrop-filter`,使用 `css-recipes.md` 中的 backdrop-blur 变通方案。 ### 浮动元素 为弹出框/工具提示赋予它们自己的 `viewTransitionName`: ```jsx {options} ``` 全局修复方案:请参见 `css-recipes.md` 中的持久元素隔离。 ## 骨架屏与内容之间的共享控件 为后备内容与正式内容中相匹配的控件赋予相同的 `viewTransitionName`: ```jsx // 后备内容 // 正式内容 ``` 不要在 `` 内部的根 DOM 节点上设置手动的 `viewTransitionName`——React 自动生成的名称会覆盖它。 ## 可复用的带动画折叠 ```jsx function AnimatedCollapse({ open, children }) { if (!open) return null; return ( {children} ); } // 使用方式:通过 startTransition 切换 ``` ## 使用 Activity 保留状态 ```jsx ``` ## 使用 `useOptimistic` 排除元素 `useOptimistic` 的值在过渡快照之前更新,从而将其排除在动画之外。适用于控件(标签);动画内容应使用已提交的状态: ```tsx const [sort, setSort] = useState('newest'); const [optimisticSort, setOptimisticSort] = useOptimistic(sort); function cycleSort() { const nextSort = getNextSort(optimisticSort); startTransition(() => { setOptimisticSort(nextSort); // 在快照之前——无动画 setSort(nextSort); // 在快照之间——有动画 }); } {items.sort(comparators[sort]).map(item => ( ))} ``` --- ## 视图过渡事件 通过 `onEnter`、`onExit`、`onUpdate`、`onShare` 进行命令式控制。始终返回清理函数。`onShare` 优先级高于 `onEnter`/`onExit`。 ```jsx { const anim = instance.new.animate( [{ transform: 'scale(0.8)', opacity: 0 }, { transform: 'scale(1)', opacity: 1 }], { duration: 300, easing: 'ease-out' } ); return () => anim.cancel(); }} > ``` `instance` 对象:`instance.old`、`instance.new`、`instance.group`、`instance.imagePair`、`instance.name`。 `types` 数组(第二个参数)允许你根据过渡类型变化动画效果。 --- ## 动画时长 | 交互类型 | 时长 | |---------|------| | 直接切换(展开/折叠) | 100–200ms | | 路由过渡(滑动) | 150–250ms | | Suspense 揭示(骨架屏 → 内容) | 200–400ms | | 共享元素变形 | 300–500ms | --- ## 故障排查 **VT 未激活:** 确保 `` 出现在任何 DOM 节点之前。确保状态更新位于 `startTransition` 内部。 **"Two ViewTransition components with the same name":** 名称必须在全局唯一。使用 ID:`name={`hero-${item.id}`}`。 **`router.back()` 及浏览器后退/前进跳过动画:** 改用带显式 URL 的 `router.push()`。请参见 SKILL.md 中的 "router.back() and Browser Back Button"。 **`flushSync` 跳过动画:** 改用 `startTransition`。 **只有更新有动画(没有进入/退出):** 如果没有 ``,React 会将切换视为更新。改为条件渲染 VT 本身,或将其包裹在 `` 中。 **布局 VT 阻止页面 VT 动画:** 嵌套的 VT 在父级 VT 内部永远不会触发进入/退出。如果布局中有一个包裹 `{children}` 的 VT,页面级别的进入/退出将静默失效。移除布局 VT。 **使用 `useOptimistic` 时列表重新排序没有动画:** 乐观值在快照之前解析。列表顺序应使用已提交的状态。 **TS 错误 "Property 'default' is missing":** 以类型为键的对象需要一个 `default` 键。 **哈希片段导致滚动跳跃:** 不使用哈希进行导航;导航后通过编程方式滚动。 **Backdrop-blur 闪烁:** 使用 `css-recipes.md` 中的 backdrop-blur 变通方案。 **`border-radius` 在过渡期间丢失:** 直接将 `border-radius` 应用于被捕获的元素。 **骨架屏控件滑走:** 为相匹配的控件赋予相同的 `viewTransitionName`。 **批处理:** 动画期间的多次更新会被批处理。A→B→C→D 变为 B→D。
可用工具列表(点击展开) - **Agent** – 启动子代理处理复杂/多步任务 - **Bash** – 执行 bash 命令 - **CronCreate/CronDelete/CronList** – 管理定时任务 - **Edit** – 精确字符串替换编辑文件 - **EnterWorktree/ExitWorktree** – 管理 git worktree - **NotebookEdit** – 编辑 Jupyter notebook 单元格 - **Read** – 读取文件内容 - **ReportFindings** – 报告代码审查发现 - **ScheduleWakeup** – 调度 /loop 唤醒 - **SendMessage** – 向其他代理发送消息 - **Skill** – 调用技能 - **TaskCreate/TaskGet/TaskList/TaskOutput/TaskStop/TaskUpdate** – 任务管理 - **WebFetch/WebSearch** – 获取 URL 内容 / 搜索网络 - **Workflow** – 执行多代理工作流脚本 - **Write** – 写入文件