React JS:图像大小在重新加载时闪烁,对象匹配:覆盖 CSS 属性

Posted

技术标签:

【中文标题】React JS:图像大小在重新加载时闪烁,对象匹配:覆盖 CSS 属性【英文标题】:React JS: Image size flickers on reload with object-fit: cover CSS property 【发布时间】:2021-01-19 01:57:49 【问题描述】:

我正在使用 CRA 创建的反应应用中显示图像。每次我重新加载网页时,图像都会奇怪地闪烁,如下所示。

最初的图片:

闪烁后的图像(实际要求):

代码:

styles.css:

img 
  width: 200px;
  height: 300px;
  object-fit: cover;

app.tsx

import React from 'react';
import foo from './foo.jpg';

import './styles.css';

const App = () => <img src=foo  />;

export default App;

package.json


  "name": "xyz",
  "version": "0.1.0",
  "private": true,
  "dependencies": 
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.5.0",
    "@testing-library/user-event": "^7.2.1",
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "react-scripts": "3.4.3"
  ,
  "scripts": 
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  ,
  "eslintConfig": 
    "extends": "react-app"
  ,
  "browserslist": 
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  

注意事项:

    该应用是一个注入样板,没有安装任何第三方库,也没有安装除此图像之外的任何其他组件。 如果object-fit: cover; 属性为 从样式表中删除,但这是为了防止图像被 像初始图像一样奇怪地拉伸/收缩。 如果问题无法重现,请保持开发者控制台打开或尝试将网络更改为任何 3G 预设。我可以通过重复重新加载轻松重现。 我相信 object-fit: cover 属性最初不会应用于图像,需要几毫秒才能启动。 请注意,DOM 结构未显示在开发者控制台的初始图像中。 任何 CSS 属性的替代项 也会有所帮助。

【问题讨论】:

尝试做一个codeandbox @DennisVash 在代码和框上无法重现。 为什么不呢?它也应该在沙箱中闪烁 @DennisVash 我相信 Codesandbox 不会在实际浏览器中呈现 React 应用程序,我们看到的只是 React 在其平台上的 div 中呈现。因此,如果我重新加载代码和框,它将无法像系统上的实际浏览器那样工作。如果我尝试通过单击那里提供的重新加载图标来重新加载,情况也是如此。 这不是它的工作原理......您实际上是说您在浏览器中运行浏览器,codesandbox 只是呈现您编写的代码,您可以检查它并自己查看,而且它具有 CRA 启动器所以它完全模拟任何机器 【参考方案1】:

我已经把这个问题搞砸了很长一段时间,我几乎确信这是 Chromium 浏览器的一个性能错误。具体来说,它似乎与图像的缓存以及浏览器在将图像加载到具有明确高度和宽度尺寸的img 容器时所做的优化有关。

我这样说是因为在我的测试中,在重新加载之前清空缓存将使浏览器在第一次绘制之前应用object-fit。我还注意到,无论object-fit 是否存在,缓存的图像都将在img 容器之前一瞬间加载到具有定义的高度和宽度的img 容器中,这些容器只有一个或没有这些维度。这似乎是发生错误的地方。 object-fit 规则仅在第二个绘制层期间应用。

也许您可以尝试使用以下 sn-p 重现此内容以确认?我在这里使用了随机占位符图像,但您可能想要使用本地图像。如果有人可以提供更详细的解释,我很想听听。如 cmets 中所述,仅使用 html/CSS 和网络节流即可重现,尽管我发现在 React 上下文中效果更加明显:

const  Fragment  = React;

const App = () => 
  return (
    <Fragment>
    
      <p>Object fit with two explicit dimensions</p>
      <img src="https://source.unsplash.com/RN6ts8IZ4_0/600x400" className="img-fit"></img>
      
      <p>Positioned element with one explicit dimension</p>
      <div className="container">
        <img src="https://source.unsplash.com/RN6ts8IZ4_0/600x400" className="img-position"></img>
      </div>
      
    </Fragment>
  );
