پوسترهای پرندهFlying Posters

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

محمدجواد

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

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

وابستگی‌ها

بدون وابستگی

نوع تعامل

چرخِ ماوس روی صحنه، پوسترها رو می‌چرخونه (صفحه اسکرول نمی‌شه) و کشیدن با نشانگر یا انگشت هم همین کار رو می‌کنه. پوسترهای نزدیکِ دو سرِ صحنه با منحنیِ نرم دور می‌زنن و از سمتِ دیگه برمی‌گردن؛ با `speed` یه رانشِ پیوسته بساز و با `lean` بذار موقعِ اسکرولِ تند به عقب خم شن. کلیکِ کوتاه روی هر پوستر با `onItemClick` گزارش می‌شه.

پراپ‌ها

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

items
Array<string | { image: string; link?: string; alt?: string }>پیش‌فرض: []

تصویرهای پوسترها؛ هر کدام برای پرکردنِ قاب برشِ مرکزی می‌خورند و برای پر شدنِ دید تکرار می‌شوند. دموی میزکار شش عکسِ محلی می‌دهد.

planeWidth
numberپیش‌فرض: 320

عرضِ هر پوستر بر حسبِ پیکسل، در دوربینِ پیش‌فرض.

planeHeight
numberپیش‌فرض: 320

ارتفاعِ هر پوستر بر حسبِ پیکسل، در دوربینِ پیش‌فرض.

distortion
numberپیش‌فرض: 3

این‌که یک گوشه‌ی پوستر چقدر زودتر از گوشه‌ی دیگر بچرخد؛ همان پیچشِ حینِ عبور، ۰ تا ۱۰.

scrollEase
numberپیش‌فرض: 0.01

پوسترها چقدر سریع خودشان را به چرخ و کشیدن برسانند؛ عددِ کمتر یعنی لغزشِ کشیده‌تر (۰٫۰۰۲ تا ۰٫۱).

cameraFov
numberپیش‌فرض: 45

زاویه‌ی دیدِ دوربین بر حسبِ درجه؛ زاویه‌ی بازتر یعنی دوردست‌تر.

نصب

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

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

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

npx shadcn@latest add @farsify/flying-posters

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

نمونه

flying-posters.tsxtsx
"use client";

import FlyingPosters from "@/components/farsify/flying-posters";

const posters = [
  "/images/peak.jpg",
  "/images/forest.jpg",
  "/images/beach.jpg",
  "/images/night.jpg",
  "/images/mist.jpg",
  "/images/city.jpg",
];

export function PosterWall() {
  return (
    <div style={{ height: 560, position: "relative" }}>
      <FlyingPosters items={posters} />
    </div>
  );
}

// چرخ ماوس و کشیدن از قبل فعال‌اند؛ صحنه باید ارتفاعِ قطعی داشته باشد

// دیوارِ گالریِ سه‌ستونه با پوسترهای عمودی و گوشه‌ی گرد
<FlyingPosters
  items={posters}
  planeWidth={220}
  planeHeight={300}
  columns={3}
  radius={14}
  axis="diagonal"
/>

// رانشِ آرام وقتی کسی اسکرول نمی‌کند + خمِ هنگامِ اسکرولِ تند
<FlyingPosters items={posters} speed={-24} lean={0.6} shading={0.3} />

// پوسترهای کروی‌گوشه با محوِ لبه‌ها
<FlyingPosters items={posters} radius={28} fade={0.55} />

// کلیکِ کوتاه روی هر پوستر
<FlyingPosters
  items={posters}
  onItemClick={(item, index) => console.log(item.image, index)}
/>

نکته‌ها

  • بدونِ هیچ وابستگی: یک برنامه‌ی WebGL2، اطلسِ بافتِ همه‌ی تصویرها و رسمِ همه‌ی پوسترها با `drawElementsInstanced` در یک فراخوانی.
  • چرخِ ماوس روی بوم صفحه رو اسکرول نمی‌کنه و خودِ پوسترها رو می‌چرخونه؛ برای اسکرولِ عادیِ صفحه، نشانگر رو از روی بوم بردار.
  • چند ستون با `columns` + ستون‌های یک‌درمیان (`alternate`) حسِ دیوارِ گالری می‌دهد؛ برای پوسترهای عمودیِ واقعی `planeHeight` را از `planeWidth` بیشتر بگیرید.
  • توی `prefers-reduced-motion` رانش و خم خاموشه و ورودِ محو فوری می‌شه؛ کشیدنِ دستی سرِ جاشه.
  • اگر WebGL2 در دسترس نباشد، صحنه ساکت می‌ماند — مرجع هم همین رفتار را دارد.

مجوز

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