Files
2nd/10_Wiki/Topics/Frontend/OffscreenCanvas Safari 제약 사항.md
T
2026-05-10 22:08:15 +09:00

5.8 KiB

id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, tags, raw_sources, last_reinforced, github_commit, tech_stack
id title category status canonical_id aliases duplicate_of source_trust_level confidence_score verification_status tags raw_sources last_reinforced github_commit tech_stack
wiki-2026-0508-offscreencanvas-safari-제약-사항 OffscreenCanvas Safari 제약 사항 10_Wiki/Topics verified self
OffscreenCanvas Safari
Safari OffscreenCanvas Limits
OffscreenCanvas WebKit
none A 0.9 applied
canvas
safari
webkit
browser-compat
offscreencanvas
2026-05-10 pending
language framework
JavaScript Browser

OffscreenCanvas Safari 제약 사항

매 한 줄

"매 Safari OffscreenCanvas 의 partial support". Safari 16.4+ 매 2D context 만 지원, WebGL/WebGPU OffscreenCanvas 는 매 17.0+ 부터 점진. iOS Safari 매 worker context 의 transferControlToOffscreen 동작 차이 + Background tab throttling 매 강함. 2026 기준 Chrome/Firefox 와 매 feature parity 거의 도달, 매 edge case 잔존.

매 핵심

매 Safari 의 timeline

  • Safari 16.4 (2023): OffscreenCanvas 2D context.
  • Safari 17.0 (2023): WebGL OffscreenCanvas in worker.
  • Safari 18.x (2025): WebGPU OffscreenCanvas (experimental flag).
  • iOS Safari 매 desktop 의 1-2 minor 늦게 따라옴.

매 주요 제약

  • Background tab throttling: 매 worker animation frame 매 1Hz 까지 drop (Chrome/Firefox 보다 강한 throttle).
  • Memory pressure: iOS 매 background tab 의 OffscreenCanvas 즉시 release — 매 reload 후 lost-context 처리 필요.
  • ImageBitmap transfer: 매 large bitmap 의 transferToImageBitmap 매 occasional jank — Chrome 보다 느림.
  • WebGL2 context loss: iOS Safari 매 page-hide 즉시 lost.
  • Worker termination: 매 page navigation 시 Safari 매 worker cleanup 의 timing 차이.

매 응용

  1. Image processing (Photopea-style) — main thread 차단 회피.
  2. Chart rendering (Plotly, Apache ECharts worker mode).
  3. Game offscreen rendering.
  4. PDF/canvas thumbnail 생성.

💻 패턴

Feature detection (매 graceful fallback)

function supportsOffscreen2D(): boolean {
  return typeof OffscreenCanvas !== "undefined"
    && typeof new OffscreenCanvas(1, 1).getContext === "function";
}

function supportsOffscreenWebGL(): boolean {
  if (!supportsOffscreen2D()) return false;
  try {
    return !!new OffscreenCanvas(1, 1).getContext("webgl2");
  } catch {
    return false;
  }
}

Safari-safe transferControlToOffscreen

const canvas = document.querySelector("canvas")!;
let offscreen: OffscreenCanvas | null = null;
let worker: Worker | null = null;

function start() {
  if (!supportsOffscreen2D()) {
    renderOnMainThread(canvas);
    return;
  }
  offscreen = canvas.transferControlToOffscreen();
  worker = new Worker(new URL("./renderer.worker.ts", import.meta.url), { type: "module" });
  worker.postMessage({ type: "init", canvas: offscreen }, [offscreen]);
}

Lost context handling (매 iOS 필수)

// renderer.worker.ts
self.addEventListener("message", (e) => {
  if (e.data.type === "init") {
    const canvas = e.data.canvas as OffscreenCanvas;
    let gl = canvas.getContext("webgl2");

    canvas.addEventListener?.("webglcontextlost", (ev) => {
      ev.preventDefault();
      console.warn("WebGL context lost (Safari background?)");
    });
    canvas.addEventListener?.("webglcontextrestored", () => {
      gl = canvas.getContext("webgl2");
      reinitScene();
    });
  }
});

Visibility-aware rendering (매 throttle 회피)

let isVisible = !document.hidden;
document.addEventListener("visibilitychange", () => {
  isVisible = !document.hidden;
  worker?.postMessage({ type: "visibility", visible: isVisible });
});

// In worker:
let raf: number;
function loop() {
  draw();
  raf = self.requestAnimationFrame?.(loop) ?? setTimeout(loop, 16);
}
self.addEventListener("message", (e) => {
  if (e.data.type === "visibility") {
    if (e.data.visible) loop();
    else cancelAnimationFrame?.(raf);
  }
});

ImageBitmap chunked transfer (매 Safari jank 회피)

async function processInChunks(blob: Blob, chunkSize = 1024) {
  const bitmap = await createImageBitmap(blob);
  for (let y = 0; y < bitmap.height; y += chunkSize) {
    const chunk = await createImageBitmap(bitmap, 0, y, bitmap.width, Math.min(chunkSize, bitmap.height - y));
    worker.postMessage({ type: "chunk", chunk, y }, [chunk]);
    await new Promise(r => setTimeout(r, 0)); // yield to UI
  }
  bitmap.close();
}

매 결정 기준

상황 Approach
2D rendering, modern browser OffscreenCanvas + worker
WebGL on iOS Safari feature-detect + main-thread fallback
Long-running animation visibilitychange 의 pause
Large bitmap chunked transfer
Production, broad support progressive enhancement, never required

기본값: feature-detect + 매 main-thread fallback path 매 항상 유지.

🔗 Graph

🤖 LLM 활용

언제: heavy canvas 작업의 worker 위임 + Safari/iOS target. 언제 X: simple animation / 매 main thread 충분.

안티패턴

  • No fallback: 매 OffscreenCanvas 의 mandatory 가정 — 매 old Safari user crash.
  • Ignore context lost: 매 iOS background → resume 후 매 black canvas.
  • Throttle 무시: 매 background tab 의 animation 매 progress 가정 — 매 desync.
  • Sync transferToImageBitmap loop: 매 매 frame transfer — 매 GC pressure.

🧪 검증 / 중복

  • Verified (caniuse OffscreenCanvas, WebKit blog 16.4/17.0/18, MDN OffscreenCanvas).
  • 신뢰도 A.

🕓 Changelog

날짜 변경
2026-05-08 Phase 1
2026-05-10 Manual cleanup — Safari timeline, throttling/lost-context/chunked-transfer 패턴