造个小轮子|项目复盘

242 阅读2分钟

背景

目前在做项目的UI改版,需要加一个公告栏,用于向用户展示公告信息;当公告信息过长时,公告栏对内容进行滚动展示;用户可以点击隐藏,并且同一窗口不再重复展示。 先来看看效果:

notice.gif

拓展

期间也造了两个其他的迷你轮子:不使用滚动插件的日期选择器带切换动画的数字展示组件 感兴趣的可以去看看:

日期组件

数字展示组件

效果演示.gif

公告栏组件实现

滚动展示

布局:

<div className='ui-notice-container'>
    <div className="icon-notice"></div>
    <div ref='con' className='ui-notice'>
        <span ref='word'>{ noticeMessage }</span>
    </div>
    <div className='iconfont icon-close' onClick={this.handleClose.bind(this)}></div>
</div>

外面包裹的容器设置

overflow:hidden

利用setInterval() 动态设置里面的用于承载公告内容的滑块的margin-left属性,实现滚动效果


if(!this.refs.con) return;
let conWidth = this.refs.con.offsetWidth,
    wordWidth = this.refs.word.offsetWidth,
    speed = 0.15,
    translateXnum = 0;
if(navigator.userAgent.indexOf('Android 4')>-1){
    wordWidth = this.refs.word.getBoundingClientRect().width;       
}
if(conWidth < wordWidth){
    clearInterval(sliderInterval);
    sliderInterval = setInterval(function(){
        if(parseInt(Math.abs(translateXnum)) > wordWidth){
            translateXnum = conWidth;
            this.refs.word.style.marginLeft = translateXnum + 'px';
        }
        translateXnum -= speed;
        this.refs.word.style.marginLeft = translateXnum + 'px';
    }.bind(this),2);
}

同一窗口不再重复展示

将控制公告栏显示/隐藏的状态存到session Storage ,用于存储显示隐藏状态

sessionStorage 属性允许你访问一个,对应当前源的 session Storage 对象。它与 localStorage 相似,不同之处在于 localStorage 里面存储的数据没有过期时间设置,而存储在 sessionStorage 里面的数据在页面会话结束时会被清除。

封装一下sessionStorage操作

let Storage = {
    get(key) {
        if(sessionStorage.getItem(key))  {
            return JSON.parse(sessionStorage.getItem(key));
        } else {
            return '';
        }
    },

    set(key, val) {
        sessionStorage.setItem(key, JSON.stringify(val));
    },

    del(key) {
        if(sessionStorage.getItem(key)) sessionStorage.removeItem(key);
    },

    clear() {
        sessionStorage.clear();
    }
};

export default Storage;

使用

import NoticeComponent from './modules/Notice';

<NoticeComponent noticeMessage={noticeMessage} />

完整代码

Notice.js

import React, {Component} from 'react';
import ls from '../../utilities/storage';
import "../../../less/notice.less";

let sliderInterval=null;

class NoticeComponent extends Component {

    constructor(props) {
        super(props);
        const flagNotice = ls.get('notice') || 'show';
        this.state = {
            show: this.props.noticeMessage && flagNotice == 'show'
        };
    }

    handleClose() {
        this.setState({
            show:false
        });
        clearInterval(sliderInterval);
        ls.set('notice','hid');
    }

    componentWillUnmount() {
        if(sliderInterval){
            clearInterval(sliderInterval);
        }
    }

    componentDidMount() {
        if(!this.refs.con) return;
        let conWidth = this.refs.con.offsetWidth,
            wordWidth = this.refs.word.offsetWidth,
            speed = 0.15,
            translateXnum = 0;
        if(navigator.userAgent.indexOf('Android 4')>-1){
            wordWidth = this.refs.word.getBoundingClientRect().width;       
        }
        if(conWidth < wordWidth){
            clearInterval(sliderInterval);
            sliderInterval = setInterval(function(){
                if(parseInt(Math.abs(translateXnum)) > wordWidth){
                    translateXnum = conWidth;
                    this.refs.word.style.marginLeft = translateXnum + 'px';
                }
                translateXnum -= speed;
                this.refs.word.style.marginLeft = translateXnum + 'px';
            }.bind(this),2);
        }
    }

    render() {
        const { noticeMessage } = this.props;
        if(this.state.show){
                return <div className='ui-notice-container'>
                    <div className="icon-notice"></div>
                    <div ref='con' className='ui-notice'>
                        <span ref='word'>{ noticeMessage }</span>
                    </div>
                    <div className='iconfont icon-close' onClick={this.handleClose.bind(this)}></div>
                </div>
        }else{
            return null;
        }
    }
}

NoticeComponent.PropTypes = {

};

export default NoticeComponent;

notice.less

// 字体大小
@defaultSize: 16;

.ui-notice-container{
    top: 0;
    background: #FFF8EE;
    color: #AF7E46;
    max-width: 800px;
    font-size: unit((13/@defaultSize),rem);
    height: unit((38/@defaultSize),rem);
    line-height: unit((38/@defaultSize),rem);
    margin: 0 unit((15/@defaultSize),rem);
    display: -webkit-box;
    display: -webkit-flex;
    display: flex;
    &.hidden{
        display: none;
    }
    .icon-notice{
        background: url(../assets/images/notice.png) no-repeat right top;
        background-size:cover;
        width:unit((33/@defaultSize),rem);
    }
    .ui-notice{
        overflow: hidden;
        -webkit-box-flex: 1;      /* OLD - iOS 6-, Safari 3.1-6 */
        -webkit-flex: 1;          /* Chrome */
        flex: 1;
        text-align: left;
        span{
            white-space: nowrap;
        }
    }
    .icon-close{
        background: url(../assets/images/close.png) no-repeat right top;
        background-size:cover;
        width:unit((14/@defaultSize),rem);
        height:unit((14/@defaultSize),rem);
        margin:unit((11/@defaultSize),rem) unit((7/@defaultSize),rem) unit((9/@defaultSize),rem) unit((3/@defaultSize),rem);
    }
}

本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情