See the Pen Masonry Layout by Oh Ikmyeong (@dpffpself) on CodePen.
한 2018년 쯤에 jQuery 라이브러리 없이 어떻게 하면 Masonry Layout을 구축할 수 있을까... 고민했었다. 이 블로그 해당 카테고리의 가장 초반 글도 그거랑 관련된 내용일꺼다... 그 때 당시엔 지금보다 훠어얼씬 더 자바스크립트를 못하고 (제이쿼리만 할 줄 알았음) 문제해결능력도 엄청 낮았기에 css의 column 속성으로 하드코딩 했었다.
이건 JS와 position:absolute를 이용하여 강제적으로 포지션값을 변경하는 방식이다.
장점은 애니메이션 효과가 적용됨 (transition)
단점은 짝수개의 컬럼일때 이미지 길이에 따라 한쪽만 길게 늘어지는 것 처럼 보이는 현상이 발생할 수도 있다.
- 각 이미지를 감싼 DOM에 position:absolute와 left, top값을 계산하여 넣는다
- 가장 밑에 있는 이미지의 top값 + 자체 높이값을 더한 값을 부모 wrap의 height 값으로 넣어준다
HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Masonry Layout</title>
<link rel="stylesheet" href="./style.css">
<script src="./main.js" defer></script>
</head>
<body>
<h1 style="text-align:center;font-size:10vmin; background:#000; color:#fff; padding:1em;">Masonry Layout</h1>
<div id="sample-wrap"></div>
<footer style="text-align:center;font-size:10vmin;background:#ccc;">I'm Footer</footer>
</body>
</html>
|
cs |
CSS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
@charset "utf-8";
*{margin:0;padding:0;box-sizing:border-box;}
img{vertical-align:middle;}
/* wrap */
.msr-wrap{
position:relative;
width:100%; max-width:900px;
margin:20px auto;
}
/* item */
.msr-item{
position:absolute;
top:0; left:0;
overflow:hidden;
transition:all .3s;
}
/* img */
.msr-item img{
width:100%; height:auto;
}
|
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
class MasonryLayoutBuilder{
set_wrap(selector){
if(selector){
this.$parent = document.querySelector(selector);
}else{
this.$parent = document.createElement('SECTION');
document.body.appendChild(this.$parent);
}
return this;
}
set_item(itemList){
this.itemList = itemList;
return this;
}
init(){
new MasonryLayout(this);
}
}//class-MasonryLayoutBuilder
class MasonryLayout{
constructor(BUILDER){
this.$parent = BUILDER?.$parent;
this.itemList = BUILDER?.itemList;
this.$wrap = null;
this.init();
}//constructor
init(){
this.make_wrap();
this.fill_wrap();
this.$parent && this.$parent.appendChild(this.$wrap);
this.adjust_size();
window.addEventListener('resize',this.adjust_size);
}//init
make_wrap(){
this.$wrap = document.createElement('UL');
this.$wrap.classList.add('msr-wrap');
}//make_wrap
fill_wrap(){
if(!this.itemList) return;
const $frag = document.createDocumentFragment();
this.itemList.forEach((src,idx)=>{
const $item = document.createElement('LI');
const $img = new Image();
$item.classList.add('msr-item');
$img.src = src;
$img.title = idx;
$item.appendChild($img);
$frag.appendChild($item);
});
this.$wrap.appendChild($frag);
}//fill_wrap
adjust_size = ()=>{
setTimeout(()=>{
this.set_pos_item();
this.set_wrap_height();
},1000);
}//adjust_size
set_pos_item(){
const $$item = Array.from(this.$wrap.children);
const winWid = window.innerWidth;
const GAP = winWid >= 600 ? 20 : 0;
const COL = winWid >= 1400 ? 3 : (winWid >= 600 ? 2 : 1);
const wid = (this.$wrap.offsetWidth / COL) - GAP + (GAP / COL);
$$item.forEach(($item,idx)=>{
$item.style.width = `${wid}px`;
const perX = idx % COL;
const left = (perX * wid) + (perX * GAP);
$item.style.left = `${left}px`
$item.style.top = `${this.get_prev_height($$item,COL,idx)}px`;
});
}//set_pos_item
get_prev_height($$item,COL,idx){
if(idx < COL) return 0;
let totalTop = 0;
for(let i=idx-COL; i>=0; i-=COL){
const $prevItem = $$item[i];
totalTop += ($prevItem.getBoundingClientRect().height) + 20;
}
return totalTop;
}//get_prev_height
set_wrap_height(){
const $$item = Array.from(this.$wrap.children);
let maxBottom = 0;
let $lastItem = null;
$$item.forEach($item =>{
const top = parseInt($item.style.top);
if(maxBottom <= top){
maxBottom = top;
$lastItem = $item;
}
})
this.$wrap.style.height = `${maxBottom + $lastItem.getBoundingClientRect().height + 20}px`;
}//set_wrap_height
}//class-MasonryLayout
/* ================= 실행 ================= */
const imgList = [
"https://images.unsplash.com/photo-1674560109079-0b1cd708cc2d?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1171&q=80",
"https://images.unsplash.com/photo-1674318012388-141651b08a51?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1932&q=80",
"https://images.unsplash.com/photo-1672661164570-d5e7e0890a69?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=687&q=80",
"https://images.unsplash.com/photo-1673993599169-32f4b7e8cbd4?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1470&q=80",
"https://images.unsplash.com/photo-1603379507360-d80df166fe4e?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=732&q=80",
"https://images.unsplash.com/photo-1639182946622-7de9d7efa6b4?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=687&q=80",
"https://images.unsplash.com/photo-1597589827317-4c6d6e0a90bd?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1160&q=80",
"https://plus.unsplash.com/premium_photo-1663954864809-4279479735bd?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1470&q=80",
"https://images.unsplash.com/photo-1644152231863-e3ae720cc399?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=688&q=80",
"https://images.unsplash.com/photo-1673941733064-2445362593ce?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1470&q=80",
"https://images.unsplash.com/photo-1621383569754-5a8744581af3?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=633&q=80",
"https://plus.unsplash.com/premium_photo-1661603771539-faa0e3ed7ca6?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1470&q=80",
"https://images.unsplash.com/photo-1674503431654-885753f78ee4?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=687&q=80",
"https://images.unsplash.com/photo-1674421338840-de8c149e8c19?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1511&q=80"
]
new MasonryLayoutBuilder()
.set_wrap('#sample-wrap')
.set_item(imgList)
.init();
|
cs |
'CSS&JS > 👀Study and Copy' 카테고리의 다른 글
[WDS]open meteo OPEN API로 현재 지역 날씨 알려주는 웹 만들기(클론코딩) (0) | 2023.02.02 |
---|---|
[JS] vanilla JS로 Masonry Layout 구현하기(2) (0) | 2023.01.25 |
[CSS+JS]3분할 슬라이드(2) (0) | 2023.01.20 |
[CSS+JS] 3분할 슬라이드(1) - 초단순 버젼 (0) | 2023.01.18 |
[Hyperplexed][CSS,JS] 카드에 후레쉬(조명) 비친 효과 내기 (0) | 2023.01.12 |