본문 바로가기
개인프로젝트/unitedPrompt

ContentSectionTwo

by useSword 2024. 1. 19.

<목표>
1. 왼쪽에 4개씩의 이미지를 보여주고 클릭하면 오른쪽에 그 이미지에 대한 설명을 나타나게 해야함.
2. 이미지가 5개가 넘어가면 페이징처리를 해야함.
3. 클릭을 했을 때, 어떤 이미지가 클릭되었는지 확인할 수 있어야함. 밝기 조절을 해서 클릭한 이미지가 무엇인지 눈에 띄게 보이게 만들었음. 테두리 색상 변경과 박스 그림자 강조 배경색 변경 등 여러가지 시도 했지만 밝기 조절이 가장 이뻐보였음.



1번쨰 이미지를 클릭해서 1번째 이미지는 "ListPlz"라는 데스크탑 어플리케이션을 만들예정이기에 그림과 같이 해놓았다.
글자의 배치같은 경우 대부분 position: absolute을 이용했기 때문에 나중에 쉽게 다시 배치를 변경 가능하다.
설명과 다른 부분이 완성되면 제대로 배치할 생각이다.

 

ContentSectionTwo.jsx

import React,{useState} from 'react';
import { FaArrowLeft, FaArrowRight } from 'react-icons/fa';
import './ContentSectionTwo.scss';

const softwareList = [
  {
    id: 1,
    name: 'Listplz',
    description: 'An advanced text editor for developers.',
    image: 'https://via.placeholder.com/150?text=Alpha+Software',
  },
  {
    id: 2,
    name: 'Chatplz',
    description: 'A powerful tool for data analysis and visualization.',
    image: 'https://via.placeholder.com/150?text=Beta+Analytics',
  },
  {
    id: 3,
    name: 'Toonplz',
    description: 'A robust security suite to protect your digital life.',
    image: 'https://via.placeholder.com/150?text=Gamma+Security',
  },
  {
    id: 4,
    name: 'Talkplz',
    description: 'A robust security suite to protect your digital life.',
    image: 'https://via.placeholder.com/150?text=Gamma+Security',
  },
  {
    id: 5,
    name: 'Talkplz',
    description: 'A robust security suite to protect your digital life.',
    image: 'https://via.placeholder.com/150?text=Gamma+Security',
  },
];


const ContentSectionTwo = () => {
  const [activeSoftware, setActiveSoftware] = useState(null);
  const [pageIndex, setPageIndex] = useState(0);

  const itemsPerPage = 4;
  const maxPageIndex = Math.ceil(softwareList.length / itemsPerPage) - 1;

  const displayedSoftware = softwareList.slice(
    pageIndex * itemsPerPage,
    pageIndex * itemsPerPage + itemsPerPage
  );

  const handleCardClick = (software) => {
    setActiveSoftware(software);
  };

  const handleNextClick = () => {
    setPageIndex(prevIndex => Math.min(prevIndex + 1, maxPageIndex));
  };

  const handlePrevClick = () => {
    setPageIndex(prevIndex => Math.max(prevIndex - 1, 0));
  };

  
  return (
    <div className="content-section-two">
      <div className="carousel-container">
        <div className="card-container">
        {displayedSoftware.map((software) => (
            <div
              key={software.id}
              className={`software-card ${activeSoftware === software ? 'active' : ''}`}
              onClick={() => handleCardClick(software)}
            >
              <img src={software.image} alt={software.name} />
            </div>
          ))}
        </div>
      </div>
      <div className="button-container">
        <button onClick={handlePrevClick} disabled={pageIndex === 0}>
          <FaArrowLeft />
        </button>
        <button onClick={handleNextClick} disabled={pageIndex === maxPageIndex}>
          <FaArrowRight />
        </button>
      </div>
      <div className="software-details">
        {activeSoftware ? (
          <>
            <div className="software-description-container">
          <p className="software-description">{activeSoftware.description}</p>
            <button className="find-dealer-button">Download</button>
            </div>
          </>
        ) : (
          <p>Select a software to see details.</p>
        )}
      </div>
      {activeSoftware && (
        <h1 className="software-title">{activeSoftware.name}</h1>
      )}
    </div>
  );
};


export default ContentSectionTwo;

 

