React에서 어셈블리 간의 컨텐트에 대한 애니메이션 설정하기

저는 Flatiron 소프트웨어 공학 훈련 캠프의 몇몇 학우들과 함께 게임 응용 프로그램 React를 개발했는데 그 결과 제가 가장 싫어하는 곳에 있다는 것을 알게 되었습니다.복잡한 CSS가 도전하는 통을 뚫어지게 쳐다보고 있다.나는 한 유저가 이동한 후에 게임판 주위에서 애니메이션을 만들고 싶다.구글에서 React 애니메이션 라이브러리(This is a great source for animation options)를 검색한 후에 저는 제 용례와 CSS와 화해하려는 소망을 더하기로 결정했습니다. 이것은 제가 완전한 CSS 노선을 가야 한다는 것을 의미합니다.이것은 나에게 있어서 결코 간단한 임무가 아니기 때문에 나는 내가 배운 것을 나눠야 한다고 생각한다.

TL;박사 01 명


내 방법의 고급 보기는 사용자가 자원을 눌렀을 때 새 구성 요소로 애니메이션화하는 것이다.이 점을 실현하기 위해서, 나는 자원을 새 구성 요소에서 상태 변화를 보이고, CSS를 사용하여 그것들을 원시 위치로 강제로 돌려보내고, 그것들을 새 위치의 애니메이션으로 설정했다.코드를 깊이 있게 이해하고 결과를 보기만 한다면 code pen 또는 본문 말미에서 전체 코드를 볼 수 있습니다.

설정


나는 가능한 한 이 문제를 줄여서 더욱 쉽게 해결했다.다음은 미리 애니메이션된 장면의 기본 템플릿 코드입니다.
//App.js
const FarmFig = ({ image, handleClick=null, setRef }) => {
  return (
      <img className="image" src={image} ref={setRef} onClick={handleClick} />
  );
};

