F.R.I.D.A.Y.

masonry 레이아웃 구성하기 본문

DEV WEB/HTML CSS JS

masonry 레이아웃 구성하기

F.R.I.D.A.Y. 2019. 8. 4. 07:09
반응형

 이런 식으로 이미지, 혹은 내부 요소를 구성하는 방법이 필요해서 검색 좀 해봤습니다. 개인적으로 몇몇과 함께 하고 있는 프로젝트에서 갤러리 레이아웃이 필요해서요. 이 스타일로 페이지를 구성할 때는 브라우저가 이 스타일을 지원하는지 확인하세요.

 

grid

grid 속성 지원 브라우저. MDN

developer.mozilla.org


part1. HTML CSS

 검색을 통해 CSS 디스플레이 속성으로 grid 가 있는 것을 발견했습니다. 이번 포스트에서는 이 grid 속성을 이용할겁니다. 이번 포스트에서는 속성에 대한 설명은 없습니다. 블럭 레이아웃을 구성하는게 목적이기 때문이죠.

 

HTML의 기본 구조는 다음과 같습니다.

<!DOCTYPE html>
<html lang="ko">

<head>
    <title>블럭 레이아웃 테스트</title>
    <style type="text/css">
        *{
            box-sizing:border-box;
            margin:0;
            padding:0;
        }
    </style>
</head>

<body>
    <div class="grid">
        
    </div>
</body>

</html>

 여기에 css로 div.grid의 크기를 정해주도록 합니다. 단, 내부 요소의 개수는 몇개인지 모르므로 width만 정해줍니다. 저는 여기에 더해 div.grid가 화면 가운데에 위치하도록 margin 속성을 넣어주었습니다. 블럭 요소의 경우에는 margin을 통해 가운데 정렬을 할 수 있습니다. 독자에게 보여줄 수 있어야하므로 background와 최소 height값을 추가로 넣었습니다. 

.grid{
    display:grid;
    max-width:900px;
    width:100%;
    margin:0 auto;
    background:#FF0;
    min-height:400px;
}

 저는 반응형 웹페이지를 구성하기 위해 max-width로 최대 너비를 정한 뒤 width는 100%를 채우도록 구성했습니다.

div.grid


 격자 무늬는 각 요소의 가로, 세로가 있습니다. 내부 격자 하나의 크기를 정해주도록 css를 구성합니다. 격자 무늬이니 내부 요소간 공간이 있어야겠네요. grid-gap 속성을 사용합니다.

grid-template-columns:repeat(auto-fill, minmax(150px, 1fr));
grid-auto-rows:20px;
grid-gap:5px;

 grid-template-columns의 auto-fill과 minmax 속성에 대해서는 아래 블로그 포스트가 잘 정리가 되어있네요.

 

min-content, max-content, minmax 활용과 css grid의 auto-fit, auto-fill

min-content와 max-content 활용 이름에서 알 수 있듯이 min-content는 최소한의 컨텐츠 크기를, max-content는 최대한의 컨텐츠 크기를 가지게 해주는 값입니다. 위의 코드를 봐주세요. 해당 코드에 우리는 가운..

justmakeyourself.tistory.com

 참, grid 안에 들어갈 아이템 요소들의 너비(width)를 최대(100%)로 채워주어야합니다.

.item{ /* .grid 내부에 들어갈 요소 클래스 값 */
	width:100%;
}

 이대로 따라오셨다면 이제 내부를 채워봅니다. 저는 온라인에서 이미지를 몇 개 가져와 넣었습니다.

 그리고 레이아웃을 확인해보면

 이상합니다. 생각대로 잘 되지 않습니다. 맞습니다. 아직 끝난게 아니니까요. HTML, CSS만 구성을 다 했을 뿐입니다.


part2. Javascript

 원래 Javascript없이도 구성을 끝내는 것은 가능합니다만, 요소의 개수에 맞추어 각 요소에 스타일 값으로 수정해주어야하는 단점이 있기 때문에 Javascript를 도입하게 됐습니다.

