为React上的滑块制作酷的粘滞效果

有许多不同的库可用于实现具有所有可能效果的滑块。 最适合React的一些是: ReactSlickSwiper 。 但是,当我的项目需要水平粘滞效果时,没有发现合适的结果。



在本文中,我们将尝试逐步创建这种滑块,也许您也将需要它!


安装所需的软件包


我们将使用Create React App创建项目


创建一个应用程序:


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 (可见幻灯片的数量)。 我们不需要其他任何东西,但是可以在此处找到swiper的所有可能参数。


创建一个单独的组件Slide,使滑块的外观准备就绪。


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

到目前为止,我们有这样一个滑块:



很酷,已经有了一个开始,我们将继续通过此滑块执行所需的操作。


添加粘性效果


刷卡器具有setTranslate setTransition两个setTranslatesetTransition事件。


物业资料何时触发什么回报
setTranslate当我们移动滑块并放下滑块时,它可以工作返回滑块当前移动的值,如果我们释放它,它将自动带到的值
setTransition当我们开始移动滑块,释放滑块以及将滑块移至所需位置时,它会起作用返回转换值(以毫秒为单位)

将其添加到我们的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 ,因此在执行幻灯片时,我们将样式悬挂在侧面。 scaleopacity值可以根据需要选择。 在我看来,以下时间间隔最合适: [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; // ,   } // ... 

好了,就是这样,我们的效果已经准备就绪! 您可以在我的github上看到完成的示例


感谢您的关注!

Source: https://habr.com/ru/post/zh-CN474254/


All Articles