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

유튜브 임베드 코드 9종 — SNS 카드도 담은 플레이스홀더 UI

프레임 폭에 맞춰 세로 비율이 다시 계산되고, 화면에 머무른 시간이 일정 값을 넘으면 가벼운 카드가 스스로 완전한 프레임으로 바뀌고, 최근 소셜 임베드는 등장 뒤 지난 시간만큼 배지 숫자가 조금씩 올라가는 임베드 UI 9종입니다.

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

아홉 화면은 폭 → 비율, 경과 시간 → 상태, 주기 → 밝기·각도라는 세 갈래 계산으로 나뉘고, 아래 표는 그 계산이 실제로 얼마나 화면을 흔들었는지를 잰 값입니다. 순서는 이 흔들림이 작은 것에서 큰 것 쪽으로가 아니라, 유튜브 계열(01~03) → 세로·SNS 계열(04~07) → 상호작용 계열(08~09) 순으로 묶어 실제 서비스에서 마주치는 배치와 가깝게 잡았습니다.

데모

아홉 화면은 requestAnimationFrame 루프가 흘려보내는 하나의 시간값을 각 화면의 apply 함수가 저마다 다른 공식으로 계산해 화면에 다시 씁니다. 실제 유튜브 iframe 이나 SNS SDK 호출은 하나도 없고, 그 자리에 어떤 값이 들어가면 UI 가 어떤 모습으로 바뀌는지만 자동 재생으로 보여줍니다. 실제 서비스에 붙일 때는 유튜브 iframe 반응형 레이아웃에 쓰는 aspect-ratio 속성이나 지연 로드 패턴을 이 계산 자리에 그대로 옮겨 쓰면 됩니다.

실측 한눈에

항목 초점 누적면적 % 강도 평균 움직인 프레임
01 유튜브 반응형 임베드 폭 → 비율 재계산 26.864 84.4 23/23
02 유튜브 라이트 파사드 경과 시간 → 상태 전환 7.068 100.6 23/23
03 유튜브 프라이버시 강화 임베드 주기 → 밝기 맥동 1.876 59.5 23/23
04 세로형 쇼츠 임베드 여백 → 배경 흐림 25.935 82.9 23/23
05 틱톡 임베드 목업 간격 → 좋아요 카운트 13.598 42.6 23/23
06 인스타그램 포스트 임베드 카드 진행률 → 그라디언트 각도 18.658 25.4 23/23
07 X 포스트 임베드 프레임 주기 → 테두리 밝기 1.878 59.4 23/23
08 클릭 후 로드 임베드 클릭 시점 → 높이 보간 31.389 70.0 22/23
09 동의 전 임베드 플레이스홀더 동의 시점 → 높이 맞바꿈 37.148 128.4 19/23

08·09 는 이벤트가 실제로 눌리기 전까지는 화면 대부분이 값을 바꾸지 않아 다른 일곱 개보다 움직인 프레임이 적게 잡힙니다. 09 는 동의 시점(700ms)과 접힘 200ms 가 지나면 안내 문구·프레임 높이가 이미 완료 값에 도달해 그대로 있고 상태 점의 pulse 도 동의 뒤로는 갱신을 멈춰서, 남은 구간 대부분은 진행바만 움직이는 19/23 이 됩니다.

임베드 UI 의 해부 — 아홉 화면이 계산하는 것

같은 "임베드 카드" 라도 계산이 어디에 걸리는지에 따라 세 갈래로 나뉩니다.

폭이 비율로 이어지는 축

01·04 는 프레임 폭을 먼저 정하고 그 폭에서 남는 값을 다시 다른 값으로 바꿉니다. 01 은 사인 곡선으로 프레임 폭을 흔든 뒤 getBoundingClientRect() 로 실제 높이를 읽어 16:9 라벨을 다시 씁니다. 04 는 반대로 폭이 커질수록 남는 여백(stageEl.clientWidth - 프레임 너비)이 줄고, 그 여백을 배경 흐림 강도로 바꿔 씁니다.

경과 시간이 상태를 넘기는 축

