在 React 中使用 requestAnimationFrame

IT技术 reactjs react-native
2021-04-29 15:58:33

我是本机react的新手,我试图优化性能。

我的 Touch 事件非常慢,我正在阅读有关性能的 RN 文档,他们提到要requestAnimationFrame与此示例一起使用

handleOnPress() {
  // Always use TimerMixin with requestAnimationFrame, setTimeout and
  // setInterval
  this.requestAnimationFrame(() => {
    this.doExpensiveAction();
  });
}

现在,这个描述听起来很模糊,我很难理解它的用法

就像,我的 RN 应用程序中有这个可触摸的事件

<TouchableWithoutFeedback   onPress={() => this.touched(this.props.coinShortName)}>

哪个调用这个方法

 touched = (id) => {

        this.setState({selectedPostId: id})
        if (this.props.coinShortName == this.state.selectedPostId ) { 
           this.setState({stateToDisplay: !this.state.stateToDisplay})
        }
    }

问题:有人可以告诉我我需要/应该如何在我的应用程序中使用它吗?

3个回答

我会将我的评论转换为答案,这样我就可以更好地格式化它,并希望将来也能帮助到某人。我不明确建议将我的答案标记为正确,但我认为这个答案应该与这个问题放在一起。

这篇文章应该给你一些背景故事requestAnimationFramehttp : //www.javascriptkit.com/javatutors/requestanimationframe.shtml

我建议阅读我上面链接的文章,然后阅读我的答案。

我只是明确提到这requestAnimationFrame可能看起来类似于setTimeout(() => {}, 0),但如果你有一部 1985 年制造的 Zack Morris 手机,它的“尽快”可能会晚 5 秒,从而使你的动画看起来很糟糕,类似于当你的角色滞后时视频游戏中的屏幕。该函数可能在正确的时间被调用,但它实际上并未在正确的时间执行。

想象一个收集阶段和一个渲染阶段会很有帮助。对不起,我不知道这些东西的确切术语,但我认为人眼可以看到 20 FPS 的平滑运动,但这意味着你有 20 个“帧”,所以这就像计算 20 次。这就像收集一堆孩子并每秒将他们推上公共汽车 20 次。将它们推入总线是一个事件,类似于重新粉刷屏幕。有时孩子们可能会被抛在后面,而下一次又会捡到更多的孩子,所以你可以想象随着时间的推移,感知流畅度的收益。

请务必注意,针对下一次重绘或下一次屏幕“更改”进行了优化。requestAnimationFrame确实在幕后工作,以确保动画在正确的时间发生并且是平滑的,这意味着像素在它们应该在正确的时间出现的位置。(我认为,如果您查看“什么是 janky 动画”的定义,并查看围绕该问题的一些讨论,您会获得很多含义。我提到这一点是因为我们想更多地了解重绘过程以及什么各种事情很重要,为什么)

我记得这requestAnimationFrame可以放弃会发生得太晚的计算。例如,如果您单击按钮并且像素从 0% 到 25% 到 50% 到 75% 到 100%(一些任意距离计算)。我们可以说 1 秒后,像素应该行进了 50% 的距离,2 秒后,它应该在 100% 处,即最终的静止位置。

像素在正确的时间出现在正确的位置比让它们准确地到达它们应该到达的每个地方更重要。requestAnimationFrame正在帮助你做到这一点。如果屏幕即将重绘,并且“它”需要运行一个需要很长时间的计算,“它”只会忽略它并跳到下一帧。这就像减脂以跟上步伐,因此避免卡顿。

requestAnimationFrame无论是在您的网络浏览器、iOS 还是 Android 中,它都是应对相同挑战的解决方案。他们都一遍遍地做这个刷屏的过程。你可以开始计算下一次重绘所需的东西,但开始太晚了,所以在下一次重绘发生时它没有完成。

想象一下,您的动画很流畅,但您的手机突然收到 20 条推送通知,这使 CPU 陷入困境,导致您的动画延迟了16.7 milliseconds. 与其在错误的时间在正确的位置显示像素,不如让像素在正确的时间requestAnimationFrame出现在正确的位置,但它可能会产生一些魔力,有时甚至不会尝试绘制像素,否则会发生,从而节省性能并增加感知平滑度。

我刚刚意识到这是一堵文字墙,但我认为它会提供信息。

这些重绘大约每秒发生 60 帧,因此requestAnimationFrame当它计算出最佳时间时,每秒可以触发 60 次。1 秒有 1000 毫秒,所以 60 FPS 是每帧一帧16.7ms如果人眼以 20FPS 的速度感知平滑度,那么理论上您可以每 45 毫秒或 30% 重新绘制一次,并且动画仍然是平滑的。

我的定义可能不准确,但我希望它们可以帮助您了解正在发生的事情。

如前所述,您需要将昂贵的操作包装在 requestAnimationFrame.

用法

<TouchableWithoutFeedback onPress={() => this.touched(this.props.coinShortName)}>

touched = (id) => {
  requestAnimationFrame(() => {
    this.setState({selectedPostId: id})
    if (this.props.coinShortName == this.state.selectedPostId ) {
     this.setState({stateToDisplay: !this.state.stateToDisplay})
    }
  });
}
<TouchableWithoutFeedback onPress={this.handlePress}>

handlePress = () => {
   this.requestAnimationFrame(() => {
     this.touched(this.props.coinShortName)
   });
}

如果您通过直接函数内部使用来删除函数中的无用id参数,那将会更好,这样您就可以编写touchedthis.props.coinShortName

handlePress = () => {
   this.requestAnimationFrame(this.touched);
} 

无论如何,该touched功能似乎并不昂贵,所以我不知道它是否会解决您的性能问题