Hacer un efecto pegajoso genial para un control deslizante en React

Hay muchas bibliotecas diferentes para implementar un control deslizante con todos los efectos posibles. Algunos de los mejores para React son: ReactSlick y Swiper . Pero cuando se requirió un efecto adhesivo horizontal para mi proyecto, no se encontró nada adecuado.



En este artículo intentaremos crear gradualmente un control deslizante de este tipo, ¡tal vez también lo necesite!


Instale los paquetes requeridos


Utilizaremos la aplicación Create React para crear el proyecto .


Crea una aplicación:


npx create-react-app my-app 

No haremos el control deslizante desde cero, pero tomemos la biblioteca Swiper , hay los eventos más adecuados para los que tendrá que conectarse (más sobre esto más adelante). Luego necesitaremos instalar los siguientes paquetes:


 npm i swiper react-id-swiper 

Y el último paquete (opcional) para usar el preprocesador sass:


 npm i node-sass 

El resultado es 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" ] } } 

Genial, ahora estamos comenzando a implementar el control deslizante.


Crea un control deslizante simple


Comencemos creando un pequeño archivo con nuestras diapositivas.


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

Después de eso, haremos un control deslizante regular con efectos predeterminados.


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

Y en consecuencia, creamos un archivo de índice para el componente.


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

El único parámetro que describimos es slidesPerView (el número de diapositivas visibles). No necesitamos nada más, pero todos los parámetros posibles del swiper se pueden encontrar aquí .


Cree una diapositiva componente separada, de modo que la apariencia del control deslizante esté lista.


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

Estilos para la diapositiva.


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

Y en consecuencia el archivo de índice:


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

Y actualice StickySlider un poco.


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

Ahora inserte este control deslizante en App.jsx , al mismo tiempo App.jsx la estructura de página mínima.


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

Y en el archivo scss correspondiente escribiremos algunos estilos.


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

Hasta ahora, tenemos un control deslizante de este tipo:



Genial, se ha comenzado, seguiremos haciendo lo que necesitamos de este control deslizante.


Añadir un efecto adhesivo


El swiper tiene dos eventos setTranslate y setTransition que setTransition .


PropiedadCuando se activaLo que vuelve
setTranslateFunciona cuando movemos el control deslizante y en el momento en que lo bajamosdevuelve el valor por el cual el control deslizante se desplaza actualmente y, en el caso de que lo liberemos, el valor al que se llevará automáticamente
setTransitionfunciona cuando comenzamos a mover el control deslizante, cuando lo liberamos y cuando el control deslizante se coloca en la posición deseadadevuelve el valor de transición en milisegundos

Agregue esto a nuestro componente StickySlider e inmediatamente StickySlider a Slider , allí será útil:


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

Le aconsejo que mueva el control deslizante y vea con más detalle lo que se muestra en este momento:


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

Yo uso ganchos para almacenar el estado. Si no está familiarizado con ellos, le aconsejo que lea la documentación (en ruso) .


Además, lo más difícil sucederá en el componente Slide .


Necesitamos el estado de sangría del borde izquierdo del control deslizante y el ancho de la diapositiva actual:


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

Se agregan una vez en la inicialización del elemento y no se modifican. Por lo tanto, usamos useEffect con una matriz vacía. Al mismo tiempo, obtenemos los parámetros no de la diapositiva en sí, sino de su contenedor técnico a través de parentElement , ya que convertiremos el contenedor actual usando la propiedad transform .


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

El momento más importante. Consideramos todo esto y lo incluimos en los estilos:


 // 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 propiedad de translate nos llega del padre y es la misma para todas las diapositivas. Por lo tanto, para encontrar una traducción individual para una diapositiva, reste offsetLeft de ella.


La variable k es un valor de 0 a 1. Usando este valor, haremos la animación. Esta es una variable clave, porque se puede usar para hacer cualquier efecto.


Ahora calculamos los estilos. La condición x >= -1 se cumple cuando la diapositiva está en la zona de animación, por lo que cuando se ejecuta, colgamos los estilos a un lado. Los valores de scale y opacity se pueden seleccionar como desee. Los siguientes intervalos me parecieron más adecuados: [0.8 : 1] para scale y [0.5 : 1] para opacity .


La propiedad de transition se suministra directamente desde el evento de la biblioteca.


Esto es lo que sucede después de agregar todo lo anterior:


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

Ahora agregue las siguientes propiedades al archivo de estilo de diapositiva:


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

Bueno, eso es todo, ¡nuestro efecto está listo! Puedes ver el ejemplo terminado en mi github .


Gracias por su atencion!

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


All Articles