# 성화 봉송 인트로 이식 안내

받는 사람 : 「온라인 미니 올림픽」(laniakea1127.github.io/online-olympic) 을 만든 개발자와 그 AI

결론 : 파일 하나(`torch-intro.js`)를 복사하고 두 줄을 붙이면 끝난다. 기존 코드는 고칠 것이 없다.

---

## 무엇인가

초등학생 온라인 운동회 개회용 2D 도트 인트로. 캔버스 하나로 그린다.

흐름 : 밤 경기장 -> 남녀 어린이 두 명이 성화를 들고 달린다 -> 낮은 성화대에 불을 옮긴다
-> 성화대가 위로 솟아오른다 -> 불이 활활 타오른다 -> 문구가 뜬다
-> 아이가 「우리들의 올림픽! 시작」을 누르면 넘어간다

- 길이 : 약 11초. 그 뒤로는 **저절로 닫히지 않고** 마지막 화면이 계속 떠 있다. 아이가 버튼을 눌러야 넘어간다
- 달리는 동안은 건너뛰기 버튼이 있고, 아무 데나 눌러도 넘어감. Esc/Space/Enter 도 됨. 마지막 화면이 되면 건너뛰기는 사라지고 시작 버튼으로 바뀐다
- 색은 기존 사이트에서 뽑았다. 배경 `#0f1b2d`, 강조 `#ffc145`, 글꼴 Black Han Sans + Noto Sans KR
- 외부 라이브러리 0. 이미지 파일 0. 네트워크 요청은 구글 폰트뿐이고 그것도 없으면 기본 글꼴로 뜬다
- 소리 : 8비트 효과음(상승음 + 팡파레). WebAudio 라 오디오 파일이 없다

## 들어 있는 파일

| 파일 | 쓰임 |
|---|---|
| `torch-intro.js` | 이식할 본체. 이것만 복사하면 된다 |
| `index.html` | 미리보기용 데모. 이식할 필요 없다 |
| `이식-안내.md` | 이 문서 |

---

## 붙이는 법

### 1단계 : 파일 두기

- 정적 HTML 사이트면 : `index.html` 과 같은 폴더에 `torch-intro.js` 를 둔다
- Vite / CRA / Next 면 : `public/torch-intro.js` 에 둔다 (번들에 넣지 말고 그냥 정적 파일로)

### 2단계 : 스크립트 한 줄

`index.html` 의 `</body>` 바로 위에 넣는다.

```html
<script src="torch-intro.js"></script>
```

GitHub Pages 가 하위 경로(`/online-olympic/`)라서 반드시 상대 경로로 쓴다. `/torch-intro.js` 처럼 앞에 슬래시를 붙이면 404 가 난다.

### 3단계 : 부르기

**입장 버튼을 누르면 인트로가 돌고, 끝나면 원래 화면으로** 가 기본 설계다.
소리는 사용자가 클릭한 뒤에만 나는 브라우저 규칙이 있어서, 반드시 클릭 핸들러 안에서 불러야 한다.

정적 HTML :

```html
<script>
  document.querySelector('#enter-btn').addEventListener('click', function () {
    TorchIntro.play({
      onDone: function () {
        startOlympic();   // 원래 하던 입장 처리를 여기로 옮긴다
      }
    });
  });
</script>
```

React :

```jsx
function handleEnter() {
  if (window.TorchIntro) {
    window.TorchIntro.play({ onDone: () => setEntered(true) });
  } else {
    setEntered(true);   // 스크립트가 안 떴어도 앱은 막히지 않게
  }
}
```

`play()` 는 Promise 도 돌려준다. `await window.TorchIntro.play()` 로 써도 된다.

---

## 옵션

전부 선택이다. 아무것도 안 넘겨도 그대로 돈다.

