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 { 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";
}
}