我正在尝试使用ReactJS制作一个基于Sprite的小型游戏。绿龙(取自HMMII)在六边形场上飞行,其行为取决于鼠标的单击。精灵会根据特殊选择的时间常数170ms随速度而变化。更准确地说:有一个表示龙的div,并且其属性(顶部,左侧,宽度,高度和背景图像)总是在变化。
在开发的第一阶段,我面临着通过重新渲染图像而引起的眨眼和闪烁的烦恼。如何避免呢?
下面介绍了我在Surge进行的一些预览中使用的多种方法。在Google Chrome浏览器中观察到最强的效果,但在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与try watch一起使用以获取图像的完整属性,但这始终是正确的-也许是因为图像的大小很小。
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" alt="" 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中要好得多。同样,在同一浏览器中,各种苍蝇也存在差异:有时没有闪烁效果,但不幸的是在大多数情况下是这样。也许我不了解在浏览器中重新渲染图像的任何基本概念。
答案 0 :(得分:0)
我认为您应该将注意力从动画本身转移到其他地方,并在每次更改Image组件状态或道具重新渲染时,更加关注React中的重新渲染。在React文档中了解生命周期方法和重新渲染。
您可以非常快速地更改状态(在您的情况下,它几乎是每秒6次),因此我认为某些浏览器的Image组件重新渲染速度不够快。尝试移出图像状态变量,该变量会更新得如此之快,一切都会好的
答案 1 :(得分:0)
我知道答案已经晚了,但在这里发布我的答案,以防有人仍然想找到解决方案,因为我发现这会带来一些流量。 一个简单的解决方法是向图像添加 CSS 过渡属性,如下所示:
transition: all .5s;
它不会阻止图像重新渲染,但至少它确实阻止了图像闪烁。