WebberUI

Pull Quote Lift

段落中的句子捲入視口時,以共享元素動畫抽出、放大為跨欄 pull quote,離開視口時退回行內位置。

載入預覽⋯

安裝

npx shadcn@latest add https://webberui.com/r/pull-quote-lift.json

或在 components.json 設定 registries 後,改用 @webberui/pull-quote-lift 安裝。

安裝依賴後,從 registry JSON(/r/pull-quote-lift.jsonfiles[0].content)複製 pull-quote-lift.tsx 原始碼到你的 components/ui/ 目錄:

npm install motion clsx tailwind-merge

使用

import { PullQuoteLift, LiftQuote } from "@/components/ui/pull-quote-lift";

<PullQuoteLift>
  <p>
    雜誌編輯會把最關鍵的一句話抽出來放大成引言。
    <LiftQuote>動畫不是裝飾,而是介面對使用者說話的方式。</LiftQuote>
    網頁同樣適用這個技巧。
  </p>
</PullQuoteLift>

PullQuoteLift 包住整段文章,LiftQuote 包住要抽出的句子。句子捲入視口時會「浮出」放大為跨欄 pull quote(附裝飾引號與上下分隔線),離開視口時退回原文行內位置。

Props

PullQuoteLift

Prop型別預設值說明
childrenReact.ReactNode文章內容,段落中可包含一或多個 LiftQuote
classNamestring外層容器的 className

LiftQuote

Prop型別預設值說明
childrenReact.ReactNode要抽出成 pull quote 的句子
quoteClassNamestring追加到抽出後 blockquote 的 className

細節

  • layoutId FLIP 原理:行內的 motion.span 與抽出後的 motion.blockquote 共用同一個 layoutId。切換時 Motion 會快照舊元素的位置與尺寸,讓新元素從舊的邊界框以 transform 過渡到自己的最終位置(FLIP),因此句子看起來是從行內「飛出」到引言區塊。
  • 字級 scale morph:FLIP 是以 scale 內插兩個尺寸差異很大的盒子,過渡期間文字會被暫時縮放變形(由行內字級 morph 到引言大字級)。這是共享元素動畫的固有特性,動畫結束後即恢復精確排版。
  • pull quote 的渲染位置:抽出後的 blockquote 以 portal 渲染到文章尾端的插槽,而不是句子所在的 <p> 內——<blockquote> 不能巢狀在 <p> 裡,portal 同時讓行內與引言兩側在同一次 commit 內切換,FLIP 快照才銜接得上。
  • 佔位策略:抽出後行內保留淡化的原句佔位,段落排版不會重排,useInView 觀測的元素尺寸因此保持穩定,避免在視口邊界來回震盪。
  • 巢狀捲動容器useInView 預設以瀏覽器視口為根,IntersectionObserver 會自動計入祖先 overflow 的裁切,所以放在固定高度的捲動容器內也能正常觸發。若容器會在動畫進行時捲動,建議把容器改為 motion 元素並加上 layoutScroll,讓 FLIP 量測正確(見示範原始碼)。
  • 往返動畫:引言區塊包在 AnimatePresence 內,回捲離開視口時會在退場的同時把位置交還給重新掛載的行內 span,兩者交叉淡接。

可及性

  • 使用者系統開啟「減少動態效果」時,不做任何動畫:原句留在段落中,pull quote 直接靜態顯示
  • 句子永遠留在段落內(抽出時為淡化版本),文章的朗讀順序恆定;抽出後的 blockquote 是裝飾性複本,帶 aria-hidden,螢幕閱讀器不會重複朗讀

On this page