如何通过一劳永逸地重新渲染来修复图像闪烁?

Posted

技术标签:

【中文标题】如何通过一劳永逸地重新渲染来修复图像闪烁?【英文标题】:How to fix image flickering by rerendering once and for all? 【发布时间】:2019-09-29 04:07:18 【问题描述】:

我正在尝试使用 ReactJS 制作一个基于精灵的小型游戏。绿龙(取自 HMMII)飞过六边形区域,它的行为取决于鼠标点击。精灵根据特别选择的时间常数 - 170ms 以速度相互改变。更准确地说:有一个代表龙的 div,它的属性(顶部、左侧、宽度、高度和背景图像)总是在变化。

在开发的第一阶段,我遇到了通过重新渲染图像而令人讨厌的闪烁和闪烁。怎样才能避免呢?

以下描述了我使用 Surge 进行的一些预览的多种方式。在谷歌浏览器中观看效果最强,但在 Firefox 中也很麻烦。

0) 起初我尝试使用基于 @keyframes 的 CSS 动画,但由于淡入淡出效果不好。而且我根本不需要任何淡入淡出效果,我需要快速重新渲染。

1) 这是最直接的尝试。单击特定字段后,componentWillReceiveProps 将创建步骤列表,然后所有这些步骤都在一致地执行。我也尝试过使用 requestAnimationFrame 而不是 setTimeout 但遇到了同样的麻烦。

    makeStep() 
        const steps = this.state;

        this.setState((prevState, props) => (
            steps: steps.slice(1),
            style: ...
        ));
    


    render() 
        const steps, style = this.state;
        steps.length ? setTimeout(this.makeStep, DRAGON_RENDER_TIME):
                       this.props.endTurn();

        return (<div id="dragon" style=style></div>);
    

结果如下:http://streuner.surge.sh/ 如您所见,龙经常在起飞和降落时消失,它会跳过一些精灵而飞行。

2) 我尝试测试文章中描述的方法: https://itnext.io/stable-image-component-with-placeholder-in-react-7c837b1ebee

在这种情况下,我已将带有背景图像的 div 更改为包含显式 img 的其他 div。起初,this.state.isLoaded 为 false,不会出现新的精灵。它仅在使用 onLoad 方法加载图像后出现。此外,我尝试使用 refs 和尝试观察图像的完整属性,但它总是正确的 - 可能是因为图像的尺寸非常小。

    setLoaded()
        this.setState((prevState, props) => (
            isLoaded: true
        ));
    


    render() 
        const isLoaded, steps, style = this.state;
        if(isLoaded) 
            steps.length ? setTimeout(this.makeStep, DRAGON_RENDER_TIME):
                           this.props.endTurn();
        

        return (<div id="wrap" style=top:style.top, left:style.left >
            <img id="dragon"  src=style.src onLoad=this.setLoaded
                 style=width:style.width,
                         height: style.height,
                         visibility: isLoaded ? "visible": "hidden"/>
        </div>);
    

结果如下:http://streuner2.surge.sh/ 不再有精灵跳过,但闪烁效果比第一种情况强得多。

3) 也许这是我最好的尝试。我已阅读此建议:https://github.com/facebook/react-native/issues/981 并决定立即渲染所有步骤图像,但只有一个不透明度 = 1,其他不透明度 = 0。

    makeStep(index) 
        const steps = this.state;

        this.setState((prevState, props) => (
            index: index + 1,
            steps: steps.map( (s, i) => (...s, opacity: (i !== index) ? 0: 1))
        ));
    


  render()  
        const index, steps = this.state;

        (index < steps.length) ? 
              setTimeout(() => this.makeStep(index), DRAGON_RENDER_TIME):
              this.props.endTurn();

        return ([steps.map((s, i) => 
                   <div className="dragon" key=i style=s></div>)]);
    

可以在这里看到结果:http://streuner3.surge.sh/ 通过重新渲染所有精灵开始新的飞行只有一个闪烁。但在我看来,代码更人为。

我想强调的是,行为总是取决于浏览器,在 Firefox 中要好得多。同一浏览器中的各种苍蝇也存在差异:有时没有闪烁效果,但在大多数情况下不幸的是。也许我不明白在浏览器中重新渲染图像的任何基本概念。

【问题讨论】:

【参考方案1】:

我认为你应该将注意力从动画本身转移到 React 中的重新渲染上,每次更改 Image 组件状态或道具时,它都在重新渲染。阅读 React 文档中的生命周期方法和重新渲染。

您更改状态的速度非常快(在您的情况下几乎是每秒 6 次),所以我认为某些浏览器在图像组件重新渲染方面不够快。尝试移出更新速度如此之快的图像状态变量,一切都会好起来的

【讨论】:

【参考方案2】:

我知道答案已经晚了,但是在这里发布我的答案以防有人仍然想找到解决方案并且因为我发现这会带来一些流量。 一个简单的解决方法是向图像添加 CSS 过渡属性,如下所示:

transition: all .5s;

它不会阻止图像重新渲染,但至少可以防止图像闪烁。

【讨论】:

以上是关于如何通过一劳永逸地重新渲染来修复图像闪烁?的主要内容,如果未能解决你的问题,请参考以下文章

redux计算数据重新渲染如何修复

一劳永逸地修复 NuGet 包和引用

如何让 chrome 重新渲染 iframe

如何修复用户控件中的闪烁

如何修复 iOS 上由 CSS 过渡和变换引起的闪烁?

如何修复反应过多的重新渲染错误?