GODRICH
#css애니메이션#버튼#로딩#전환효과#스크롤#대시보드#실전ui#디자인트렌드

로딩 스켈레톤 ui 9종 — 차트가 계산으로 완성되는 순간까지

로딩 스켈레톤 ui 아홉 가지 가운데 하나는 막대마다 도착 지연을 열 번호로 계산해 순서대로 실제 높이로 펴지고, 하나는 히트맵 칸이 대각선 거리로 계산된 지연만큼 차례로 색을 채우며, 하나는 화면이 동작 줄이기를 선호하면 계산 없이 값이 바로 자리를 바꿉니다.

아홉 항목 모두 click과 keydown을 리스너로 받아 다시 불러오기·다시 시도 버튼을 마우스와 키보드 양쪽으로 누를 수 있고, 09 는 여기에 더해 matchMedia의 change 이벤트까지 실제로 구독해 브라우저의 동작 줄이기 설정이 바뀌는 순간을 감지합니다. 아홉 항목 어디에도 requestAnimationFrame은 없고 Math.random·fetch·new Date도 없습니다 — 값은 전부 코드 안에 미리 적어 둔 배열이고, 계산은 CSS @keyframes·transition과 --real·--phase·--delay 같은 커스텀 프로퍼티, 그리고 setTimeout 시퀀싱만으로 이뤄집니다.

데모

자동 재생 · 눌러서 해당 항목으로 · 전부 한 zip

막대마다 지연 하나만 계산하는 01 에서 시작해 선의 길이를 재는 02, 링의 구간 비율을 각도로 나누는 03, 행 지연을 더하는 04, 칸의 행·열 합으로 순서를 매기는 05 를 거쳐, 계열 지연에 감쇠를 곱해 누적하는 06, 유클리드 거리로 대각선 웨이브를 만드는 07, 실패를 상태 기계로 다루는 08, 마지막으로 실제 설정을 구독해 계산 자체를 건너뛰는 09 순서로 배치했습니다.

아홉 가지 차트 로딩 스켈레톤 — 계산 한 겹이 어디서 도는가

항목 누적면적 강도 평균 움직인 프레임
01 바 스태거 16.22% 85.2 16/23
02 라인 리빌 1.89% 101.8 13/23
03 도넛 모프 6.74% 48.2 18/23
04 스파크라인 웨이브 2.56% 83.8 16/23
05 그리드 순서 28.61% 29.1 9/23
06 멀티시리즈 캐스케이드 3.49% 117.0 15/23
07 히트맵 대각 웨이브 21.91% 49.1 14/23
08 실패·빈 상태 7.96% 35.2 12/23
09 리듀스드 모션 즉시전환 9.88% 89.6 15/23

누적면적은 05(28.61%)가 가장 넓고 07(21.91%)이 그다음이며, 02(1.89%)가 가장 좁습니다. 강도 평균은 06(117.0)이 가장 높고 02(101.8)가 다음으로 높은데, 계열마다 감쇠 계수를 곱해 늘어나는 지연 폭이 구간마다 픽셀 변화를 크게 만들기 때문입니다. 움직인 프레임은 03(18/23)이 가장 많이, 05(9/23)는 가장 적게 움직였고 강도는 05(29.1)가 가장 낮습니다.

01 — 열 지연 한 겹

barDelay(i) = i * STAGGER_MS가 막대 순서(i)를 밀리초 지연으로 바꿔 --phase 커스텀 프로퍼티에 넣습니다.

02 — 길이 측정 한 겹

getTotalLength()로 잰 경로 길이를 PX_PER_PCT(6)로 나눠 몇 %까지 그려야 실제 경로가 완성되는지 구합니다.

03 — 각도 분배 한 겹

전체 둘레 CIRC = 2πR(R=45)에서 구간별 누적 비율만큼 stroke-dashoffset을 옮겨 링 하나를 네 색 구간으로 나눕니다.

04 — 행 지연 한 겹

rowDelay(i) = i * ROW_WAVE_MS가 목록의 행 번호를 지연으로 바꿔 위에서 아래로 순서를 매깁니다.

