我想在加载最终头像图像时加载不同的图像(假头像)。这个想法是检测道具图像何时加载并更改状态。是否可以?一些想法?谢谢!
class ImageUser extends React.Component {
constructor(props) {
super(props);
this.state = {userImageLoaded: false};
let imageSrc = "";
if (!this.props.userImage) {
imageSrc = this.props.noUserImage;
} else {
imageSrc = this.props.userImage;
}
this.loadingImage = <img className={styles.imageUser}
src={this.props.loadingImage} alt="2"/>;
this.userImage =
<img onLoad={this.setState({userImageLoaded: true})}
className={styles.imageUser} src={imageSrc}
alt="1"/>;
}
render() {
let image = "";
if (this.state.userImageLoaded) {
image = this.userImage;
} else {
image = this.loadingImage;
}
return (
<div>
{image}
</div>
);
}
}
export default ImageUser;
有多种方法可以做到这一点,但最简单的是显示隐藏的最终图像,然后在加载后将其翻转为可见。
class Foo extends React.Component {
constructor(){
super();
this.state = {loaded: false};
}
render(){
return (
<div>
{this.state.loaded ? null :
<div
style={{
background: 'red',
height: '400px',
width: '400px',
}}
/>
}
<img
style={this.state.loaded ? {} : {display: 'none'}}
src={this.props.src}
onLoad={() => this.setState({loaded: true})}
/>
</div>
);
}
}
与 Brigand 接受的答案相同,但带有 Hooks:
const Foo = ({ src }) => {
const [loaded, setLoaded] = useState(false);
return (
<div>
{loaded ? null : (
<div
style={{
background: 'red',
height: '400px',
width: '400px'
}}
/>
)}
<img
style={loaded ? {} : { display: 'none' }}
src={src}
onLoad={() => setLoaded(true)}
/>
</div>
);
};
使用元素引用的相同想法,但使用功能组件和带有打字稿的钩子:
import React from 'react';
export const Thumbnail = () => {
const imgEl = React.useRef<HTMLImageElement>(null);
const [loaded, setLoaded] = React.useState(false);
const onImageLoaded = () => setLoaded(true);
React.useEffect(() => {
const imgElCurrent = imgEl.current;
if (imgElCurrent) {
imgElCurrent.addEventListener('load', onImageLoaded);
return () => imgElCurrent.removeEventListener('load', onImageLoaded);
}
}, [imgEl]);
return (
<>
<p style={!loaded ? { display: 'block' } : { display: 'none' }}>
Loading...
</p>
<img
ref={imgEl}
src="https://via.placeholder.com/60"
alt="a placeholder"
style={loaded ? { display: 'inline-block' } : { display: 'none' }}
/>
</>
);
};
您可以更进一步,在更改图像时添加淡入过渡。下面的代码是我的
CrossFadeImage
组件。只需复制并使用它来代替普通的 img
组件即可。
CrossFadeImage
有 2 个图像,top
和 bottom
。 bottom
堆叠在 top
上,用于显示需要动画的图像,在本例中是切换时会淡出的旧图像,
在空闲状态下,
top
显示当前图像,而bottom
是上一个图像,但处于透明状态
CrossFadeImage
在检测到props.src
变化时会做以下事情
top
的 src 设置为新图像,并将 bottom
的 src 设置为下一帧将淡出的当前图像bottom
设置为透明以开始过渡import React from "react";
const usePrevious = <T extends any>(value: T) => {
const ref = React.useRef<T>();
React.useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
};
const useRequestAnimationFrame = (): [(cb: () => void) => void, Function] => {
const handles = React.useRef<number[]>([]);
const _raf = (cb: () => void) => {
handles.current.push(requestAnimationFrame(cb));
};
const _resetRaf = () => {
handles.current.forEach((id) => cancelAnimationFrame(id));
handles.current = [];
};
return [_raf, _resetRaf];
};
type ImageProps = {
src: string;
alt?: string;
transitionDuration?: number;
curve?: string;
};
const CrossFadeImage = (props: ImageProps) => {
const { src, alt, transitionDuration = 0.35, curve = "ease" } = props;
const oldSrc = usePrevious(src);
const [topSrc, setTopSrc] = React.useState<string>(src);
const [bottomSrc, setBottomSrc] = React.useState<string>("");
const [bottomOpacity, setBottomOpacity] = React.useState(0);
const [display, setDisplay] = React.useState(false);
const [raf, resetRaf] = useRequestAnimationFrame();
React.useEffect(() => {
if (src !== oldSrc) {
resetRaf();
setTopSrc("");
setBottomSrc("");
raf(() => {
setTopSrc(src);
setBottomSrc(oldSrc!);
setBottomOpacity(99);
raf(() => {
setBottomOpacity(0);
});
});
}
});
return (
<div
className="imgContainer"
style={{
position: "relative",
height: "100%"
}}
>
{topSrc && (
<img
style={{
position: "absolute",
opacity: display ? "100%" : 0,
transition: `opacity ${transitionDuration}s ${curve}`
}}
onLoad={() => setDisplay(true)}
src={topSrc}
alt={alt}
/>
)}
{bottomSrc && (
<img
style={{
position: "absolute",
opacity: bottomOpacity + "%",
transition: `opacity ${transitionDuration}s ${curve}`
}}
src={bottomSrc}
alt={alt}
/>
)}
</div>
);
};
export default CrossFadeImage;
<CrossFadeImage
src={image}
alt="phonee"
transitionDuration={0.35}
curve="ease-in-out"
/>
https://stackoverflow.com/a/43115422/9536897 很有用,谢谢。
我想强化你并补充 对于背景图像
constructor(){
super();
this.state = {loaded: false};
}
render(){
return (
<div>
{this.state.loaded ? null :
<div
style={{
background: 'red',
height: '400px',
width: '400px',
}}
/>
}
<img
style={{ display: 'none' }}
src={this.props.src}
onLoad={() => this.setState({loaded: true})}
/>
<div
style={ {
background: `url(${this.props.src})`
,display: this.state.loaded?'none':'block'
}}
/>
</div>
);
}
}```
检测图像何时加载的更好方法是创建对元素的引用,然后向该引用添加事件侦听器。您可以避免在元素中添加事件处理程序代码,并使代码更易于阅读,如下所示:
class Foo extends React.Component {
constructor(){
super();
this.state = {loaded: false};
this.imageRef = React.createRef();
}
componentDidMount() {
this.imageRef.current.addEventListener('load', onImageLoad);
}
onImageLoad = () => {
this.setState({loaded: true})
}
render(){
return (
<div>
{this.state.loaded ? null :
<div
style={{
background: 'red',
height: '400px',
width: '400px',
}}
/>
}
<img
ref={this.imageRef}
style={{ display: 'none' }}
src={this.props.src}
/>
<div
style={{
background: `url(${this.props.src})`
,display: this.state.loaded?'none':'block'
}}
/>
</div>
);
}
}
顺风接受答案
const [isImageLoaded, setIsImageLoaded] = useState(false)
{!isImageLoaded && <img width={30} src='/images/spinner.svg' />}
<img
className={`mx-4 ${!isImageLoaded && 'hidden'}`}
width={30}
src="imageUrl"
onLoad={() => setIsImageLoaded(true)}
/>
这是一个最小的 React 示例,以 React 徽标开头,并将其替换为上传的图像 -
import React from 'react'
import logo from './logo.svg'
import './App.css'
export default function App() {
function loadImage(event) {
const file = event.target.files && event.target.files[0]
if (file) {
const img = document.querySelector("#image")
img.onload = () => window.URL.revokeObjectURL(img.src) // free memory
img.src = window.URL.createObjectURL(file)
}
}
return (
<div className="App">
<input type="file" id="inputfile" accept=".jpg" onChange={loadImage} />
<br/><br/>
<img src={logo} alt="upload" id="image" width={600} />
</div>
)
}
我只想补充一件事。接受的答案很好。但是当 src 在 props 中发生变化时,它不会显示加载组件。要处理 props 更改,您可以在类组件中实现 componentDidUpdate 并在功能组件中实现 useEffect。
class Foo extends React.Component {
constructor(){
super();
this.state = {loaded: false};
}
componentDidUpdate(prevProps){
if(prevProps.src!==this.props.src){
this.setState({loaded : false})
}
}
render(){
return (
<div>
{this.state.loaded ? null :
<div
style={{
background: 'red',
height: '400px',
width: '400px',
}}
/>
}
<img
style={this.state.loaded ? {} : {display: 'none'}}
src={this.props.src}
onLoad={() => this.setState({loaded: true})}
/>
</div>
);
}
}
或者,如果您想显示加载图像或错误图像,则可以使用 npm 包“simple-react-image”。只需使用安装即可
npm 我简单的反应图像
然后使用它。另外,您可以查看示例here。
import React from 'react';
import { Image as Img } from 'simple-react-image';
class Foo extends React.Component {
render(){
return (
<div>
<Img
errorImage="https://www.freeiconspng.com/thumbs/error-icon/error-icon-32.png" //image in case of error
fallback="https://i.gifer.com/ZZ5H.gif"// image in case of loading
src={this.props.src}
onStateChange={(imageState)=>{
this.setState({imageState});//can be loading,loaded,error
}}
/>
</div>
);
}
}
这是 @Brigand 的修改版本,已经使用 React State Hook 进行了修改:
const imageWithLoading = ({ data }) => {
const [loaded, setLoaded] = useState(false)
return (
{loaded ? null :
<p>Change this to loading component..</p>
}
<img
src={data.img.path}
style={loaded ? {} : { display: 'none' }}
onLoad={() => setLoaded(true)}
/>
)
}
import React, {FC,useState,useEffect} from "react"
interface ILoadingImg {
url:string,
classOk?:string,
classError?:string,
classLoading?:string
}
const LoadingImg: FC<ILoadingImg> = ({
url,
classOk,
classError,
classLoading
}) => {
const [isLoad,setIsLoad] = useState<boolean>(false)
const [error,setError] = useState<string|undefined>(undefined)
useEffect(() =>{
const image = new Image()
image.onerror = () =>{
setError(`error loading ${url}`)
setIsLoad( false)
};
image.onload = function() {
setIsLoad( true)
/*
//and you can get the image data
imgData = {
src: this.src,
width:this.width,
height:this.height
}
*/
}
image.src = url
return () => setIsLoad(false)
},[url])
if(!isLoad){
return <div className={classLoading}>Loading...</div>
}
if(error){
return <div className={classError}>{error}</div>
}
return <img src={url} className={classOk} />
}
export default LoadingImg