02·08·09 는 경과 시간이 어떤 값을 넘는 순간 상태 자체가 바뀝니다. 02 는 FACADE_DELAY 를 넘기면 cardEl.dataset.state 가 lite 에서 frame 으로 바뀌고, 08·09 는 클릭·동의라는 실제 이벤트가 시점을 정한 뒤 그 시점부터 easeOutCubic 곡선으로 높이를 보간합니다.

주기가 밝기·각도를 다시 구하는 축

03·05·06·07 은 클릭 없이 주기 함수만으로 화면을 계속 고쳐 씁니다. 03·07 은 사인 곡선으로 밝기(glow)를 맥동시키고, 05 는 LIKE_INTERVAL 마다 좋아요 숫자를 정수로 올리며 하트를 튕기고, 06 은 진행률을 그대로 그라디언트 각도(--spin)로 바꿔 회전시킵니다.

화면별 코드

01유튜브 반응형 임베드 — 폭에 맞춰 비율을 다시 구하는 영상 프레임

프레임 폭을 사인 곡선으로 흔든 뒤 실제 렌더 높이를 되읽어 16:9 라벨을 다시 씁니다.

폭을 흔들고 실제 높이를 되읽는 계산

apply(t) 는 매 프레임 BASE + RANGE*Math.sin(2*Math.PI*t/CYCLE_MS) 로 프레임 폭을 정하고, getBoundingClientRect() 로 브라우저가 실제로 그린 높이를 다시 읽어 "16:9 · 가로 × 세로px" 라벨을 채웁니다. reduce(prefers-reduced-motion) 가 켜져 있으면 apply(0) 한 번만 실행하고 루프를 시작하지 않습니다.

