|
@@ -4,8 +4,6 @@ TOC: 웹 워커에서 OffscreenCanvas 사용하기
|
|
|
|
|
|
[`OffscreenCanvas`](https://developer.mozilla.org/ko/docs/Web/API/OffscreenCanvas)는 비교적 최근 도입된 브라우저 API로 아직 크로미움 기반 브라우저에서만 사용가능하지만, 갈수록 대부분의 브라우저에서 이 API를 사용할 수 있을 겁니다. `OffscreenCanvas`를 이용하면 [웹 워커(Web Worker)](https://developer.mozilla.org/ko/docs/Web/API/Web_Workers_API)에서 캔버스를 렌더링해 복잡한 3D 장면 등의 무거운 작업을 별도 프로세스에서 처리할 수 있습니다. 이러면 무거운 작업을 처리할 때 브라우저가 덜 버벅이도록 할 수 있죠. 또한 데이터도 워커에서 불러와 처리하므로 페이지 초기 로드 시 버벅임을 훨씬 줄일 수 있습니다.
|
|
[`OffscreenCanvas`](https://developer.mozilla.org/ko/docs/Web/API/OffscreenCanvas)는 비교적 최근 도입된 브라우저 API로 아직 크로미움 기반 브라우저에서만 사용가능하지만, 갈수록 대부분의 브라우저에서 이 API를 사용할 수 있을 겁니다. `OffscreenCanvas`를 이용하면 [웹 워커(Web Worker)](https://developer.mozilla.org/ko/docs/Web/API/Web_Workers_API)에서 캔버스를 렌더링해 복잡한 3D 장면 등의 무거운 작업을 별도 프로세스에서 처리할 수 있습니다. 이러면 무거운 작업을 처리할 때 브라우저가 덜 버벅이도록 할 수 있죠. 또한 데이터도 워커에서 불러와 처리하므로 페이지 초기 로드 시 버벅임을 훨씬 줄일 수 있습니다.
|
|
|
|
|
|
-OffscreenCanvas의 문제점 중 하나는 웹 워커가 아직 ES 모듈(import, export 키워드 등)을 지원하지 않는다는 겁니다. 때문에 다른 예제와 달리 이 글에서는 클래스 스크립트 방식을 사용할 겁니다.
|
|
|
|
-
|
|
|
|
사용법은 꽤나 직관적입니다. 먼저 [반응형 디자인에 관한 글](threejs-responsive.html)에서 썼던 예제를 가져오도록 하죠.
|
|
사용법은 꽤나 직관적입니다. 먼저 [반응형 디자인에 관한 글](threejs-responsive.html)에서 썼던 예제를 가져오도록 하죠.
|
|
|
|
|
|
이 사이트 대부분의 예제는 스크립트를 해당 HTML 파일에 인라인으로 작성했습니다. 반면에 워커는 일반적으로 별도의 스크립트 파일로 분리해 작성합니다.
|
|
이 사이트 대부분의 예제는 스크립트를 해당 HTML 파일에 인라인으로 작성했습니다. 반면에 워커는 일반적으로 별도의 스크립트 파일로 분리해 작성합니다.
|
|
@@ -22,13 +20,13 @@ function main() {
|
|
...
|
|
...
|
|
```
|
|
```
|
|
|
|
|
|
-그리고 `new Worker(워커 스크립트 경로)`로 워커를 생성한 뒤, 워커에 `offscreen` 객체를 넘깁니다.
|
|
|
|
|
|
+그리고 `new Worker(워커 스크립트 경로, { type: 'module' })`로 워커를 생성한 뒤, 워커에 `offscreen` 객체를 넘깁니다.
|
|
|
|
|
|
```js
|
|
```js
|
|
function main() {
|
|
function main() {
|
|
const canvas = document.querySelector('#c');
|
|
const canvas = document.querySelector('#c');
|
|
const offscreen = canvas.transferControlToOffscreen();
|
|
const offscreen = canvas.transferControlToOffscreen();
|
|
- const worker = new Worker('offscreencanvas-cubes.js');
|
|
|
|
|
|
+ const worker = new Worker('offscreencanvas-cubes.js', { type: 'module' });
|
|
worker.postMessage({ type: 'main', canvas: offscreen }, [ offscreen ]);
|
|
worker.postMessage({ type: 'main', canvas: offscreen }, [ offscreen ]);
|
|
}
|
|
}
|
|
main();
|
|
main();
|
|
@@ -38,7 +36,7 @@ main();
|
|
|
|
|
|
워커에 메시지를 보내려면 [`worker.postMessage`](https://developer.mozilla.org/ko/docs/Web/API/Worker/postMessage)에 하나 또는 두 개의 인자를 넘겨주어 호출하면 됩니다. 첫 번째 인자는 워커에 전달할 객체로, 이 객체는 그대로 전달되지 않고 [복사됩니다](https://developer.mozilla.org/ko/docs/Web/API/Web_Workers_API/Structured_clone_algorithm). 두 번째 인자는 옵션으로 첫 번째 인자 중 그대로 *전달하기* 원하는 객체를 배열로 지정합니다. 여기에 지정한 객체는 복사되지 않죠. 그대로 워커에 *전달되지만* 객체를 보낸 스크립트에서는 더 이상 사용이 불가능해집니다. 이것도 모든 객체를 전달할 수 있는 게 아니라 특정 타입의 객체만을 전달할 수 있죠. 당연하게도 이 중에는 `OffscreenCanvas`도 있습니다. 정리하자면 `offscreen` 객체를 전달하고 나면 이 객체는 이 스크립트에서 더 이상 쓸모가 없어집니다.
|
|
워커에 메시지를 보내려면 [`worker.postMessage`](https://developer.mozilla.org/ko/docs/Web/API/Worker/postMessage)에 하나 또는 두 개의 인자를 넘겨주어 호출하면 됩니다. 첫 번째 인자는 워커에 전달할 객체로, 이 객체는 그대로 전달되지 않고 [복사됩니다](https://developer.mozilla.org/ko/docs/Web/API/Web_Workers_API/Structured_clone_algorithm). 두 번째 인자는 옵션으로 첫 번째 인자 중 그대로 *전달하기* 원하는 객체를 배열로 지정합니다. 여기에 지정한 객체는 복사되지 않죠. 그대로 워커에 *전달되지만* 객체를 보낸 스크립트에서는 더 이상 사용이 불가능해집니다. 이것도 모든 객체를 전달할 수 있는 게 아니라 특정 타입의 객체만을 전달할 수 있죠. 당연하게도 이 중에는 `OffscreenCanvas`도 있습니다. 정리하자면 `offscreen` 객체를 전달하고 나면 이 객체는 이 스크립트에서 더 이상 쓸모가 없어집니다.
|
|
|
|
|
|
-워커의 `message` 이벤트를 이용하면 메시지를 받을 수 있습니다. `postMessage`에서 넘긴 객체는 `event.data`에 담겨 리스너에 전달되죠. 아까 위 코드에서는 `type: 'main'` 속성을 객체에 선언해 워커에 넘겨줬습니다. 이 `type` 속성으로 워커 내에 다른 함수를 호출하도록 해봅시다. 이러면 메인 스크립트에서 워커 내의 함수를 호출하기가 훨씬 쉬워질 겁니다.
|
|
|
|
|
|
+워커의 `message` 이벤트를 이용하면 메시지를 받을 수 있습니다. `postMessage`에서 넘긴 객체는 `event.data`에 담겨 리스너에 전달되죠. 아까 위 코드에서는 `type: 'main'` 속성을 객체에 선언해 워커에 넘겨줬습니다. 이 `type` 속성은 브라우저의 메인 스레드에서는 쓸 일이 없는 값으로, 워커 내에 다른 함수를 호출하는 키값으로 사용할 겁니다. 이러면 메인 스크립트에서 워커 내의 함수를 호출하기가 훨씬 쉬워지겠죠.
|
|
|
|
|
|
```js
|
|
```js
|
|
const handlers = {
|
|
const handlers = {
|
|
@@ -58,13 +56,7 @@ self.onmessage = function(e) {
|
|
|
|
|
|
이제 [반응형 디자인에 관한 글](threejs-responsive.html)에서 가져온 예제의 `main` 함수를 수정해야 합니다.
|
|
이제 [반응형 디자인에 관한 글](threejs-responsive.html)에서 가져온 예제의 `main` 함수를 수정해야 합니다.
|
|
|
|
|
|
-물론 그전에 먼저 Three.js를 `offscreencanvas-cubes.js`에 불러와야겠죠.
|
|
|
|
-
|
|
|
|
-```js
|
|
|
|
-importScripts('resources/threejs/r119/build/three.min.js');
|
|
|
|
-```
|
|
|
|
-
|
|
|
|
-DOM에서 캔버스에 접근하는 대신 이벤트의 `data`에서 캔버스를 받습니다.
|
|
|
|
|
|
+DOM에서 캔버스에 접근하는 대신 이벤트의 `data` 속성에서 캔버스 요소를 받도록 합니다.
|
|
|
|
|
|
```js
|
|
```js
|
|
-function main() {
|
|
-function main() {
|
|
@@ -150,7 +142,7 @@ function render(time) {
|
|
메인 스크립트로 돌아와 페이지 크기가 바뀔 때마다 워커의 `size` 함수를 실행하도록 합니다.
|
|
메인 스크립트로 돌아와 페이지 크기가 바뀔 때마다 워커의 `size` 함수를 실행하도록 합니다.
|
|
|
|
|
|
```js
|
|
```js
|
|
-const worker = new Worker('offscreencanvas-picking.js');
|
|
|
|
|
|
+const worker = new Worker('offscreencanvas-picking.js', { type: 'module' });
|
|
worker.postMessage({ type: 'main', canvas: offscreen }, [ offscreen ]);
|
|
worker.postMessage({ type: 'main', canvas: offscreen }, [ offscreen ]);
|
|
|
|
|
|
+function sendSize() {
|
|
+function sendSize() {
|
|
@@ -203,7 +195,7 @@ function main() {
|
|
+ return;
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
const offscreen = canvas.transferControlToOffscreen();
|
|
const offscreen = canvas.transferControlToOffscreen();
|
|
- const worker = new Worker('offscreencanvas-picking.js');
|
|
|
|
|
|
+ const worker = new Worker('offscreencanvas-picking.js'. { type: 'module' });
|
|
worker.postMessage({ type: 'main', canvas: offscreen }, [ offscreen ]);
|
|
worker.postMessage({ type: 'main', canvas: offscreen }, [ offscreen ]);
|
|
|
|
|
|
...
|
|
...
|
|
@@ -231,17 +223,49 @@ function main() {
|
|
|
|
|
|
`offscreencanvas-worker-cubes.js`
|
|
`offscreencanvas-worker-cubes.js`
|
|
|
|
|
|
-`shared-cubes.js`와 `offscreencanvas-worker-cubes.js`는 단순히 이전 `offscreencanvas-cubes.js` 파일을 쪼갠 것입니다. 메인 HTML 파일에 `main`이라는 함수가 있어 `main` 함수만 `init`으로 바꿨죠.
|
|
|
|
|
|
+`shared-cubes.js`와 `offscreencanvas-worker-cubes.js`는 단순히 이전 `offscreencanvas-cubes.js` 파일을 쪼갠 것입니다. 먼저 `offscreencanvas-cube.js`를 `shared-cube.js`로 옮긴 뒤, 메인 HTML 파일에 이미 `main` 함수가 있어 `main` 함수의 이름만 `init`으로 바꿔야 하죠. 여기에 추가로 `init`과 `state` 함수를 export 시켜줘야 합니다.
|
|
|
|
|
|
-`offscreencanvas-worker-cubes.js`의 구조는 아주 단순합니다.
|
|
|
|
|
|
+```js
|
|
|
|
+import * as THREE from './resources/threejs/r119/build/three.module.js';
|
|
|
|
+
|
|
|
|
+-const state = {
|
|
|
|
++export const state = {
|
|
|
|
+ width: 300, // 캔버스 기본값
|
|
|
|
+ height: 150, // 캔버스 기본값
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+-function main(data) {
|
|
|
|
++export function init(data) {
|
|
|
|
+ const { canvas } = data;
|
|
|
|
+ const renderer = new THREE.WebGLRenderer({ canvas });
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+그리고 Three.js와 관련 없는 부분을 잘라냅니다.
|
|
|
|
|
|
```js
|
|
```js
|
|
-'use strict';
|
|
|
|
|
|
+-function size(data) {
|
|
|
|
+- state.width = data.width;
|
|
|
|
+- state.height = data.height;
|
|
|
|
+-}
|
|
|
|
+-
|
|
|
|
+-const handlers = {
|
|
|
|
+- main,
|
|
|
|
+- size,
|
|
|
|
+-};
|
|
|
|
+-
|
|
|
|
+-self.onmessage = function(e) {
|
|
|
|
+- const fn = handlers[e.data.type];
|
|
|
|
+- if (!fn) {
|
|
|
|
+- throw new Error('no handler for type: ' + e.data.type);
|
|
|
|
+- }
|
|
|
|
+- fn(e.data);
|
|
|
|
+-};
|
|
|
|
+```
|
|
|
|
|
|
-/* global importScripts, init, state */
|
|
|
|
|
|
+방금 잘라낸 부분을 `offscreencanvas-worker-cubes.js`에 붙여넣고, `shared-cubes.js`를 import 합니다. 또한 `main` 대신 `init`을 호출하도록 합니다.
|
|
|
|
|
|
-importScripts('resources/threejs/r119/build/three.min.js');
|
|
|
|
-+importScripts('shared-cubes.js');
|
|
|
|
|
|
+```js
|
|
|
|
+import { init, state } from './shared-cubes.js';
|
|
|
|
|
|
function size(data) {
|
|
function size(data) {
|
|
state.width = data.width;
|
|
state.width = data.width;
|
|
@@ -263,13 +287,11 @@ self.onmessage = function(e) {
|
|
};
|
|
};
|
|
```
|
|
```
|
|
|
|
|
|
-위 코드에서는 Three.js 관련 스크립트인 `shared-cubes.js`를 불러왔습니다.
|
|
|
|
-
|
|
|
|
메인 페이지에서도 마찬가지로 Three.js와 `shared-cubes.js`를 추가합니다.
|
|
메인 페이지에서도 마찬가지로 Three.js와 `shared-cubes.js`를 추가합니다.
|
|
|
|
|
|
```html
|
|
```html
|
|
-<script src="resources/threejs/r119/build/three.min.js"></script>
|
|
|
|
-<script src="shared-cubes.js"><script>
|
|
|
|
|
|
+<script type="module"></script>
|
|
|
|
++import { init, state } from './shared-cubes.js';
|
|
```
|
|
```
|
|
|
|
|
|
이전에 추가했던 에러 메시지용 HTML과 CSS를 제거합니다.
|
|
이전에 추가했던 에러 메시지용 HTML과 CSS를 제거합니다.
|
|
@@ -306,7 +328,7 @@ function main() {
|
|
- return;
|
|
- return;
|
|
- }
|
|
- }
|
|
- const offscreen = canvas.transferControlToOffscreen();
|
|
- const offscreen = canvas.transferControlToOffscreen();
|
|
-- const worker = new Worker('offscreencanvas-picking.js');
|
|
|
|
|
|
+- const worker = new Worker('offscreencanvas-picking.js', { type: 'module' });
|
|
- worker.postMessage({ type: 'main', canvas: offscreen }, [ offscreen ]);
|
|
- worker.postMessage({ type: 'main', canvas: offscreen }, [ offscreen ]);
|
|
+ if (canvas.transferControlToOffscreen) {
|
|
+ if (canvas.transferControlToOffscreen) {
|
|
+ startWorker(canvas);
|
|
+ startWorker(canvas);
|
|
@@ -321,8 +343,8 @@ function main() {
|
|
```js
|
|
```js
|
|
function startWorker(canvas) {
|
|
function startWorker(canvas) {
|
|
const offscreen = canvas.transferControlToOffscreen();
|
|
const offscreen = canvas.transferControlToOffscreen();
|
|
- const worker = new Worker('offscreencanvas-worker-cubes.js');
|
|
|
|
- worker.postMessage({type: 'init', canvas: offscreen}, [offscreen]);
|
|
|
|
|
|
+ const worker = new Worker('offscreencanvas-worker-cubes.js', { type: 'module' });
|
|
|
|
+ worker.postMessage({ type: 'main', canvas: offscreen }, [ offscreen ]);
|
|
|
|
|
|
function sendSize() {
|
|
function sendSize() {
|
|
worker.postMessage({
|
|
worker.postMessage({
|
|
@@ -339,6 +361,13 @@ function startWorker(canvas) {
|
|
}
|
|
}
|
|
```
|
|
```
|
|
|
|
|
|
|
|
+메시지에 `'main'` 대신 `'init'`을 보냅니다.
|
|
|
|
+
|
|
|
|
+```js
|
|
|
|
+- worker.postMessage({ type: 'main', canvas: offscreen }, [ offscreen ]);
|
|
|
|
++ worker.postMessage({ type: 'init', canvas: offscreen }, [ offscreen ]);
|
|
|
|
+```
|
|
|
|
+
|
|
워커를 사용할 수 없는 경우 다음과 같이 실행합니다.
|
|
워커를 사용할 수 없는 경우 다음과 같이 실행합니다.
|
|
|
|
|
|
```js
|
|
```js
|
|
@@ -451,7 +480,7 @@ self.onmessage = function(e) {
|
|
|
|
|
|
function startWorker(canvas) {
|
|
function startWorker(canvas) {
|
|
const offscreen = canvas.transferControlToOffscreen();
|
|
const offscreen = canvas.transferControlToOffscreen();
|
|
- const worker = new Worker('offscreencanvas-worker-picking.js');
|
|
|
|
|
|
+ const worker = new Worker('offscreencanvas-worker-picking.js', { type: 'module' });
|
|
worker.postMessage({ type: 'init', canvas: offscreen }, [ offscreen ]);
|
|
worker.postMessage({ type: 'init', canvas: offscreen }, [ offscreen ]);
|
|
|
|
|
|
+ sendMouse = (x, y) => {
|
|
+ sendMouse = (x, y) => {
|
|
@@ -541,7 +570,7 @@ window.addEventListener('touchend', clearPickPosition);
|
|
|
|
|
|
좀 더 욕심을 내 `OrbitControls`까지 추가해봅시다. `OrbitControls`는 DOM에 꽤 다양하게 접근하기에 처리해줘야 할 것이 좀 많습니다. 제대로 작동하려면 마우스 이벤트, 터치 이벤트, 키보드 이벤트를 모두 처리해줘야 하죠.
|
|
좀 더 욕심을 내 `OrbitControls`까지 추가해봅시다. `OrbitControls`는 DOM에 꽤 다양하게 접근하기에 처리해줘야 할 것이 좀 많습니다. 제대로 작동하려면 마우스 이벤트, 터치 이벤트, 키보드 이벤트를 모두 처리해줘야 하죠.
|
|
|
|
|
|
-여태까지는 전역 `state` 객체를 사용했지만, `OrbitControls`의 경우는 객체 속성이 너무 많아 그걸 전부 다 하드 코딩하는 건 너무 번거롭습니다. `OrbitControls`는 필요한 DOM 이벤트의 대부분을 인자로 받는 HTML 요소에 바인딩합니다. 이를 이용해 DOM 요소와 같은 구조의 객체를 넘겨준다면 어떨까요? `OrbitControls`에 필요한 기능만 살려서 말이죠.
|
|
|
|
|
|
+여태까지는 전역 `state` 객체를 사용했지만, `OrbitControls`의 경우는 객체 속성이 너무 많아 그걸 전부 다 하드 코딩하는 건 너무 번거롭습니다. `OrbitControls`는 필요한 DOM 이벤트의 대부분을 인자로 받는 `HTMLElement`에 바인딩합니다. 이를 이용해 DOM 요소와 같은 구조의 객체를 넘겨준다면 어떨까요? `OrbitControls`에 필요한 기능만 살려서 말이죠.
|
|
|
|
|
|
[`OrbitControls`의 소스 코드](https://github.com/gfxfundamentals/threejsfundamentals/blob/master/threejs/resources/threejs/r119/examples/js/controls/OrbitControls.js)를 분석해보니 아래의 이벤트만 지원하면 될 듯합니다.
|
|
[`OrbitControls`의 소스 코드](https://github.com/gfxfundamentals/threejsfundamentals/blob/master/threejs/resources/threejs/r119/examples/js/controls/OrbitControls.js)를 분석해보니 아래의 이벤트만 지원하면 될 듯합니다.
|
|
|
|
|
|
@@ -568,7 +597,9 @@ wheel 이벤트는 `deltaY` 속성만,
|
|
아래는 워커 안의 코드입니다.
|
|
아래는 워커 안의 코드입니다.
|
|
|
|
|
|
```js
|
|
```js
|
|
-class ElementProxyReceiver extends THREE.EventDispatcher {
|
|
|
|
|
|
+import { EventDispatcher } from './resources/threejs/r119/build/three.module.js';
|
|
|
|
+
|
|
|
|
+class ElementProxyReceiver extends EventDispatcher {
|
|
constructor() {
|
|
constructor() {
|
|
super();
|
|
super();
|
|
}
|
|
}
|
|
@@ -641,18 +672,13 @@ self.onmessage = function(e) {
|
|
};
|
|
};
|
|
```
|
|
```
|
|
|
|
|
|
-`OrbitControls` 모듈도 불러와야겠죠.
|
|
|
|
|
|
+Three.js의 공통 코드에 `OrbitControls` 모듈도 불러와 설정해야 합니다.
|
|
|
|
|
|
```js
|
|
```js
|
|
-importScripts('resources/threejs/r119/build/three.min/js');
|
|
|
|
-+importScripts('resources/threejs/r119/examples/js/controls/OrbitControls.js');
|
|
|
|
-*importScripts('shared-orbitcontrols.js');
|
|
|
|
-```
|
|
|
|
-
|
|
|
|
-그리고 공통 Three.js 코드를 아래와 같이 수정합니다.
|
|
|
|
|
|
+import * as THREE from './resources/threejs/r119/build/three.module.js';
|
|
|
|
++import { OrbitControls } from './resources/threejs/r119/examples/jsm/controls/OrbitControls.js';
|
|
|
|
|
|
-```js
|
|
|
|
-function init(data) {
|
|
|
|
|
|
+export function init(data) {
|
|
- const { canvas } = data;
|
|
- const { canvas } = data;
|
|
+ const { canvas, inputElement } = data;
|
|
+ const { canvas, inputElement } = data;
|
|
const renderer = new THREE.WebGLRenderer({ canvas });
|
|
const renderer = new THREE.WebGLRenderer({ canvas });
|
|
@@ -752,7 +778,7 @@ class ElementProxy {
|
|
```js
|
|
```js
|
|
function startWorker(canvas) {
|
|
function startWorker(canvas) {
|
|
const offscreen = canvas.transferControlToOffscreen();
|
|
const offscreen = canvas.transferControlToOffscreen();
|
|
- const worker = new Worker('offscreencanvas-worker-orbitcontrols.js');
|
|
|
|
|
|
+ const worker = new Worker('offscreencanvas-worker-orbitcontrols.js', { type: 'module' });
|
|
|
|
|
|
+ const eventHandlers = {
|
|
+ const eventHandlers = {
|
|
+ contextmenu: preventDefaultHandler,
|
|
+ contextmenu: preventDefaultHandler,
|
|
@@ -979,7 +1005,7 @@ class ElementProxy {
|
|
이제 공통 Three.js 코드에서 `state` 전역 변수를 쓰지 않으니 삭제합니다.
|
|
이제 공통 Three.js 코드에서 `state` 전역 변수를 쓰지 않으니 삭제합니다.
|
|
|
|
|
|
```js
|
|
```js
|
|
--const state = {
|
|
|
|
|
|
+-export const state = {
|
|
- width: 300, // 캔버스 기본값
|
|
- width: 300, // 캔버스 기본값
|
|
- height: 150, // 캔버스 기본값
|
|
- height: 150, // 캔버스 기본값
|
|
-};
|
|
-};
|
|
@@ -1011,22 +1037,17 @@ function render(time) {
|
|
...
|
|
...
|
|
```
|
|
```
|
|
|
|
|
|
-`OrbitControls`는 키보드 이벤트를 감지하기 위해 `window`에 이벤트 리스너를 바인딩합니다. 이게 좋은 방식인지는 모르겠지만, 소스 코드를 직접 수정하지 않는 한 이 동작을 고칠 수는 없습니다. 워커에는 `window` 객체가 존재하지 않거든요.
|
|
|
|
|
|
+`OrbitControls`는 마우스 이벤트를 감지하기 위해 해당 요소의 `ownerDocument`에 `mousemove`와 `mouseup` 리스너를 추가합니다(마우스가 창 밖으로 나갔을 경우를 위해).
|
|
|
|
|
|
-또 `document`의 `mousemove`와 `mouseup` 이벤트도 사용합니다. 이는 마우스가 창 밖으로 나간다든가 하는 경우에도 마우스의 움직임을 감지하기 위한 것이지만, `window`와 마찬가지로 워커에는 `document` 객체가 없습니다.
|
|
|
|
-
|
|
|
|
-거기다 `OrbitControls`에 넘겨준 요소가 `document`와 일치하는지의 여부도 검사합니다.
|
|
|
|
|
|
+또한 코드는 전역 `document` 객체를 참조하지만 워커에는 전역 `document` 객체가 없습니다.
|
|
|
|
|
|
이 문제는 간단한 편법(hack)을 써 해결할 수 있습니다. 다시 한 번 워커의 경유 객체를 이용하도록 하죠.
|
|
이 문제는 간단한 편법(hack)을 써 해결할 수 있습니다. 다시 한 번 워커의 경유 객체를 이용하도록 하죠.
|
|
|
|
|
|
```js
|
|
```js
|
|
function start(data) {
|
|
function start(data) {
|
|
const proxy = proxyManager.getProxy(data.canvasId);
|
|
const proxy = proxyManager.getProxy(data.canvasId);
|
|
-+ self.window = proxy; // HACK!
|
|
|
|
-+ self.document = { // HACK!
|
|
|
|
-+ addEventListener: proxy.addEventListener.bind(proxy),
|
|
|
|
-+ removeEventListener: proxy.removeEventListener.bind(proxy),
|
|
|
|
-+ };
|
|
|
|
|
|
++ proxy.ownerDocument = proxy; // HACK!
|
|
|
|
++ self.document = {} // HACK!
|
|
init({
|
|
init({
|
|
canvas: data.canvas,
|
|
canvas: data.canvas,
|
|
inputElement: proxy,
|
|
inputElement: proxy,
|