Изменение свойства background-image вызывает мерцание в Firefox

Я работаю над компонентом, который вращает серию фоновых изображений в баннере на моей странице. Проблема, с которой я сталкиваюсь, заключается в том, что когда URL-адрес свойств background-image изменяется через состояние, он, кажется, вызывает вспышку белого цвета. Это мигание не происходит все время в Chrome, но постоянно происходит в Firefox, а иногда и в Safari. Для дополнительного контекста я использую Mac OSX.

Сначала я предположил, что это связано с тем, что изображения извлекаются браузером, когда они запрашиваются, но, чтобы избежать этого, я сделал несколько соображений для предварительной выборки путем рендеринга скрытого тега изображения с ресурсом.

{this.props.items.map(item => (
  <img src = {item} style = {{ display: "none" }} />
))}

Я также пытался создать новое изображение в методе rotate, который предварительно выбирает следующий элемент вращения перед переходом, но, похоже, ни один из них не работает.

const img = new Image();
img.src = this.props.items[index + 1];

Где я ошибаюсь? Я приложил пример компонента ниже. Любая помощь будет оценена по достоинству.

class Cats extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      background: props.items[0],
      index: 0
    };

    this.rotate = this.rotate.bind(this);
  }

  // Let's you see when the component has updated.
  componentDidMount() {
    this.interval = setInterval(() => this.rotate(), 5000);
  }

  componentDidUnmount() {
    clearInterval(this.interval);
  }

  rotate() {
    const maximum = this.props.items.length - 1;
    const index = this.state.index === maximum ? 0 : this.state.index + 1;

    this.setState({
      background: this.props.items[index],
      index
    });
  }

  render() {
    return (
      <div
        className = "background"
        style = {{ backgroundImage: `url(${this.state.background})` }}
      >
        {this.props.items.map(item => (
          <img src = {item} style = {{ display: "none" }} />
        ))}
      </div>
    );
  }
}

ReactDOM.render(
  <Cats
    items = {[
      "https://preview.redd.it/8lt2w3du0zb31.jpg?width=640&crop=smart&auto=webp&s=58d0eb6771296b3016d85ee1828d1c26833fd022",
      "https://preview.redd.it/120qmpjmg1c31.jpg?width=640&crop=smart&auto=webp&s=1b01fc0c3f20098e6bb1f4126c3c2a54b7bc2b8e",
      "https://preview.redd.it/guprqpenoxb31.jpg?width=640&crop=smart&auto=webp&s=ace24e96764bb40a01e7d167a88d35298db76a1c",
      "https://preview.redd.it/mlzq0x1o0xb31.jpg?width=640&crop=smart&auto=webp&s=b3fd159069f45b6c354de975daffde21f04c3ad5"
    ]}
  />,
  document.querySelector(".wrapper")
);
html, body, .wrapper {
  width: 100%;
  height: 100%;
}

.background {
  position: static;
  background-size: cover;
  height: 100%;
  width: 100%;
  transition: background-image 1s ease-in-out;
}
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react.min.js"></script>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react-dom/15.6.1/react-dom.min.js"></script>

<div class = "wrapper"></div>
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
4
0
1 242
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

К сожалению, кажется, что это мерцание является известная ошибка в Firefox, вызванным его декодером изображения, который не будет декодировать изображение, пока оно не будет отображено в первый раз. В приведенном ниже фрагменте я создал перекрывающиеся элементы div, один из которых загружает следующее изображение чуть раньше и располагается позади другого. Таким образом, когда другие «мерцают», правильное изображение уже отображается позади, а не на белом фоне.

Вы также можете теоретически очень быстро отобразить все изображения в скрытом div, а затем вернуть его в белый цвет, поскольку изображения нужно отображать только один раз, чтобы декодер работал.

В зависимости от долгосрочной цели этого проекта, наиболее правильным способом решения этой проблемы может быть использование <canvas> для рендеринга ваших изображений. Элемент холста использует другой декодер, который не вызывает мерцания.

class Cats extends React.Component {
  constructor(props) {
    super(props);
    
    this.props.items.forEach((item) => {
       const img = new Image(640, 640);
       img.src = item;
    });

    this.state = {
      background: props.items[0],
      preloadBackground: props.items[1],
      index: 0
    };

    this.rotate = this.rotate.bind(this);
  }

  // Let's you see when the component has updated.
  componentDidMount() {
    this.interval = setInterval(() => this.rotate(), 5000);
  }

  componentDidUnmount() {
    clearInterval(this.interval);
  }

  rotate() {
    const maximum = this.props.items.length - 1;
    const index = this.state.index === maximum ? 0 : this.state.index + 1;

    this.setState({
      preloadBackground: this.props.items[index],
      index
    });
    
    setTimeout(() => {
      this.setState({
        background: this.props.items[index],
      });
    }, 100);
  }

  render() {
    return (
      <div className = "pane">
        <div
          className = "preload-background"
          style = {{ backgroundImage: `url(${this.state.preloadBackground})` }}
        >
        </div>
        <div
          className = "background"
          style = {{ backgroundImage: `url(${this.state.background})` }}
        >
        </div>
      </div>
    );
  }
}

ReactDOM.render(
  <Cats
    items = {[
      "https://preview.redd.it/8lt2w3du0zb31.jpg?width=640&crop=smart&auto=webp&s=58d0eb6771296b3016d85ee1828d1c26833fd022",
      "https://preview.redd.it/120qmpjmg1c31.jpg?width=640&crop=smart&auto=webp&s=1b01fc0c3f20098e6bb1f4126c3c2a54b7bc2b8e",
      "https://preview.redd.it/guprqpenoxb31.jpg?width=640&crop=smart&auto=webp&s=ace24e96764bb40a01e7d167a88d35298db76a1c",
      "https://preview.redd.it/mlzq0x1o0xb31.jpg?width=640&crop=smart&auto=webp&s=b3fd159069f45b6c354de975daffde21f04c3ad5"
    ]}
  />,
  document.querySelector(".wrapper")
);
html, body, .wrapper, .pane {
  width: 100%;
  height: 100%;
}

.background {
  position: static;
  background-size: cover;
  height: 100%;
  width: 100%;
  transition: background-image 1s ease-in-out;
}

.preload-background {
  position: absolute;
  background-size: cover;
  height: 100%;
  width: 100%;
  z-index: -1;
  transition: background-image 1s ease-in-out;
}
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react.min.js"></script>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react-dom/15.6.1/react-dom.min.js"></script>

<div class = "wrapper"></div>

Вы можете использовать метод decode(), который сообщит вам, когда изображение будет декодировано и готово к использованию.

https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decode

В твоем случае:

const img = new Image();
img.src = this.props.items[index + 1];
img.decode()
.then(() => {
  // image is decoded and ready to use
})
.catch((encodingError) => {
  // do something with the error.
})

Другие вопросы по теме