React
react.html
html
<!DOCTYPE html>
<html lang="en" class="w-full h-full">
<head>
<meta charset="UTF-8" />
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<title>React</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body class="w-full h-full">
<div id="root" class="w-full h-full"></div>
<script type="text/babel">
const IMAGES_PATH = [
'./images/1.jpg',
'./images/2.jpg',
'./images/3.jpg',
'./images/4.jpg',
'./images/5.jpg',
];
const MAX_SIZE = IMAGES_PATH.length;
const useSlider = ({maxSize}) => {
const [currentPage, setCurrentPage] = React.useState(1);
const handlePrevPage = () => {
setCurrentPage(prev => {
return prev === 1 ? maxSize : prev - 1;
});
};
const handleNextPage = () => {
setCurrentPage(prev => {
return prev === maxSize ? 1 : prev + 1;
})
};
return {
currentPage,
handlePrevPage,
handleNextPage,
}
};
const Button = ({onClick, children}) => {
return (
<button
className="rounded-lg text-sm font-semibold py-2 px-4 bg-slate-900 text-white"
onClick={onClick}>
{children}
</button>
)
}
const SliderItem = ({path}) => {
return (
<div className="w-full p-2 shrink-0 flex justify-center items-center">
<img src={path} className="max-w-full max-h-full"/>
</div>
)
};
const MyApp = () => {
const {currentPage, handlePrevPage, handleNextPage} = useSlider({maxSize: MAX_SIZE});
return <>
<div className="flex justify-center items-center gap-x-3 text-lg p-2">
<Button onClick={handlePrevPage}>
Prev
</Button>
<span>{currentPage} / {MAX_SIZE}</span>
<Button onClick={handleNextPage}>
Next
</Button>
</div>
<div className="max-h-[500px] w-[100%] h-[50%] bg-[#ddd] overflow-hidden">
<div className={`-translate-x-[${(currentPage - 1) * 100}%] w-full h-full flex duration-100`}>
{
IMAGES_PATH
.map((path) => <SliderItem path={path} key={path} />)
}
</div>
</div>
</>;
}
const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);
root.render(<MyApp />);
</script>
</body>
</html>
Vanila Js
index.html
html
<!doctype html>
<html lang="en" class="w-full h-full">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="w-full h-full">
<div class="flex justify-center items-center gap-x-3 text-lg p-2">
<button
class="rounded-lg text-sm font-semibold py-2 px-4 bg-slate-900 text-white"
onclick="slider.prevPage()">
Prev
</button>
<span id="status"></span>
<button
class="rounded-lg text-sm font-semibold py-2 px-4 bg-slate-900 text-white"
onclick="slider.nextPage()">
Next
</button>
</div>
<div class="max-h-[500px] w-[100%] h-[50%] bg-[#ddd] overflow-hidden">
<div id="slider" class="w-full h-full flex duration-100">
<!-- 렌더링 영역 -->
</div>
</div>
<script type="module" src="main.js"></script>
</body>
</html>
main.js
js
import {IMAGES_PATH, MAX_SIZE} from "./constants.js";
import {createSlider} from "./createSlider.js";
import {$, assign} from "./utils.js";
const changeSliderPage = currentPage => {
const sliderPages = $('#slider');
const {originClass} = sliderPages.dataset;
assign(sliderPages, {
className: `-translate-x-[${(currentPage - 1) * 100}%] ${originClass}`
});
};
const changeStatus = currentPage => {
assign($("#status"), {
textContent: `${currentPage} / ${MAX_SIZE}`
})
};
window.onload = () => {
const sliderContent = IMAGES_PATH
.map((path) => {
return `<div class="w-full p-2 shrink-0 flex justify-center items-center">
<img src="${path}" class="max-w-full max-h-full" />
</div>`
})
.join('');
const sliderPages = $('#slider');
assign(sliderPages, {
innerHTML: sliderContent
});
assign(sliderPages.dataset, {
originClass:
sliderPages.dataset.originClass ?? sliderPages.className
});
window.slider = createSlider({maxSize: MAX_SIZE});
slider.watchState(changeSliderPage);
slider.watchState(changeStatus);
};
createSlider.js
js
export const createSlider = ({maxSize}) => {
const state = {
watchers: [],
currentPage: 1,
maxSize,
};
const runWatcher = (watcher) => {
watcher(state.currentPage);
};
const runWatchers = () => {
state.watchers.forEach(runWatcher);
};
const watchState = (watcher) => {
state.watchers.push(watcher);
runWatcher(watcher);
};
const mutations = {
nextPage: () => {
if (state.currentPage === state.maxSize) {
state.currentPage = 1;
} else {
state.currentPage += 1;
}
runWatchers();
},
prevPage: () => {
if (state.currentPage === 1) {
state.currentPage = state.maxSize;
} else {
state.currentPage -= 1;
}
runWatchers();
},
};
return {
...mutations,
watchState,
}
};
utils.js
js
export const $ = selector => document.querySelector(selector);
export const assign = Object.assign;
constants.js
js
export const IMAGES_PATH = [
'./images/1.jpg',
'./images/2.jpg',
'./images/3.jpg',
'./images/4.jpg',
'./images/5.jpg',
];
export const MAX_SIZE = IMAGES_PATH.length;