diff --git a/packages/frontend/src/pages/room.vue b/packages/frontend/src/pages/room.vue index b98e03fe92..c7ea2fe838 100644 --- a/packages/frontend/src/pages/room.vue +++ b/packages/frontend/src/pages/room.vue @@ -93,7 +93,6 @@ SPDX-License-Identifier: AGPL-3.0-only
Toggle Light - Save Exit edit mode Edit mode addObject @@ -101,6 +100,11 @@ SPDX-License-Identifier: AGPL-3.0-only Export Import
+
+ 保存していない変更があります + 戻す + 保存 +
@@ -118,6 +122,7 @@ import MkSwitch from '@/components/MkSwitch.vue'; import MkRange from '@/components/MkRange.vue'; import { RoomController } from '@/world/room/controller.js'; import { cm } from '@/world/utility.js'; +import { deepClone } from '@/utility/clone.js'; const canvas = useTemplateRef('canvas'); @@ -134,6 +139,7 @@ function resize() { const isZenMode = ref(false); const isRoomSettingsOpen = ref(false); +const isChanged = ref(false); const data = localStorage.getItem('roomData') != null ? { ...JSON.parse(localStorage.getItem('roomData')!), ...{ heya: { @@ -204,6 +210,8 @@ const data = localStorage.getItem('roomData') != null ? { ...JSON.parse(localSto console.log(data); +let latestData = deepClone(data); + const controller = new RoomController(data); onMounted(async () => { @@ -213,6 +221,10 @@ onMounted(async () => { window.addEventListener('resize', resize); + watch(controller.roomState, () => { + isChanged.value = true; + }); + //watch(controller.selected, (v) => { // if (v == null) { // interacions.value = []; @@ -332,7 +344,14 @@ function getRgb(hex: string | number): [number, number, number] | null { } function save() { - localStorage.setItem('roomData', JSON.stringify(controller.roomState.value)); + latestData = deepClone(controller.roomState.value); + localStorage.setItem('roomData', JSON.stringify(latestData)); + isChanged.value = false; +} + +async function revert() { + await controller.reset(latestData); + isChanged.value = false; } function expor() { diff --git a/packages/frontend/src/world/room/controller.ts b/packages/frontend/src/world/room/controller.ts index b495c5c4d6..0cd4bdbd5a 100644 --- a/packages/frontend/src/world/room/controller.ts +++ b/packages/frontend/src/world/room/controller.ts @@ -17,6 +17,7 @@ export class RoomController { private worker: Worker | null = null; private engine: RoomEngine | null = null; private canvas: HTMLCanvasElement | null = null; + private isCanvasDragging = false; public isReady = ref(false); public isSitting = ref(false); public isEditMode = ref(false); @@ -32,6 +33,14 @@ export class RoomController { constructor(roomState: RoomState) { this.roomState = shallowRef(roomState); + + this.onCanvasKeydown = this.onCanvasKeydown.bind(this); + this.onCanvasKeyup = this.onCanvasKeyup.bind(this); + this.onCanvasWheel = this.onCanvasWheel.bind(this); + this.onCanvasPointerdown = this.onCanvasPointerdown.bind(this); + this.onCanvasPointermove = this.onCanvasPointermove.bind(this); + this.onCanvasPointerup = this.onCanvasPointerup.bind(this); + this.onCanvasClick = this.onCanvasClick.bind(this); } public async init(canvas: HTMLCanvasElement, workerMode = false) { @@ -82,73 +91,87 @@ export class RoomController { }); } - this.canvas.addEventListener('keydown', (ev) => { - if (this.worker != null) { - this.worker.postMessage({ type: 'dom:keydown', ev: { code: ev.code, shiftKey: ev.shiftKey } }); - } else if (this.engine != null) { - this.engine.domEvents.emit('keydown', { code: ev.code, shiftKey: ev.shiftKey }); - } - ev.preventDefault(); - ev.stopPropagation(); - return false; - }); + this.canvas.addEventListener('keydown', this.onCanvasKeydown); + this.canvas.addEventListener('keyup', this.onCanvasKeyup); + this.canvas.addEventListener('wheel', this.onCanvasWheel); + this.canvas.addEventListener('pointerdown', this.onCanvasPointerdown); + this.canvas.addEventListener('pointermove', this.onCanvasPointermove); + this.canvas.addEventListener('pointerup', this.onCanvasPointerup); + this.canvas.addEventListener('click', this.onCanvasClick); + } - this.canvas.addEventListener('keyup', (ev) => { - if (this.worker != null) { - this.worker.postMessage({ type: 'dom:keyup', ev: { code: ev.code, shiftKey: ev.shiftKey } }); - } else if (this.engine != null) { - this.engine.domEvents.emit('keyup', { code: ev.code, shiftKey: ev.shiftKey }); - } - ev.preventDefault(); - ev.stopPropagation(); - return false; - }); + private onCanvasKeydown(ev: KeyboardEvent) { + if (this.worker != null) { + this.worker.postMessage({ type: 'dom:keydown', ev: { code: ev.code, shiftKey: ev.shiftKey } }); + } else if (this.engine != null) { + this.engine.domEvents.emit('keydown', { code: ev.code, shiftKey: ev.shiftKey }); + } + ev.preventDefault(); + ev.stopPropagation(); + return false; + } - this.canvas.addEventListener('pointerdown', (ev) => { - // todo - }); + private onCanvasKeyup(ev: KeyboardEvent) { + if (this.worker != null) { + this.worker.postMessage({ type: 'dom:keyup', ev: { code: ev.code, shiftKey: ev.shiftKey } }); + } else if (this.engine != null) { + this.engine.domEvents.emit('keyup', { code: ev.code, shiftKey: ev.shiftKey }); + } + ev.preventDefault(); + ev.stopPropagation(); + return false; + } - this.canvas.addEventListener('wheel', (ev) => { - if (this.worker != null) { - this.worker.postMessage({ type: 'dom:wheel', ev: { deltaY: ev.deltaY } }); - } else if (this.engine != null) { - this.engine.domEvents.emit('wheel', { deltaY: ev.deltaY }); - } - ev.preventDefault(); - ev.stopPropagation(); - return false; - }); + private onCanvasWheel(ev: WheelEvent) { + if (this.worker != null) { + this.worker.postMessage({ type: 'dom:wheel', ev: { deltaY: ev.deltaY } }); + } else if (this.engine != null) { + this.engine.domEvents.emit('wheel', { deltaY: ev.deltaY }); + } + ev.preventDefault(); + ev.stopPropagation(); + return false; + } - let isDragging = false; + private onCanvasPointerdown(ev: PointerEvent) { + this.canvas.setPointerCapture(ev.pointerId); + } - this.canvas.addEventListener('pointerdown', (ev) => { - this.canvas.setPointerCapture(ev.pointerId); - }); + private onCanvasPointermove(ev: PointerEvent) { + if (this.canvas.hasPointerCapture(ev.pointerId)) { + this.isCanvasDragging = true; + } + } - this.canvas.addEventListener('pointermove', (ev) => { - if (this.canvas.hasPointerCapture(ev.pointerId)) { - isDragging = true; - } - }); + private onCanvasPointerup(ev: PointerEvent) { + window.setTimeout(() => { + this.isCanvasDragging = false; + this.canvas.releasePointerCapture(ev.pointerId); + }, 0); + } - this.canvas.addEventListener('pointerup', (ev) => { - window.setTimeout(() => { - isDragging = false; - this.canvas.releasePointerCapture(ev.pointerId); - }, 0); - }); + private onCanvasClick(ev: MouseEvent) { + if (this.isCanvasDragging) return; + if (this.worker != null) { + this.worker.postMessage({ type: 'dom:click', ev: { offsetX: ev.offsetX, offsetY: ev.offsetY } }); + } else if (this.engine != null) { + this.engine.domEvents.emit('click', { offsetX: ev.offsetX, offsetY: ev.offsetY }); + } + ev.preventDefault(); + ev.stopPropagation(); + return false; + } - this.canvas.addEventListener('click', (ev) => { - if (isDragging) return; - if (this.worker != null) { - this.worker.postMessage({ type: 'dom:click', ev: { offsetX: ev.offsetX, offsetY: ev.offsetY } }); - } else if (this.engine != null) { - this.engine.domEvents.emit('click', { offsetX: ev.offsetX, offsetY: ev.offsetY }); - } - ev.preventDefault(); - ev.stopPropagation(); - return false; - }); + public async reset(roomState: RoomState, canvas?: HTMLCanvasElement, workerMode = false) { + this.destroy(); + this.roomState.value = roomState; + this.isReady.value = false; + this.isSitting.value = false; + this.isEditMode.value = false; + this.grabbing.value = null; + this.selected.value = null; + this.initializeProgress.value = 0; + await this.init(canvas ?? this.canvas!, workerMode); } public enterEditMode() { @@ -259,6 +282,14 @@ export class RoomController { } public destroy() { + this.canvas?.removeEventListener('keydown', this.onCanvasKeydown); + this.canvas?.removeEventListener('keyup', this.onCanvasKeyup); + this.canvas?.removeEventListener('wheel', this.onCanvasWheel); + this.canvas?.removeEventListener('pointerdown', this.onCanvasPointerdown); + this.canvas?.removeEventListener('pointermove', this.onCanvasPointermove); + this.canvas?.removeEventListener('pointerup', this.onCanvasPointerup); + this.canvas?.removeEventListener('click', this.onCanvasClick); + if (this.worker != null) { this.worker.terminate(); this.worker = null;