diff --git a/src/utils/UIUtils.ts b/src/utils/UIUtils.ts new file mode 100644 index 0000000..432bd84 --- /dev/null +++ b/src/utils/UIUtils.ts @@ -0,0 +1,62 @@ +export class UIUtils { + + public static removeElement(el: HTMLElement): void { + el.parentNode.removeChild(el) + } + + public static fadeIn( + element: HTMLElement, + duration = 200, + onFinish: () => void = () => {} + ): void { + return UIUtils.fade(element, duration, 0, 1, onFinish); + } + + public static fadeOut( + element: HTMLElement, + duration = 200, + onFinish: () => void = () => {} + ): void { + return UIUtils.fade(element, duration, 1, 0, onFinish); + } + + public static fade( + element: HTMLElement, + duration = 200, + fromValue = 0, toValue = 1, + onFinish: () => void = () => {} + ): void { + const setter = (v: number) => element.style.opacity = v.toString(); + return UIUtils.animate(setter, duration, fromValue, toValue, onFinish); + } + + public static animate( + setter: (value: number) => void, + duration = 200, + fromValue = 0, + toValue = 1, + onFinish: () => void = () => {} + ): void { + if (duration <= 0) { + setter(toValue); + onFinish() + return; + } + + setter(fromValue); + const startTime = Date.now(); + const callback = () => { + const currentTime = Date.now(); + const timeDiff = Math.min((currentTime - startTime) / duration, 1); + const value = fromValue - (fromValue - toValue) * timeDiff; + if (timeDiff >= 1) { + setter(toValue) + onFinish() + } else { + setter(value) + requestAnimationFrame(callback) + } + }; + requestAnimationFrame(callback); + } +} \ No newline at end of file diff --git a/src/view/MainView.ts b/src/view/MainView.ts index 70fb285..f521382 100644 --- a/src/view/MainView.ts +++ b/src/view/MainView.ts @@ -1,5 +1,6 @@ import { PathResolver } from "../utils/PathResolver"; import { TextUtils } from "../utils/TextUtils"; +import { UIUtils } from "../utils/UIUtils"; import { Characters } from "./model/Characters"; import { FadeInfo } from "./model/FadeInfo"; import { MapPlaces } from "./model/MapPlaces"; @@ -15,9 +16,12 @@ export class MainView implements ViewModel { private places: MapPlaces; private characters: Characters; - private windowTag: HTMLElement; - private textAuthorTag: HTMLElement; - private textContentTag: HTMLElement; + private readonly elWindow = document.getElementById('window'); + private readonly elTextAuthor = document.getElementById('textAuthor'); + private readonly elTextContent = document.getElementById('textContent'); + private readonly elBackground1 = document.getElementById('background1'); + private readonly elBackground2 = document.getElementById('background2'); + private readonly elMenu = document.getElementById('menu'); private musicPlayerAudio: HTMLAudioElement; private soundPlayerAudio: HTMLAudioElement; @@ -37,10 +41,6 @@ export class MainView implements ViewModel { private nextCommandRunnable: () => void; constructor(private debug: boolean = false) { - this.windowTag = document.getElementById('window'); - this.textAuthorTag = document.getElementById('textAuthor'); - this.textContentTag = document.getElementById('textContent'); - this.musicPlayerAudio = new Audio(); this.soundPlayerAudio = new Audio(); this.soundLoopPlayerAudio = new Audio(); @@ -65,7 +65,7 @@ export class MainView implements ViewModel { this.places = places; this.characters = characters; characters.init(); - document.getElementById('menu').style.display = 'none'; + this.elMenu.style.display = 'none'; document.getElementById('mainMenuButton').onclick = (e) => this.showMainMenu(); document.getElementById('background').onclick = (e) => this.onTouch(e); this.nextCommandRunnable = () => { @@ -76,7 +76,7 @@ export class MainView implements ViewModel { private onTouch(e: MouseEvent): void { if (this.blockTap) return; - if (document.getElementById('menu').style.display !== 'none') return; + if (this.elMenu.style.display !== 'none') return; if ((e.target as HTMLElement).tagName.toLowerCase() === 'li') return; if ((e.target as HTMLElement).id === 'mainMenuButton') return; @@ -85,11 +85,11 @@ export class MainView implements ViewModel { } public text(text: string): void { - this.textAuthorTag.innerText = ''; + this.elTextAuthor.innerText = ''; if (TextUtils.isEmpty(text)) this.windowHide(""); else { this.windowShow(""); - this.formatString(this.textContentTag, text); + this.formatString(this.elTextContent, text); } } @@ -99,9 +99,9 @@ export class MainView implements ViewModel { else { this.windowShow(""); const person = this.characters.get(whoid); - this.textAuthorTag.textContent = person.name; - this.textAuthorTag.style.color = this.toColor(person.color); - this.formatString(this.textContentTag, text); + this.elTextAuthor.textContent = person.name; + this.elTextAuthor.style.color = this.toColor(person.color); + this.formatString(this.elTextContent, text); } } @@ -109,9 +109,9 @@ export class MainView implements ViewModel { let edited = TextUtils.replaceAll(text, "\\{w.*?\\}", ""); if (TextUtils.contains(edited, "{center}")) { edited = TextUtils.replaceAll(edited, "\\{center\\}", ""); - this.textContentTag.style.textAlign = "center"; + this.elTextContent.style.textAlign = "center"; } else { - this.textContentTag.style.textAlign = "left"; + this.elTextContent.style.textAlign = "left"; } if (TextUtils.contains(edited, "{html}")) { edited = TextUtils.replaceAll(edited, "\\{html\\}", ""); @@ -155,36 +155,30 @@ export class MainView implements ViewModel { } let animationTime = 0; - document.getElementById('background1').style.background = document.getElementById('background2').style.background; - document.getElementById('background1').style.display = 'block'; + this.elBackground1.style.background = this.elBackground2.style.background; + this.elBackground1.style.display = 'block'; if (effect in Transition.DEFAULT) { const transition = Transition.DEFAULT[effect]; if (transition["type"] === TransitionType.TYPE_FADE) { animationTime = transition["inTime"] + transition["outTime"]; - document.getElementById('background2').style.display = 'none'; - document.getElementById('background2').style.background = background; - document.getElementById('background2').style.backgroundSize = 'cover'; - - // TODO - //$('#background1').fadeOut(transition["outTime"], function () { - // $('#background2').fadeIn(transition["inTime"], function () { - // $('#background1').hide(); - // $(this).show(); - // }); - //}); - document.getElementById('background1').style.display = 'none'; - document.getElementById('background2').style.display = 'block'; - document.getElementById('background2').style.backgroundSize = 'cover'; + this.elBackground2.style.display = 'none'; + this.elBackground2.style.background = background; + this.elBackground2.style.backgroundSize = 'cover'; + UIUtils.fadeOut(this.elBackground1, transition["outTime"], () => { + this.elBackground2.style.display = 'block'; + this.elBackground1.style.display = 'none'; + UIUtils.fadeIn(this.elBackground2, transition["inTime"]); + }); this.pause(animationTime, false); return; } } - document.getElementById('background1').style.display = 'none'; - document.getElementById('background2').style.background = background; - document.getElementById('background2').style.backgroundSize = 'cover'; - document.getElementById('background1').style.display = 'block'; + this.elBackground1.style.display = 'none'; + this.elBackground2.style.background = background; + this.elBackground2.style.backgroundSize = 'cover'; + this.elBackground1.style.display = 'block'; this.pause(animationTime, false); } @@ -207,11 +201,9 @@ export class MainView implements ViewModel { img.onload = () => { this.setSpritePosition(img, position); if (effect in Transition.DEFAULT) { - var transition = Transition.DEFAULT[effect]; + const transition = Transition.DEFAULT[effect]; if (transition["type"] === TransitionType.TYPE_FADE) { - // img.style.display = 'none'; - // TODO - // $(this).hide().fadeIn(transition["inTime"]); + UIUtils.fadeIn(img, transition["inTime"]); return; } } @@ -239,16 +231,14 @@ export class MainView implements ViewModel { if (effect in Transition.DEFAULT) { const transition = Transition.DEFAULT[effect]; if (transition["type"] === TransitionType.TYPE_FADE) { - // TODO - //img.fadeOut(transition["outTime"], function () { - // $(this).remove(); - //}); - img.remove(); + UIUtils.fadeOut( + img, transition["outTime"], + () => UIUtils.removeElement(img)); } else { - img.remove(); + UIUtils.removeElement(img); } } else { - img.remove(); + UIUtils.removeElement(img); } delete this.spriteInContainer[whoid]; } @@ -298,12 +288,12 @@ export class MainView implements ViewModel { menuContainer.appendChild(li); i++; } - document.getElementById('menu').style.display = 'block'; + this.elMenu.style.display = 'block'; } private createMenuItemClickFunction(pos: number) { return function () { - document.getElementById('menu').style.display = 'none'; + this.elMenu.style.display = 'none'; this.navigable.setPosition(pos); }; } @@ -319,7 +309,7 @@ export class MainView implements ViewModel { this.addMainMenuItem("Next scene", () => this.navigable.nextScene()); // TODO this.addMainMenuItem("Save", () => { - document.getElementById('menu').style.display = 'none'; + this.elMenu.style.display = 'none'; // this.saveState(); }); const saves = JSON.parse(localStorage.saves || null) || {}; @@ -329,9 +319,9 @@ export class MainView implements ViewModel { // this.addMainMenuItem("Deleteā€¦", () => this.showRemoveStateMenu()); //} this.addMainMenuItem("Close", () => { - document.getElementById('menu').style.display = 'none'; + this.elMenu.style.display = 'none'; }); - document.getElementById('menu').style.display = 'block'; + this.elMenu.style.display = 'block'; } private addMainMenuItem(name: string, func: () => void) { @@ -351,12 +341,12 @@ export class MainView implements ViewModel { li.onclick = this.createMapPlaceClickFunction(key); menuContainer.appendChild(li); } - document.getElementById('menu').style.display = 'block'; + this.elMenu.style.display = 'block'; } private createMapPlaceClickFunction(zoneLabel: string): () => void { return () => { - document.getElementById('menu').style.display = 'none'; + this.elMenu.style.display = 'none'; this.navigable.jumpLabel(zoneLabel); this.navigable.next(); }; @@ -406,9 +396,10 @@ export class MainView implements ViewModel { }); this.musicPlayerAudio.play(); if (fade.fadeIn) { - this.musicPlayerAudio.volume = 0.0; - // TODO - // $(this.musicPlayerAudio).animate({ volume: 1.0 }, fade.duration * 1000); + UIUtils.animate( + (value: number) => this.musicPlayerAudio.volume = value, + fade.duration * 1000, + 0, 1); } else { this.musicPlayerAudio.volume = 1.0; } @@ -429,9 +420,10 @@ export class MainView implements ViewModel { }); this.soundPlayerAudio.play(); if (fade.fadeIn) { - this.soundPlayerAudio.volume = 0.0; - // TODO - // $(this.soundPlayerAudio).animate({ volume: 1.0 }, fade.duration * 1000); + UIUtils.animate( + (value: number) => this.soundPlayerAudio.volume = value, + fade.duration * 1000, + 0, 1); } else { this.soundPlayerAudio.volume = 1.0; } @@ -451,10 +443,12 @@ export class MainView implements ViewModel { }); this.soundLoopPlayerAudio.play(); if (fade.fadeIn) { - // $(this.soundLoopPlayerAudio).prop("volume", 0.0); - // $(this.soundLoopPlayerAudio).animate({ volume: 1.0 }, fade.duration * 1000); + UIUtils.animate( + (value: number) => this.soundLoopPlayerAudio.volume = value, + fade.duration * 1000, + 0, 1); } else { - // $(this.soundLoopPlayerAudio).prop("volume", 1.0); + this.soundLoopPlayerAudio.volume = 1.0; } } catch (e) { console.log("soundloop: " + name + " " + e); @@ -472,10 +466,12 @@ export class MainView implements ViewModel { }); this.ambiencePlayerAudio.play(); if (fade.fadeIn) { - // $(this.ambiencePlayerAudio).prop("volume", 0.0); - // $(this.ambiencePlayerAudio).animate({ volume: 1.0 }, fade.duration * 1000); + UIUtils.animate( + (value: number) => this.ambiencePlayerAudio.volume = value, + fade.duration * 1000, + 0, 1); } else { - // $(this.ambiencePlayerAudio).prop("volume", 1.0); + this.ambiencePlayerAudio.volume = 1.0; } } catch (e) { console.log("ambience: " + name + " " + e); @@ -502,10 +498,10 @@ export class MainView implements ViewModel { if (this.musicPlayerAudio.paused) return; if (fade.fadeOut) { - // TODO - // $(this.musicPlayerAudio).animate({ volume: 0.0 }, fade.duration * 1000, function () { - // this.pause(); - // }); + UIUtils.animate( + (value: number) => this.musicPlayerAudio.volume = value, + fade.duration * 1000, + 1, 0, () => this.musicPlayerAudio.pause()); } else { this.musicPlayerAudio.pause(); } @@ -515,9 +511,10 @@ export class MainView implements ViewModel { if (this.soundPlayerAudio.paused) return; if (fade.fadeOut) { - // $(this.soundPlayerAudio).animate({ volume: 0.0 }, fade.duration * 1000, function () { - this.soundPlayerAudio.pause(); - // }); + UIUtils.animate( + (value: number) => this.soundPlayerAudio.volume = value, + fade.duration * 1000, + 1, 0, () => this.soundPlayerAudio.pause()); } else { this.soundPlayerAudio.pause(); } @@ -527,9 +524,10 @@ export class MainView implements ViewModel { if (this.soundLoopPlayerAudio.paused) return; if (fade.fadeOut) { - // $(this.soundLoopPlayerAudio).animate({ volume: 0.0 }, fade.duration * 1000, function () { - this.soundLoopPlayerAudio.pause(); - // }); + UIUtils.animate( + (value: number) => this.soundLoopPlayerAudio.volume = value, + fade.duration * 1000, + 1, 0, () => this.soundLoopPlayerAudio.pause()); } else { this.soundLoopPlayerAudio.pause(); } @@ -539,17 +537,18 @@ export class MainView implements ViewModel { if (this.ambiencePlayerAudio.paused) return; if (fade.fadeOut) { - // $(this.ambiencePlayerAudio).animate({ volume: 0.0 }, fade.duration * 1000, function () { - this.ambiencePlayerAudio.pause(); - // }); + UIUtils.animate( + (value: number) => this.ambiencePlayerAudio.volume = value, + fade.duration * 1000, + 1, 0, () => this.ambiencePlayerAudio.pause()); } else { this.ambiencePlayerAudio.pause(); } } public windowShow(effect: string): boolean { - if (this.windowTag.style.visibility !== "visible") { - this.windowTag.style.visibility = "visible"; + if (this.elWindow.style.visibility !== "visible") { + this.elWindow.style.visibility = "visible"; } if (!TextUtils.isEmpty(effect)) { this.background(this.backgroundType, this.backgroundName, effect); @@ -559,8 +558,8 @@ export class MainView implements ViewModel { } public windowHide(effect: string): boolean { - if (this.windowTag.style.visibility !== "hidden") { - this.windowTag.style.visibility = "hidden"; + if (this.elWindow.style.visibility !== "hidden") { + this.elWindow.style.visibility = "hidden"; } if (!TextUtils.isEmpty(effect)) { this.background(this.backgroundType, this.backgroundName, effect); @@ -570,10 +569,10 @@ export class MainView implements ViewModel { } private windowSwitchVisibility(): void { - if (this.windowTag.style.visibility === "hidden") { - this.windowTag.style.visibility = "visible"; + if (this.elWindow.style.visibility === "hidden") { + this.elWindow.style.visibility = "visible"; } else { - this.windowTag.style.visibility = "hidden"; + this.elWindow.style.visibility = "hidden"; } }