如何检测何时加载图像,通过道具提供,并在React中更改状态?

时间:2017-03-30 10:31:15

标签: javascript image reactjs loading state

我想在加载最终头像时加载不同的图像(假化身)。想法是检测何时加载道具图像并改变状态。可能吗?一些想法?谢谢!

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;

7 个答案:

答案 0 :(得分:12)

有几种方法可以做到这一点,但最简单的方法是隐藏最终图像,然后在加载后将其翻转为可见。

JSBin Demo

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

答案 1 :(得分:1)

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>
    );
  }
}```

答案 2 :(得分:1)

通过在更改图像时添加淡入过渡,可以使操作更进一步。下面的代码是我的CrossFadeImage组件。只需复制并使用它即可代替普通的img组件。

CrossFadeImage有2张图像,topbottombottom堆叠在 top 上,用于显示需要动画处理的图像,在这种情况下,切换时将褪色的旧图像,

在空闲状态下,top显示当前图像,而bottom是前一个图像,但显示为透明

CrossFadeImage在检测到props.src更改时将执行以下操作

  • 重置两个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"
/>

实时演示

Edit demo app on CodeSandbox

答案 3 :(得分:0)

答案与Brigand接受的答案相同,但带有挂钩:

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

答案 4 :(得分:0)

检测何时加载图像的更好方法是创建对该元素的引用,然后向该引用添加事件侦听器。您可以避免在元素中添加事件处理程序代码,并使代码更易于阅读,例如:

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

答案 5 :(得分:0)

这是一个最小的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>
  )
}

答案 6 :(得分:-1)

我的解决方案:

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