更改背景图片属性会导致Firefox中的闪烁

时间:2019-07-23 18:05:29

标签: javascript css reactjs firefox

我正在研究一个组件,该组件可以旋转页面横幅中的一系列背景图像。我遇到的问题是,当background-image属性url通过状态更改时,似乎会引起白色闪烁。这种闪烁似乎并不会一直在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>

1 个答案:

答案 0 :(得分:0)

不幸的是,这种闪烁似乎是由其图像解码器引起的known bug in Firefox,该图像解码器在第一次显示图像之前不会解码图像。在下面的代码段中,我创建了重叠的div,一个div稍早加载了下一个图像,而另一个则位于其后。这样,当其他“闪烁”时,适当的图像已经显示在后面,而不是白色背景。

理论上,您也可以非常快速地在隐藏的div中显示所有图像,然后将其设置回白色,因为图像只需要显示一次即可使解码器工作。

根据该项目的长期目标,解决此问题的最正确方法可能是使用<canvas>来渲染图像。 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>