11日目

41121 ワード

1.image-crouselの実装image-carouselを直接実施し,後で適用することにした.
最初はreact-slickを使いたかったのですが、使い方を学ぶよりも、まず原理を理解して自分で実施してからライブラリを使うほうがいいと思いますので、googlingで原理を理解して自分で実施しました.

  • 本人の理解に沿って整理する
    1.最後のイメージを最初に追加します.
    2.最後尾に最初の画像を追加します.
    3.改行せずに横並びで画像を配置します.( overflow: hidden )
    4.右クリックしてtransform: translateX(100%)に移動します.(transition適用),(左と反対)

  • さらに理解する必要がある
    1.右側でも左側でも、終了時にボタンを押して、最初に追加した画像を表示します.(上図定理1,2の原因)
    2.画像移動終了後にtransitionを一旦閉じ、1番目の画像に移動してtransitionを開く.

  • 説明の理解
    画像名が1,2,3の場合、[3-1, 1, 2, 3, 1-1]の順に展開される.
    (3-11-1と記載されている.)
    その後、3から1-1に右に移動します.1-1transitionが終了した後、transitionを閉じ、1-1から1に移動し、さらにtransitionを開く.
    これにより、ユーザは、何の変化もなく、内部から1に移行したため、無限の画像を見ることができる.
    逆もまた然り.
  • 次に、画像を移動する矢印または現在の位置を示す変数を追加します.
  • /components/common/ImageCarousel/index.jsx
  • // 2021/12/23 - image-carousel ( 게시글 읽기 모달 and 게시글 생성 모달에 사용 ) - by 1-blue
    
    import React, { useCallback, useEffect, useRef, useState } from "react";
    import Proptypes from "prop-types";
    
    // styled-components
    import { Wrapper } from "./style";
    
    const ImageCarousel = ({ children, speed, length, height }) => {
      const wrapperRef = useRef(null);
      const dotRef = useRef(null);
      const [imageNodes, setImageNodes] = useState(null);
      const [dotNodes, setDotNodes] = useState(null);
      const [currentIndex, setCurrentIndex] = useState(1);
      const [click, setClick] = useState(true);
    
      // 2021/12/23 - 이미지 노드들 배열로 모아서 state에 넣는 함수 - by 1-blue
      useEffect(() => {
        setImageNodes([...wrapperRef.current.childNodes]);
      }, [wrapperRef.current]);
    
      // 2021/12/23 - 첫 이미지 지정 - by 1-blue
      useEffect(() => {
        imageNodes?.forEach(imageNode => (imageNode.style.transform = `translateX(-${currentIndex * 100}%)`));
        setTimeout(() => {
          imageNodes?.forEach(imageNode => (imageNode.style.transition = `all ${speed}ms`));
        }, 100);
      }, [imageNodes]);
    
      // 2021/12/23 - 다음 이미지로 넘기는 함수 - by 1-blue
      const onClickNextButton = useCallback(() => {
        if (!click) return;
    
        // dot 모두 초기화 ( 이전에 이동이 앞인지 뒤인지 알 수 없으니 모두 초기화 )
        dotNodes.forEach(dotNode => (dotNode.style.color = "white"));
    
        // 이미지 변경
        imageNodes.forEach(imageNode => (imageNode.style.transform = `translateX(-${(currentIndex + 1) * 100}%)`));
        setCurrentIndex(prev => (prev + 1 === imageNodes.length - 1 ? 1 : prev + 1));
    
        // 마지막 이미지에서 다음버튼을 누를 경우 실행
        if (currentIndex + 1 === imageNodes.length - 1) {
          setClick(false);
          setTimeout(() => {
            imageNodes.forEach(imageNode => (imageNode.style.transition = `all 0s`));
          }, 900);
          setTimeout(() => {
            imageNodes.forEach(imageNode => (imageNode.style.transform = `translateX(-${1 * 100}%)`));
          }, 1000);
          setTimeout(() => {
            imageNodes.forEach(imageNode => (imageNode.style.transition = `all ${speed}ms`));
            setClick(true);
          }, 1010);
    
          // 현재 이미지와 dot 동기화
          dotNodes[currentIndex - length].style.color = "black";
        } else {
          // 현재 이미지와 dot 동기화
          dotNodes[currentIndex].style.color = "black";
        }
      }, [imageNodes, currentIndex, click, dotNodes, length]);
    
      // 2021/12/23 - 이전 이미지로 넘기는 함수 - by 1-blue
      const onClickPrevButton = useCallback(() => {
        if (!click) return;
    
        // dot 모두 초기화 ( 이전에 이동이 앞인지 뒤인지 알 수 없으니 모두 초기화 )
        dotNodes.forEach(dotNode => (dotNode.style.color = "white"));
    
        imageNodes.forEach(imageNode => (imageNode.style.transform = `translateX(-${(currentIndex - 1) * 100}%)`));
        setCurrentIndex(prev => (prev - 1 === 0 ? imageNodes.length - 2 : prev - 1));
    
        // 첫 이미지에서 이전버튼을 누를 경우 실행
        if (currentIndex - 1 === 0) {
          setClick(false);
          setTimeout(() => {
            imageNodes.forEach(imageNode => (imageNode.style.transition = `all 0s`));
          }, 250);
          setTimeout(() => {
            imageNodes.forEach(imageNode => (imageNode.style.transform = `translateX(-${(imageNodes.length - 2) * 100}%)`));
          }, 500);
          setTimeout(() => {
            imageNodes.forEach(imageNode => (imageNode.style.transition = `all ${speed}ms`));
            setClick(true);
          }, 510);
          // 현재 이미지와 dot 동기화
          dotNodes[length - 1].style.color = "black";
        } else {
          // 현재 이미지와 dot 동기화
          dotNodes[currentIndex - 2].style.color = "black";
        }
      }, [imageNodes, currentIndex, click, dotNodes, length]);
    
      // 2021/12/23 - dot 노드들 배열로 모아서 state에 넣는 함수들 - by 1-blue
      useEffect(() => {
        setDotNodes([...dotRef.current.childNodes]);
      }, [dotRef.current]);
    
      // 2021/12/23 - 첫 이미지와 dot 동기화 - by 1-blue
      useEffect(() => {
        if (!dotNodes) return;
        dotNodes[0].style.color = "black";
      }, [dotNodes]);
    
      return (
        <Wrapper height={height}>
          {/* 이미지들 */}
          <ul ref={wrapperRef} className="image-container">
            {children}
          </ul>
    
          {/* 이미지 이동 버튼 */}
          <button type="button" onClick={onClickNextButton} className="next-button">
            {">"}
          </button>
          <button type="button" onClick={onClickPrevButton} className="prev-button">
            {"<"}
          </button>
    
          {/* 이미지 현재 위치를 표시하는 노드들 */}
          <ul className="dots" ref={dotRef}>
            {Array(length)
              .fill()
              .map((v, i) => (
                <li key={i}></li>
              ))}
          </ul>
    
          <span className="image-number">{`${currentIndex} / ${length}`}</span>
        </Wrapper>
      );
    };
    
    ImageCarousel.propTypes = {
      children: Proptypes.node.isRequired,
      speed: Proptypes.number,
      height: Proptypes.number,
    };
    
    ImageCarousel.defaultProps = {
      speed: 1000,
      height: 100,
    };
    
    export default ImageCarousel;
  • リファレンスサイト
    反応炉の実施
    バニラJSで実現
  • の最後の部分
    1.難点と解決方法
    実装中は、導入イメージの場所が容易ではなく、機能の実現が困難なため、時間がかかります.
    また、transitionを閉じて画像を移動したが、常にtransitionの問題に遭遇し、原因を特定できないため、setTimeoutを使用して問題を解決し、順番に実行した.
    また、currentIndexを用いて現在の画像が何であるかを判断し、stateの値がcurrentIndexなのか+1なのか、どのような計算を適用すれば正常に動作するのか、混乱させ、理解するよりも、一つ一つ適用して問題を解決する方がよい.