WebberUI

Cart Arc

拋物入袋:點擊加入購物車時商品縮圖沿貝茲弧線飛向購物車,圖示果凍擠壓、徽章數字彈跳遞增,支援連點併發飛行。

載入預覽⋯

Playground

即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。

0.72
1
0
<CartArc />

安裝

npx shadcn@latest add https://webberui.com/r/cart-arc.json

或在 components.json 設定 registries 後,改用 @webberui/cart-arc 安裝。

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

npm install motion clsx tailwind-merge lucide-react

使用

CartArc 是容器兼狀態中樞,內部放入若干 CartArcItem 觸發器與一個 CartArcCart 目標。點擊觸發器時,一顆縮圖會沿貝茲弧線拋向購物車,命中後圖示擠壓、徽章數字彈跳遞增。

import { CartArc, CartArcCart, CartArcItem } from "@/components/ui/cart-arc";

<CartArc>
  <div className="flex items-center justify-between">
    <span>今日甜點</span>
    <CartArcCart />
  </div>

  <CartArcItem image="/matcha.jpg">加入購物車</CartArcItem>
</CartArc>;

讓縮圖從商品圖片(而非按鈕)起飛時,把 originRef 指向該圖片元素,並用 flyContent 指定飛行內容:

const thumbRef = React.useRef<HTMLDivElement>(null);

<div ref={thumbRef}>
  <img src="/matcha.jpg" alt="抹茶拿鐵" />
</div>
<CartArcItem originRef={thumbRef} flyContent={<img src="/matcha.jpg" alt="" />}>
  加入
</CartArcItem>;

受控總數

不傳 count 時元件自行管理總數(非受控),可用 defaultCount 設定初始值、onCountChange 監聽變化。傳入 count 則切換為受控模式,由外部掌控數字。

const [count, setCount] = React.useState(0);

<CartArc count={count} onCountChange={setCount}>
  {/* ... */}
</CartArc>;

命令式觸發

進階情境可用 useCartArc() 於任意時機、從任意視口座標拋出縮圖:

import { useCartArc } from "@/components/ui/cart-arc";

function BuyNow() {
  const { addToCart, count } = useCartArc();
  return (
    <button
      onClick={(e) => {
        const r = e.currentTarget.getBoundingClientRect();
        addToCart(
          { x: r.left + r.width / 2, y: r.top + r.height / 2 },
          { image: "/matcha.jpg", quantity: 2 },
        );
      }}
    >
      立即購買({count})
    </button>
  );
}

Props

CartArc

Prop型別預設值說明
childrenReact.ReactNode內含 CartArcItemCartArcCart
countnumber受控模式的商品總數
defaultCountnumber0非受控模式的初始總數
onCountChange(count: number) => void總數變動時回呼
durationnumber0.72飛行時長(秒)
classNamestring附加到容器的樣式

CartArcItem

透傳所有原生 button 屬性,另外提供:

Prop型別預設值說明
imagestring飛行縮圖的圖片網址
flyContentReact.ReactNode自訂飛行縮圖內容,優先於 image
quantitynumber1加入的數量,累加到總數
originRefReact.RefObject<HTMLElement | null>飛行起點的參考元素,未提供時以按鈕自身為起點
onClick(e) => void拋出縮圖後照常執行

CartArcCart

透傳所有原生 button 屬性,另外提供:

Prop型別預設值說明
iconReact.ReactNodelucide ShoppingCart自訂購物車圖示

useCartArc()

回傳型別說明
countnumber目前購物車內的商品總數
addToCart(origin, options?) => void從視口座標 origin 拋出縮圖並累加;options 支援 imagecontentquantity

細節與可及性

  • 縮圖以二次貝茲曲線飛行:控制點抬高到兩端之上形成向上拋的拋物線,過程中縮小、末段淡出並帶輕微旋轉
  • 目標座標於每次點擊時即時量測 getBoundingClientRect,捲動或版面變動皆能命中
  • 飛行圖層以 createPortal 疊在 document.body 最上層,帶 aria-hiddenpointer-events-none,不干擾指標與輔助科技
  • 連點時多顆縮圖併發飛行,同時上限 16 顆,超過時直接計數不飛以保護效能;每顆命中皆會計數,總數不遺漏
  • 命中時圖示以 scaleX/scaleY 反向的果凍擠壓關鍵影格回彈,徽章數字以 spring 彈跳替換(舊數字下退、新數字上彈)
  • 購物車帶 aria-label 標示件數,並以 aria-live="polite" 區域播報總數變化
  • 飛行動畫在卸載或命中回收時 stop() 清理,不會在卸載後更新
  • 使用者系統開啟「減少動態效果」時不飛行、不擠壓、徽章不彈跳,但加入購物車的計數行為照常運作

On this page