جعل تأثير لزجة بارد لشريط التمرير على React

هناك العديد من المكتبات المختلفة لتنفيذ شريط التمرير مع كافة التأثيرات المحتملة. بعض الأفضل لـ React هي: ReactSlick و Swiper . ولكن عندما كان مطلوبًا تأثير لاصق أفقي لمشروعي ، لم يتم العثور على شيء مناسب.



سنحاول في هذه المقالة إنشاء شريط تمرير كهذا تدريجيًا ، وربما ستحتاج أيضًا إليه!


تثبيت الحزم المطلوبة


سوف نستخدم تطبيق Create React لإنشاء المشروع .


إنشاء تطبيق:


npx create-react-app my-app 

لن نقوم بتمرير شريط التمرير من نقطة الصفر ، ولكننا نأخذ مكتبة Swiper ، فهناك أنسب الأحداث التي ستحتاج إلى ربطها (المزيد حول هذا لاحقًا). ثم سنحتاج إلى تثبيت الحزم التالية:


 npm i swiper react-id-swiper 

والحزمة الأخيرة (اختياري) لاستخدام المعالج الأولي sass:


 npm i node-sass 

والنتيجة هي package.json:


package.json


 { "name": "sticky-slider", "version": "0.1.0", "private": true, "dependencies": { "node-sass": "^4.13.0", "react": "^16.11.0", "react-dom": "^16.11.0", "react-id-swiper": "^2.3.2", "react-scripts": "3.2.0", "swiper": "^5.2.0" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" }, "eslintConfig": { "extends": "react-app" }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] } } 

عظيم ، الآن بدأنا في تطبيق شريط التمرير.


إنشاء شريط التمرير بسيط


لنبدأ بإنشاء ملف صغير باستخدام الشرائح الخاصة بنا.


src/data.json


 [ { "title": "Slide 1", "color": "#aac3bf" }, { "title": "Slide 2", "color": "#c9b1bd" }, { "title": "Slide 3", "color": "#d5a29c" }, { "title": "Slide 4", "color": "#82a7a6" }, { "title": "Slide 5", "color": "#e6af7a" }, { "title": "Slide 6", "color": "#95be9e" }, { "title": "Slide 7", "color": "#97b5c5" } ] 

بعد ذلك ، سنقوم بإنشاء شريط تمرير عادي به تأثيرات افتراضية.


 // src/components/StickySlider/StickySlider.jsx import React from 'react'; import Swiper from 'react-id-swiper'; import 'react-id-swiper/lib/styles/css/swiper.css'; import data from '../../data'; const StickySlider = () => { const params = { slidesPerView: 3, }; return ( <Swiper {...params}> {data.map((item, idx) => ( <div key={idx}> {item.title} </div> ))} </Swiper> ); }; export default StickySlider; 

وفقًا لذلك ، نقوم بإنشاء ملف فهرس للمكون.


 // src/components/StickySlider/index.js export { default } from './StickySlider'; 

المعلمة الوحيدة التي وصفناها هي slidesPerView (عدد الشرائح المرئية). لا نحتاج إلى أي شيء آخر ، ولكن يمكن العثور على جميع المعلمات الممكنة للممسحة هنا .


قم بإنشاء شريحة منفصلة ، بحيث يكون مظهر شريط التمرير جاهزًا.


 // src/components/Slide/Slide.jsx import React from 'react'; import css from './Slide.module.scss'; const Slide = ({ children, color }) => { return ( <div className={css.container}> <div className={css.content} style={{ background: color }} /> <footer className={css.footer}> {children} </footer> </div> ); }; export default Slide; 

أنماط للشريحة.


 // src/components/Slide/Slide.module.scss .container { margin: 0 1em; border-radius: 4px; overflow: hidden; background-color: #fff; } .content { box-sizing: border-box; padding: 50% 0; } .footer { color: #333; font-weight: 700; font-size: 1.25em; text-align: center; padding: 1em; } 

وبالتالي ملف الفهرس:


 // src/components/Slide/index.js export { default } from './Slide'; 

وتحديث StickySlider قليلا.


 // src/components/StickySlider/StickySlider.jsx import React from 'react'; import Swiper from 'react-id-swiper'; import 'react-id-swiper/lib/styles/css/swiper.css'; import Slide from '../Slide'; import data from '../../data'; const StickySlider = () => { const params = { slidesPerView: 3, }; return ( <Swiper {...params}> {data.map((item, idx) => ( <div key={idx}> {/*   */} <Slide color={item.color}> {item.title} </Slide> </div> ))} </Swiper> ); }; export default StickySlider; 

الآن قم بإدخال شريط التمرير هذا في App.jsx ، في نفس الوقت ، App.jsx بنية الصفحة الأدنى.


 // App.jsx import React from 'react'; import StickySlider from './components/StickySlider'; import css from './App.module.scss'; const App = () => { return ( <div className={css.container}> <h1 className={css.title}>Sticky slider</h1> <div className={css.slider}> <StickySlider /> </div> </div> ); }; export default App; 

وفي ملف scss المقابل ، سنكتب بعض الأنماط.


 // App.module.scss .container { padding: 0 15px; } .title { font-weight: 700; font-size: 2.5em; text-align: center; margin: 1em 0; } .slider { margin: 0 -15px; } 

حتى الآن ، لدينا شريط التمرير هذا:



رائع ، لقد تم البدء ، سنواصل القيام بما نحتاج إليه من شريط التمرير هذا.


إضافة تأثير لزجة


يحتوي swiper على حدثين setTranslate و setTransition .


ممتلكاتعندما يتم تشغيلهاما يعود
setTranslateإنه يعمل عندما ننقل شريط التمرير وفي الوقت الذي نخفض فيهتقوم بإرجاع القيمة التي يتم بها تحريك شريط التمرير حاليًا ، وفي حالة إصداره ، فإن القيمة التي سيتم جلبها إليه تلقائيًا
setTransitionيعمل عندما نبدأ في تحريك شريط التمرير ، وعندما نحرره ، وعندما يتم وضع شريط التمرير في الموضع المطلوبتقوم بإرجاع قيمة الانتقال بالمللي ثانية

أضف هذا إلى مكون StickySlider بنا StickySlider الفور إلى Slider ، وسيكون هناك مفيد:


 // src/components/StickySlider/StickySlider.jsx import React, { useState, useEffect } from 'react'; import Swiper from 'react-id-swiper'; import 'react-id-swiper/lib/styles/css/swiper.css'; import Slide from '../Slide'; import data from '../../data'; const StickySlider = () => { const [swiper, updateSwiper] = useState(null); const [translate, updateTranslate] = useState(0); const [transition, updateTransition] = useState(0); const params = { slidesPerView: 3, }; useEffect(() => { if (swiper) { swiper.on('setTranslate', (t) => { updateTranslate(t); }); swiper.on('setTransition', (t) => { updateTransition(t); }); } }, [swiper]); return ( <Swiper getSwiper={updateSwiper} {...params}> {data.map((item, idx) => ( <div key={idx}> <Slide translate={translate} transition={transition} color={item.color} > {item.title} </Slide> </div> ))} </Swiper> ); }; export default StickySlider; 

أنصحك بتحريك شريط التمرير ومشاهدة ما يتم عرضه في هذه اللحظة بمزيد من التفصيل:


 // src/components/StickySlider/StickySlider.jsx // ... useEffect(() => { if (swiper) { swiper.on('setTranslate', (t) => { console.log(t, 'translate'); updateTranslate(t); }); swiper.on('setTransition', (t) => { console.log(t, 'transform'); updateTransition(t); }); } }, [swiper]); // .. 

يمكنني استخدام السنانير لتخزين الدولة. إذا لم تكن على دراية بهم ، أنصحك بقراءة الوثائق (باللغة الروسية) .


علاوة على ذلك ، فإن أصعب شيء سيحدث في مكون Slide .


نحتاج إلى حالة المسافة البادئة من الحد الأيسر لشريط التمرير وعرض الشريحة الحالية:


 // src/components/StickySlider/StickySlider.jsx // ... const container = useRef(null); const [offsetLeft, updateOffsetLeft] = useState(0); const [width, updateWidth] = useState(1); // ... 

يتم إضافتها مرة واحدة عند تهيئة العنصر ولا يتم تغييرها. لذلك نحن نستخدم useEffect مع مجموعة فارغة. في الوقت نفسه ، نحصل على معلمات ليس من الشريحة نفسها ، ولكن في برنامجها الفني من خلال parentElement ، حيث أننا سنقوم بتحويل المجمع الحالي باستخدام خاصية transform .


 // src/components/StickySlider/StickySlider.jsx // ... useEffect(() => { setTimeout(() => { const parent = container.current.parentElement; updateOffsetLeft(parent.offsetLeft); updateWidth(parent.offsetWidth); }, 0); }, []); // ... 

أهم لحظة. نحن نعتبر هذا الأمر برمته ونرميه في الأساليب:


 // src/components/Slide/Slide.jsx // ... const x = -translate - offsetLeft; const k = 1 - x / width; // [0 : 1] const style = x >= -1 ? { transform: `translateX(${x}px) scale(${k * 0.2 + 0.8})`, // [0.8 : 1] opacity: k < 0 ? 0 : k * 0.5 + 0.5, // [0.5 : 1] transition: `${transition}ms`, } : {}; // ... 

تأتي خاصية translate إلينا من الوالد وهي نفسها بالنسبة لجميع الشرائح. لذلك ، للعثور على ترجمة فردية لشريحة واحدة ، قم بطرح offsetLeft منها.


المتغير k هو قيمة من 0 إلى 1. باستخدام هذه القيمة ، سنفعل الرسوم المتحركة. هذا متغير رئيسي ، لأنه يمكن استخدامه لإجراء أي تأثيرات.


الآن نحن نحسب الأنماط. الشرط x >= -1 يكون راضيًا عندما تكون الشريحة في منطقة الرسوم المتحركة ، لذلك عندما يتم تنفيذها ، نقوم بتعليق الأنماط على الجانب. يمكن تحديد قيم scale opacity كما يحلو لك. بدا لي أن الفواصل الزمنية التالية هي الأنسب: [0.8 : 1] scale و [0.5 : 1] opacity .


يتم توفير خاصية transition مباشرة من حدث المكتبة.


إليك ما يحدث بعد إضافة كل ما سبق:


 // src/components/Slide/Slide.jsx import React, { useRef, useEffect, useState } from 'react'; import css from './Slide.module.scss'; const Slide = ({ children, translate, transition, color }) => { const container = useRef(null); const [offsetLeft, updateOffsetLeft] = useState(0); const [width, updateWidth] = useState(1); useEffect(() => { setTimeout(() => { const parent = container.current.parentElement; updateOffsetLeft(parent.offsetLeft); updateWidth(parent.offsetWidth); }, 0); }, []); const x = -translate - offsetLeft; const k = 1 - x / width; // [0 : 1] const style = x >= -1 ? { transform: `translateX(${x}px) scale(${k * 0.2 + 0.8})`, // [0.8 : 1] opacity: k < 0 ? 0 : k * 0.5 + 0.5, // [0.5 : 1] transition: `${transition}ms`, } : {}; return ( <div ref={container} style={style} className={css.container}> <div className={css.content} style={{ background: color }} /> <footer className={css.footer}> {children} </footer> </div> ); }; export default Slide; 

أضف الآن الخصائص التالية إلى ملف نمط الشريحة:


 // src/components/Slide/Slide.module.scss .container { // ... transform-origin: 0 50%; //      transition-property: opacity, transform; // ,   } // ... 

حسنًا ، هذا كل شيء ، تأثيرنا جاهز! يمكنك أن ترى المثال النهائي على جيثب بلدي .


شكرا لاهتمامكم!

Source: https://habr.com/ru/post/ar474254/


All Articles