05 — 순서 합산 한 겹

cellOrder(row, col) = row + col이 칸의 행과 열을 더해 왼쪽 위부터 대각선으로 번지는 순서를 정합니다.

06 — 감쇠 누적 한 겹

seriesDelay(i)가 STEP_MS * DAMPING^(k-1)을 1부터 i까지 더해, 계열이 늘수록 지연 간격이 등비로 줄어듭니다.

07 — 유클리드 거리 한 겹

waveDelayMs(row, col)이 오른쪽 위 모서리를 파원으로 두고 distance / WAVE_SPEED * 1000으로 거리 자체를 지연으로 바꿉니다.

08 — 지수 백오프 한 겹

backoffDelay(n) = BASE_BACKOFF_MS * BACKOFF_FACTOR^n이 재시도 횟수(n)로 다음 대기 시간을 매번 늘립니다.

09 — 실시간 판정 한 겹

matchMedia가 돌려주는 참·거짓값 하나로 지연·웨이브 계산 경로 전체를 건너뛸지 정합니다.

패턴별 코드

아홉 항목에서 계산이 실제로 도는 함수만 하나씩 옮겨 왔습니다. 나머지 마크업과 리스너 코드는 각 데모 카드의 코드 탭에서 그대로 볼 수 있습니다.

01지연이 막대마다 계산되는 바 차트 로딩 스켈레톤

회색 막대가 먼저 놓인 뒤 값이 오면 각 막대의 등장 지연을 열 번호로 계산해 그 순서대로 css 막대 그래프 애니메이션이 실제 높이까지 펴집니다. 대시보드에서 통계 막대그래프가 그려지기 전 자리 잡는 자리에 맞습니다.

  var VALUES = [38, 82, 55, 96, 29, 68];
  var STAGGER_MS = 70;
  var SKELETON_SCALE = 0.16;
  var root = document.getElementById('cb');
  var chart = document.getElementById('cbChart');
  var reload = document.getElementById('cbReload');
  var bars = VALUES.map(function (v, i) { return document.getElementById('cbBar' + i); });

  // 계산 겹: 막대 등장 지연을 열 번호(i)로 계산한다 — 열이 오른쪽일수록 늦게 실제 높이로 펴진다
  function barDelay(i) { return i * STAGGER_MS; }

  bars.forEach(function (bar, i) {
    bar.style.setProperty('--real', String(VALUES[i] / 100));
    bar.style.setProperty('--phase', barDelay(i) + 'ms');
  });

  function paintNow(i, value) {
    bars[i].style.transform = 'scaleY(' + (value / 100) + ')';
    bars[i].style.opacity = '1';
  }
  function skeletonNow(i) {
    bars[i].style.transform = 'scaleY(' + SKELETON_SCALE + ')';
    bars[i].style.opacity = '.32';
  }
지연이 막대마다 계산되는 바 차트 로딩 스켈레톤
새 창에서 열기

01 · 지연이 막대마다 계산되는 바 차트 로딩 스켈레톤 — 측정 누적면적 16.224% · 움직인 프레임 16/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

--phase--realtransform-origin

02평평한 선이 실제 경로로 풀리는 라인 차트 로딩

평평한 점선이 먼저 지나가고 실제 값이 오면 선의 전체 길이를 구해 그 길이만큼 그어지는 시간을 계산해 굴곡진 경로로 풀어냅니다. 월별 추이 라인 차트가 데이터를 받기 전 대기하는 자리에 맞습니다.

  // 계산 겹: 선의 전체 길이(getTotalLength)로 그려지는 데 필요한 구간(%)을 정한다 — 길수록 오래 그려진다
  function drawPercent(len) { return Math.max(20, Math.min(48, Math.round(len / PX_PER_PCT))); }

  function prepare() {
    var len = Math.round(real.getTotalLength());
    var pct = drawPercent(len);
    real.style.strokeDasharray = String(len);
    real.style.strokeDashoffset = String(len);
    styleTag.textContent =
      '@keyframes clDraw {' +
      '0%{stroke-dashoffset:' + len + ';}' +
      '14%{stroke-dashoffset:' + len + ';}' +
      pct + '%{stroke-dashoffset:0;}' +
      '86%{stroke-dashoffset:0;}' +
      '100%{stroke-dashoffset:' + len + ';}}';
    real.style.animation = 'clDraw 2000ms ease-in-out infinite';
    return { len: len, pct: pct };
  }
