가늘고 빈 그림은 보간이 붙잡지 못해요
같은 도구로 아케이드 캐비닛은 9프레임이 깔끔하게 나왔는데, 크로스헤어는 재생하면 깜빡였습니다. 결국 원본 픽셀을 정수 단위로 밀어서 조립했어요.
카드에 커서를 올리면 오른쪽 위에 작은 엠블럼이 떠오르고, 그게 9프레임으로 움직여요. 두 장을 만들었는데 결과가 갈렸습니다.
| 엠블럼 | 보간 결과 |
|---|---|
| 아케이드 캐비닛 | 9프레임이 깔끔하게 나옴 |
| 네온 크로스헤어 | 프레임마다 획이 통째로 사라지고 모양이 제각각 |
캐비닛은 덩어리가 크고 속이 찬 그림이에요. 크로스헤어는 가늘고 빈 곳이 많고요. 가늘고 빈 그림은 모델이 붙잡을 게 없습니다. 재생하면 움직이는 게 아니라 깜빡이는 걸로 보여요.
대칭이면 밀면 됩니다
크로스헤어는 두 포즈가 같은 중심을 기준으로 대칭이고, 바깥 조각이 안으로 모일 뿐이었어요. 그러면 조각을 방향별로 묶어서 실측한 거리만큼 정수 픽셀로 밀면 끝입니다. 정수라서 리샘플링이 없고, 도트가 뭉개지지 않아요. 중심부만 두 포즈를 알파로 교차시켰습니다.
방향을 나눌 때 캔버스 한가운데를 쓰면 안 돼요. 그림이 1px만 아래로 치우쳐 있어도 가로팔이 “왼쪽아래 대각선”으로 분류돼서 엉뚱한 방향으로 움직입니다. 그림 자신의 무게중심을 쓰고, 2.5px쯤 데드존을 두세요.
재생은 시트 한 장 + steps()
프레임을 <img> 여러 장으로 겹치지 않아요. 가로로 이어 붙인 시트 한 장을 배경으로 깔고 background-position-x만 움직입니다.
.emblem {
background-image: url('/art/emblem-aimbooster.png');
animation: play 900ms steps(9, jump-none) infinite alternate;
}
alternate라서 갔다가 되감아 돌아와요. 시작과 끝이 같은 그림이라 이음매가 생길 수 없습니다.
jump-none이라야 0%와 100%를 모두 밟아요. 그냥 steps(9)면 마지막 프레임을 건너뜁니다.
멈출 때는 animation을 끄지 않고 animation-play-state: paused만 둡니다. 껐다 켜면 매번 0번 프레임으로 튀는데, 재생 상태만 바꾸면 멈췄던 그 프레임에서 그대로 이어져요. 손가락으로 보는 화면에는 호버가 영영 오지 않으니 @media (hover: none)에서는 계속 돌려 둡니다.
포즈는 한 번에 주문하세요
포즈 넷을 한 번에 주문하면 후보 16장이 같은 화풍으로 나와요. 따로따로 뽑으면 선 굵기나 채도가 어긋나서, 붙여 놓으면 다른 사람이 그린 것처럼 보입니다.
같은 색 위에 같은 색을 얹으면 묻혀요 (분홍 카드 위 분홍 크로스헤어). 짧은 검정
drop-shadow를 두 겹 겹치면 도트 윤곽을 따라 테두리가 생겨서 떨어져 보입니다.
애니메이터에는 한 변 256px 제한이 있어요
width × height × frame_count ≤ 524288도 같이 걸립니다. 그래서 움직이는 키아트는 400×248이 아니라 256×160으로 새로 뽑았어요.
큰 이미지를 base64로 넘기려다 실패한 것도 배운 점입니다 — 9~18k 자짜리 문자열은 도구 인자로 넘기다 깨져요. 생성 결과가 URL로 호스팅된다면 URL을 그대로 넘기는 게 base64를 만질 필요가 없는 유일한 길입니다.