哇塞,有好吃的~

React编写一个移动H5的纵向翻屏组件

前言

  • 由于工作需要,需要做一版在手机上查看的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']}
/>

小结

  • 先记一下,以后如果有更好的思路再优化,先暂时用着
posted @ 2020-08-28 10:37  风行者夜色  阅读(840)  评论(0编辑  收藏  举报