تا اینجا استوریبورد را کشیدیم و اسکلت صفحه را ساختیم. حالا وارد مرحلهٔ ابزارها میشویم و اولین چیزی که سراغش میرویم، همان جادوی نامرئی است که سایتهای حرفهای را متفاوت نشان میدهد: اسکرول نرم.
اگر تا حالا سایتی دیدهای که موقع اسکرول حس میکردی صفحه «سنگین و لغزنده» است — انگار داری یک کشوی روغنخورده را هل میدهی — آن حس، محصول اسکرول نرم است. جالب اینکه بیشتر کاربران نمیفهمند چه چیزی فرق دارد؛ فقط حس میکنند سایت گرانقیمتتر است.

لیست مطالب
اسکرول نرم دقیقاً چه میکند
اسکرول پیشفرض مرورگر پلهای است. هر بار که چرخ ماوس را میچرخانی یا انگشتت را روی صفحه میکشی، صفحه یک مقدار مشخص میپرد. این پرشها روی مانیتور معمولی چندان به چشم نمیآیند، ولی وقتی صفحه پر از انیمیشن باشد، همان پرشها انیمیشنها را هم پلهای میکنند.
اسکرول نرم بین «جایی که هستی» و «جایی که باید بروی» یک میانجی میگذارد و در هر فریم، مقدار کمی از فاصله را طی میکند. نتیجه: حرکتی پیوسته که به تدریج کند میشود و میایستد. فرمول ریاضیاش به این سادگی است:
موقعیت فعلی += (موقعیت هدف - موقعیت فعلی) × ضریب نرمی
اگر ضریب نرمی مثلاً ۰٫۱ باشد، در هر فریم یکدهم فاصلهٔ باقیمانده طی میشود؛ اول سریع، بعد کندتر و کندتر. همین یک خط، تمام منطق اسکرول نرم است.
مزیت واقعی: هماهنگی انیمیشنها
مهمتر از زیبایی، یک مزیت فنی وجود دارد: وقتی اسکرول نرم فعال است، انیمیشنهایی که به موقعیت اسکرول گره خوردهاند (همان «اسکراب» که در درس بعد میبینیم) هم نرم میشوند. بدون آن، انیمیشنهای اسکرابشده روی موس چرخشی حالت پرشی و ناخوشایندی دارند.
سه راه برای رسیدن به اسکرول نرم
| روش | حجم | کیفیت | مناسب |
|---|---|---|---|
scroll-behavior:smooth در CSS |
صفر | فقط برای پرش به لنگر | منوی داخلی صفحه |
| کتابخانهٔ Lenis | حدود ۳ کیلوبایت | عالی و استاندارد صنعت | صفحههای سینمایی |
| پیادهسازی دستی | حدود ۱ کیلوبایت | خوب، ولی جزئیات زیاد دارد | یادگیری و پروژهٔ سبک |
نکتهٔ مهم: scroll-behavior:smooth در CSS اسکرول نرم نیست. آن فقط پرش به لنگرها را نرم میکند و روی چرخاندن ماوس هیچ اثری ندارد. خیلیها این دو را با هم اشتباه میگیرند.
راهاندازی Lenis در پنج دقیقه
کتابخانهٔ Lenis امروز استاندارد عملی این کار است: سبک، بدون وابستگی و سازگار با کتابخانههای انیمیشن. سادهترین راهاندازی:
<script src="https://unpkg.com/[email protected]/dist/lenis.min.js"></script>
<script>
const lenis = new Lenis({
duration: 1.1, // طول حرکت؛ عدد بزرگتر = سنگینتر
smoothWheel: true, // نرمکردن چرخ ماوس
});
function raf(time){
lenis.raf(time);
requestAnimationFrame(raf);
}
requestAnimationFrame(raf);
</script>
همین. اگر این چند خط را به صفحهای که در درس قبل ساختیم اضافه کنی، بلافاصله تفاوت را حس میکنی.
چرا آن حلقهٔ raf لازم است
تابع requestAnimationFrame از مرورگر میخواهد قبل از هر بار رسم صفحه، کد ما را اجرا کند — یعنی حدود شصت بار در ثانیه. کتابخانه در هر فراخوانی، یک قدم کوچک به سمت موقعیت هدف برمیدارد. اگر این حلقه را ننویسی، کتابخانه هرگز حرکت نمیکند. این رایجترین اشتباه تازهکارهاست.

