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


![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


К сожалению, кажется, что это мерцание является известная ошибка в 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.
})