์ ๋ง๋๋ก ์ฌ๋ผ์ด๋ ์ผ๋ฅผ ๊ตฌํํ๋ ค๋ค 3์ผ๊ฐ ์ฉ์ผํ ๋์ ์ป์ด๋ธ ์ฝ๋๋ฅผ ์๋ํ๋ฌ ์๋ค! ๋๋ถ์ ๋ฆฌ์กํธ ๊ณต๋ถ์ ์์ฃผ ํฐ ๋์์ด ๋์๋๋ฐ, ์ฝ๋ ๋ํ ๋ด ์ ๋ง๋๋ก์ด๋ฏ๋ก ํน์ฌ ์ง์ ๋ถํ๋ค๊ณ ๋๋ผ์ ๋ค๋ฉด ์ทจํฅ ์ฐจ์ด๋ค!! ๋๋ด์ ๋๋ค ํผ๋๋ฐฑ ํ์ํฉ๋๋ค๐ค๐ค + ์ฌ๋ฌ ๋ถ๋ค๊ป์ ์์ฑํด ์ฃผ์ ๋ฌดํ ์ฌ๋ผ์ด๋ ์ผ ๊ธ์ ์ฐธ๊ณ ํด ๊ณต๋ถํ๊ณ ์ ๋ฆฌํ์ต๋๋ค
โ๐ป ๋ฌดํ ์ฌ๋ผ์ด๋ ์ผ ์๋ฆฌ
๋จผ์ ์๋ฆฌ๋ฅผ ์์๋ณด์. ์๋์ ๊ฐ์ด ์ฌ๋ผ์ด๋ ์ผ๋ก ๋ณด์ฌ์ค ์ด๋ฏธ์ง 4์ฅ์ ๊ฐ๋ก๋ก ์ด์ด ๋ถ์ธ ๋ฐฐ์ด์ ๋ง๋ค์ด ์ค๋ค.

import bg1 from "../img/1.jpg";
import bg2 from "../img/2.jpg";
import bg3 from "../img/3.jpg";
import bg4 from "../img/4.jpg";
/* bg img Array */
const bgArr = [
{ img: bg1, key: 1 },
{ img: bg2, key: 2 },
{ img: bg3, key: 3 },
{ img: bg4, key: 4 },
];
์ด์ ๊ทธ ์์ TV๋ณผํ์ฒ๋ผ ์ ๋ฐฐ์ด์ ๋๋ ค์ค ๊ฒ์ด๋ค. ๋ฌดํ ์ฌ๋ผ์ด๋ ์ผ๋ฅผ ๊ตฌํํ๋ค๊ณ ๋ฌดํํ ๋ฐฐ์ด์ ๋ง๋ค ์ ์์ผ๋ ๋์์์ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค! TV๋ณผํ์ฒ๋ผ ์์ ๋ง๋ ์ด๋ฏธ์ง ๋ฐฐ์ด์ ๋๋ ๋ง์์ค๋ค๊ณ ์๊ฐํ๋ฉด ์ฝ๋ค.


