#javascript #html #css #reactjs #redux
#javascript #HTML #css #reactjs #сокращение
Вопрос:
Я показываю изображение в приложении react, созданном с использованием 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} alt="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 также была бы полезна.
Комментарии:
1. Попробуйте создать codesandbox
2. @DennisVash это невозможно воспроизвести в codesandbox.
3. Почему бы и нет? Он также должен мерцать в песочнице
4. @DennisVash Я считаю, что codesandbox не отображает приложение React в реальном браузере, и мы видим, что React просто отображается в div на их платформе. Итак, если я перезагружу codesandbox, он не будет работать так, как это делал бы реальный браузер в системе. То же самое будет, если я попытаюсь перезагрузить, нажав на значок перезагрузки, указанный там.
5. Это не так, как это работает… Вы на самом деле сказали, что запускаете браузер в браузере, codesandbox просто отображает код, который вы пишете, вы можете проверить его и убедиться сами, более того, у него есть CRA starter, поэтому он точно имитирует любую машину
Ответ №1:
Повозившись с этим довольно долго, я в значительной степени убежден, что это ошибка производительности в браузерах Chromium. В частности, это, по-видимому, связано с кэшированием изображений и оптимизациями, которые браузер выполняет при загрузке изображений в img
контейнер с явными размерами высоты и ширины.
Я говорю это, потому что в моих тестах очистка кэша перед перезагрузкой заставит браузер применить object-fit
до того, как произойдет первая покраска. Я также заметил, что независимо от object-fit
наличия, кэшированные изображения будут загружены в img
контейнер с определенной высотой и шириной за долю секунды до img
контейнеров только с одним или ни с одним из этих размеров. Похоже, именно здесь возникает ошибка. object-fit
Правило применяется только во время второго слоя рисования.
Может быть, вы можете попытаться воспроизвести это со следующим фрагментом для подтверждения? Я использовал здесь случайное изображение-заполнитель, но вы можете захотеть использовать локальное изображение. Если кто-нибудь может предоставить более подробное объяснение, мне было бы интересно его услышать. Как упоминалось в комментариях, это воспроизводимо только с помощью 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>
Несмотря на это, второе изображение в приведенном выше фрагменте является одним из возможных решений этой проблемы, если она продолжает доставлять вам огорчения. Вы выбираете размер, который хотите масштабировать в родительском контейнере, задавая img
либо 100% высоту, либо ширину. Затем его можно центрировать (или иным образом отрегулировать) с помощью комбинации смещения направления и преобразования. Очевидно, что это не так динамично, как object-fit
потому, что вам нужно заранее решить, как масштабировать изображение, чтобы получить cover
contain
эффект или.
Некоторые альтернативные решения включают:
1. Вместо этого используйте фоновое изображение
Я обнаружил, что свойство background
CSS, которое object-fit
в конечном итоге было получено из, не страдает от той же ошибки. Используйте a 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 amp;amp; (
<>
<img src={foo} alt="foo"></img>
<img src={bar} alt="bar"></img>
</>
);
};
Комментарии:
1. Я действительно ценю, что вы так глубоко вникли в эту проблему. Я внимательно прочитаю ваш ответ. Но я прочитал кратко, и я хотел бы добавить, что изображения не загружаются в приложение, а хранятся на сервере. Итак, я не думаю, что использование
<link>
было бы хорошей практикой, и, кроме того, мне придется добавлятьinline style
ко всем компонентам в моем приложении, чтобы предоставить фоновое изображение для каждого из них, что снизило бы производительность. И, в любом случае, нужно ли поднимать проблему для браузеров chromium или производственной сборки Chrome, чтобы быть конкретным?2. Вы абсолютная легенда для этой статьи. Я не могу поверить, насколько одинок этот пост. Это золото, большое вам спасибо!
3. Использование фонового изображения работает! Спасибо! Мой вариант использования:
<div style={{ background: `url('${imageUrl}') center / contain`, backgroundRepeat: 'no-repeat' }}></div>