Add fadeIn/fadeOut for bg, sprites and audio

This commit is contained in:
aNNiMON 2024-03-13 22:37:00 +02:00
parent 60f5efdace
commit cc3e39c6b9
2 changed files with 147 additions and 86 deletions

62
src/utils/UIUtils.ts Normal file
View File

@ -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);
}
}

View File

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