์๋ฒฝํ ๋์์์ ์ํด ์์ฒ๋ผ ์ด๋ฏธ์ง ๋ฐฐ์ด์ ์, ๋ค์ ๊ฐ๊ฐ ๋ง์ง๋ง ์ด๋ฏธ์ง, ์ฒซ ๋ฒ์งธ ์ด๋ฏธ์ง๋ฅผ ๋ถ์ฌ์ค ๊ฒ์ด๋ค. ์ฌ๋ผ์ด๋ ์ ํ ํจ๊ณผ๊ฐ ์์ผ๋ฏ๋ก, ์ด ๊ณผ์ ์ ์๋ตํ๋ฉด ์ ๋๋ค! ์ด ์น๊ตฌ๋ฅผ ์ฌ๋ผ์ด๋ ๋ฐฐ์ด๋ก ๋ถ๋ฅด์.
const BG_NUM = bgArr.length;
const beforeSlide = bgArr[BG_NUM - 1];
const afterSlide = bgArr[0];
let slideArr = [beforeSlide, ...bgArr, afterSlide]; // create slide array (last, origin(first,...,last) ,first) for infinite slide show
const SLIDE_NUM = slideArr.length;
TV๋ณผํ์ผ๋ก ๋ง๋ค์ด์ฃผ๊ธฐ ์ํ html์ ๋ค์๊ณผ ๊ฐ์ด ์์ฑํ๋ค.
const Background = styled.div`
width: 100%;
height: 100vh;
overflow: hidden;
position: relative;
`;
/* bg img slider */
const SlideBtn = styled.div`
z-index: 100;
position: absolute;
display: flex;
align-items: center;
justify-content: center;
`;
const ImgContainer = styled.div`
display: flex;
overflow: hidden;
`;
const ImgBox = styled.div`
width: 100%;
height: 100vh;
overflow: hidden;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
`;
return (
<>
<Background>
<SlideBtn
className="Left"
onMouseEnter={stopAutoSlide}
onMouseLeave={intervalHandler}
onClick={() => slideHandler(-1)}
>
<FontAwesomeIcon icon={faChevronLeft} size="4x" />
</SlideBtn>
<ImgContainer
ref={slideRef}
style={{
width: `${100 * SLIDE_NUM}vw`,
transition: "all 500ms ease-in-out",
transform: `translateX(${
-1 * ((100 / slideArr.length) * slideIndex)
}%)`,
}}
>
{slideArr.map((item, index) => (
<ImgBox key={index}>
<img src={item.img} />
</ImgBox>
))}
</ImgContainer>
<SlideBtn
className="Right"
onMouseEnter={stopAutoSlide}
onMouseLeave={intervalHandler}
onClick={() => slideHandler(+1)}
>
<FontAwesomeIcon icon={faChevronRight} size="4x" />
</SlideBtn>
</Background>
</>);
๊ทธ๋ผ ์ด์ ๋๋ ๋ง์๋ณด์!