class App extends React.Component {
  constructor(){
    super()
           this.center = React.createRef();
  this.state = {
    this.state = {
      images: {
        tl:
          "https://lh3.googleusercontent.com/proxy/YSIR4H4fU2Tf5vmbmeixy6m6ZcTXvS9wEo8q4gxOiqEg8XXPod1ZaGJbc8-wngYJwkR6QHEfjvO3w4QogZJqVH5nJjhJaMk",
        c:
          "https://lh3.googleusercontent.com/proxy/29-YDS42UPIZNuPicKnpkmh2sw_th3Sa41d6iiGT8XH1vXfjfpNgUCK1-CxlMlT40eaJP25ylJ8IRUiCEBwTfIyuBB8izJ8",
        br:
          "https://pngarchive.com/public/uploads/small/11559054782q4dsokodad1svijk1zzlyrjdwtmlygkkaxxvooeqevdyrbomu3b5vwcwst0ndcidr89gdf0nyleyffoncphgazeqmnpmdubfypow.png",
      },
  }
} 
handleClick = ({target}) => {
  this.setState(prevState => {
      //switch clicked image with the center image
         if (prevState.images.tl === target.src) {
           prevState.images.tl = prevState.images.c;
           prevState.images.c = target.src
         } else {
           prevState.images.br = prevState.images.c;
           prevState.images.c = target.src
         }
         return {images: prevState.images}
       })
     }

          render() {
            const{tl, c, br} = this.state.images
            return (
              <div className="container">
                <div className="top-left">
                  <FarmFig image={tl} handleClick={this.handleClick}/>
                </div>
                <div className="container">
                  <FarmFig image={c} setRef={this.center} />
                </div>
                <div className="bot-right">
                  <FarmFig image={br} handleClick={this.handleClick} />
                </div>
              </div>
            )
          }
        }
/* css */
.container {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
  justify-content: center;
  height: 90vh;
  width: 100%;
}
.top-left {
  align-self: flex-start;
  transform: rotate(180deg)
}
.bot-right {
  align-self: flex-end;
}
.image {
  width: 175px;
}

지금까지 기본적으로 이미 준비가 다 되었다.우리는 세 장의 사진이 있다. 왼쪽 위, 중간, 오른쪽 아래.왼쪽 상단이나 오른쪽 하단의 그림을 클릭하면, 클릭한 그림과 중심 그림을 교환하는 상태 변화를 촉발합니다.우리는 또한 ref 중심에 있는 그림을 포함하여 곧 그것을 사용할 것이다.이제 이 과도 애니메이션을 만들어 봅시다.

애니메이션 추가


이미지에 애니메이션 효과를 적용하려면 먼저 상태에 항목을 추가해야 합니다.
this.state = {
      images: {
        tl:
          "https://lh3.googleusercontent.com/proxy/YSIR4H4fU2Tf5vmbmeixy6m6ZcTXvS9wEo8q4gxOiqEg8XXPod1ZaGJbc8-wngYJwkR6QHEfjvO3w4QogZJqVH5nJjhJaMk",
        c:
          "https://lh3.googleusercontent.com/proxy/29-YDS42UPIZNuPicKnpkmh2sw_th3Sa41d6iiGT8XH1vXfjfpNgUCK1-CxlMlT40eaJP25ylJ8IRUiCEBwTfIyuBB8izJ8",
        br:
          "https://pngarchive.com/public/uploads/small/11559054782q4dsokodad1svijk1zzlyrjdwtmlygkkaxxvooeqevdyrbomu3b5vwcwst0ndcidr89gdf0nyleyffoncphgazeqmnpmdubfypow.png",
      },
      animate: true,
      transition: {
        center: {
          startTop: 0,
          startRight: 0,
        },
        corner: {
          farmItem: null,
          startTop: 0,
          startRight: 0,
        },
      },
    };
이미지를 다시 렌더링한 후 조정된 시작 위치를 추적하기 위해 transition 객체를 추가했습니다.교환된 이미지를 새 어셈블리에서 렌더링하고 원래 위치로 이동한 다음 새 위치로 애니메이션화합니다.
다음에 우리는 그림을 클릭한 후에 이 조정된 시작 위치를 계산해야 한다.이것은 우리의 handlClick 함수에서 완성될 것이다.
 handleClick = ({ target }) => {
    // find location of clicked image
    const imageStartTop =
      target.getBoundingClientRect().top + document.documentElement.scrollTop;

    const imageStartRight =
      target.getBoundingClientRect().right +
      document.documentElement.scrollLeft;

    //find ending location of clicked image
    let endLoc = this.getCenterLoc();
    let selectedImage;
    this.setState((prevState) => {
      if (prevState.images.tl === target.src) {
        // Swap the selected and center images
        selectedImage = "tl";
        prevState.images.tl = prevState.images.c;
        prevState.images.c = target.src;
      } else {
        selectedImage = "br";
        prevState.images.br = prevState.images.c;
        prevState.images.c = target.src;
      }
      return {
        images: prevState.images,
        // We set animate to false to temporarily to allow images to relocate
        animate: false,
        transition: {
          center: {
            // y distance in px the center image needs to move
            startTop: imageStartTop - endLoc[0],
            // x distance in px the center image needs to move
            startRight: imageStartRight - endLoc[1],
          },
          corner: {
            farmItem: selectedImage,
            // y distance in px the clicked image needs to move
            startTop: endLoc[0] - imageStartTop,
            // y distance in px the clicked image needs to move
            startRight: endLoc[1] - imageStartRight,
          },
        },
      };
    });
     // Wait briefly then change the animation flag to trigger a re-render and the animation. 
    setTimeout(() => this.setState({ animate: true }), 200);
  };
어디:
getCenterLoc = () => {
    const imageEndTop =
      this.center.current.getBoundingClientRect().top +
      document.documentElement.scrollTop;

    const imageEndRight =
      this.center.current.getBoundingClientRect().right +
      document.documentElement.scrollLeft;
    return [imageEndTop, imageEndRight];
  };
이곳에서 많은 일이 발생했지만, 결국 이것은 번거로운 감법 문제일 뿐이다.우리는 뷰포트를 기준으로 클릭 및 중심 이미지의 위치를 얻기 위해 중심 이미지ref와 클릭한 이벤트 대상을 사용합니다.element.getBoundingClientRect()는 문서에 대한 좌표를 제시하고 페이지가 굴러갈 경우 문서의 편향을 추가element합니다.그런 다음 중심 이미지와 선택한 이미지 사이의 차이에 따라 상태를 설정합니다.
그 다음에 우리는 document.documentElement.scrollTop를 사용하여 상태에서 setTimeout 로고를 변경하고 애니메이션을 터치하기 전에 잠시 기다립니다.
이제 이미지를 클릭할 때 새 위치를 적용해야 합니다.우리의 렌더링 방법에서, 우리는 삼원 함수를 추가하여 animate 속성을 검사할 것이다
const animClass = this.state.animate ? "force-move" : "";
그리고 우리는 이 새로운 클래스와 조정된 위치를 해당하는 this.state.animate 구성 요소에 전달하고 FarmFig와 CSS 파일을 다음과 같이 업데이트할 수 있다.
const FarmFig = ({ image, handleClick = null, setRef, locs, animClass }) => {
  return (
        <img
          className={`image ${animClass}`}
          src={image}
          ref={setRef}
          onClick={handleClick}
          style={{
          transform: `translate(${locs[1]}px, ${locs[0]}px)`,
        }}
        />
  );
};
.force-move {
    transition: transform 1s;
    transition-delay: 1s;
    transform: translate(0, 0) !important;
  }
후..드디어 저희가 이동 애니메이션이 생겼어요!!!

이거 좋아요.나는 처음에 공간을 가로지르는 것이 아니라 공간에서 부분을 이동하는 애니메이션이 필요했다.불행하게도 CSS는 같은 대상의 여러 종류 FarmFig 사이에서 지연을 설정하는 것을 지원하지 않습니다.나의 해결 방안은 transformsdivs 주위에 추가FarmFig를 추가하고 시간이 지연된 모든 사람을 바꾸는 것이다. (이것은 틀림없이 좀 불편하게 느낄 것이다. 누가 더 좋은 생각이 있습니까?)
마지막으로 Dell img 및 CSS 파일을 업데이트합니다.
<div
      className={`${animClass}X`}
      style={{
        transform: `translate(${locs[1]}px`,
      }}
    >
      <div
        className={`${animClass}Y`}
        style={{
          transform: `translateY(${locs[0]}px)`,
        }}
      >
        <img
          className={`image`}
          src={image}
          ref={setRef}
          onClick={handleClick}
        />
...
.force-moveX {
    transition: transform 1s;
    transform: translate(0) !important;
  }
  .force-moveY {
    transition: transform 1s;
    transition-delay: 1s;
    transform: translateY(0) !important;
  }
봐라!

마무리


부인할 수 없습니다. CSS만 사용하여 React에서 사용자 정의 애니메이션을 구축하는 것은 상당히 많은 코드가 필요합니다.물론 모든 상황이 다 적용되는 것은 아니다.그러나 프로젝트에 의존하는 라이브러리의 수량을 제한하고 싶다면 독특한 용례가 있거나 CSS에 직면할 때가 됐다고 결정하면 된다.도움이 됐으면 좋겠어요. 제가 뭘 완전히 망치면!

전체 코드


코드 펜


비밀 번호


// App.js
const FarmFig = ({ image, handleClick = null, setRef, locs, animClass }) => {
  return (
    <div
      className={`${animClass}X`}
      style={{
        transform: `translate(${locs[1]}px`,
      }}
    >
      <div
        className={`${animClass}Y`}
        style={{
          transform: `translateY(${locs[0]}px)`,
        }}
      >
        <img
          className={`image`}
          src={image}
          ref={setRef}
          onClick={handleClick}
        />
      </div>
    </div>
  );
};

class App extends React.Component {
  constructor() {
    super();
    this.center = React.createRef();
    this.state = {
      images: {
        tl:
          "https://lh3.googleusercontent.com/proxy/YSIR4H4fU2Tf5vmbmeixy6m6ZcTXvS9wEo8q4gxOiqEg8XXPod1ZaGJbc8-wngYJwkR6QHEfjvO3w4QogZJqVH5nJjhJaMk",
        c:
          "https://lh3.googleusercontent.com/proxy/29-YDS42UPIZNuPicKnpkmh2sw_th3Sa41d6iiGT8XH1vXfjfpNgUCK1-CxlMlT40eaJP25ylJ8IRUiCEBwTfIyuBB8izJ8",
        br:
          "https://pngarchive.com/public/uploads/small/11559054782q4dsokodad1svijk1zzlyrjdwtmlygkkaxxvooeqevdyrbomu3b5vwcwst0ndcidr89gdf0nyleyffoncphgazeqmnpmdubfypow.png",
      },
      animate: true,
      transition: {
        center: {
          startTop: 0,
          startRight: 0,
        },
        corner: {
          farmItem: null,
          startTop: 0,
          startRight: 0,
        },
      },
    };
  }
  getCenterLoc = () => {
    const imageEndTop =
      this.center.current.getBoundingClientRect().top +
      document.documentElement.scrollTop;

    const imageEndRight =
      this.center.current.getBoundingClientRect().right +
      document.documentElement.scrollLeft;
    return [imageEndTop, imageEndRight];
  };

