30 lines
2.2 KiB
TypeScript
30 lines
2.2 KiB
TypeScript
export class GyroController {
|
|
private enabled = false;
|
|
private pending = false;
|
|
private generation = 0;
|
|
private hasReading = false;
|
|
private base: { beta: number; gamma: number } | null = null;
|
|
constructor(private readonly setView: (x: number, y: number) => void, private readonly status: (message: string) => void) {}
|
|
async toggle(): Promise<boolean> {
|
|
if (this.enabled || this.pending) { this.dispose(); return false; }
|
|
if (!window.isSecureContext || !('DeviceOrientationEvent' in window)) { this.status('자이로스코프를 사용할 수 없습니다.'); return false; }
|
|
const generation = ++this.generation; this.pending = true;
|
|
try {
|
|
const device = window.DeviceOrientationEvent as typeof DeviceOrientationEvent & { requestPermission?: () => Promise<string> };
|
|
this.status('센서 권한을 기다리는 중…');
|
|
if (device.requestPermission && await device.requestPermission() !== 'granted') throw new Error('권한 거절됨');
|
|
if (generation !== this.generation) return false;
|
|
this.base = null; this.hasReading = false; this.pending = false; this.enabled = true; window.addEventListener('deviceorientation', this.onOrientation); this.status('센서값을 기다리는 중…'); return true;
|
|
} catch (error) { this.pending = false; if (generation === this.generation) this.status(`자이로스코프를 사용할 수 없습니다 (${(error as Error).message})`); return false; }
|
|
}
|
|
reset(): void { this.base = null; }
|
|
dispose(): void { ++this.generation; this.pending = false; this.enabled = false; this.base = null; window.removeEventListener('deviceorientation', this.onOrientation); }
|
|
private onOrientation = (event: DeviceOrientationEvent): void => {
|
|
if (!this.enabled || event.beta == null || event.gamma == null) return;
|
|
if (!this.base) this.base = { beta: event.beta, gamma: event.gamma };
|
|
const angle = (screen.orientation?.angle ?? 0) * Math.PI / 180;
|
|
const x = event.gamma - this.base.gamma; const y = event.beta - this.base.beta;
|
|
this.setView((x * Math.cos(angle) + y * Math.sin(angle)) / 35, (y * Math.cos(angle) - x * Math.sin(angle)) / 35); if (!this.hasReading) { this.hasReading = true; this.status('자이로스코프 활성'); }
|
|
};
|
|
}
|