React編寫一個移動H5的縱向翻屏元件
阿新 • • 發佈:2020-08-28
前言
- 由於工作需要,需要做一版在手機上檢視的
H5
的廣告頁。廣告頁面基本都是一塊內容佔滿一屏,然後上滑下滑就翻頁,所以需要一個這樣的翻頁功能。
程式碼實現
- 廢話不多說,直接上程式碼
/** * 一個放置多個滿屏頁面的容器,支援上下滑動 */ // ScrollBox.jsx import React, { Component } from 'react'; import './ScrollBox.less'; class ScrollBox extends Component { state = { pageIndex: 0, // 當前在第幾屏,從0開始算 } startY = 0; // touch的起始Y座標 handleTouchMove = (e) => { const y = e.changedTouches[0].clientY; const deltaY = y - this.startY; const height = this.root.offsetHeight; const len = this.props.list.length; const pageIndex = this.state.pageIndex; if(pageIndex === 0 && deltaY > 0) { return; } if(pageIndex === len - 1 && deltaY < 0) { return; } this.scrollBox.style.top = `${-(height * pageIndex - deltaY)}px`; } handleTouchStart = (e) => { this.startY = e.changedTouches[0].clientY; } handleTouchEnd = (e) => { const { list = [] } = this.props; let { pageIndex } = this.state; const y = e.changedTouches[0].clientY; const deltaY = y - this.startY; const len = list.length; if(deltaY > 50) { if(pageIndex > 0) { pageIndex -= 1; } } else if(deltaY < -50) { if(pageIndex < len - 1) { pageIndex += 1; } } this.setState({ pageIndex }, () => { this.anmateFunc(); }) } anmateFunc = () => { const currentStr = this.scrollBox.style.top; let current = 0; if(currentStr) { current = Number(currentStr.slice(0, currentStr.length - 2)); } const height = this.root.offsetHeight; const target = -(height * this.state.pageIndex); const delta = target - current; const part = delta / 20; const moveOnce = (i) => { this.scrollBox.style.top = `${current + (i * part)}px`; if(++i < 21) { ((index) => { setTimeout(() => { moveOnce(index); }, 10); })(i) } } moveOnce(1); } componentDidMount() { this.root = document.getElementById('root'); this.root.addEventListener('touchstart', this.handleTouchStart); this.root.addEventListener('touchend', this.handleTouchEnd); this.root.addEventListener('touchmove', this.handleTouchMove); } componentWillUnmount() { this.root.removeEventListener('touchstart', this.handleTouchStart); this.root.removeEventListener('touchend', this.handleTouchEnd); this.root.removeEventListener('touchmove', this.handleTouchMove); } render() { const { list = [] } = this.props; const { pageIndex } = this.state; return ( <div className="ScrollBox" ref={scrollBox => this.scrollBox = scrollBox} > { list.map((child, index) => { return ( <div className={`page ${pageIndex === index ? 'active' : ''}`} style={{ background: colors[index] }} key={`page-${index}`}> <div style={{ fontSize: '1rem' }} className={`animate ${index % 2 === 0 ? 'animate-slideLeft' : 'animate-slideRight'}`}> { child } </div> </div> ) }) } </div> ) } } const colors = ['red', 'orange', 'yellow', 'green']; export default ScrollBox;
// ScrollBar.less .ScrollBox { position: absolute; top: 0; left: 0; .page { width: 100vw; height: 100vh; } // 當頁面滑到可視區域時,可以新增一個active的類,方便做動畫的管理,只有當頁面出現到可視區域才加上動畫效果,否則就不加。因為廣告頁大多是需要動畫效果的 .active { .animate { animation-timing-function: ease-in-out; animation-duration: 0.6s; animation-iteration-count: 1; &-slideLeft { animation-name: slideLeft; } &-slideRight { animation-name: slideRight; } @keyframes slideLeft { 0% { transform: translateX(100%); } 100% { transform: translateX(0); } } @keyframes slideRight { 0% { transform: translateX(-100%); } 100% { transform: translateX(0); } } } } }
// 使用
<ScrollBox
list={['page1', 'page2', 'page3', 'page4']}
/>
小結
- 先記一下,以後如果有更好的思路再優化,先暫時用著