평평한 선이 실제 경로로 풀리는 라인 차트 로딩
새 창에서 열기

02 · 평평한 선이 실제 경로로 풀리는 라인 차트 로딩 — 측정 누적면적 1.89% · 움직인 프레임 13/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

getTotalLengthstroke-dasharraygetAnimations

03대기 링이 실제 구간으로 바뀌는 도넛 차트 로딩

점선 링이 도는 동안 기다리다가 값이 오면 각 구간의 비율을 계산해 css 도넛 차트 애니메이션이 그 구간 색으로 나뉘어 채워집니다. 완료율 도넛 차트가 집계되기 전 잠깐 도는 자리에 맞습니다.

  var VALUES = [35, 25, 22, 18];
  var R = 45;
  var CIRC = 2 * Math.PI * R;
  var root = document.getElementById('cd');
  var svg = document.getElementById('cdSvg');
  var reload = document.getElementById('cdReload');
  var segs = [0, 1, 2, 3].map(function (i) { return document.getElementById('cdSeg' + i); });

  // 계산 겹: 각 구간의 비율을 원둘레 길이로 바꾸고, 시작 각도(누적 비율)로 순서를 정한다
  function layout() {
    var cum = 0;
    VALUES.forEach(function (v, i) {
      var frac = v / 100;
      var segLen = CIRC * frac;
      segs[i].style.strokeDasharray = segLen.toFixed(2) + ' ' + (CIRC - segLen).toFixed(2);
      segs[i].style.strokeDashoffset = (-cum).toFixed(2);
      segs[i].style.setProperty('--delay', (cum / CIRC * 60) + '%');
      cum += segLen;
    });
  }
  layout();
대기 링이 실제 구간으로 바뀌는 도넛 차트 로딩
새 창에서 열기

03 · 대기 링이 실제 구간으로 바뀌는 도넛 차트 로딩 — 측정 누적면적 6.744% · 움직인 프레임 18/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

--delaystroke-dasharraytransform-origin

04행마다 물결로 번지는 스파크라인 로딩

목록의 행마다 평평한 점선이 먼저 놓이고 값이 오면 행 번호로 물결 지연을 계산해 위에서 아래로 차례로 실제 스파크라인이 나타납니다. 종목·지표 목록에서 행마다 추이를 미리 보여주는 자리에 맞습니다.

  var ROW_WAVE_MS = 90;
  var list = document.getElementById('cw');
  var reals = [0, 1, 2, 3].map(function (i) { return document.getElementById('cwReal' + i); });

  // 계산 겹: 행 번호(i)로 물결 지연을 계산한다 — 위에서 아래로 갈수록 늦게 실제 선이 드러난다
  function rowDelay(i) { return i * ROW_WAVE_MS; }

  reals.forEach(function (real, i) {
    var len = Math.round(real.getTotalLength());
    var row = real.closest('.cw__row');
    var flat = row.querySelector('.cw__flat');
    real.style.setProperty('--len', String(len));
    real.style.strokeDasharray = String(len);
    real.style.strokeDashoffset = String(len);
    real.style.animationDelay = rowDelay(i) + 'ms';
    flat.style.animationDelay = rowDelay(i) + 'ms';
    row.style.animationDelay = rowDelay(i) + 'ms';
  });
행마다 물결로 번지는 스파크라인 로딩
새 창에서 열기

04 · 행마다 물결로 번지는 스파크라인 로딩 — 측정 누적면적 2.563% · 움직인 프레임 16/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

--lengetTotalLengthanimationDelay

05격자선이 거리순으로 나타나는 차트 로딩

12개 칸이 회색 격자로 먼저 놓이고 값이 오면 각 칸의 행과 열을 더한 값으로 순서를 계산해 왼쪽 위부터 대각선 방향으로 하나씩 실제 값 레이블로 바뀝니다. 여러 지표를 한 번에 늘어놓는 리포트 화면에서 값이 순서대로 채워지는 자리에 맞습니다.

  var STEP_MS = 55;
  var grid = document.getElementById('cg');
  var reload = document.getElementById('cgReload');
  var cells = Array.prototype.slice.call(grid.querySelectorAll('.cg__cell'));

  // 계산 겹: 칸의 (행+열) 대각 거리로 순서를 매긴다 — 왼쪽 위부터 대각선으로 번져 나간다
  function cellOrder(row, col) { return row + col; }

  cells.forEach(function (cell) {
    var row = Number(cell.getAttribute('data-row'));
    var col = Number(cell.getAttribute('data-col'));
    var delay = cellOrder(row, col) * STEP_MS;
    cell.style.setProperty('--delay', delay + 'ms');
  });

격자선이 거리순으로 나타나는 차트 로딩
새 창에서 열기

05 · 격자선이 거리순으로 나타나는 차트 로딩 — 측정 누적면적 28.608% · 움직인 프레임 9/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

--delaytransform-originsetTimeout

06계열마다 감쇠로 늦게 자리잡는 멀티시리즈 로딩

여러 계열 스켈레톤이 동시에 놓였다가 값이 오면 계열 번호에 감쇠 계수를 곱한 지연으로 하나씩 실제 값에 자리잡아 전부가 한꺼번에 바뀌지 않습니다. 여러 지표를 겹쳐 보여주는 비교 차트가 값을 받는 자리에 맞습니다.

  var STEP_MS = 220;
  var DAMPING = 0.55;
  var root = document.getElementById('cm');
  var reload = document.getElementById('cmReload');
  var series = [0, 1, 2].map(function (i) { return document.getElementById('cmSeries' + i); });

  // 계산 겹: 계열 지연을 감쇠(등비) 누적으로 계산한다 — 뒤 계열일수록 늘어나는 간격이 줄어들며 안정된다
  function seriesDelay(i) {
    var total = 0;
    for (var k = 1; k <= i; k += 1) { total += STEP_MS * Math.pow(DAMPING, k - 1); }
    return Math.round(total);
  }

  series.forEach(function (path, i) {
    var len = Math.round(path.getTotalLength());
    path.style.setProperty('--len', String(len));
    path.style.strokeDasharray = String(len);
    path.style.strokeDashoffset = String(len);
    path.style.animationDelay = seriesDelay(i) + 'ms';
  });
계열마다 감쇠로 늦게 자리잡는 멀티시리즈 로딩
새 창에서 열기

06 · 계열마다 감쇠로 늦게 자리잡는 멀티시리즈 로딩 — 측정 누적면적 3.492% · 움직인 프레임 15/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

--lengetTotalLengthanimationDelay

07칸이 대각선으로 채워지는 히트맵 로딩

회색 칸이 격자로 먼저 놓이고 값이 오면 각 칸에서 오른쪽 위 모서리까지의 거리를 파동 속도로 나눠 지연을 구해 그 순서대로 실제 색 농도로 바뀝니다. 달력형 히트맵이나 상관관계 표가 값을 받기 전 자리 잡는 자리에 맞습니다.

  var COLS = 4;
  var ROWS = 4;
  var WAVE_SPEED = 6; // 칸 단위 / 초 — 값이 클수록 파동이 격자를 빨리 훑는다
  var VALUES = [
    12, 38, 61, 90,
    27, 55, 82, 34,
    70, 20, 46, 65,
    88, 63, 18, 41
  ];
  var grid = document.getElementById('ch');
  var reload = document.getElementById('chReload');
  var cells = [];

  function bucket(v) { return Math.min(3, Math.floor(v / 25)); }

  // 계산 겹: 오른쪽 위 모서리를 파원으로 두고 유클리드 거리를 파동 속도로 나눠 지연을 구한다
  function waveDelayMs(row, col) {
    var dx = (COLS - 1 - col);
    var dy = row;
    var distance = Math.sqrt(dx * dx + dy * dy);
    return Math.round((distance / WAVE_SPEED) * 1000);
  }
칸이 대각선으로 채워지는 히트맵 로딩
새 창에서 열기

07 · 칸이 대각선으로 채워지는 히트맵 로딩 — 측정 누적면적 21.908% · 움직인 프레임 14/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

animationDelaygetAnimationstransform-origin

08실패하면 빈 상태로 흩어지는 차트 로딩

로딩 막대 세 줄이 놓인 채 정해진 대기 시간이 지나면 상태 하나가 실패·빈 데이터·성공 가운데 하나로 결정되어 패널이 옅어지며 다음 상태로 바뀌고, 다시 시도를 누르면 백오프 계산으로 다음 대기 시간이 매번 늘어납니다. 차트 값을 못 받아왔을 때 사용자에게 다시 시도를 권하는 자리에 맞습니다.

  var BASE_BACKOFF_MS = 240;
  var BACKOFF_FACTOR = 1.6;
  var RETRY_OUTCOMES = ['fail', 'empty', 'success'];

  var root = document.getElementById('cx');
  var panel = document.getElementById('cxPanel');
  var retry = document.getElementById('cxRetry');
  var states = {};
  ['loading', 'fail', 'empty', 'success'].forEach(function (name) {
    states[name] = panel.querySelector('[data-state="' + name + '"]');
  });
  var autoTimers = [];
  var attempt = 0;

  function reduceMotion() { return window.matchMedia('(prefers-reduced-motion: reduce)').matches; }

  // 계산 겹: 재시도 횟수(attempt)로 지수 백오프 대기 시간을 계산한다 — 실패가 반복될수록 다음 시도까지 더 오래 기다린다
  function backoffDelay(n) { return Math.round(BASE_BACKOFF_MS * Math.pow(BACKOFF_FACTOR, n)); }
실패하면 빈 상태로 흩어지는 차트 로딩
새 창에서 열기

08 · 실패하면 빈 상태로 흩어지는 차트 로딩 — 측정 누적면적 7.962% · 움직인 프레임 12/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

matchMediaclassListsetTimeout

09동작 줄이기에서 계산 없이 바로 바뀌는 차트 로딩

화면이 동작 줄이기를 선호하면 지연·웨이브 계산을 전부 건너뛰고 스켈레톤이 값으로 즉시 바뀌는 경로로 갈아탑니다. 전정 질환이나 산만함에 민감한 사용자를 위한 접근성 설정 자리에 맞습니다.

  var VALUES = [64, 88, 42, 76];
  var STAGGER_MS = 80;
  var root = document.getElementById('cr');
  var status = document.getElementById('crStatus');
  var toggle = document.getElementById('crToggle');
  var normalBars = [0, 1, 2, 3].map(function (i) { return document.getElementById('crN' + i); });
  var reducedBars = [0, 1, 2, 3].map(function (i) { return document.getElementById('crR' + i); });

  // 계산 겹: 같은 공식에 감쇠 계수(reduced)를 곱한다 — 줄인 모션이면 지연이 전부 0 으로 접힌다
  function barDelay(i, reduced) { return reduced ? 0 : i * STAGGER_MS; }

  normalBars.forEach(function (bar, i) {
    bar.style.setProperty('--real', String(VALUES[i] / 100));
    bar.style.setProperty('--phase', barDelay(i, false) + 'ms');
  });
  reducedBars.forEach(function (bar, i) {
    bar.style.setProperty('--real', String(VALUES[i] / 100));
    bar.style.setProperty('--phase', barDelay(i, true) + 'ms');
  });
동작 줄이기에서 계산 없이 바로 바뀌는 차트 로딩
새 창에서 열기

09 · 동작 줄이기에서 계산 없이 바로 바뀌는 차트 로딩 — 측정 누적면적 9.878% · 움직인 프레임 15/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

--phasematchMediatransform-origin

값이 UI를 바꾸는 곳 — 지연 계산·대각선 웨이브·감쇠

01 의 STAGGER_MS(70)와 04 의 ROW_WAVE_MS(90)는 둘 다 "순서(i) × 상수" 형태의 같은 계산이지만 01 은 막대 하나하나, 04 는 행 전체(스파크라인 한 줄)에 지연을 겁니다. 06 의 STEP_MS·DAMPING(220·0.55)은 05 의 STEP_MS(55)와 이름은 같아도 서로 다른 항목의 상수라 값을 바꿔도 서로 영향을 주지 않고, 06 은 감쇠를 누적해 더하는 반면 05 는 row + col을 한 번만 곱합니다. 08 의 BASE_BACKOFF_MS·BACKOFF_FACTOR(240·1.6)는 재시도 횟수가 늘수록 대기 시간이 벌어지는 지수 계산이라, 차트가 시작되는 지연을 정하는 나머지 여덟 개의 선형·등비 계산과는 늘어나는 속도 자체가 다릅니다.

이웃 가지

스켈레톤 UI CSS 9종 — 복붙하면 되는 로딩 화면은 카드·목록 자리에 회색 블록만 두고 값이 오면 사라지는 데 초점이 있고, 이 편처럼 도착 지연을 함수로 계산해 순서를 매기는 항목은 없습니다. css 그래프 만들기 SVG로 9종 — 라이브러리 없이는 값이 이미 있는 상태에서 그려지는 애니메이션을 다루지만, 이 편은 값이 오기 전 스켈레톤에서 값으로 넘어가는 전환 자체를 계산합니다. canvas 그래프 그리기 — 라이브러리 없이 그리는 애니메이션 차트 9종에는 03·07 과 겹쳐 보이는 링·격자 패턴이 있지만 그쪽은 이미 그려진 차트를 다시 그리는 쪽이고, 이 편은 로딩 상태라는 맥락에서 같은 도형을 다시 얹었습니다.

어디서 깨지는가 — 접근성·reduced-motion·키보드 포커스

01·02·03·05·06·07 여섯 개는 차트 컨테이너에 role="img"와 aria-label을 두고 그리는 동안 aria-busy="true"를, 다 그려지면 aria-busy="false"를 실제로 오르내립니다. 04 는 스파크라인 넷 각각에 role="img"를 두되 aria-busy는 목록(<ul>) 전체에 한 번만 걸고, 08 은 role="img" 대신 패널에 aria-live="polite"를 둬 상태 문장이 바뀔 때마다 스크린리더가 읽게 하며 실패·빈 아이콘은 aria-hidden="true"로 숨깁니다. 09 는 두 차트 각각에 role="img"·aria-busy를 두고 그 위에 aria-live="polite" 상태 문장과 aria-pressed 토글 버튼을 더해 지금 어떤 설정으로 보고 있는지 세 겹으로 알립니다.

reduce 상태에서 각자 무엇을 멈추는가

아홉 항목 모두 @media (prefers-reduced-motion: reduce)에서 트랜지션을 없애 스켈레톤이 즉시 실제 값으로 바뀝니다. 이 가운데 08 은 reduceMotion() 함수로 같은 조건을 JS 에서도 확인해 자동 재생 timer 자체를 만들지 않고, 09 는 그 확인을 한 겹 더 써서 matchMedia가 참을 돌려주면 지연 계산 함수 자체를 호출하지 않고 값을 즉시 넣습니다. 나머지 일곱은 rAF 나 별도 JS 분기 없이 CSS 트랜지션만 꺼지므로 멈출 대상이 그것 하나뿐입니다.

이 아홉 계산을 그대로 옮기려면 아이디어보다 함수 안의 상수를 가져가는 편이 빠릅니다. 코드와 SCSS 를 그대로 담은 zip 은 tx28eqn3 문장 속 비밀번호로 열리고, 압축 파일 자체는

로 받을 수 있습니다.

키보드·스크린리더로 확인할 것

