منوی بی‌نهایتInfinite Menu

کره‌ای از کاشی‌های تصویر که با کشیدن می‌چرخه، نزدیک‌ترین کاشی رو روبه‌رو می‌آره و عنوان و توضیحش رو نشون می‌ده — با پخشِ خودکار و چهار پریستِ شکلی.

محمدجواد

ساخته شده توسط محمدجواد

سازنده فارسی‌فای، بچه دولوپر

وابستگی‌ها

بدون وابستگی

نوع تعامل

کره رو با نشانگر یا انگشت بچرخون؛ سرعتِ رهاکردن با لختی ادامه پیدا می‌کنه و نزدیک‌ترین کاشی خودش رو روبه‌رو می‌آره. کلیک روی کاشیِ روبه‌رو فقط انتخابش می‌کنه؛ برای بازکردن/کنش باید `onItemClick` یا `link` بدی (وگرنه فلشِ کنش ساخته نمی‌شه). کلیدهای جهت کاشیِ همسایه رو می‌برن و Enter همون انتخاب رو صدا می‌زنه؛ هاور/فوکوس پخشِ خودکار رو می‌ایستونه.

پراپ‌ها

گزینه‌هایی که می‌تونی به کامپوننت بدی؛ ستاره‌دارها اجباری‌اند.

items
InfiniteMenuItem[]پیش‌فرض: []

کاشی‌ها؛ هر مورد `image` (اجباری)، `title`، `description` و `link` اختیاری دارد. دموی میزکار شش تصویرِ محلی با عنوان فارسی می‌دهد.

count
numberپیش‌فرض: 160

تعداد کاشی‌های کره؛ ۶ تا ۴۰۰ (به‌صورتِ خودکار محدود می‌شه).

tileSize
numberپیش‌فرض: 0.63

اندازه‌ی هر کاشی نسبت به شعاعِ کره، ۰٫۲ تا ۱٫۲.

roundness
numberپیش‌فرض: 0.2

گردیِ گوشه‌ی کاشی‌ها، ۰ تا ۱.

zoom
numberپیش‌فرض: 1.65

نزدیکیِ دوربین به کره؛ ۰٫۲ تا ۴.

pullBack
numberپیش‌فرض: 1

چقدر کره هنگامِ کشیدن عقب برود، ۰ تا ۳.

نصب

۱. این آدرس رو یک‌بار توی components.json بذار تا shadcn مخزن فارسی‌فای رو بشناسه.

components.jsonjson
{
  "registries": {
    "@farsify": "https://farsify.ir/r/{name}.json"
  }
}

۲. بعد با دستورِ زیر کامپوننت رو نصب کن.

npx shadcn@latest add @farsify/infinite-menu

پیش‌نیاز: Tailwind CSS v4 و صفحهٔ راست‌به‌چپ dir="rtl".

نمونه

infinite-menu.tsxtsx
"use client";

import InfiniteMenu from "@/components/farsify/infinite-menu";

const photos = [
  {
    image: "/images/coast.jpg",
    title: "ساحل",
    description: "خطِ روشنِ افق و موجِ آرام.",
  },
  {
    image: "/images/forest.jpg",
    title: "جنگل",
    description: "مه میانِ تنه‌های بلند.",
  },
  {
    image: "/images/city.jpg",
    title: "شهر",
    description: "چراغ‌ها و پنجره‌های روشن.",
  },
];

export function Gallery() {
  return (
    <div style={{ height: 480, position: "relative" }}>
      <InfiniteMenu items={photos} count={160} theme="dark" />
    </div>
  );
}

// بدونِ کنشِ بیرونی؛ فلش پنهان می‌ماند و فقط عنوان/توضیح می‌آید
<InfiniteMenu items={photos} showInfo />

// برای ساختنِ فلشِ کنش، onItemClick یا link بدهید؛ وگرنه کلیک فقط می‌چرخاند
<InfiniteMenu items={photos} onItemClick={(item) => console.log(item)} />

// کارتِ سبک‌تر برای موبایل: کاشی‌های درشت‌تر و تعدادِ کمتر
<InfiniteMenu items={photos} count={90} tileSize={0.9} dim={0.25} />

نکته‌ها

  • همه‌ی تصاویر توی یه اطلس می‌نشینن و کاشی‌ها با `drawElementsInstanced` توی یه فراخوانی کشیده می‌شن؛ نه وابستگیِ `three` لازمه نه یه draw call برای هر کاشی.
  • توی RTL عنوان سمتِ راست (شروع) و توضیح سمتِ چپ (پایان) می‌نشینه؛ توی چیدمانِ باریک اطلاعات پایین‌راست و فلش پایین‌چپ می‌ره و فلش هم افقی آینه می‌شه.
  • فاصله‌ی حروف صفره؛ `letter-spacing` مثبت/منفیِ مرجع روی خطِ فارسی اتصالِ حروف رو خراب می‌کنه.
  • اگه WebGL2 نباشه، به‌جای بومِ خالی شبکه‌ای از همون تصاویر نشون داده می‌شه؛ محتوا و عنوان از دست نمی‌رن.
  • توی `prefers-reduced-motion` معرفی و چرخشِ خودکار خاموش می‌شه؛ کشیدنِ دستی و کلیدها کار می‌کنن.
  • برای گالریِ سبک‌تر `count` رو کم و `tileSize` رو زیاد کن؛ برای کلاژِ ریزِ شلوغ برعکس.

مجوز

کد بعد از نصب مالِ خودته؛ هر تغییری خواستی بده و توی پروژه‌ی خودت نگهش دار. برای پروژه‌های شخصی و تجاری آزاده، حتی اگه کدت بسته باشه. فقط اسم و لینک فارسی‌فای رو یه جا توی پروژه نگه دار.