بروزرسانی: 22 آبان 1404
نحوه متحرک سازی در اسکرول با جاوا اسکریپت Vanilla
نمایش عناصر بر اساس موقعیت اسکرول آنها یک انتخاب طراحی بسیار محبوب در هنگام ساخت صفحات وب است، اما معمولاً شامل استفاده از یک پلاگین یا کتابخانه است. در این آموزش نحوه پیاده سازی انیمیشن بر روی اسکرول را با استفاده از وانیلی جاوا اسکریپت و CSS خواهید آموخت.
مزیت اصلی استفاده از پیاده سازی سفارشی (برخلاف کتابخانه) این است که به ما امکان می دهد عملکردهای خود را برای دسترسی و عملکرد بهینه کنیم.
1. ساختار صفحه را تعریف کنید
ما طرح صفحه خود را با استفاده از HTML ایجاد می کنیم و سپس یک نام کلاس مشترک به عناصری که می خواهیم در اسکرول متحرک سازی کنیم اختصاص می دهیم. این نام کلاس همان چیزی است که ما در جاوا اسکریپت هدف قرار خواهیم داد.
در دمو بالا، نام کلاس به عناصر اختصاص داده شد js-scroll بنابراین HTML چیزی شبیه به این است:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | This animation fades in from the top. |
9 | |
10 |
2. یک ظاهر طراحی شده با CSS
CSS بسیاری از کارهای سنگین را انجام می دهد زیرا سبک انیمیشن هر عنصر را تعیین می کند. در این مورد، ما یک انیمیشن اسکرول با نام کلاس ایجاد خواهیم کرد scrolled.
این یک نمونه از یک انیمیشن ساده جاوا اسکریپت محو شده است:
1 | .js-scroll { |
2 | opacity: 0; |
3 | transition: opacity 500ms; |
4 | }
|
5 | |
6 | .js-scroll.scrolled { |
7 | opacity: 1; |
8 | } |
با این کد، هر js-scroll عنصر در صفحه با کدورت پنهان است 0 تا نام کلاس scrolled به آن اعمال می شود.
3. هدف گذاری عناصر با جاوا اسکریپت
هنگامی که طرح بندی و استایل های خود را داشتیم، توابع جاوا اسکریپت را ایجاد می کنیم تا نام کلاس را به عناصر زمانی که به نمایش اسکرول می کنند اختصاص دهیم. همچنین به جای CSS، عناصر موجود در جاوا اسکریپت را محو می کنیم، زیرا می خواهیم در صورتی که مرورگر جاوا اسکریپت را فعال نکرده باشد، عناصر قابل مشاهده باشند.
ما منطق را به این صورت تجزیه می کنیم:
- همه را دریافت کنید
js-scrollعناصر موجود در صفحه - محو کردن عناصر
- تشخیص اینکه چه زمانی عنصر در قسمت دید قرار دارد
- اختصاص دهید
scrolledنام کلاس به عنصر در صورت مشاهده.
عناصر هدف در صفحه
ما همه را هدف قرار خواهیم داد js-scroll عناصر موجود در صفحه با استفاده از document.querySelectorAll() روش. می بایست شبیه به این باشه:
1 | const scrollElements = document.querySelectorAll(".js-scroll"); |
محو کردن عناصر
ابتدا باید آن را حذف کنیم opacity:0 برای .js-scroll در CSS ما سپس این خط را در جاوا اسکریپت خود قرار می دهیم:
1 | scrollElements.forEach((el) => {
|
2 | el.style.opacity = 0 |
3 | }) |
در صورتی که جاوا اسکریپت در مرورگر غیرفعال باشد، این امکان را به عناصر می دهد تا سبک پیش فرض خود را داشته باشند.
تشخیص زمانی که یک عنصر در نمای است
با تعیین اینکه آیا فاصله عنصر از بالای صفحه کمتر از ارتفاع قسمت قابل مشاهده صفحه است، می توانیم تشخیص دهیم که چه زمانی یک عنصر در دید کاربر است.
در جاوا اسکریپت از getBoundingClientRect().top روشی برای بدست آوردن فاصله یک عنصر از بالای صفحه و window.innerHeight یا document.documentElement.clientHeight برای بدست آوردن ارتفاع درگاه دید
ما ایجاد خواهیم کرد elementInView عملکرد با استفاده از منطق بالا:
1 | const elementInView = (el) => { |
2 | const elementTop = el.getBoundingClientRect().top; |
3 | |
4 | return ( |
5 | elementTop <= (window.innerHeight || document.documentElement.clientHeight) |
6 | );
|
7 | }; |
ما می توانیم این تابع را تغییر دهیم تا تشخیص دهد که چه زمانی عنصر اسکرول شده است x پیکسل ها را به صفحه وارد کنید، یا تشخیص دهید که درصدی از صفحه اسکرول شده است.
1 | const elementInView = (el, scrollOffset = 0) => { |
2 | const elementTop = el.getBoundingClientRect().top; |
3 | |
4 | return ( |
5 | elementTop <= |
6 | ((window.innerHeight || document.documentElement.clientHeight) - scrollOffset) |
7 | );
|
8 | }; |
در این حالت تابع برمی گردد true اگر عنصر توسط علامت اسکرول شده باشد scrollOffset مقدار را وارد صفحه کنید اصلاح منطق عملکرد متفاوتی را برای هدف قرار دادن عناصر بر اساس اسکرول درصد به ما می دهد.
1 | const elementInView = (el, percentageScroll = 100) => { |
2 | const elementTop = el.getBoundingClientRect().top; |
3 | |
4 | return ( |
5 | elementTop <= |
6 | ((window.innerHeight || document.documentElement.clientHeight) * (percentageScroll/100)) |
7 | );
|
8 | }; |
یک مزیت اضافی از پیاده سازی سفارشی این است که می توانیم منطق را متناسب با نیازهای خاص خود تعریف کنیم.
نام کلاس را به عنصر اختصاص دهید
اکنون که می توانیم تشخیص دهیم که آیا عنصر ما به صفحه رفته است یا خیر، باید تابعی را برای نمایش عنصر تعریف کنیم – در این حالت، عنصر را با اختصاص دادن عنصر نمایش می دهیم. scrolled نام کلاس.
1 | const displayScrollElement = (element) => { |
2 | element.classList.add("scrolled"); |
3 | }; |
سپس منطق خود را با تابع نمایش ترکیب می کنیم و از آن استفاده می کنیم forEach روشی برای فراخوانی تابع در همه js-scroll عناصر.
1 | const handleScrollAnimation = () => { |
2 | scrollElements.forEach((el) => { |
3 | if (elementInView(el, 100)) { |
4 | displayScrollElement(el); |
5 | }
|
6 | })
|
7 | } |
یک ویژگی اختیاری این است که وقتی عنصر دیگر در معرض دید نیست، آن را به حالت پیش فرض بازنشانی کنید. ما می توانیم این کار را با تعریف a انجام دهیم hideScrollElement تابع و گنجاندن آن در یک else بیانیه تابع فوق ما:
1 | const hideScrollElement = (element) => { |
2 | element.classList.remove("scrolled"); |
3 | };
|
4 | |
5 | const handleScrollAnimation = () => { |
6 | scrollElements.forEach((el) => { |
7 | if (elementInView(el, 100)) { |
8 | displayScrollElement(el); |
9 | } else { |
10 | hideScrollElement(el); |
11 | }
|
12 | })
|
13 | } |
در نهایت، روش فوق را به شنونده رویداد پیمایشی در پنجره منتقل می کنیم تا هر زمان که کاربر پیمایش کند، اجرا شود.
1 | window.addEventListener(\'scroll\', () => { |
2 | handleScrollAnimation(); |
3 | }) |
و voilà، ما تمام توابع مورد نیاز برای متحرک سازی را در اسکرول پیاده سازی کرده ایم.
ما می توانیم ببینیم که منطق در این دمو چگونه کار می کند:
کد کامل به این شکل است. جاوا اسکریپت:
1 | const scrollOffset = 100; |
2 | |
3 | const scrollElement = document.querySelector(".js-scroll"); |
4 | |
5 | const elementInView = (el, offset = 0) => { |
6 | const elementTop = el.getBoundingClientRect().top; |
7 | |
8 | return ( |
9 | elementTop <= |
10 | ((window.innerHeight || document.documentElement.clientHeight) - offset) |
11 | );
|
12 | };
|
13 | |
14 | const displayScrollElement = () => { |
15 | scrollElement.classList.add(\'scrolled\'); |
16 | }
|
17 | |
18 | const hideScrollElement = () => { |
19 | scrollElement.classList.remove(\'scrolled\'); |
20 | }
|
21 | |
22 | const handleScrollAnimation = () => { |
23 | if (elementInView(scrollElement, scrollOffset)) { |
24 | displayScrollElement(); |
25 | } else { |
26 | hideScrollElement(); |
27 | }
|
28 | }
|
29 | |
30 | window.addEventListener(\'scroll\', () => { |
31 | handleScrollAnimation(); |
32 | }) |
CSS:
1 | .js-scroll { |
2 | width: 50%; |
3 | height: 300px; |
4 | background-color: #DADADA; |
5 | transition: background-color 500ms; |
6 | }
|
7 | |
8 | .js-scroll.scrolled { |
9 | background-color: aquamarine; |
10 | } |
4. انیمیشن های بیشتر با CSS
بیایید دوباره به اولین نسخه ی نمایشی نگاهی بیندازیم:
می بینیم که عناصر با انیمیشن های مختلف جاوا اسکریپت ظاهر می شوند. این کار با اختصاص انیمیشن های مختلف CSS به نام کلاس ها انجام شد. HTML این دمو به این صورت است:
1 | |
2 | |
3 | |
4 | |
5 | This animation fades in. |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | This animation slides in to the top. |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | This animation slides in from the left. |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | This animation slides in from the right. |
27 | |
28 |
کلاس های کنار js-scroll کلاس چیزی است که ما در CSS برای مدیریت انیمیشن های مختلف هدف قرار می دهیم. در شیوه نامه CSS خود، خواهیم داشت:
1 | .scrolled.fade-in { |
2 | animation: fade-in 1s ease-in-out both; |
3 | }
|
4 | |
5 | .scrolled.fade-in-bottom { |
6 | animation: fade-in-bottom 1s ease-in-out both; |
7 | }
|
8 | |
9 | .scrolled.slide-left { |
10 | animation: slide-in-left 1s ease-in-out both; |
11 | }
|
12 | |
13 | .scrolled.slide-right { |
14 | animation: slide-in-right 1s ease-in-out both; |
15 | }
|
16 | |
17 | @keyframes slide-in-left { |
18 | 0% { |
19 | transform: translateX(-100px); |
20 | opacity: 0; |
21 | }
|
22 | 100% { |
23 | transform: translateX(0); |
24 | opacity: 1; |
25 | }
|
26 | }
|
27 | |
28 | @keyframes slide-in-right { |
29 | 0% { |
30 | transform: translateX(100px); |
31 | opacity: 0; |
32 | }
|
33 | 100% { |
34 | transform: translateX(0); |
35 | opacity: 1; |
36 | }
|
37 | }
|
38 | |
39 | @keyframes fade-in-bottom { |
40 | 0% { |
41 | transform: translateY(50px); |
42 | opacity: 0; |
43 | }
|
44 | 100% { |
45 | transform: translateY(0); |
46 | opacity: 1; |
47 | }
|
48 | }
|
49 | |
50 | @keyframes fade-in { |
51 | 0% { |
52 | opacity: 0; |
53 | }
|
54 | 100% { |
55 | opacity: 1; |
56 | }
|
57 | } |
ما نیازی به ایجاد هیچ تغییری در کد جاوا اسکریپت نداریم زیرا منطق یکسان است. این بدان معناست که ما می توانیم تعداد زیادی انیمیشن مختلف را در یک صفحه بدون نوشتن توابع جدید داشته باشیم.
5. افزایش کارایی با دریچه گاز
هرگاه تابعی را در یک اسکرول شنونده قرار دهیم، آن تابع فراخوانی می شود هر زمان کاربر صفحه را اسکرول می کند. پیمایش یک صفحه 500 پیکسلی می تواند باعث شود که یک تابع حداقل 50 بار فراخوانی شود. اگر بخواهیم عناصر زیادی را در صفحه قرار دهیم، این می تواند باعث کند شدن صفحه ما به میزان قابل توجهی شود.
عملکرد دریچه گاز برای نجات!
ما می توانیم تعداد دفعات فراخوانی یک تابع را با استفاده از "تابع گاز" کاهش دهیم. یک تابع دریچه گاز جاوا اسکریپت یک تابع مرتبه بالاتر است که تابعی را که تنها یک بار در یک بازه زمانی مشخص به آن ارسال شده فراخوانی می کند.
این به ویژه برای رویدادهای پیمایش مفید است، زیرا نیازی به شناسایی هر پیکسلی که کاربر اسکرول می کند نیست. به عنوان مثال، اگر یک تابع دریچه گاز با تایمر 100 میلی ثانیه داشته باشیم، این تابع فقط یک بار به ازای هر 100 میلی ثانیه اسکرول کاربر فراخوانی می شود.
یک تابع دریچه گاز جاوا اسکریپت را می توان به صورت زیر پیاده سازی کرد:
1 | //initialize throttleTimer as false
|
2 | let throttleTimer = false; |
3 | |
4 | const throttle = (callback, time) => { |
5 | //don\'t run the function while throttle timer is true
|
6 | if (throttleTimer) return; |
7 | |
8 | //first set throttle timer to true so the function doesn\'t run
|
9 | throttleTimer = true; |
10 | |
11 | setTimeout(() => { |
12 | //call the callback function in the setTimeout and set the throttle timer to false after the indicated time has passed
|
13 | callback(); |
14 | throttleTimer = false; |
15 | }, time); |
16 | } |
ما می توانیم پنجره خود را در شنونده رویداد اسکرول به شکل زیر تغییر دهیم
1 | window.addEventListener(\'scroll\', () => {
|
2 | throttle(handleScrollAnimation, 250); |
3 | }) |
حالا ما handleScrollAnimation در حالی که کاربر در حال پیمایش است، عملکرد هر 250 میلی ثانیه فراخوانی می شود.
در اینجا نسخه ی نمایشی به روز شده به نظر می رسد:
6. بهبود دسترسی
هنگام اجرای یک ویژگی سفارشی، عملکرد تنها مورد نیاز نیست. ما همچنین نیاز به طراحی برای دسترسی داریم. طراحی برای دسترسی به معنای در نظر گرفتن انتخاب ها و شرایط کاربران است. ممکن است برخی از کاربران اصلاً تمایلی به داشتن انیمیشن نداشته باشند، بنابراین باید آن را در نظر بگیریم.
پرس و جو رسانه حرکت کاهش یافته
ما می توانیم این کار را با کوئری prefers-reduced-motion و پیاده سازی جاوا اسکریپت انجام دهیم.
"ترجیح می دهد-حرکت کاهش یافته (...) برای تشخیص اینکه آیا کاربر درخواست کرده است که سیستم مقدار حرکت غیر ضروری را که استفاده می کند به حداقل برساند استفاده می شود - MDN
با اصلاح کد بالا، پرس و جو در CSS به این شکل خواهد بود:
1 | @media (prefers-reduced-motion) { |
2 | .js-scroll { |
3 | opacity: 1; |
4 | }
|
5 | .scrolled { |
6 | animation: none !important; |
7 | }
|
8 | } |
با این خطوط کد، اطمینان حاصل می کنیم که عناصر متحرک همیشه قابل مشاهده هستند و انیمیشن برای همه عناصر خاموش است.
کوئری prefers-reduced-motion به طور کامل در همه مرورگرها پشتیبانی نمی شود، بنابراین می توانیم یک جاوا اسکریپت را اضافه کنیم:
1 | const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)"); |
2 | |
3 | window.addEventListener("scroll", () => { |
4 | //check if mediaQuery exists and if the value for mediaQuery does not match \'reduce\', return the scrollAnimation.
|
5 | if (mediaQuery && !mediaQuery.matches) { |
6 | handleScrollAnimation() |
7 | }
|
8 | }); |
به این ترتیب، اگر کاربر کاهش حرکت را ترجیح دهد، handleScrollAnimation تابع به هیچ وجه فراخوانی نمی شود.
ما اکنون یک پیاده سازی بسیار کارآمد و کاملاً در دسترس از ویژگی «انیمیته در اسکرول» داریم که در همه مرورگرها کار می کند!
آموزش های کاربردی بیشتر جاوا اسکریپت
منبع: https://webdesign.tutsplus.com/animate-on-scroll-with-javascript--cms-36671t