0

我试图用React和CSS类做一个动画。我创建了一个live demo,如果您访问它并单击开始按钮,您将看到文本逐渐淡入。这是我之后所需的动画。以不可预知的方式用setState和类名反应更新DOM

但是,当您多次点击“开始”并且我无法确定原因时,似乎存在一致性问题。

该问题:下面是该问题的录音,您可以看到号码1的行为并不像预期的那样。

live demo

React Animation

过程:点开始,将取消以往任何requestAnimationFrame”和它最初的形式将重置状态。然后它调用showSegments()函数,该函数的干净状态没有附加className。

此功能然后通过状态映射到状态中的每个段添加一个isActive。然后我们用地图渲染dom并应用新的状态。

这应该会创建一个平滑的分段动画,因为每个类都会逐个放下。但是,当我在Chrome(版本56.0.2924.87(64位))和iOS上测试它时,它非常不稳定,有时它可以很好地工作,有时第一个DOM元素不会生成动画,它只会保持向上并且可见它已完成与“isActive”的过渡状态。

我试图在safari中复制这个问题,但它工作得很好,我很新的反应,所以我不知道这是否是最好的方式去做事情,希望有人可以提供一些洞察力为什么这表现得相当不稳定!

/* MotionText.js */ 
import React, { Component } from 'react'; 
import shortid from 'shortid'; 

class MotionText extends Component { 
    constructor(props) { 
    super(props); 

    this.showSegments = this.showSegments.bind(this); 
    this.handleClickStart = this.handleClickStart.bind(this); 
    this.handleClickStop = this.handleClickStop.bind(this); 

    this.initialState =() => { return { 
     curIndex: 0, 
     textSegments: [ 
     ...'123456789123456789123456789123456789' 
     ].map(segment => ({ 
     segment, 
     id: shortid.generate(), 
     className: null 
     })) 
    }}; 
    this.state = this.initialState(); 
    } 

    handleClickStop() { 
    cancelAnimationFrame(this.rafId); 
    } 

    handleClickStart(){ 
    cancelAnimationFrame(this.rafId); 

    this.setState(this.initialState(),() => { 
     this.rafId = requestAnimationFrame(this.showSegments); 
    }); 
    } 

    showSegments() { 
    this.rafId = requestAnimationFrame(this.showSegments); 

    const newState = Object.assign({}, this.state); 
    newState.textSegments[this.state.curIndex].className = 'isActive'; 

    this.setState(
     { 
     ...newState, 
     curIndex: this.state.curIndex + 1 
     }, 
    () => { 
     if (this.state.curIndex >= this.state.textSegments.length) { 
      cancelAnimationFrame(this.rafId); 
     } 
     } 
    ); 

    } 

    render(){ 
    const innerTree = this.state.textSegments.map((obj, key) => (
     <span key={obj.id} className={obj.className}>{obj.segment}</span> 
    )); 

    return (
     <div> 
     <button onClick={this.handleClickStart}>Start</button> 
     <button onClick={this.handleClickStop}>Stop</button> 
     <hr /> 
     <div className="MotionText">{innerTree}..</div> 
     </div> 
    ) 
    } 
} 

export default MotionText; 

谢谢您的时间,如果有任何疑问,请向

WebpackBin Demo

回答

1

更改方法是这样的工作

render(){ 
    let d = new Date(); 
    const innerTree = this.state.textSegments.map((obj, key) => (
     <span key={d.getMilliseconds() + obj.id} className={obj.className}>{obj.segment}</span> 
    )); 

    return (
     <div> 
     <button onClick={this.handleClickStart}>Start</button> 
     <button onClick={this.handleClickStop}>Stop</button> 
     <hr /> 
     <div className="MotionText">{innerTree}..</div> 
     </div> 
    ) 
    } 

如何这可以帮助是,在键变得不同于之前分配的第一个跨度被渲染的键。您可以通过任何方式使键与以前不同,这将有助于您获得此动画。否则React不会再次渲染它,因此您将永远不会在动画中看到它。

+0

有趣的是,我尝试了建议的代码,但它有一个奇怪的效果,根本不显示CSS动画,我不知道这是因为反应是将附加的类渲染为子类,而不是将类应用于现有的孩子... https://www.webpackbin.com/bins/-KhG9kZHz59GbdjKE5M2 – shaune

+0

它在我把代码保存在同一个窗口时工作。是的,你是对的,动画在这之后就迷了路。 – Surender

+0

你可以使用在数字前添加空格的黑客。像这样的'123456789123456789123456789123456789'与原来的代码。由于只有第一个跨度保持在那里,并不影响这个作品。 – Surender