Skip to content

Commit

Permalink
Merge pull request #18 from GleamingStar/feature/carousel
Browse files Browse the repository at this point in the history
Feature/carousel
  • Loading branch information
jjunyjjuny committed Apr 22, 2021
2 parents 9c7c77f + 013ad52 commit 2ea9a0b
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 107 deletions.
15 changes: 15 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
// IntelliSense를 사용하여 가능한 특성에 대해 알아보세요.
// 기존 특성에 대한 설명을 보려면 가리킵니다.
// 자세한 내용을 보려면 https://go.microsoft.com/fwlink/?linkid=830387을(를) 방문하세요.
"version": "0.2.0",
"configurations": [
{
"type": "pwa-chrome",
"request": "launch",
"name": "Launch Chrome against localhost",
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}"
}
]
}
21 changes: 21 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@jjunyjjuny/react-carousel": "0.0.2",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-icons": "^4.2.0",
"react-scripts": "4.0.3",
"styled-components": "^5.2.3",
"web-vitals": "^1.0.1"
Expand Down
197 changes: 114 additions & 83 deletions src/js/util/Carousel.js
Original file line number Diff line number Diff line change
@@ -1,163 +1,194 @@
import { useRef, useState } from "react";
import styled, { css, keyframes } from "styled-components";

// 1. 진행 방향에 요소가 있는가?
// - 있다면 리렌더 없이 slideContainer
// 2. 없다면, VList가 비어있는가?
// - 비어있다면, 반대변에서 가져와서 새로 리렌더링 후에, slideContainer
// - VList에 요소가 남았다면 pop or shift해서 방향에 맞게 추가하고 리렌더링 후에 slideContanier