function SetGridItemHeight(){
	let grid = document.getElementsByClassName('grid')[0];
	let rowHeight = parseInt(window.getComputedStyle(grid).getPropertyValue('grid-auto-rows'));
	let rowGap = parseInt(window.getComputedStyle(grid).getPropertyValue('grid-row-gap'));

	let item = grid.getElementsByClassName('item');
	for(let i =0; i < item.length;++i){
		item[i].style.gridRowEnd = 
        		`span ${Math.ceil((item[i].offsetHeight)/(rowHeight + rowGap))}`;
	}


}

 rowHeight는 그리드의 grid-auto-rows 속성값을, rowGap은 그리드의 grid-row-gap(문서상에서는 grid-gap이었던) 속성값을 불러들인 값을 저장하고 있습니다. (rowHeight + rowGap) 부분에 속성값을 그대로 밀어 넣어도 됩니다. 그렇지 않은 이유는 CSS의 값은 수시로 변경될 수 있습니다. CSS의 값이 변경될 때를 대비해 코드의 수정을 막기 위해 이와 같이 처리했습니다.

 

 이 함수를 작성/적용하고 다시 레이아웃을 봅시다.

 처음에 비하면 그런대로 괜찮은 모습을 하고 있습니다. 그렇지만 아쉽습니다.

 붉은 사각형으로 표시한 것과 같이 바로 아래 요소와의 간격이 정형화 되지 않습니다. 이 원인은 이미지 비율에 있습니다. width가 n인 이미지가 존재한다면 이 이미지의 height는 현재 CSS 기준으로 k * (grid-auto-rows) + (k-1) * (grid-gap); k = n / ( grid-gap + grid-auto-rows ) 인 크기어야합니다. 그런데 이 비율이 맞지 않으니 발생하는 문제인거죠. 따라서 이 이미지를 래핑해줍니다. 전 div.item으로 수정해주었습니다.

 내부 요소가 수정되었으니 이제 내부 요소도 CSS를 수정해주어야겠네요.

.item{
	width:100%;
	overflow:hidden;
}

.item > img{
	width:100%;
}

 이렇게 작성한 후 확인해보면 아래와 같이 나오게 됩니다.

 HTML 구조를 변경했지만 Javascript는 변경된 구조에 맞추어 수정하지 않았기 때문이죠. 코드를 수정해줍니다.

function SetGridItemHeight(){
    let grid = document.getElementsByClassName('grid')[0];
    let rowHeight = parseInt(window.getComputedStyle(grid).getPropertyValue('grid-auto-rows'));
    let rowGap = parseInt(window.getComputedStyle(grid).getPropertyValue('grid-row-gap'));

    let item = grid.getElementsByClassName('item');
    for(let i =0; i < item.length;++i){
        item[i].style.gridRowEnd = `span ${Math.floor((item[i].children[0].offsetHeight)/25)}`
        // Math.ceil -> Math.floor
        // item[i].offsetHeight -> item[i].children[0].offsetHeight
    }
}

 이렇게 작성하고 난 뒤 레이아웃을 보면 정상적으로 보여지게 됩니다.

블럭 레이아웃이 완성되었습니다.

 이제 페이지의 크기가 변경될 때, 즉 브라우저의 크기를 변경시킬 때와 브라우저를 처음 열 때 이 함수를 적용하도록 함수 바인딩을 진행해주면 완성입니다.

window.addEventListener("load", SetGridItemHeight);
window.addEventListener("resize", SetGridItemHeight);

반응 확인

 브라우저의 크기에 따라 알아서 맞춰짐을 확인할 수 있습니다.


part3. Extra

 part2. 까지만 완성해도 준수합니다. 그러나 우리는 조금 더 무언가를 원합니다. 지금 레이아웃에서 개발자 도구를 통해 이미지를 확인해보면 중앙에 위치하지 않습니다.

overflow 속성을 주지 않은 것(왼쪽)과 속성을 준 것(오른쪽)

 우리는 이미지가 가운데 맞춤이 되기를 희망합니다. 그러니 내부 요소 CSS에 이 값을 추가합니다.

.item{
    width:100%;
    overflow:hidden;
    position:relative;
}

.item > img{
    width:100%;
    position:absolute;
    left:50%;
    top:50%;
    transform:translate(-50%, -50%);
}

이미지의 상단(왼쪽)과 하단(오른쪽)

 이젠 중심으로 맞춰진 것을 볼 수 있습니다.

 

block-layout.html
0.00MB


참고 문서

 

Masonry style layout with CSS Grid

I’ve been working on a way of using CSS Grid and a small amount of JavaScript to make Masonry style layouts. I reproduce all the…

medium.com

# index

728x90
반응형
Comments