  handleClick = ({ target }) => {
    // find location of clicked image
    const imageStartTop =
      target.getBoundingClientRect().top + document.documentElement.scrollTop;

    const imageStartRight =
      target.getBoundingClientRect().right +
      document.documentElement.scrollLeft;

    //find location of ending location
    let endLoc = this.getCenterLoc();
    let selectedImage;
    this.setState((prevState) => {
      if (prevState.images.tl === target.src) {
        selectedImage = "tl";
        prevState.images.tl = prevState.images.c;
        prevState.images.c = target.src;
      } else {
        selectedImage = "br";
        prevState.images.br = prevState.images.c;
        prevState.images.c = target.src;
      }
      return {
        images: prevState.images,
        animate: false,
        transition: {
          center: {
            startTop: imageStartTop - endLoc[0],
            startRight: imageStartRight - endLoc[1],
          },
          corner: {
            farmItem: selectedImage,
            startTop: endLoc[0] - imageStartTop,
            startRight: endLoc[1] - imageStartRight,
          },
        },
      };
    });
    setTimeout(() => this.triggerAnim(), 200);
  };

  triggerAnim = () => {
    this.setState({ animate: true });
  };

  getOldLoc = (loc) => {
    const { corner } = this.state.transition;
    let top, right;
    if (corner.farmItem === loc) {
      top = corner.startTop;
      right = corner.startRight;
    } else {
      top = 0;
      right = 0;
    }
    return [top, right];
  };

