grunt-spritesmith로 CSS Sprites 자동화하기라는 글을 올렸었는데 요즘은 모바일 대응도 중요하므로 이미지를 사용할 때 레티나 디스플레이에도 대응을 해야 한다. 일반적인 이미지는 x2, x3 같은 이미지를 준비해서 미디어 쿼리 등으로 디스플레이에 맞게 보여주지만 Sprites 같은 경우는 x2배 이미지를 Sprites로 합치면 크기도 다시 조정해야 하고 위치도 새로 조정해야 한다. 이 부분이 Sprites 이미지를 쓸 때 어려운 부분 중 하나인데 다행히고 grunt-spritesmith가 Retina 지원도 하고 있다. grunt-spritesmith의 사용법은 이전 글에 올렸으므로 여기서는 Retina 파라미터에 대해서만 정리한다.
Retina 파라미터
grunt-spritesmith로 sprite 이미지를 만드는 다음과 같은 Grunt 설정이 있다고 해보자.
grunt.initConfig({
sprite:{
all: {
src: 'icons/*.png',
dest: 'sprites/icons.png',
destCss: 'css/sprites.css',
cssSpritesheetName: 'icons-spritesheet'
}
}
});
설정에서 보듯이 Sprite 이미지로 만들 아이콘은 icons
폴더 아래 있고 Sprite 이미지는 sprites/icons.png
로 생성되고 CSS는 css/sprites.css
에 생성된다.
레티나 디스플레이가 나온 이후 요즘에는 모바일 기기에서 device pixel ratio가 2가 넘는 기기가 많아졌으므로 이런 기기에서도 아이콘이나 이미지가 깔끔하게 보이려면 (device pixel ratio가 2인 경우) 2배 크기의 이미지를 사용해야 한다. 이 말은 2배 크기의 아이콘으로 만든 Sprite 이미지가 추가로 필요하다는 의미인데 다행히도 grunt-spritesmith가 이를 지원한다.
icons/
├── app-2x.png
├── app.png
├── brightness-2x.png
└── brightness.png
먼저 위와 같이 icons
폴더에 2배 크기의 아이콘을 추가한다. 위에서 -2x
를 파일명에 붙인 것처럼 1배 크기의 아이콘과 같은 파일명을 사용하지만 2배 크기의 아이콘을 구별할 수 있는 접미사가 붙는 형태가 좋다.
grunt.initConfig({
sprite:{
all: {
src: 'icons/*.png',
retinaSrcFilter: 'icons/*-2x.png',
dest: 'sprites/icons.png',
retinaDest: 'sprites/icons-2x.png',
destCss: 'css/sprites.css',
cssSpritesheetName: 'icons-spritesheet'
}
}
});
이제 Gruntfile.js를 위와 같이 수정한다. retinaSrcFilter
는 src
이미지 중에서 레티나 용 아이콘을 필터링하는 역할을 한다. 그래서 전체 src에 포함된 이미지 중에서 retinaSrcFilter
에 속한 이미지는 레티나용으로 처리하고 그렇지 않은 이미지는 일반 이미지로 처리해서 스프라이트 이미지를 만든다. retinaDest
는 레티나용 sprite 이미지를 만들 위치를 지정한다.
$ grunt sprite
Running "sprite:all" (sprite) task
Files "css/sprites.css", "sprites/icons.png", "sprites/icons-2x.png" created.
grunt sprite
를 실행하면 CSS 파일과 2개의 sprite 이미지가 생긴 것을 볼 수 있다.
.icon-app {
background-image: url(../sprites/icons.png);
background-position: 0px 0px;
width: 24px;
height: 24px;
}
.icon-brightness {
background-image: url(../sprites/icons.png);
background-position: -24px 0px;
width: 24px;
height: 24px;
}
@media (-webkit-min-device-pixel-ratio: 2),
(min-resolution: 192dpi) {
.icon-app {
background-image: url(../sprites/icons-2x.png);
background-size: 48px 24px;
}
.icon-brightness {
background-image: url(../sprites/icons-2x.png);
background-size: 48px 24px;
}
}
여기서 생성된 CSS 파일의 내용을 보면 미디어 쿼리를 이용해서 레티나 디스플레이에서는 2배 크기의 아이콘을 사용하도록 지정한 것을 볼 수 있다. background-size
가 지정된 이유는 sprite 이미지 자체가 2배 크기이므로 이를 절반 크기고 줄여서 정상적으로 표시되도록 한 것이다. 사용할 때는 .icon-app
나 .icon-brightness
같은 클래스를 사용하면 별도의 처리 없이도 레티나용 이미지를 사용할 수 있다.
주의할 점은 사용하는 아이콘이 모두 레티나용으로 있어야만 가능하고 일부만 레티나로 만드는 것은 불가능하다. 1배 크기의 이미지와 2배 크기의 이미지의 개수가 맞지 않으면 다음과 같은 오류가 발생한다.
$ grunt sprite
Fatal error: Retina settings detected but 1 retina images were found. We have 2 normal images and expect these numbers to line up. Please double check `retinaSrcFilter`.
또한, 레티나 이미지는 반드시 1배 크기의 이미지의 정확히 2배 크기여야 한다. 이 둘의 크기가 다르다면 생성 시 다음과 같이 경고가 발생한다. 이는 정확히 2배 크기가 아니면 2배 크기의 sprite 이미지의 크기도 달라지므로 background-size
로 크기를 조정해서 위치를 정확히 잡을 수 없기 때문이라고 생각한다.
$ grunt sprite
Running "sprite:all" (sprite) task
>> Normal sprite has inconsistent size with retina sprite. "brightness" is 24x24 while "brightness-2x" is 46x46.
Files "css/sprites.css", "sprites/icons.png", "sprites/icons-2x.png" created.
Stylus를 사용하는 경우
Sass나 Less 등의 전처리자를 사용하는 경우에도 레티나용 Sprite 이미지를 만들 수 있는데 여기서는 Stylus를 기준으로 설명한다. Gruntfile.js
에서 destCss: 'css/sprites.css'
대신 destCss: 'css/sprites.styl'
로 지정하면 된다.
$app_name = 'app';
$app_x = 0px;
$app_y = 0px;
$app_offset_x = 0px;
$app_offset_y = 0px;
$app_width = 24px;
$app_height = 24px;
$app_total_width = 24px;
$app_total_height = 24px;
$app_image = '../sprites/icons.png';
$app = 0px 0px 0px 0px 24px 24px 24px 24px '../sprites/icons.png' 'app';
$app_2x_name = 'app-2x';
$app_2x_x = 0px;
$app_2x_y = 0px;
$app_2x_offset_x = 0px;
$app_2x_offset_y = 0px;
$app_2x_width = 48px;
$app_2x_height = 48px;
$app_2x_total_width = 48px;
$app_2x_total_height = 48px;
$app_2x_image = '../sprites/icons-2x.png';
$app_2x = 0px 0px 0px 0px 48px 48px 48px 48px '../sprites/icons-2x.png' 'app-2x';
$app_group_name = 'app';
$app_group = 'app' $app $app_2x;
$retina_groups = $app_group;
2배 크기의 아이콘을 위한 각 값이 별도로 생성되므로 각각 가져다가 사용할 수 있고 sprites
믹스인 외에도 레티나 용 클래스도 같이 만들 수 있는 retinaSprite
와 retinaSprites
도 생성된다. 사용하는 아이콘 전부를 만들려면 retinaSprites
믹스인에 $retina_groups
를 전달하면 되고 개별로 따로 생성해서 사용하려면 retinaSprite
믹스인에 각 아이콘의 _group
변수를 전달하면 된다.
retinaSprite($retina_group) {
$normal_sprite = $retina_group[1];
$retina_sprite = $retina_group[2];
sprite($normal_sprite)
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
spriteImage($retina_sprite)
spriteBackgroundSize($normal_sprite)
}
}
retinaSprites($retina_groups) {
for $retina_group in $retina_groups {
$sprite_name = $retina_group[0];
.{$sprite_name} {
retinaSprite($retina_group);
}
}
}
레티나 파라미터 문서를 보면 여러 가지 sprite 이미지를 만들 때 서로 충돌 나지 않도록 cssRetinaSpritesheetName
나 cssRetinaGroupsName
로 그룹이나 sprite 시트의 이름을 지정할 수 있다.
Comments