const Carousel = ({ children: items, itemsPerPeice, onClickItem }) => {
const virtureSlides = useRef(createVirtureSlides(items, itemsPerPeice));
const initialSlide = virtureSlides.current.shift();
const [realSlides, setRealSlieds] = useState([initialSlide]);
import { useEffect, useRef, useState } from "react";
import styled, { css } from "styled-components";
import { HiOutlineChevronLeft, HiOutlineChevronRight } from "react-icons/hi";

const Carousel = ({
children: items,
itemsPerPeice,
onClickItem,
gap = "0.5rem",
autoFit = false,
}) => {
const [initialSlide, virture] = createVirtureSlides(items, itemsPerPeice);
const virtureSlides = useRef(virture);
const container = useRef();
const currentSlideIndex = useRef(0);
const direction = useRef("none");
const [realSlides, setRealSlieds] = useState([initialSlide]);
const didMountRef = useRef(false);
useEffect(() => {
if (didMountRef.current) {
slideContainer(direction.current);
}
});

const move = (dir) => {
didMountRef.current = true;
direction.current = dir;
if (isThereItem(dir)) {
slideContainer(dir);
changeCurrentIndex(dir);
slideContainer();
return;
}
const newSlides = isEmptyVirtureSlides()
? getMovedSlides(dir)
: getAddedSlides();

setRealSlieds((newSlides) => newSlides);
: getAddedSlides(dir);
setRealSlieds(newSlides);
};

const getMovedSlides = (dir) => {
const newSlides = [...realSlides];
if (dir === "prev") {
currentSlideIndex.current -= 1;
direction.current = "prev";
// setRealSlieds([newSlide, ...realSlides]);
const opposite = newSlides.pop();
newSlides.unshift(opposite);
container.current.style.transition = "none";
container.current.style.transform = "translate(calc(-100%))";
} else {
currentSlideIndex.current += 1;
direction.current = "next";
// setRealSlieds([...realSlides, newSlide]);
const i = currentSlideIndex.current;
const opposite = newSlides.shift();
newSlides.push(opposite);
container.current.style.transition = "none";
container.current.style.transform = `translate(-${i * 100 - 100}%)`;
}

return newSlides;
};
const getMovedSlides = (dir) => {
let newSlides = [...realSlides];
const getAddedSlides = (dir) => {
let newSlide;
let newSlides;
if (dir === "prev") {
const opposite = newSlides.current.pop();
newSlides.unshift(opposite);
newSlide = virtureSlides.current.pop();
newSlides = [newSlide, ...realSlides];
container.current.style.transition = "none";
container.current.style.transform = "translate(-100%)";
} else {
const opposite = newSlides.current.shift();
newSlides.push(opposite);
currentSlideIndex.current++;
newSlide = virtureSlides.current.shift();
newSlides = [...realSlides, newSlide];
}

return newSlides;
};
const getAddedSlides = () => {};
const changeCurrentIndex = (dir) => {
currentSlideIndex.current =
dir === "prev"
? --currentSlideIndex.current
: ++currentSlideIndex.current;
};
const slideContainer = (dir) => {
container.current.style.transform =
dir === "prev" ? "translate(100%)" : "translate(-100%)";
const i = currentSlideIndex.current;
container.current.style.transition = `all 1s ease`;
// container.current.style.transform = `translateX(calc(-${i * 100}% ${
// dir === "next" ? `- ${gap}` : ""
// }))`;
container.current.style.transform = `translateX(-${i * 100}%)`;
};

const renderList = () => {
const list = realSlides;
return list.map((slide) => {
return list.map((slide, index) => {
return (
<Slide>
{slide.map((item) => (
<SlideItem>{item}</SlideItem>
<Slide key={index} gap={gap}>
{slide.map((item, index) => (
<SlideItem
gap={gap}
autoFit={autoFit}
key={index}
onClick={onClickItem}
itemsPerPeice={itemsPerPeice}
>
{item}
</SlideItem>
))}
</Slide>
);
});
};
const isThereItem = (direction) => {
if (direction === "prev") {
return currentSlideIndex !== 0;
return currentSlideIndex.current !== 0;
} else {
return currentSlideIndex !== realSlides.length - 1;
return currentSlideIndex.current !== realSlides.length - 1;
}
};
const isEmptyVirtureSlides = () => {
return virtureSlides.current.length === 0;
};
return (
<CarouselWrapper>
<Button prev onClick={() => move("prev")} />
<CarouselContainer ref={container} direction={direction.current}>
{renderList()}
</CarouselContainer>
<Button next onClick={() => move("next")} />
<Button prev onClick={() => move("prev")}>
<HiOutlineChevronLeft />
</Button>
<CarouselContent>
<CarouselContainer gap={gap} ref={container} dir={direction.current}>
{renderList()}
</CarouselContainer>
</CarouselContent>
<Button next onClick={() => move("next")}>
<HiOutlineChevronRight />
</Button>
</CarouselWrapper>
);
};

const createVirtureSlides = (items, itemsPerPeice) => {
return items.reduce((result, item, index) => {
const newItems = items.reduce((result, item, index) => {
const [i, j] = divmod(index, itemsPerPeice);
result[i] ? (result[i][j] = item) : (result[i] = [item]);
return result;
}, []);
return [newItems.shift(), newItems];
};

const divmod = (a, b) => {
return [parseInt(a / b), a % b];
};
const moveToPrev = keyframes`
from{
transform: translateX(0px);
}to{
transform: translateX(100%);
};
`;

const moveToNext = keyframes`
from{
transform: translateX(100%);
}to{
transform: translateX(0);
};
`;
const CarouselWrapper = styled.div`
background-color: #b5b5b5;
/* overflow: hidden; */
position: relative;
background: #b5b5b5;
display: flex;
`;
const CarouselContent = styled.div`
width: 90%;
overflow: hidden;
`;
const CarouselContainer = styled.div`
transition: all 1s ease;
display: flex;
${({ direction }) => {
if (direction === "none") return;
return direction === "prev"
? css`
animation: ${moveToPrev} 1s;
`
: css`
animation: ${moveToNext} 1s;
`;
}}
display: inline-flex;
width: 100%;
padding-right: ${({ gap }) => `${gap}`};
`;
const Slide = styled.ul`
background-color: #777777;
background: #777777;
display: flex;
width: 100%;
flex: 1 0 auto;
padding: 0;
padding-right: ${({ gap }) => `${gap}`};
&::first-child {
}
`;
const SlideItem = styled.li`
list-style-type: none;
width: 100%;
${({ autoFit, itemsPerPeice, gap }) => css`
width: ${autoFit ? `calc(100%/${itemsPerPeice})` : "auto"};
& + & {
margin-left: ${gap};
}
`}
`;
const Button = styled.div`
position: absolute;
width: 50px;
height: 50px;
background: orange;
top: 0;
flex: 1;
z-index: 2;
${(props) => css`
${props.prev &&
css`
left: 10px;
left: 10%;
`}
${props.next &&
css`
right: 10px;
right: 10%;
`}
`}
`};
`;
export default Carousel;
Loading

0 comments on commit 2ea9a0b

Please sign in to comment.