  render() {
    const { tl, c, br } = this.state.images;
    const { center } = this.state.transition;
    const animClass = this.state.animate ? "force-move" : "";
    return (
      <div className="container">
        <div className="top-left">
          <FarmFig
            image={tl}
            handleClick={this.handleClick}
            locs={this.getOldLoc("tl")}
            animClass={animClass}
          />
        </div>
        <div className="center">
          <FarmFig
            image={c}
            setRef={this.center}
            locs={[center.startTop, center.startRight]}
            animClass={animClass}
          />
        </div>
        <div className="bot-right">
          <FarmFig
            image={br}
            handleClick={this.handleClick}
            locs={this.getOldLoc("br")}
            animClass={animClass}
          />
        </div>
      </div>
    );
  }
}
\* css *\
.container {
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    height: 90vh;
    width: 100%;
  }
  .center {
    align-self: center;
  }
  .top-left {
    align-self: flex-start;
  }
  .bot-right {
    align-self: flex-end;
  }
  .image {
    width: 150px;
    height: 130px;
  }

  .force-moveX {
    transition: transform 1s;
    transform: translate(0) !important;
  }
  .force-moveY {
    transition: transform 1s;
    transition-delay: 1.5s;
    transform: translateY(0) !important;
  }

좋은 웹페이지 즐겨찾기