์ค๋ฅธ์ชฝ์์ ์ผ์ชฝ ๋ฐฉํฅ์ผ๋ก ์ฌ๋ผ์ด๋๊ฐ ๋์ด๊ฐ๊ณ ์๋ค๋ฉด, ์ด๋ฏธ์ง 1-2-3-4 (์ฌ๋ผ์ด๋ ๋ฐฐ์ด[1]-[2]-[3]-[4]) ๊ฐ ์์๋๋ก ํ๋ฉด์ ๋ํ๋ ๊ฒ์ด๋ค. ๊ทธ๋ฆฌ๊ณ ๋ค์ ์ด๋ฏธ์ง 1 (์ฌ๋ผ์ด๋ ๋ฐฐ์ด[5]) ์ด ํ๋ฉด์ ๋ณด์ฌ์ง๋ค. ์ด๋ ์ ํ ํจ๊ณผ๋ฅผ ์ญ์ ํ๊ณ ๋น ๋ฅด๊ฒ ์ฌ๋ผ์ด๋ ๋ฐฐ์ด[1]๋ก ์๊ฐ์ด๋ํ๋ค. ์ฌ๋ผ์ด๋ ๋ฐฐ์ด[1] ๋ํ ์ด๋ฏธ์ง 1 ์ด๋ฏ๋ก ํ๋ฉด์์ผ๋ก๋ ์๋ฌด ๋ณํ๊ฐ ์๋ ๊ฒ์ฒ๋ผ ๋ณด์ผ ๊ฒ์ด๋ค. ์ด์ ๋ค์ ์์ฐ์ค๋ฝ๊ฒ ์ค๋ฅธ์ชฝ์์ ์ผ์ชฝ ๋ฐฉํฅ์ผ๋ก ์ฌ๋ผ์ด๋๊ฐ ์งํ๋ ์ ์๋ค. TV ๋ณผํ์ด ๋ ๊ฒ์ด๋ค!!
๋ฐ๋๋ฐฉํฅ ๋ํ ๋ง์ฐฌ๊ฐ์ง์ด๋ค.
ํธ๋์ง์ ์ ๋๋ฉ์ด์ ์ด 500ms๊ฐ ์๋ํ๋ฏ๋ก 500ms์ ๋๋ ์ด๋ฅผ ์ฃผ๊ณ ์๊ฐ์ด๋ ์ํค๋ฉฐ, ์ ํํจ๊ณผ ์ญ์ ํ ๋ณต๊ตฌ ์๊ธฐ๋ฅผ ์๊ฐ์ด๋ ์ดํ๋ก ๊ณ ์ ํด ์ฃผ๊ธฐ ์ํด ํจ๊ณผ ๋ณต๊ตฌ๋ ์๊ฐ์ด๋์ผ๋ก๋ถํฐ 100ms์ ๋๋ ์ด๋ฅผ ์ค๋ค.
const [slideIndex, setSlideIndex] = useState(1);
const [slideInterval, setSlideInterval] = useState(6000); // slideInterval 6 secs
const slideRef = useRef<HTMLDivElement>(null);
useInterval(() => setSlideIndex((prev) => prev + 1), slideInterval); // auto slide show with slideInterval
/* InfiniteSlideHandler attachs last/first imgs with origin last/first imgs to make slide seem infinite */
const InfiniteSlideHandler = (flytoIndex: number) => {
setTimeout(() => {
if (slideRef.current) {
slideRef.current.style.transition = "";
}
setSlideIndex(flytoIndex);
setTimeout(() => {
if (slideRef.current) {
slideRef.current.style.transition = "all 500ms ease-in-out";
}
}, 100);
}, 500);
};
if (slideIndex === SLIDE_NUM - 1) {
// if first img (slide array's last item) -> go to origin first img
InfiniteSlideHandler(1);
}
if (slideIndex === 0) {
// if last img (slide array's first item) -> go to origin last img
InfiniteSlideHandler(BG_NUM);
}
const intervalHandler = () => {
// when InfiniteSlideHandler works for first img (slide array's last item), control slideInterval to show transition animation normally
if (slideIndex === SLIDE_NUM - 1) {
setSlideInterval(500);
} else {
setSlideInterval(6000);
}
};
โ๐ป ์ฌ๋ผ์ด๋ ์ปจํธ๋กค ๋ฒํผ
์ ์ฝ๋์ ๋ฑ์ฅํ๋ useInterval ํ ์ ์๋์ ๊ฐ๋ค.
/* useInterval Hook */
interface IUseInterval {
(callback: () => void, interval: number): void;
}
const useInterval: IUseInterval = (callback, interval) => {
const savedCallback = useRef<(() => void) | null>(null);
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
useEffect(() => {
function tick() {
if (savedCallback.current) {
savedCallback.current();
}
}
if (interval !== null && interval !== 10000000) {
let id = setInterval(tick, interval);
return () => clearInterval(id);
}
}, [interval]);
};
๊ธฐ์กด์ ํ ์ฝ๋์ ์กฐ๊ธ ๋ค๋ฅธ ์ ์ interval !== 10000000๋ผ๋ ์กฐ๊ฑด๋ฌธ์ด ์ถ๊ฐ๋์๋ค๋ ์ ์ด๋ค!
๊ทธ ์ด์ ๋ ๋ฐ๋ก ์ฌ๋ผ์ด๋ ์ปจํธ๋กค๋ฌ ๋ฒํผ์ ๋ง๋ค์๊ธฐ ๋๋ฌธ์ด๋ค.
์ฌ๋ผ์ด๋ ์ปจํธ๋กค๋ฌ ๋ฒํผ์ ์์๋ก ์ข์ฐ๋ฐฉํฅ ์กฐ์์ด ๊ฐ๋ฅํ ๋ฒํผ์ด๋ค. ํ์์ ํ์ดํ์ฒ๋ผ ๋ฐฐ์นํ๋ค.

๊ทธ๋ผ ์ฌ๊ธฐ์ ๋ฐ์ํ๋ ๋ฌธ์ !!
๋ด๊ฐ ๋ฒํผ์ ํด๋ฆญํด ์์๋ก ๋ค์ ํ์ด์ง๋ก ๋์ด๊ฐ์์๋ ์๊ฐ์ ํ๋ฅธ๋ค๋ ๊ฒ์ด๋ค.. ์ฆ, ์ด๋ฏธ์ง 1๋ก ์๋ ์ฌ๋ผ์ด๋ ๋ ์์ ์ผ๋ก๋ถํฐ 6์ด๊ฐ ์ธก์ ๋๋ฏ๋ก, ํ์ดํ ๋ฒํผ์ ์ด์ฉํด 5.9์ด ์๊ธฐ์ ๋ด๊ฐ ์ด๋ฏธ์ง 3์ ๋๋ฌํ๋ค๋ฉด 0.1์ด ๋ง์ ์ด๋ฏธ์ง 4๋ก ์ฌ๋ผ์ด๋ ๋๋ค๋ ๊ฑฐ๋ค!
๊ทธ๋์ ์ ์กฐ๊ฑด๋ฌธ์ ์ถ๊ฐํด, ์ฌ๋ผ์ด๋ ์ปจํธ๋กค ๋ฒํผ ์์ ๋ง์ฐ์ค๊ฐ ์ฌ๋ผ๊ฐ์๋ ๋์์ setInterval์ด ๋์ํ์ง ์๊ฒ ๋ง๋ค์ด ์๋ ์ฌ๋ผ์ด๋๋ฅผ ๋ฉ์ถ์ด์ฃผ์๋ค. ๋ง์ฐ์ค๊ฐ ๋ฒํผ์ ๋ฒ์ด๋๋ฉด ๋ค์ 6์ด ๊ฐ๊ฒฉ์ผ๋ก ์๋ ์ฌ๋ผ์ด๋๊ฐ ์์๋๋ค.
/* SlideHandler for buttons */
const slideHandler = (direction: number) => {
setSlideIndex((prev) => prev + direction);
};
/* stopAutoSlide when controlling slide with buttons */
const stopAutoSlide = () => {
setSlideInterval(10000000);
};
์์ ๋ชจ๋ ๋ด์ฉ์ ์ ๋ฆฌํ๋ฉด ์๋์ ๊ฐ๋ค!
๐ค๐ป ์ ์ฒด ์ฝ๋
import styled from "styled-components";
import bg1 from "../img/1.jpg";
import bg2 from "../img/2.jpg";
import bg3 from "../img/3.jpg";
import bg4 from "../img/4.jpg";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faChevronLeft,
faChevronRight,
} from "@fortawesome/free-solid-svg-icons";
import { useEffect, useRef, useState } from "react";
const Background = styled.div`
width: 100%;
height: 100vh;
overflow: hidden;
position: relative;
.Left {
top: 50%;
left: 3%;
transform: translate(-50%, -50%);
color: rgba(235, 235, 235, 0.3);
&:hover {
color: rgba(235, 235, 235, 0.5);
}
}
.Right {
top: 50%;
left: 97%;
transform: translate(-50%, -50%);
color: rgba(235, 235, 235, 0.3);
&:hover {
color: rgba(235, 235, 235, 0.5);
}
}
`;
/* bg img slider */
const SlideBtn = styled.div`
z-index: 100;
position: absolute;
display: flex;
align-items: center;
justify-content: center;
`;
const ImgContainer = styled.div`
display: flex;
overflow: hidden;
`;
const ImgBox = styled.div`
width: 100%;
height: 100vh;
overflow: hidden;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
`;
/* bg img Array */
const bgArr = [
{ img: bg1, key: 1 },
{ img: bg2, key: 2 },
{ img: bg3, key: 3 },
{ img: bg4, key: 4 },
];
/* useInterval Hook */
interface IUseInterval {
(callback: () => void, interval: number): void;
}
const useInterval: IUseInterval = (callback, interval) => {
const savedCallback = useRef<(() => void) | null>(null);
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
useEffect(() => {
function tick() {
if (savedCallback.current) {
savedCallback.current();
}
}
if (interval !== null && interval !== 10000000) {
let id = setInterval(tick, interval);
return () => clearInterval(id);
}
}, [interval]);
};
function Home() {
const [slideIndex, setSlideIndex] = useState(1);
const [slideInterval, setSlideInterval] = useState(6000); // slideInterval 6 secs
const slideRef = useRef<HTMLDivElement>(null);
const BG_NUM = bgArr.length;
const beforeSlide = bgArr[BG_NUM - 1];
const afterSlide = bgArr[0];
let slideArr = [beforeSlide, ...bgArr, afterSlide]; // create slide array (last, origin(first,...,last) ,first) for infinite slide show
const SLIDE_NUM = slideArr.length;
useInterval(() => setSlideIndex((prev) => prev + 1), slideInterval); // auto slide show with slideInterval
/* InfiniteSlideHandler attachs last/first imgs with origin last/first imgs to make slide seem infinite */
const InfiniteSlideHandler = (flytoIndex: number) => {
setTimeout(() => {
if (slideRef.current) {
slideRef.current.style.transition = "";
}
setSlideIndex(flytoIndex);
setTimeout(() => {
if (slideRef.current) {
slideRef.current.style.transition = "all 500ms ease-in-out";
}
}, 100);
}, 500);
};
if (slideIndex === SLIDE_NUM - 1) {
// if first img (slide array's last item) -> go to origin first img
InfiniteSlideHandler(1);
}
if (slideIndex === 0) {
// if last img (slide array's first item) -> go to origin last img
InfiniteSlideHandler(BG_NUM);
}
const intervalHandler = () => {
// when InfiniteSlideHandler works for first img (slide array's last item), control slideInterval to show transition animation normally
if (slideIndex === SLIDE_NUM - 1) {
setSlideInterval(500);
} else {
setSlideInterval(6000);
}
};
/* SlideHandler for buttons */
const slideHandler = (direction: number) => {
setSlideIndex((prev) => prev + direction);
};
/* stopAutoSlide when controlling slide with buttons */
const stopAutoSlide = () => {
setSlideInterval(10000000);
};
return (
<>
<Background>
<SlideBtn
className="Left"
onMouseEnter={stopAutoSlide}
onMouseLeave={intervalHandler}
onClick={() => slideHandler(-1)}
>
<FontAwesomeIcon icon={faChevronLeft} size="4x" />
</SlideBtn>
<ImgContainer
ref={slideRef}
style={{
width: `${100 * SLIDE_NUM}vw`,
transition: "all 500ms ease-in-out",
transform: `translateX(${
-1 * ((100 / slideArr.length) * slideIndex)
}%)`,
}}
>
{slideArr.map((item, index) => (
<ImgBox key={index}>
<img src={item.img} />
</ImgBox>
))}
</ImgContainer>
<SlideBtn
className="Right"
onMouseEnter={stopAutoSlide}
onMouseLeave={intervalHandler}
onClick={() => slideHandler(+1)}
>
<FontAwesomeIcon icon={faChevronRight} size="4x" />
</SlideBtn>
</Background>
</>
);
}
export default Home;
'๐ป Dev > ๐ช ์น (web)' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[vscode] VScode ํ์ด์ฌ ํ๋ผ์คํฌ ๊ฐ๋ฐํ๊ฒฝ ๊ตฌ์ถํ๊ธฐ (0) | 2022.12.12 |
---|---|
[vscode] VScode์ Prettier ๊น๊ณ ์ ์ฉํ๊ธฐ (0) | 2021.08.15 |
[html/css] HTML, CSS, JS ๋? (0) | 2021.08.01 |