01~08 여덟 개는 다시 불러오기(또는 다시 시도) <button>에 keydown 리스너를 붙여 Enter·Space 를 눌러도 마우스 클릭과 같은 함수를 호출하고, 09 는 같은 자리에 놓인 미리보기 토글 버튼이 aria-pressed를 눌러 순간 뒤집습니다. 초점을 준 상태에서 화면 확대·스크린리더 낭독 순서를 확인하려면 탭 키로 버튼까지 이동한 뒤 스페이스로 눌러 aria-busy가 true→false 로, 09 라면 aria-pressed가 false→true 로 실제로 바뀌는지 보면 됩니다. role="img"와 aria-live 각각의 스크린리더 규약은 MDN aria-busy 속성과 MDN ARIA 라이브 리전 문서에 정리돼 있습니다.

변형 세 가지와 접근성

변형 값 느낌
막대 스태거 지연 값 낮추기 01 STAGGER_MS 70 → 30 막대들이 실제 높이로 펴지는 순서 간격이 좁아져 거의 동시에 펴지는 쪽에 가까워진다.
멀티시리즈 캐스케이드 감쇠 값 조정 06 DAMPING 0.55 → 0.75 등비 감쇠가 더 천천히 줄어들어 계열마다 실제 값에 자리잡는 시간차가 늘어난다.
히트맵 대각선 웨이브 속도 값 조정 07 WAVE_SPEED 6 → 3 같은 거리라도 지연이 두 배로 늘어나 칸들이 대각선을 따라 채워지는 체감 속도가 느려진다.

세 변형 모두 상수 하나만 바뀔 뿐 click·keydown·matchMedia 리스너 구성이나 role="img"·aria-busy·aria-live 흐름은 손대지 않습니다. 숫자 하나만 조정됐을 뿐이라 스크린리더 사용자가 듣는 안내 문장의 짜임도 그대로고, 셋 중 어느 값을 골라도 접근성이 좁아지는 곳은 없습니다.

로딩 스켈레톤 ui 예제는 어디서 보나?

이 편 맨 위 라이브 격자가 그 예제이고, 항목 아래 붙은 코드는 계산이 실제로 도는 함수 부분만 잘라 옮겨 뒀습니다. 값이 그려지는 그래프 애니메이션을 더 보려면 css 그래프 만들기 SVG 9종을, 이미 값이 있는 상태에서 도형만 다시 그리는 쪽을 보려면 canvas 그래프 그리기 9종을 함께 열어보면 됩니다.

FAQ

스켈레톤 로딩 화면의 지연 값을 항목마다 어디서 바꾸나요?

01 은 STAGGER_MS, 04 는 ROW_WAVE_MS, 06 은 STEP_MS·DAMPING, 07 은 WAVE_SPEED처럼 각 항목 스크립트 맨 위에 놓인 상수 하나만 바꾸면 됩니다. 02·03·08 처럼 지연이 상수 하나가 아니라 getTotalLength()나 BASE_BACKOFF_MS·BACKOFF_FACTOR처럼 계산으로 나오는 항목은 그 계산식 안의 숫자를 바꿔야 결과가 달라집니다.

이 로딩 화면을 실제 API 값과 연결하려면 어떻게 하나요?

아홉 항목 모두 값이 VALUES나 비슷한 이름의 배열에 미리 적혀 있으므로, fetch 응답이 오는 시점에 그 배열을 실제 데이터로 바꿔 넣고 같은 지연 함수를 그대로 호출하면 됩니다. 08 처럼 실패·빈 상태가 있는 항목은 실제 요청이 실패했을 때 같은 상태 이름(fail·empty)으로 전환하기만 하면 백오프 계산까지 그대로 재사용됩니다.

reduced-motion을 켠 화면에서도 아홉 가지 차트 로딩이 전부 되나요?

네, 됩니다. reduce 가 막는 건 트랜지션과(08·09 는) 자동 재생·지연 계산 분기뿐이고, 다시 불러오기·다시 시도 버튼을 눌러 값을 새로 받는 동작 자체는 아홉 항목 모두 그대로 동작합니다. 08·09 는 계산을 아예 건너뛰므로 오히려 더 즉각적으로 반응합니다.

압축 비밀번호를 입력하세요

비밀번호는 이 글 본문 안에 있습니다. 읽다 보면 나옵니다.