تنظیمات مهم و معنای واقعیشان
const lenis = new Lenis({
duration: 1.1, // ۰٫۸ سبک و چابک | ۱٫۵ سنگین و سینمایی
easing: t => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
smoothWheel: true, // چرخ ماوس
wheelMultiplier: 1, // شدت واکنش به چرخ
touchMultiplier: 1.6, // شدت واکنش به لمس
infinite: false,
});
| تنظیم | اگر کم کنی | اگر زیاد کنی |
|---|---|---|
duration |
سریع و تیز، شبیه اسکرول عادی | سنگین و کند؛ بیش از ۱٫۶ آزاردهنده میشود |
wheelMultiplier |
باید بیشتر بچرخانی | با کمترین حرکت، جهش زیاد |
touchMultiplier |
روی موبایل کند حس میشود | لغزش زیاد و کنترل سخت |
توصیهٔ عملی من: با duration: 1.1 شروع کن. اگر پروژه لوکس و آرام است، تا ۱٫۴ برو. برای سایت خبری یا فروشگاهی، اصلاً اسکرول نرم نگذار — کاربری که دنبال اطلاعات سریع است، از این حس خوشش نمیآید.
پیادهسازی دستی، برای وقتی که نمیخواهی کتابخانه اضافه کنی
اگر پروژهات کوچک است و نمیخواهی وابستگی اضافه کنی، این نسخهٔ ساده کار را راه میاندازد:
const content = document.querySelector('#smooth-content');
let current = 0, target = 0;
const ease = 0.09;
// ارتفاع بدنه را برابر ارتفاع واقعی محتوا کن
function setHeight(){
document.body.style.height = content.scrollHeight + 'px';
}
setHeight();
addEventListener('resize', setHeight);
function loop(){
target = window.scrollY;
current += (target - current) * ease;
content.style.transform = `translate3d(0, ${-current.toFixed(2)}px, 0)`;
requestAnimationFrame(loop);
}
loop();
و CSS لازمش:
#smooth-wrapper{position:fixed; inset:0; overflow:hidden;}
#smooth-content{will-change:transform;}
منطقش ساده است: محتوا را ثابت میکنیم، ارتفاع بدنه را بهاندازهٔ محتوا بزرگ میکنیم تا نوار اسکرول واقعی وجود داشته باشد، و در هر فریم محتوا را با transform به بالا میبریم. استفاده از translate3d بهجای top عمدی است: انتقال روی کارت گرافیک انجام میشود و بسیار روانتر است.
البته این نسخه چند چیز را ندارد که کتابخانه دارد: پشتیبانی درست از صفحهکلید، لنگرها، و اسکرول داخل عناصر تودرتو. برای پروژهٔ جدی، کتابخانه انتخاب عاقلانهتری است.
هفت مشکل رایج و راهحلشان
۱. لینکهای داخلی صفحه کار نمیکنند
وقتی اسکرول را تحویل کتابخانه میدهی، پرشهای #id دیگر درست عمل نمیکنند. راهحل:
document.querySelectorAll('a[href^="#"]').forEach(a => {
a.addEventListener('click', e => {
e.preventDefault();
lenis.scrollTo(a.getAttribute('href'), { offset: -80 });
});
});
مقدار offset منفی برای وقتی است که هدر چسبان داری و نمیخواهی عنوان زیر آن پنهان شود.
۲. موقعیت اسکرول موقع تغییر اندازه بههم میریزد
بعد از هر تغییر در ارتفاع محتوا (مثلاً باز شدن یک آکاردئون یا لود شدن تصاویر)، به کتابخانه خبر بده:
addEventListener('load', () => lenis.resize());
new ResizeObserver(() => lenis.resize())
.observe(document.querySelector('#smooth-content'));
۳. داخل پنجرهٔ بازشو هم اسکرول نرم اعمال میشود
وقتی یک مودال باز است، معمولاً میخواهی اسکرول صفحه متوقف شود:
function openModal(){ lenis.stop(); document.body.dataset.modal = '1'; }
function closeModal(){ lenis.start(); delete document.body.dataset.modal; }
۴. روی موبایل حس بدی دارد
این شایعترین شکایت است. اسکرول لمسی در سیستمعامل موبایل خودش فیزیک بومی دارد و نرمکردن دوبارهاش اغلب حس «تأخیر» میدهد. توصیهٔ من: روی موبایل خاموشش کن.
const isTouch = matchMedia('(hover:none) and (pointer:coarse)').matches;
const lenis = new Lenis({
duration: 1.1,
smoothWheel: true,
smoothTouch: false, // پیشفرض هم همین است؛ صریح نگهش دار
});
if (isTouch) lenis.destroy();
۵. کاربر کاهش حرکت را فعال کرده است
احترام به این تنظیم اختیاری نیست:
const reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;
if (!reduce) {
const lenis = new Lenis({ duration: 1.1 });
// ... حلقهٔ raf
}
۶. عنصر چسبان (sticky) درست کار نمیکند
اگر از پیادهسازی دستی با transform استفاده کنی، position:sticky داخل آن ظرف از کار میافتد؛ چون transform روی والد، یک زمینهٔ چیدمان جدید میسازد. راهحل: یا از کتابخانه استفاده کن که با این حالت سازگار است، یا پین را با ابزار انیمیشن انجام بده.
۷. نوار اسکرول ناپدید میشود
در پیادهسازی دستی، اگر ارتفاع بدنه را درست تنظیم نکنی، نوار اسکرول محو میشود و کاربر گم میشود. همیشه ارتفاع بدنه را با ارتفاع واقعی محتوا هماهنگ نگه دار.