//ContentSectionTwo.scss
.content-section-two {
  justify-content: center; // Center the content vertically
  align-items: center; // Center the content horizontally
  display: flex;
  width: 100vw;
  height: 100vh;

  position: relative;
  background-color: #000;
  z-index: 2;
}

.carousel-container {
  transform: translate(5%, 13%);
  height: 100%;
  width: 100%;
  .card-container {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    width: 80%;
    height: 80%;
    .software-card {
      box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
      cursor: pointer;
      transition:
        transform 0.3s ease,
        box-shadow 0.3s ease;
      position: relative;
    
      overflow: hidden;
      z-index: 2;
    
      &.active {

        background-color: rgba(255, 255, 255, 0.1); // 배경색 약간 변경
        box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4); // 박스 그림자 강조
        filter: brightness(130%); // 밝기 조절
        z-index: 3;
      }
  
      img {
        width: 100%;
        height: 100%;
        object-fit: cover;
      }
    }

  }

  
}


.button-container {
  position: absolute;
  bottom: 3%; // 이미지 하단에서 10px 떨어진 위치
  left: 22%;
  background-color: #000;

  display: flex;


  button {
    background-color: transparent; 
    display: flex;
    justify-content: center;
    align-items: center;
    margin: 0 50px;
    padding: 5px 40px;
    color: #fff;
    border: none;
    border-radius: 5px;
    cursor: pointer;

    &:hover {
      background-color: #555;
    }

    &:disabled {
      cursor: not-allowed;
    }
    svg {
      font-size: 20px;
    }
  }
}


.software-details {
  flex: 0 0 30%;
  padding: 2em;
  border-radius: 10px;
  position: relative;
  z-index: 5;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.software-title {
  font-family: "Oswald", sans-serif;
  font-family: "Playfair Display", serif;
  font-size: 8em;
  color: #f0f0f0;
  margin-bottom: 1em;
  text-align: center;
  position: absolute;
  top: 50px;
  width: 100%;
  padding-top: 10%;
  padding-left: 40%;
}

.software-description {
  font-family: "Oswald", sans-serif;
  font-size: 1em;
  color: #f0f0f0;
  text-align: center;
  margin-top: auto;
}
.find-dealer-button {
  align-self: center;
  margin-top: 1em;
  padding: 10px 20px;
  background-color: #333;
  color: #fff;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  text-transform: uppercase;
  font-weight: bold;

  &:hover {
    background-color: #555;
  }
}
@media (max-width: 1200px) {
  .card-container {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 768px) {
  .content-section-two {
    flex-direction: column;
    align-items: center;
  }

  .carousel-container,
  .software-details {
    flex: 0 0 100%;
    max-width: 100%;
  }

  .card-container {
    grid-template-columns: 1fr;
  }
}



<시도한 점>
1. 처음에 antd 라이브러리의 Carousels을 사용했지만 화면상에 크기 조절과 Carousels안의 이미지들이 클릭하도록 만들어도 넘어가는 부분들이 깨져서 bootstrap Carousels을 시도 했지만 마찬가지로 여러 불편한 점 떄문에 react-bootstrap으로 시도했다가 생각대로 잘 안되어서 Carousels말고 페이징처리로 마무리 했다.
도중에 세션마다 높이들이 어긋나 고생을 좀 했다.

2. ContentSectionThree을 만들다가 footer로 높이를 각 세션마다 빼야 footer를 배치해야 높이가 맞겠구나 이런 병신같은 생각을 해서 개짓거리를 했다. 새벽에 술먹으면서 코딩한 죄같다.
mainPage.scss에서 높이를 calc(100vh - 60px);로 해놔서 그런걸 다른 모든 Section높이들을 설정하느라 몇시간을 날려먹은지 모르겠다. 해결은 했고 이 높이 때문에 싹 다 다시 리펙토링해서 나름 만족한 결과였다.

//고치기 전
.section {
  scroll-snap-align: start;
  height:  calc(100vh - 60px);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  background-color: var(--secondary-color);
  border-radius: 15px;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
  margin: 20px 0;
}

//고친 후
.section {
  scroll-snap-align: start;
  height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  background-color: var(--secondary-color);
  border-radius: 15px;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
  margin: 20px 0;
}

'개인프로젝트 > unitedPrompt' 카테고리의 다른 글

예전 아이콘  (0) 2024.01.20
ContentSectionThree 눈 내리기 추가  (0) 2024.01.20
ContentSectionThree  (0) 2024.01.18