Faire un effet collant cool pour un curseur sur React

Il existe de nombreuses bibliothèques différentes pour implémenter un curseur avec tous les effets possibles. Certains des meilleurs pour React sont: ReactSlick et Swiper . Mais lorsqu'un effet collant horizontal était requis pour mon projet, rien de convenable n'a été trouvé.



Dans cet article, nous allons essayer de créer progressivement un tel curseur, peut-être en aurez-vous aussi besoin!


Installez les packages requis


Nous utiliserons l'application Create React pour créer le projet .


Créez une application:


npx create-react-app my-app 

Nous ne ferons pas le curseur à partir de zéro, mais prenons la bibliothèque Swiper , il y a les événements les plus appropriés pour lesquels vous devrez vous connecter (plus à ce sujet plus tard). Ensuite, nous devrons installer les packages suivants:


 npm i swiper react-id-swiper 

Et le dernier paquet (facultatif) pour utiliser le préprocesseur sass:


 npm i node-sass 

Le résultat est 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" ] } } 

Très bien, nous commençons maintenant à implémenter le curseur.


Créer un curseur simple


Commençons par créer un petit fichier avec nos diapositives.


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" } ] 

Après cela, nous allons créer un curseur régulier avec des effets par défaut.


 // 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; 

Et en conséquence, nous créons un fichier d'index pour le composant.


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

Le seul paramètre que nous avons décrit est slidesPerView (le nombre de diapositives visibles). Nous n'avons besoin de rien d'autre, mais tous les paramètres possibles du balai se trouvent ici .


Créez une diapositive de composant distincte, afin que l'apparence du curseur soit prête.


 // 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; 

Styles pour la diapositive.


 // 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; } 

Et en conséquence le fichier d'index:


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

Et mettez à jour StickySlider un peu.


 // 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; 

Insérez maintenant ce curseur dans App.jsx , en même temps, App.jsx la structure de page minimale.


 // 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; 

Et dans le fichier scss correspondant, nous écrirons quelques styles.


 // 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; } 

Jusqu'à présent, nous avons un tel curseur:



Cool, un début a été fait, nous continuerons à faire ce dont nous avons besoin de ce curseur.


Ajoutez un effet collant


Le swiper a deux événements setTranslate et setTransition dont setTranslate setTransition .


BiensQuand est déclenchéCe qui revient
setTranslateCela fonctionne quand on déplace le curseur et au moment où on le baisserenvoie la valeur par laquelle le curseur est actuellement décalé, et dans le cas où nous le relâchons, la valeur à laquelle il sera automatiquement amené
setTransitioncela fonctionne lorsque nous commençons à déplacer le curseur, lorsque nous le relâchons et lorsque le curseur est amené à la position souhaitéerenvoie la valeur de transition en millisecondes

Ajoutez-le à notre composant StickySlider et transférez-le immédiatement à Slider , il vous sera utile:


 // 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; 

Je vous conseille de déplacer le curseur et de voir plus en détail ce qui s'affiche en ce moment:


 // 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]); // .. 

J'utilise des crochets pour stocker l'état. Si vous ne les connaissez pas, je vous conseille de lire la documentation (en russe) .


De plus, la chose la plus difficile se produira dans le composant Slide .


Nous avons besoin de l'état d'indentation à partir de la bordure gauche du curseur et de la largeur de la diapositive actuelle:


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

Ils sont ajoutés une fois lors de l'initialisation de l'élément et ne sont pas modifiés. Par conséquent, nous utilisons useEffect avec un tableau vide. Dans le même temps, nous obtenons les paramètres non pas de la diapositive elle-même, mais de son wrapper technique via parentElement , car nous convertirons le wrapper actuel à l'aide de la propriété transform .


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

Le moment le plus important. Nous considérons tout cela et le jetons dans les styles:


 // 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`, } : {}; // ... 

La propriété translate nous vient du parent et est la même pour toutes les diapositives. Par conséquent, pour rechercher une traduction individuelle pour une diapositive, soustrayez offsetLeft de celle-ci.


La variable k est une valeur de 0 à 1. En utilisant cette valeur, nous ferons l'animation. Il s'agit d'une variable clé, car elle peut être utilisée pour créer des effets.


Maintenant, nous calculons les styles. La condition x >= -1 est satisfaite lorsque la diapositive est dans la zone d'animation, donc lorsqu'elle est exécutée, nous suspendons les styles sur le côté. Les valeurs d' scale et d' opacity peuvent être sélectionnées comme vous le souhaitez. Les intervalles suivants m'ont paru les plus appropriés: [0.8 : 1] pour l' scale et [0.5 : 1] pour l' opacity .


La propriété de transition est fournie directement à partir de l'événement de bibliothèque.


Voici ce qui se passe après avoir ajouté tout ce qui précède:


 // 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; 

Ajoutez maintenant les propriétés suivantes au fichier de style de diapositive:


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

Et bien voilà, notre effet est prêt! Vous pouvez voir l'exemple fini sur mon github .


Merci de votre attention!

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


All Articles