ساخت این لایه با کمک هوش مصنوعی
نقش: توسعهدهندهٔ فرانتاند متخصص حرکت و اسکرول.
کار: لایهٔ اسکرول نرم را به صفحهٔ موجود اضافه کن.
قیدها:
- از کتابخانهٔ Lenis از CDN استفاده کن.
- حلقهٔ requestAnimationFrame را درست ببند.
- روی دستگاه لمسی کتابخانه غیرفعال شود.
- اگر prefers-reduced-motion فعال بود، اصلاً اجرا نشود.
- لینکهای داخلی #anchor با scrollTo و آفست منفی ۸۰ کار کنند.
- بعد از load و هر تغییر اندازه، resize صدا زده شود.
- duration پیشفرض ۱٫۱ باشد و در یک ثابت بالای فایل قرار بگیرد.
خروجی: یک بلوک script کامل + سه خط توضیح دربارهٔ اینکه
duration را برای چه حالوهوایی چقدر بگذارم.
هیچ چیز دیگری از صفحه را تغییر نده.
جملهٔ آخر مهم است؛ وگرنه مدل معمولاً کل صفحه را بازنویسی میکند و ساختاری که با زحمت ساختهای از بین میرود. این همان اصل «مرزگذاری» است که در پرامپتنویسی به زبان ساده هم رویش تأکید کردهایم.
کِی اسکرول نرم نگذاریم
این ابزار همیشه درست نیست. در این موارد از آن پرهیز کن:
- سایت خبری یا وبلاگ: کاربر میخواهد سریع اسکن کند؛ نرمی برایش تأخیر است.
- پنل کاربری و داشبورد: کارایی مهمتر از حس است.
- صفحههای خیلی بلند: رسیدن به انتها آزاردهنده میشود.
- فروشگاه با فهرست طولانی محصولات: سرعت پیمایش اولویت دارد.
قاعدهٔ ساده: اگر صفحه برای خواندهشدن سریع ساخته شده، اسکرول نرم نگذار. اگر برای تجربهشدن ساخته شده، بگذار.
پرسشهای پرتکرار
آیا اسکرول نرم روی سئو اثر منفی دارد؟
خیر، تا وقتی محتوا در HTML موجود باشد. مشکل زمانی پیش میآید که محتوا فقط با جاوااسکریپت ساخته شود، که ربطی به اسکرول نرم ندارد.
سرعت سایت را کند میکند؟
کتابخانه حدود سه کیلوبایت است که ناچیز است. اما اجرای دائمی حلقهٔ فریم، مصرف باتری را کمی بالا میبرد. به همین دلیل روی موبایل خاموشکردنش هم از نظر تجربه و هم از نظر مصرف منطقی است.
با وردپرس هم کار میکند؟
بله. کافی است اسکریپت را در قالب یا با افزونهٔ درج کد اضافه کنی. فقط حواست باشد بعضی قالبها خودشان اسکرول سفارشی دارند و تداخل ایجاد میشود.
میشود فقط در یک بخش از صفحه فعالش کرد؟
بله، کتابخانه امکان اعمال روی یک عنصر خاص را دارد؛ اما در عمل بهندرت نتیجهٔ خوبی میدهد، چون کاربر بین دو حس متفاوت اسکرول گیج میشود.
جمعبندی
اسکرول نرم، لایهای است که با کمترین کد بیشترین تفاوت حسی را ایجاد میکند: یک کتابخانهٔ سهکیلوبایتی، یک حلقهٔ فریم، و چند تنظیم. اما یادت باشد که این ابزار برای صفحههای تجربهمحور ساخته شده، نه هر صفحهای؛ و روی موبایل معمولاً بهتر است خاموش بماند.
در درس بعدی میرویم سراغ چیزی که واقعاً صفحه را سینمایی میکند: پین و اسکراب با GSAP ScrollTrigger — جایی که اسکرول کاربر تبدیل به نوار زمان انیمیشن میشود. مسیر کامل را در نقشهٔ راه طراحی سایت سینمایی دنبال کن و در @MrChatGPT_IR از انتشار درسهای تازه باخبر شو.
