React项目实战(四)滚动的数字

6,055 阅读3分钟

上一篇React项目实战(三)尝试实现一个拉动刷新组件

需求

分析:我们要实现一个数字滚动效果,保证每个位上的数字都至少滚动一周,滚动效果是自下往上。让我们先思考一下几个细节:
① 方案的选取:css?js?
② 滚动的内容是什么?如果使用css动画用什么实现,如果使用js用什么实现?
③ 怎么实现每个位上的数字依次停止?

目前实现

基于background-position-y实现定位到具体数字,使用transition-duration实现每个位上的数字依次暂停,模仿掘金页面的实现效果如下:

可以跳过的一节

说一下我的思路历程吧,我一开始居然没想到用动画来实现,而是用setTimeout递归地插入每个位上的数字,写完之后才发现没有实现滚动。然后又search了一下滚动效果,发现了一个HTML标签<marquee>,虽然我在浏览器上可以用,但是MDN提示我们这个标签已废弃惹。好吧,一开始就应该想到动画的。

marquee

预备知识

  • react提供了一个react-transition-group库,我们可以通过几种方式实现

    • 定义className结合css样式
      //官方案例
      function App() {
        const [inProp, setInProp] = useState(false);
        return (
          <div>
            <CSSTransition in={inProp} timeout={200} classNames="my-node">
              <div>
                {"I'll receive my-node-* classes"}
              </div>
            </CSSTransition>
            <button type="button" onClick={() => setInProp(true)}>
              Click to Enter
            </button>
          </div>
        );
      }
      
      .my-node-enter {
        opacity: 0;
      }
      .my-node-enter-active {
        opacity: 1;
        transition: opacity 200ms;
      }
      .my-node-exit {
        opacity: 1;
      }
      .my-node-exit-active {
        opacity: 0;
        transition: opacity 200ms;
      }
      
    • 使用钩子
      • 进入状态:onEnter、onEntering、onEntered
      • 退出状态:onExit、onExiting、onExited
  • css的background属性

    属性 作用
    background-size length|percentage|cover|contain 规定背景图像的尺寸,可用于自适应
    background-repeat repeat|repeat-x|repeat-y|no-repeat|inherit 定义了图像的平铺模式
    background-image url 元素设置背景图像

具体实现

  • 背景图片的设置
    因为找不到掘金的图片,所以自己用ps做了一个24px ✖ 480px的背景图片,在实际使用中,我们要记住这个长宽比是1:2,方便后面做自适应调整,初始style设置如下:
    span {
        display: inline-block;
        background-image: url("number-bg-24-480.png");
        background-repeat: repeat-y;
        background-position: center 0;
        background-size: 1.5rem 30rem;
        width: 1.5rem;
        height: 3rem;
    }
    
  • 切分字符串为数组
     this.setState({
       data: nextProps.str.split('')
     })
    
  • 具体动画设置:回顾一下我们预先定义的每个位上数字大小为1.5rem ✖ 3rem,那么总长度为30rem,我们要使得每个数字至少自下而上滚动一周,最后依次停止,具体设置如下:
    <TransitionGroup className="text-list">
        {this.state.data.map((val, index) => {
            return (
            <CSSTransition
                key={index}
                timeout={500}
                onEnter={e => {
                  e.style.backgroundPositionY=`0`
                }}
                onEntering={
                  e => {
                    e.style.backgroundPositionY=`${-3*val - 30}rem`
                    e.style.transitionProperty="background-position-y"
                    e.style.transitionDuration=`${(index+1)*this.state.delay}ms`
                    e.style.transitionTimingFunction="ease-out"
                  }
                }
              >
                <span key={index} />
            </CSSTransition>
            )
         })}
    </TransitionGroup>
    

大功告成啦,虽然是一个很简单的小动画,看起来还是很炫酷的嘛,顺带回顾了一下css的一些基础知识。 项目地址