简体   繁体   English

更改 background-image 属性会导致 Firefox 闪烁

[英]Changing background-image property causes a flicker in Firefox

I'm working on a component that rotates a series of background images in a banner on my page.我正在开发一个组件,该组件可以在我的页面上的横幅中旋转一系列背景图像。 The problem I'm running into is that when the background-image properties url is changed via state it seems to cause a flash of white.我遇到的问题是,当background-image属性 url 通过 state 更改时,它似乎会导致白色闪烁。 This flashing doesn't seem to happen all the time in Chrome, but does happen consistently in Firefox and sometimes Safari.这种闪烁在 Chrome 中似乎并不总是发生,但在 Firefox 中确实发生,有时在 Safari 中也会发生。 For additional context I'm using Mac OSX.对于其他上下文,我使用的是 Mac OSX。

At first I assumed this was because the images are being retrieved by the browser when they are requested, but to avoid this I've made some considerations for pre-fetching by rendering a hidden image tag with the resource.起初我认为这是因为浏览器在请求图像时会检索它们,但为了避免这种情况,我通过渲染带有资源的隐藏图像标签来考虑预取。

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

I've also tried creating a new image in the rotate method that pre-fetches the next rotation item ahead of the transition, but neither seem to work.我还尝试在rotate方法中创建一个新图像,在转换之前预取下一个旋转项目,但似乎都不起作用。

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

Where am I going wrong here?我哪里出错了? I've attached an example of the component below.我在下面附上了一个组件的例子。 Any help would be appreciated.任何帮助,将不胜感激。

 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>

Unfortunately, it seems like this flicker is a known bug in Firefox caused by its image decoder, which won't decode an image until it's displayed for the first time.不幸的是,这种闪烁似乎是Firefox 中的一个已知错误,由其图像解码器引起,它在第一次显示图像之前不会解码图像。 In the snippet below, I created overlapping divs, one which loads the next image slightly earlier and sits behind the other.在下面的代码片段中,我创建了重叠的 div,一个 div 稍早加载下一个图像并位于另一个之后。 This way when the other "flickers," the proper image is already displayed behind, rather than a white background.这样当其他“闪烁”时,正确的图像已经显示在后面,而不是白色背景。

You could also theoretically display all the images in the hidden div really quickly, then set it back to white, since the images only need to be displayed once for the decoder to work.理论上,您还可以非常快速地显示隐藏 div 中的所有图像,然后将其设置回白色,因为图像只需要显示一次即可让解码器工作。

Depending on the long-term goal for this project, the most proper way around this problem may be to use a <canvas> to render your images.根据这个项目的长期目标,解决这个问题的最合适的方法可能是使用<canvas>来渲染你的图像。 The canvas element uses a different decoder which won't cause a flicker. 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>

You can use decode() method that will let you know when image is decoded and ready to be used.您可以使用decode()方法让您知道图像何时解码并准备好使用。

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

In your case:在你的情况下:

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.
})

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM