Как исправить мерцание изображения путем повторного рендеринга раз и навсегда? - PullRequest
1 голос
/ 11 мая 2019

Я пытаюсь сделать небольшую игру на основе спрайтов с 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 это намного лучше.Кроме того, есть различия в разнообразии мух в одном и том же браузере: иногда нет эффекта мерцания, но в большинстве случаев это, к сожалению, так.Может быть, я не понимаю какой-либо основной концепции рендеринга изображений в браузере.

1 Ответ

0 голосов
/ 12 мая 2019

Я думаю, вам следует отвлечься от самой анимации и уделять больше внимания перерисовке в React каждый раз, когда вы изменяете состояние компонента Image или поддерживает его перерисовку.Прочтите о методах жизненного цикла и повторном рендеринге в React docs.

Вы очень быстро меняете состояние (в вашем случае это почти 6 раз в секунду), поэтому я полагаю, что некоторые браузеры недостаточно быстры с рендерингом компонента Image.Попробуйте выйти из переменных состояния изображения, которые обновляются так быстро, и все будет хорошо

...