function apply(t) {
  var w = BASE + RANGE * Math.sin(2 * Math.PI * t / CYCLE_MS);
  frameEl.style.width = w.toFixed(1) + 'px';
  var rect = frameEl.getBoundingClientRect();
  readoutEl.textContent = '16:9 · ' + Math.round(w) + ' × ' + Math.round(rect.height) + 'px';
}
apply(0);
if (reduce) return;
var start = null;
function loop(ts) {
  if (start === null) start = ts;
  var t = (ts - start) % CYCLE_MS;
  apply(t);
  requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
유튜브 반응형 임베드 — 폭에 맞춰 비율을 다시 구하는 영상 프레임
새 창에서 열기

01 · 유튜브 반응형 임베드 — 폭에 맞춰 비율을 다시 구하는 영상 프레임 — 측정 누적면적 26.864% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

aspect-ratiogetBoundingClientRectrequestAnimationFrame

02유튜브 라이트 파사드 — 경과 시간을 재서 완전한 프레임으로 넘어가는 임베드

카드는 처음엔 미리보기 이미지 한 장짜리 "라이트" 상태로 있다가, 경과 시간이 FACADE_DELAY 를 넘는 순간 진짜 프레임으로 넘어갑니다.

지연 시간을 넘기면 상태가 바뀌는 계산

isFrame = t >= FACADE_DELAY 로 상태를 나누고, 프레임 상태일 때만 진행률을 0~1 로 계산해 진행바 폭(scaleX)과 "0:00 / 0:12" 형태의 시간 라벨을 채웁니다. 리드아웃에는 경과 초와 전환 소요 초를 함께 적습니다.

function apply(t) {
  var isFrame = t >= FACADE_DELAY;
  cardEl.dataset.state = isFrame ? 'frame' : 'lite';
  var progress = isFrame ? (t - FACADE_DELAY) / (CYCLE_MS - FACADE_DELAY) : 0;
  progressFillEl.style.transform = 'scaleX(' + progress.toFixed(4) + ')';
  var sec = Math.floor(progress * TOTAL_SEC);
  timeLabelEl.textContent = '0:' + String(sec).padStart(2, '0') + ' / 0:12';
  readoutEl.textContent = '경과 ' + (t / 1000).toFixed(2) + '초 · 전환 ' + transitionSec + '초';
}
apply(0);
if (reduce) return;
var start = null;
function loop(ts) {
  if (start === null) start = ts;
  var t = (ts - start) % CYCLE_MS;
  apply(t);
  requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
유튜브 라이트 파사드 — 경과 시간을 재서 완전한 프레임으로 넘어가는 임베드
새 창에서 열기

02 · 유튜브 라이트 파사드 — 경과 시간을 재서 완전한 프레임으로 넘어가는 임베드 — 측정 누적면적 7.068% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

datasetaria-livematchMedia

03유튜브 프라이버시 강화 임베드 — 밝기를 주기적으로 다시 구해 맥동하는 배지

"추적 쿠키 없음" 배지가 사인 곡선을 따라 밝기와 크기를 함께 바꾸며 은근하게 숨 쉬는 것처럼 보입니다.

사인 값 하나로 투명도·크기를 같이 바꾸는 계산

pulse = 0.5 + 0.5*Math.sin(2*Math.PI*t/700) 값 하나를 배지 글로우의 투명도와 배지 자체의 scale 에 함께 대입해, 밝기와 크기가 어긋나지 않고 같은 박자로 움직입니다.

function apply(t) {
  var pulse = 0.5 + 0.5 * Math.sin(2 * Math.PI * t / 700);
  badgeGlowEl.style.opacity = (0.1 + 0.45 * pulse).toFixed(3);
  badgeEl.style.transform = 'scale(' + (1 + 0.04 * pulse).toFixed(4) + ')';
  readoutEl.textContent = '밝기 ' + Math.round(pulse * 100) + '%';
}
apply(0);
if (reduce) return;
var start = null;
function loop(ts) {
  if (start === null) start = ts;
  var t = (ts - start) % CYCLE_MS;
  apply(t);
  requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
유튜브 프라이버시 강화 임베드 — 밝기를 주기적으로 다시 구해 맥동하는 배지
새 창에서 열기

03 · 유튜브 프라이버시 강화 임베드 — 밝기를 주기적으로 다시 구해 맥동하는 배지 — 측정 누적면적 1.876% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

aria-liverequestAnimationFramematchMedia

04세로형 쇼츠 임베드 — 남는 폭만큼 배경 흐림을 다시 구하는 프레임

세로 9:16 캡션이 붙은 프레임이 커졌다 작아지는 동안, 그 뒤로 보이는 배경의 흐림 강도가 함께 바뀝니다.

여백을 흐림 강도로 바꾸는 계산

토큰(--base-fw·--amp-fw)을 매 프레임 다시 읽어 프레임 폭을 정한 뒤, 무대 폭에서 실제 프레임 너비를 뺀 여백(leftover)을 BG_BLUR_PX + leftover/14 로 바꿔 배경에 blur() 로 적용합니다.

function apply(t) {
  var frameWrapStyle = getComputedStyle(frameWrapEl);
  var BASE_FW = parseFloat(frameWrapStyle.getPropertyValue('--base-fw')) || 118;
  var AMP_FW = parseFloat(frameWrapStyle.getPropertyValue('--amp-fw')) || 16;
  var fw = BASE_FW + AMP_FW * Math.sin(2 * Math.PI * t / CYCLE_MS);
  frameEl.style.width = fw.toFixed(1) + 'px';
  var leftover = stageEl.clientWidth - frameEl.getBoundingClientRect().width;
  var blur = BG_BLUR_PX + leftover / 14;
  bgEl.style.filter = 'blur(' + blur.toFixed(1) + 'px)';
  readoutEl.textContent = '여백 ' + Math.round(leftover) + 'px · 흐림 ' + blur.toFixed(1) + 'px';
}
세로형 쇼츠 임베드 — 남는 폭만큼 배경 흐림을 다시 구하는 프레임
새 창에서 열기

04 · 세로형 쇼츠 임베드 — 남는 폭만큼 배경 흐림을 다시 구하는 프레임 — 측정 누적면적 25.935% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

backdrop-filteraspect-ratiowill-change

05틱톡 임베드 목업 — 경과 시간만큼 좋아요 숫자를 다시 구하는 카드

하트가 일정 간격마다 튕기면서 좋아요 숫자가 그 간격만큼 정수로 올라갑니다.

간격마다 숫자를 올리고 튕김을 겹치는 계산

likes = LIKE_BASE + Math.floor(t/LIKE_INTERVAL)*LIKE_STEP 로 숫자를 계단식으로 올리고, 같은 간격 안의 경과(since)를 하트 팝 애니메이션·버스트 확산·플래시 세 가지 투명도·크기 계산에 나눠 씁니다.

function apply(t) {
  var likes = LIKE_BASE + Math.floor(t / LIKE_INTERVAL) * LIKE_STEP;
  var since = t % LIKE_INTERVAL;
  var pop = since < 180 ? 1 + 0.28 * (1 - since / 180) : 1;
  heartWrapEl.style.transform = 'scale(' + pop.toFixed(3) + ')';
  likeCountEl.textContent = likes.toLocaleString('en-US');
  readoutEl.textContent = '간격 ' + (LIKE_INTERVAL / 1000).toFixed(1) + '초 · 좋아요 ' + likes.toLocaleString('en-US');
  var prog = since / LIKE_INTERVAL;
  var burstScale = 0.3 + prog * 2.3;
  var burstOpacity = Math.max(0, 0.85 * (1 - prog * 1.15));
  burstEl.style.transform = 'translate(-50%, -50%) scale(' + burstScale.toFixed(3) + ')';
  burstEl.style.opacity = burstOpacity.toFixed(3);
  var flashOpacity = since < 160 ? 0.4 * (1 - since / 160) : 0;
  flashEl.style.opacity = flashOpacity.toFixed(3);
}
틱톡 임베드 목업 — 경과 시간만큼 좋아요 숫자를 다시 구하는 카드
새 창에서 열기

05 · 틱톡 임베드 목업 — 경과 시간만큼 좋아요 숫자를 다시 구하는 카드 — 측정 누적면적 13.598% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

writing-modeobject-fitwhite-space

06인스타그램 포스트 임베드 카드 — 각도를 계속 다시 구해 흐르는 로딩 그라디언트

로딩 자리의 그라디언트가 각도를 계속 다시 구하며 회전하고, 그 위로 반짝임 한 줄이 폭을 가로질러 지나갑니다.

진행률 하나로 각도와 반짝임 위치를 같이 구하는 계산

angle = Math.floor((t/CYCLE_MS)*360) 을 CSS 변수 --spin 에 그대로 넘겨 그라디언트를 돌리고, 같은 진행률(prog)을 반짝임 줄의 가로 이동(sweep) 계산에도 같이 씁니다.

function apply(t) {
  var angle = Math.floor((t / CYCLE_MS) * 360);
  slotEl.style.setProperty('--spin', angle + 'deg');
  var prog = t / CYCLE_MS;
  var sweep = -30 + prog * 160;
  shimmerEl.style.transform = 'translateX(' + sweep.toFixed(1) + '%)';
  readoutEl.textContent = '각도 ' + angle + '°';
}
인스타그램 포스트 임베드 카드 — 각도를 계속 다시 구해 흐르는 로딩 그라디언트
새 창에서 열기

06 · 인스타그램 포스트 임베드 카드 — 각도를 계속 다시 구해 흐르는 로딩 그라디언트 — 측정 누적면적 18.658% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

linear-gradientsetPropertyborder-radius

07X 포스트 임베드 프레임 — 테두리 밝기를 주기적으로 다시 구해 빛나는 카드

인용문 카드 테두리가 사인 곡선을 따라 밝아졌다 어두워졌다를 반복합니다.

밝기 값 하나를 CSS 변수로 넘기는 계산

glow = 0.5 + 0.5*Math.sin(2*Math.PI*t/GLOW_MS) 를 CSS 변수 --glow 에 그대로 대입해 테두리 그라디언트 밝기를 정하고, 같은 값을 백분율로 바꿔 리드아웃에도 적습니다.

function apply(t) {
  var glow = 0.5 + 0.5 * Math.sin(2 * Math.PI * t / GLOW_MS);
  cardEl.style.setProperty('--glow', glow.toFixed(3));
  glowValueEl.textContent = Math.round(glow * 100) + '%';
}
if (reduce) {
  apply(0);
  return;
}
var start = null;
function loop(ts) {
  if (start === null) start = ts;
  var t = (ts - start) % CYCLE_MS;
  apply(t);
  requestAnimationFrame(loop);
}
X 포스트 임베드 프레임 — 테두리 밝기를 주기적으로 다시 구해 빛나는 카드
새 창에서 열기

07 · X 포스트 임베드 프레임 — 테두리 밝기를 주기적으로 다시 구해 빛나는 카드 — 측정 누적면적 1.878% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

requestAnimationFramesetPropertyaria-live

08클릭 후 로드 임베드 — 크기 차이를 계산해 부드럽게 커지는 플레이스홀더

누르기 전까지는 자리표시자 높이 그대로 있다가, 누르는 순간부터 실제 프레임 높이까지 곡선을 그리며 커집니다.

누른 시점부터 높이 차이를 보간하는 계산

누르기 전에는 placeholderEl.offsetHeight 를 그대로 카드 높이로 쓰고 힌트 화살표만 옅게 pulse 합니다. 누른 뒤에는 easeOutCubic 로 진행률 p 를 구해 phH + (FRAME_H - phH) * p 로 높이를 채우고, 진행률 55% 를 넘는 지점부터 프레임을 서서히 드러내며 리드아웃의 "현재 높이" 값(예: 168px)도 같이 갱신합니다.

function apply(t) {
  var h;
  var hintOpacity = 0.86 + 0.14 * Math.sin(2 * Math.PI * t / HINT_MS);
  if (!pressed) {
    h = placeholderEl.offsetHeight;
    cardEl.style.height = h + 'px';
    hintArrowEl.style.opacity = hintOpacity.toFixed(3);
  } else {
    var elapsed = t - pressTime;
    if (elapsed < 0) elapsed += CYCLE_MS;
    var delta = FRAME_H - phH;
    var p = easeOutCubic(Math.min(1, elapsed / EXPAND_MS));
    h = phH + delta * p;
    cardEl.style.height = h.toFixed(1) + 'px';
    var frameOpacity = p <= 0.55 ? 0 : Math.min(1, (p - 0.55) / 0.45);
    frameEl.style.opacity = frameOpacity.toFixed(3);
    var sweepSpan = CYCLE_MS - PRESS_AT - EXPAND_MS;
    var sweepP = Math.max(0, Math.min(1, (elapsed - EXPAND_MS) / sweepSpan));
    progressBarEl.style.transform = 'scaleX(' + sweepP.toFixed(3) + ')';
  }
  heightValueEl.textContent = '높이 ' + Math.round(h) + 'px';
}
클릭 후 로드 임베드 — 크기 차이를 계산해 부드럽게 커지는 플레이스홀더
새 창에서 열기

08 · 클릭 후 로드 임베드 — 크기 차이를 계산해 부드럽게 커지는 플레이스홀더 — 측정 누적면적 31.389% · 움직인 프레임 22/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

requestAnimationFrameoffsetHeightaria-pressed

09동의 전 임베드 플레이스홀더 — 안내 문구 높이를 계산해 접히는 카드

"동의하고 불러오기" 를 누르기 전까지는 안내 문구가 자리를 차지하고, 누른 뒤에는 그 높이가 접히면서 프레임 높이로 그대로 넘어갑니다.

안내 높이를 프레임 높이로 맞바꾸는 계산

동의 전에는 상태 점 투명도만 사인 곡선으로 pulse 합니다. 동의 시점부터는 easeOutCubic 진행률 p 로 안내 높이(noticeH*(1-p))를 줄이는 동시에 프레임 높이(FRAME_BASE_H + noticeH*p)를 늘려, 줄어드는 만큼 정확히 늘어나는 맞바꿈으로 만듭니다.

function apply(t) {
  var dotOpacity = 0.86 + 0.14 * Math.sin(2 * Math.PI * t / DOT_MS);
  if (!consented) {
    statusDotEl.style.opacity = dotOpacity.toFixed(3);
    return;
  }
  var elapsed = t - consentTime;
  if (elapsed < 0) elapsed += CYCLE_MS;
  var p = easeOutCubic(Math.min(1, elapsed / CONSENT_FOLD_MS));
  noticeEl.style.height = (noticeH * (1 - p)).toFixed(1) + 'px';
  frameEl.style.height = (FRAME_BASE_H + noticeH * p).toFixed(1) + 'px';
  var sweepSpan = CYCLE_MS - CONSENT_AT - CONSENT_FOLD_MS;
  var sweepP = Math.max(0, Math.min(1, (elapsed - CONSENT_FOLD_MS) / sweepSpan));
  progressBarEl.style.transform = 'scaleX(' + sweepP.toFixed(3) + ')';
}
동의 전 임베드 플레이스홀더 — 안내 문구 높이를 계산해 접히는 카드
새 창에서 열기

09 · 동의 전 임베드 플레이스홀더 — 안내 문구 높이를 계산해 접히는 카드 — 측정 누적면적 37.148% · 움직인 프레임 19/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

requestAnimationFrameoffsetHeightaria-expanded

어디서 쓰이나 — 블로그 본문과 상품 상세 페이지의 영상 자리

블로그 글 중간에 영상 하나를 넣을 때는 보통 01 의 반응형 프레임을 그대로 쓴다. 본문 폭이 화면 크기에 따라 달라지는 구조에서는 프레임 자체가 폭을 흔들 때마다 높이를 되읽어야, 유튜브 반응형 html 코드처럼 화면이 좁아져도 영상이 잘리지 않는다.

이커머스 상품 상세 페이지에서 후기 섹션 옆에 세로형 영상(04)을 붙이는 경우는 조금 다르다. 상품 이미지 갤러리와 폭을 나눠 써야 하므로 남는 공간이 매번 달라지고, 그 남는 폭만큼 배경을 흐리는 계산이 있어야 세로 프레임이 좌우 빈 공간에서 붕 뜬 것처럼 보이지 않는다.

개인정보 동의 배너를 먼저 띄우는 팀이라면 09 의 동의 전 플레이스홀더를 쿠키 배너 로직에 그대로 연결해 두면, 방문자가 동의 버튼을 누르기 전까지는 실제 임베드가 화면에 나타나지 않는다.

느린 네트워크·연속 클릭·동의 취소에서 계산이 깨지는 지점

느린 네트워크 — 라이트 파사드가 늦게 넘어간다

02 는 시간이 FACADE_DELAY 를 넘는지만 보고 상태를 바꾸므로, 실제 네트워크에서 진짜 iframe 로드가 그보다 오래 걸리면 화면은 이미 "완전한 프레임" 표시로 넘어갔는데 실제 영상은 아직 준비되지 않는 틈이 생길 수 있습니다. 유튜브 임베드 자동재생 옵션을 켜 둔 서비스라면 이 틈이 곧바로 재생 시작 지연으로 이어집니다.

연속 클릭 — 사이클이 돌아오면 처음부터 다시 시작된다

08 은 pressed 플래그가 참이 된 뒤로는 같은 사이클 안에서 클릭을 다시 읽지 않으므로, phy8esss 압축 파일 안의 원본 소스를 보면 알 수 있듯 한 사이클 안에서 몇 번을 눌러도 높이 보간은 이미 시작된 시점을 그대로 따라갑니다. 대신 루프가 2초 주기의 처음으로 돌아오는 순간 자리표시자 상태로 초기화되어, 다음 사이클의 자동 시점(PRESS_AT)이나 새 클릭을 다시 기다립니다.

동의 취소 — 접힘 뒤 다음 사이클에서 되돌아온다

09 는 동의가 이루어진 사이클 안에서는 접힘이 다시 펼쳐지지 않지만, 루프가 2초 주기의 처음으로 돌아오면 동의 전 상태로 초기화되어 안내 문구가 다시 나타납니다. 실제 서비스에서 "한번 동의하면 끝까지 유지"되는 모습을 원한다면 이 초기화를 건너뛰는 별도 처리를 직접 추가해야 합니다.

값이 화면을 바꾸는 곳 — 비율·경과 시간·밝기

같은 세 종류의 값이 아홉 화면에 반복해서 나타나므로, 어디서 무엇으로 바뀌는지를 따로 모아 둡니다.

비율 — 폭에서 높이·여백을 되구하는 곳

01 은 폭을 흔든 뒤 getBoundingClientRect() 로 실제 높이를 되읽어 16:9 라벨을 채우고, 04 는 같은 폭 계산을 반대로 써서 남는 여백을 배경 흐림 강도로 바꿉니다. 두 화면 모두 CSS 가 이미 정한 비율을 JS 가 다시 재는 방식이라, 실제 유튜브 반응형 html 코드에서 aspect-ratio 로 처리하는 부분과 값의 흐름이 같습니다.

경과 시간 — 지연·클릭·동의 시점을 기준으로 삼는 곳

02 는 FACADE_DELAY(1200ms) 를 넘는 순간 lite 에서 frame 으로 상태를 바꾸고, 08 은 클릭 시점(PRESS_AT) 뒤 EXPAND_MS 동안, 09 는 동의 시점(CONSENT_AT) 뒤 CONSENT_FOLD_MS 동안 각각 높이를 보간합니다. 세 화면 모두 "그 시점 이전엔 정적, 이후엔 곡선" 이라는 같은 뼈대를 씁니다.

밝기 — 사인 주기 하나로 여러 속성을 같이 흔드는 곳

03·07 은 각각 배지·테두리의 밝기를 사인 곡선 하나로 구하고, 그 값을 크기(03)나 CSS 변수(07)에 그대로 얹습니다. 트위터 영상 임베드처럼 07 도 밝기 강조 하나만으로 카드 등장을 눈에 띄게 하고, 05 의 하트 팝은 사인형이 아니라 선형 감쇠지만 "하나의 진행값을 여러 속성에 나눠 쓴다" 는 계산 구조는 같습니다.

세 가지 변형과 reduced-motion 을 같이 넣는 이유

변형 바꾸는 값 바뀌는 느낌
라이트 파사드 전환 지연 02 FACADE_DELAY 1200ms → 3000ms 라이트 카드가 훨씬 오래 머물러, 진짜 로드를 기다리는 듯한 느낌이 강해집니다
동의 오버레이 접힘 시간 09 CONSENT_FOLD_MS 200ms → 500ms 안내 문구가 천천히 접히면서 높이가 늘어나는 과정이 눈에 더 잘 보입니다
세로 프레임 배경 흐림 강도 04 BG_BLUR_PX 8px → 24px 배경이 훨씬 흐려져 세로 프레임이 앞으로 튀어나오는 대비가 커집니다

reduced-motion 이 끄는 것과 남기는 것

아홉 화면 모두 prefers-reduced-motion 을 읽어, 켜져 있으면 apply(0) 한 번만 그리고 루프를 시작하지 않습니다. 08·09 처럼 실제 클릭·동의로 상태가 바뀌는 화면은 버튼에 키보드 포커스와 Enter/Space 입력을 함께 연결해, 마우스 없이도 같은 전환을 볼 수 있게 해 두었습니다.

임베드를 붙이기 전에 자주 나오는 질문

이 데모로 실제 유튜브 영상이 재생되나요?

아니요. 아홉 화면 모두 실제 iframe 이나 SDK 호출 없이 시간·클릭 값을 계산해 화면만 바꾸는 시뮬레이션입니다. 실제 재생이 필요하면 이 계산 위에 진짜 embed URL 을 얹어야 합니다.

라이트 파사드와 클릭 후 로드는 뭐가 다른가요?

02 는 시간이 지나면 저절로 완전한 프레임으로 넘어가고, 08 은 사용자가 실제로 눌러야만 높이 보간이 시작됩니다. 자동 전환이 필요하면 02, 데이터를 아끼면서 사용자 의도를 기다리려면 08 쪽 계산이 더 가깝습니다.

세로형 임베드나 재생 버튼까지 더 넣고 싶으면 어디를 보면 되나요?

세로 비율 계산을 더 다양한 화면비로 넓히려면 블로그 위젯 디자인 9종 의 카드 레이아웃을, 진행바 옆에 실제 재생·일시정지 버튼까지 붙이려면 동영상 플레이어 만들기 9종 을 함께 보면 이어서 만들 수 있습니다.

더 읽을거리

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

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