Skip to content

Commit 03b6202

Browse files
committed
fix: improve the way to use ngZone.runOutsideAngular() in time, screen-size and pointer service
1 parent eaea9d0 commit 03b6202

File tree

5 files changed

+56
-23
lines changed

5 files changed

+56
-23
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,4 @@ Thumbs.db
4343

4444
# Custom
4545
documentation
46+
pnpm-lock.yaml

src/app/three-viewer/engine/service/pointer/pointer.service.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Injectable, NgZone } from '@angular/core';
1+
import { Injectable, NgZone, OnDestroy, Renderer2, RendererFactory2 } from '@angular/core';
22
import { Consumer } from '../type/Consumer';
33

44
/**
@@ -7,23 +7,33 @@ import { Consumer } from '../type/Consumer';
77
@Injectable({
88
providedIn: 'root'
99
})
10-
export class PointerService {
10+
export class PointerService implements OnDestroy {
11+
12+
13+
private unlisten: Function | undefined;
1114

1215
/**
1316
* Constructor
1417
* Listens on the pointermove event and executes the callback of the engine (if set) outside of Angular to prevent
1518
* Change Detection triggering
1619
*/
17-
constructor(private readonly ngZone: NgZone) {
20+
constructor(private readonly ngZone: NgZone, rendererFactory: RendererFactory2) {
21+
const renderer2 = rendererFactory.createRenderer(null, null);
1822
this.ngZone.runOutsideAngular(
1923
() => {
20-
window.addEventListener('pointermove', (pointerEvent) => {
24+
this.unlisten = renderer2.listen(window, 'pointermove', (pointerEvent) => {
25+
console.log(pointerEvent);
26+
NgZone.assertNotInAngularZone();
2127
this.engineConsumer(pointerEvent);
2228
});
2329
}
2430
);
2531
}
2632

33+
ngOnDestroy(): void {
34+
if (this.unlisten) this.unlisten();
35+
}
36+
2737
/**
2838
* Sets the consumer that will be used by the engine to determine its pointermove behaviour based on event.
2939
* @param consumer Callback that needs to be executed outside the NgZone to avoid heavy change detection processes.

src/app/three-viewer/engine/service/size/screen-size.service.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Injectable, NgZone } from '@angular/core';
1+
import { Injectable, NgZone, OnDestroy, RendererFactory2 } from '@angular/core';
22
import { ISize } from './ISize';
33
import { Consumer } from '../type/Consumer';
44

@@ -8,16 +8,26 @@ import { Consumer } from '../type/Consumer';
88
@Injectable({
99
providedIn: 'root'
1010
})
11-
export class ScreenSizeService {
11+
export class ScreenSizeService implements OnDestroy {
12+
private unlisten: Function | undefined;
13+
1214
/**
1315
* Constructor
1416
* Listen to the resize outside of Angular to execute the Engine's consumer to avoid ChangeDetection triggers
1517
*/
16-
constructor(private readonly ngZone: NgZone) {
18+
constructor(private readonly ngZone: NgZone, rendererFactory: RendererFactory2) {
1719
this.ngZone.runOutsideAngular(() => {
18-
window.addEventListener('resize', () => {
19-
this.engineConsumer(ScreenSizeService.getSize());
20-
});
20+
21+
const renderer2 = rendererFactory.createRenderer(null, null);
22+
this.ngZone.runOutsideAngular(
23+
() => {
24+
this.unlisten = renderer2.listen(window, 'resize', () => {
25+
NgZone.assertNotInAngularZone();
26+
this.engineConsumer(ScreenSizeService.getSize());
27+
});
28+
}
29+
);
30+
2131
});
2232
}
2333

@@ -66,4 +76,10 @@ export class ScreenSizeService {
6676
* Slot to keep track of the engine consumer to execute out of the NgZone on resize
6777
*/
6878
private engineConsumer: Consumer<ISize> = () => {};
79+
80+
ngOnDestroy(): void {
81+
if (this.unlisten) {
82+
this.unlisten();
83+
}
84+
}
6985
}

src/app/three-viewer/engine/service/time/time.service.ts

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { Injectable, NgZone } from '@angular/core';
22
import { IExperienceTime } from './IExperienceTime';
3-
import { Consumer } from '../type/Consumer';
43

54

65
/**
@@ -32,17 +31,24 @@ export class TimeService {
3231
* Handles the time of the experience by emitting on each frame an event to forward updates.
3332
* Executed outside the NgZone to avoid heavy load and performance issues due to the Change Detection.
3433
*/
35-
tick() {
36-
this.ngZone.runOutsideAngular(() => {
37-
const currentTime = Date.now();
38-
this.experienceTime.delta = currentTime - this.experienceTime.current;
39-
this.experienceTime.current = currentTime;
34+
launch() {
35+
this.ngZone.runOutsideAngular(() => this.tick());
36+
}
37+
38+
/**
39+
* Recursive call of the animation to handle the time of the experience
40+
* @private
41+
*/
42+
private tick() {
43+
NgZone.assertNotInAngularZone()
44+
const currentTime = Date.now();
45+
this.experienceTime.delta = currentTime - this.experienceTime.current;
46+
this.experienceTime.current = currentTime;
4047

41-
this.experienceTime.elapsed = this.experienceTime.current - this.experienceTime.start;
48+
this.experienceTime.elapsed = this.experienceTime.current - this.experienceTime.start;
4249

43-
this.engineConsumer(this.experienceTime);
44-
window.requestAnimationFrame(() => this.tick());
45-
});
50+
this.engineConsumer(this.experienceTime);
51+
window.requestAnimationFrame(() => this.tick());
4652
}
4753

4854
/**
@@ -57,13 +63,13 @@ export class TimeService {
5763
* Sets the consumer that will be used by the engine to determine its update behaviour based on the new time bundle
5864
* @param consumer Callback that needs to be executed outside the NgZone to avoid heavy change detection processes.
5965
*/
60-
setConsumer(consumer: Consumer<IExperienceTime>): void {
66+
setConsumer(consumer: (experienceTime: IExperienceTime) => void): void {
6167
this.engineConsumer = consumer;
6268
}
6369

6470
/**
6571
* Slot to keep track of the engine consumer to execute out of the NgZone on tick
6672
*/
67-
private engineConsumer: Consumer<IExperienceTime> = () => {};
73+
private engineConsumer: (experienceTime: IExperienceTime) => void = () => {};
6874

6975
}

src/app/three-viewer/three-viewer.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export class ThreeViewerComponent implements OnInit, OnDestroy {
4545
this.engine = new Engine(this.canvasRef.nativeElement);
4646

4747
this.timeService.setConsumer((experienceTime) => this.engine?.update(experienceTime));
48-
this.timeService.tick(); //First impulsion of the tick loop
48+
this.timeService.launch(); //First impulsion of the tick loop
4949

5050
this.screenSizeService.setConsumer((size) => this.engine?.resize(size));
5151

0 commit comments

Comments
 (0)