| 옵션 | 기본값 | 설명 |
|---|---|---|
| `title` | `'온라인 미니 올림픽'` | 큰 제목 |
| `sub` | `'어디에 있든 우리는 같은 운동장'` | 둘째 줄 |
| `tag` | `''` | 셋째 줄. 비워 두면 안 뜬다 |
| `endLabel` | `'우리들의 올림픽! 시작'` | 마지막 화면 버튼 글자. `''` 로 두면 버튼 없이 저절로 닫힌다 |
| `onDone` | `null` | 시작 버튼을 눌렀거나 건너뛰었을 때 호출. 여기서 원래 화면을 띄운다 |
| `speed` | `100` | 주자 속도(px/s). 낮추면 인트로가 길어진다 |
| `sound` | `true` | 효과음 |
| `skipButton` | `true` | 오른쪽 위 건너뛰기 버튼 |
| `clickToSkip` | `true` | 화면 아무 데나 눌러도 넘어감 |
| `once` | `false` | `true` 면 이 브라우저에서 한 번만 재생. 두 번째부터는 바로 `onDone` |
| `storageKey` | `'torchIntroSeen'` | `once` 가 쓰는 localStorage 키 |
| `loadFonts` | `true` | 구글 폰트를 알아서 부른다. 이미 부르고 있으면 중복으로 안 넣는다 |
| `mount` | `null` | `null` 이면 화면 전체를 덮는다. 엘리먼트를 주면 그 안에만 그린다 (그 엘리먼트에 `position:relative` 필요) |
| `zIndex` | `99999` | 덮개 z-index. 사이트 헤더보다 높아야 한다 |

아이들이 매번 보기 지루해할 것 같으면 `once: true` 를 켠다. 다만 처음 한 번의 감동이 목적이므로 기본은 꺼 두었다.

---

## 확인할 것

붙인 뒤 이것만 보면 된다.

- 입장 버튼을 누르면 검은 밤 경기장이 뜨고 아이 둘이 오른쪽으로 달린다
- 낮은 성화대 앞에 서고, 불이 옮겨 붙고, 성화대가 위로 솟는다
- 큰 불꽃이 타오르고 그 아래에 제목과 「우리들의 올림픽! 시작」 버튼이 뜬다
- 그 화면은 그대로 머문다. 버튼을 눌러야 사라지고 `onDone` 이 불린다
- 달리는 도중 건너뛰기 버튼을 누르면 즉시 `onDone` 이 불린다
- 휴대폰 세로 화면에서도 16:9 로 가운데 맞춰 들어간다

## 주의할 것

- `onDone` 을 안 넘기면 인트로만 돌고 끝난다. 원래 입장 처리를 반드시 `onDone` 안으로 옮긴다
- 인트로가 도는 동안 덮개가 화면 전체를 막는다. 뒤쪽 화면을 미리 그려 두면 사라질 때 자연스럽다
- `prefers-reduced-motion` 을 켠 사용자에게는 달리는 장면을 건너뛰고 마지막 화면만 잠깐 보여준다. 일부러 그렇게 했다
- 캔버스 내부 해상도는 192x108 고정이다. 화면 크기에 맞춰 정수배로 확대되므로 도트가 뭉개지지 않는다. 이 값을 바꾸면 좌표가 전부 틀어진다
- 그림 좌표는 `torch-intro.js` 위쪽 상수로 모여 있다. `RIM_LOW`(불붙이는 높이), `RIM_HIGH`(다 솟았을 때 높이), `RISE_SEC`(솟는 시간), `CX`(성화대 위치)
- `torch-intro.js` 를 고쳐 올릴 때는 `<script src="torch-intro.js?v=2">` 의 숫자를 올린다. 안 그러면 브라우저가 옛 파일을 계속 쓴다

## 고치고 싶을 때

- 주자 옷 색 : `PAL_GIRL`, `PAL_BOY` 의 `j`(상의), `p`(하의), `h`(머리)
- 주자 모양 : `UPPER_GIRL`, `UPPER_BOY`, `LEGS` 의 문자 그림. 한 글자가 도트 하나다
- 성화대가 솟는 높이 : `RIM_HIGH` 를 줄이면 더 높이 솟는다. 너무 줄이면 불꽃 끝이 화면 위로 잘린다
- 불꽃 크기 : `flame(sx, rim - 2, 17 * lit, 28 * lit, t)` 의 두 숫자 (너비, 높이)
