https://www.youtube.com/watch?v=ikbwJy84f2Y
영상과 코드가 다릅니다. 아이템의 갯수가 변화할시 일일히 CSS 수정 안 하고 걍 JS가 알아서 계산해서 CSS 대입하도록 했음.
See the Pen 3d carousel by Oh Ikmyeong (@dpffpself) on CodePen.
HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3d Gallery</title>
<link rel="stylesheet" href="./style.css">
<script src="./main.js" type="module"></script>
</head>
<body>
<a href="https://www.youtube.com/watch?v=ikbwJy84f2Y" target="_blank">3D Image Gallery in CSS & Javascript</a>
<div id="scene">
<ul id="gall">
<li class="gall-item">1</li>
<li class="gall-item">2</li>
<li class="gall-item">3</li>
<li class="gall-item">4</li>
<li class="gall-item">5</li>
<li class="gall-item">6</li>
<li class="gall-item">7</li>
<li class="gall-item">8</li>
<li class="gall-item">9</li>
<li class="gall-item">10</li>
</ul>
</div>
<div id="btns">
<button class="btn" data-direction="prev">PREV</button>
<button class="btn" data-direction="next">NEXT</button>
</div>
</body>
</html>
|
cs |
CSS
아이템들을 중앙에 위치하게 하기 위해선, 아이템의 position은 absolute로,
아이템을 감싼 직속 부모의 크기는 아이템의 크기와 같도록 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
@charset "utf-8";
*{margin:0;padding:0;box-sizing:border-box;}
li{list-style-type:none;}
html,body{
width:100%;
}
body{
display:flex;flex-flow:column nowrap;
justify-content:center; align-items:center;
gap:60px;
min-height:100vh;
background:#666;
}
/* scene */
#scene{
display:flex;
justify-content:center;align-items:center;
position:relative; overflow-x:hidden;
width:100%; aspect-ratio:16/9;
perspective:100vmax;
background:#000;
}
/* gall */
#gall{
outline:1px solid red;
position:relative;
background:red;
width:30vmin; aspect-ratio:5/3;
max-width:250px;
transform-style:preserve-3d;
}
.gall-item{
display:flex;
justify-content:center; align-items:center;
position:absolute;
width:100%; height:100%;
background:rgba(255,255,255,.6);
border:2px solid salmon;
font-size:5vmin;
backdrop-filter:blur(5px);
user-select:none;
-webkit-box-reflect:below 0px linear-gradient(transparent,#0004);
}
/* */
#btns{
position:relative;
}
.btn{
font-family:inherit;
padding:1em 3em;
}
|
cs |
JS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
const $gall = document.getElementById('gall');
const $$item = document.querySelectorAll('.gall-item');
const $btns = document.getElementById('btns');
const deg = 360 / $$item.length;
let gallDeg = 0;
const POS = {
x : 0
}
/** 배치 */
function style_item(){
const itemWidth = $$item[0].offsetWidth;
const getZ = get_z({deg, itemWidth});
$$item.forEach(($item,idx)=>{
$item.style.transform = `rotateY(${idx * deg}deg) translateZ(${getZ}px)`;
});
}//style_item
/** 거리 구해오기 */
function get_z(INFO = {}){
const {deg,itemWidth} = INFO;
const widthHalf = (itemWidth / 2);
const radient = (Math.PI / 180 ) * (deg / 2);
const r = widthHalf / Math.tan(radient);
const gap = Math.min(window.innerWidth, window.innerHeight) / 50;
const result = parseInt(r);
console.log(radient, Math.tan(radient), result);
return result + gap;
}//get_z
/** 마우스 다운 */
function on_mouse_down(e){
POS.x = e.clientX;
window.addEventListener('mouseup',on_mouse_up,{once:true});
}//on_mouse_down
/** 터치 시작 */
function on_touch_start(e){
POS.x = e.touches[0].clientX;
window.addEventListener('touchend',on_touch_end,{once:true});
}
/** 마우스 업 */
function on_mouse_up(e){
const newX = e.clientX - POS.x;
cacul_final_deg(newX);
$gall.addEventListener('mousedown', on_mouse_down, {once:true});
}//on_mouse_up
/** 터치 끝 */
function on_touch_end(e){
const newX = e.changedTouches[0].clientX - POS.x;
cacul_final_deg(newX);
$gall.addEventListener('touchstart',on_touch_start,{once:true});
}//on_touch_end
/**
* 회전시킬지 말지 정해서 gallDeg값 변경
* @param {Number} newX
* @returns
*/
function cacul_final_deg(newX){
if(Math.abs(newX) < 50) return;
const finalDeg = newX > 0 ? deg : -1 * deg ;
gallDeg += finalDeg;
rotate_gallery();
}//cacul_final_deg
/**
* 갤러리 회전시키기
*/
function rotate_gallery(){
$gall.animate([{
transform : `rotateY(${gallDeg}deg)`,
}],{
duration : 1000,
fill : "both",
easing : "ease-in-out"
});
}//rotate_gallery
/* --------------------------- */
/* 실행 */
style_item();
$gall.addEventListener('mousedown', on_mouse_down, {once:true});
$gall.addEventListener('touchstart',on_touch_start,{once:true});
/* 윈도우 창 변경시 */
window.addEventListener('resize',()=>{
style_item();
});
/* 버튼으로 움직이기 */
$btns.addEventListener('click',(e)=>{
if(e.target.tagName != "BUTTON") return;
const finalDeg = e.target.dataset.direction == "prev" ? deg : -1 * deg ;
gallDeg += finalDeg;
rotate_gallery();
});
|
cs |
'CSS&JS > 👀Study and Copy' 카테고리의 다른 글
[Hyperplexed] 카운터 애니메이션 (1) | 2023.12.12 |
---|---|
[Kevin Powell]CSS,JS로 marquee 태그 같은 무한 스크롤 애니메이션 구현하기 (0) | 2023.10.04 |
[WDS] 마크다운 문법 (1) | 2023.08.24 |
[Hyperplexed]클릭, 그리드 그라데이션 효과 (0) | 2023.07.12 |
[Hyperplexed]opacity, blur 효과 매직완드 갤러리 (0) | 2023.07.12 |