;

ReactDOM.render(<App/>, document.body);
.img-fit 
  width: 200px;
  height: 300px;
  object-fit: cover;


.container 
  width: 200px;
  height: 300px;
  overflow: hidden;
  position: relative;


.img-position 
  height: 100%;
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

无论如何,上面 sn-p 中的第二张图片是解决此问题的一种可能的解决方法,如果它继续让您感到悲伤的话。您可以通过将img 设置为 100% 的高度或宽度来选择要在父容器上缩放到的尺寸。然后可以通过方向偏移和变换的组合将其居中(或以其他方式调整)。这显然不像object-fit 那样动态,因为您必须事先决定如何缩放图像以获得covercontain 效果。

一些替代解决方案包括:

1.改用背景图片

我发现object-fit 最终派生自的background CSS 属性不会出现相同的错误。使用div 代替img 标记并将图像放在其背景上:

div 
  width: 200px;
  height: 300px;
  background: url('path/yourimg.jpg') center / cover;

2。预加载 (HTML)

我发现的另一个修复方法是预加载图像,这会向浏览器发出信号,表明它是一个关键资源,应该立即获取。这似乎支持了问题最终归结为浏览器优化的观点。

不幸的是,这并不真正符合 React 标准,因为您只有一个 HTML 页面,并且很容易被这些链接填满,但无论如何它都能正常工作。把这个放在head:

<link rel="preload" href="path/yourimage.jpg" as="image"/>

3。预加载(反应)

您可以在 React 中模拟预加载,方法是等待图像的 onload 事件触发,然后再使其对用户可见。这就像在 JSX 中使用带有相关类的单行代码一样快速而肮脏:

// app.js

const App = () => <img src=foo onLoad=e => e.target.classList.add('visible')/>;

// index.css

img 
  width: 200px;
  height: 300px;
  object-fit: cover;
  visibility: hidden;


.visible 
  visibility: visible;
;

或者,如果您正在加载一批图像,您可以在显示组件/批次本身之前等待它们全部加载。这会带来一些潜在的布局变化的缺点,并且可能需要一些优化来处理大批量,但你明白了:

import foo from 'path/foo.jpg';
import bar from 'path/bar.jpg';

const App = () => 
    const [loaded, setLoaded] = useState(false);

    useEffect(() => 
        const loadArray = [foo, bar].map(src => 
            return new Promise((res, rej) => 
                const img = new Image();

                img.src = src;
                img.onload = () => res();
                img.onerror = () => rej();
            )
        );

        Promise.allSettled(loadArray).then(() => setLoaded(true));
    , []);

    return loaded && (
        <>
            <img src=foo ></img>
            <img src=bar ></img>
        </>
    );
;

【讨论】:

非常感谢您深入研究了这个问题。我会仔细阅读你的答案。但我确实简要阅读了,我想补充一点,图像不是在应用程序中烘焙,而是存储在服务器上。所以,我认为使用&lt;link&gt; 不是一个好习惯,而且我必须将inline style 添加到我的应用程序的所有组件中,以便为每个组件提供背景图像,这会吃掉表现。而且,是否有针对 chromium 浏览器或 chrome 生产构建的具体问题? 你是这篇文章的绝对传奇。我无法相信这篇文章有多孤独。这是金子,非常感谢!

以上是关于React JS:图像大小在重新加载时闪烁,对象匹配:覆盖 CSS 属性的主要内容,如果未能解决你的问题,请参考以下文章

预加载、缓存 gif 图像以避免在 React Native 中闪烁

在 React.js 中加载异步数据时防止 UI 闪烁

重新加载和调用requestImageForAsset时UICollectionView闪烁

移动/调整窗口大小时闪烁

Laravel 5.3 - Vue 2 在页面重新加载时闪烁

避免在调整大小时移动其控件的对话框上闪烁