Я пытаюсь сделать небольшую игру на основе спрайтов с ReactJS.Зеленый дракон (взят из HMMII) летит через шестиугольное поле, и его поведение зависит от щелчка мышью.Спрайты изменяют друг друга со скоростью в зависимости от специально выбранной постоянной времени - 170 мс.Точнее: есть div, представляющий дракона, и его свойства (top, left, width, height и background-image) всегда меняются.
На первом этапе разработки я столкнулся с раздражающим миганиеми мерцание путем перерисовки изображения.Как этого избежать?
Ниже описано несколько способов, которые я использовал с некоторыми превью, сделанными с помощью Surge.Самый сильный эффект наблюдается в Google Chrome, но в Firefox также есть проблемы.
0) Сначала я пытался использовать CSS-анимацию, основанную на @keyframes, но это не помогло из-за эффекта затухания.И мне вообще не нужны эффекты затухания, мне нужен быстрый рендеринг.
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 с background-image надругой div, содержащий явное img.Сначала this.state.isLoaded имеет значение false, и новый спрайт не появится.Появляется только после загрузки изображения методом onLoad.Также я пытался использовать ссылки с попыткой отслеживания для полного свойства изображения, но это всегда так - возможно, потому что размер изображения очень мал.
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 это намного лучше.Кроме того, есть различия в разнообразии мух в одном и том же браузере: иногда нет эффекта мерцания, но в большинстве случаев это, к сожалению, так.Может быть, я не понимаю какой-либо основной концепции рендеринга изображений в браузере.