diff --git a/README.md b/README.md
index 541aee0..eb6af48 100644
--- a/README.md
+++ b/README.md
@@ -47,28 +47,11 @@ To generate the latest charword_table.json
```
### Webpage & JavaScript
-Add query string `settings` to bring up the settings panel to adjust your settings. Then use appropriate JavaScript code to load your settings
+Add query string `aklive2d` to bring up the settings panel to adjust your settings.
+Settings can be adjusted under `window.aklive2d` or by dispatching custom events (under `window.aklive2d.events`) to `document`.
+Examples can be found at `showcase/src/libs/wallpaper_engine.js`.
-``` javascript
-settings.setFPS(integer) // set FPS
-settings.setLogoDisplay(boolean) // display logo or not
-settings.setLogoRatio(float) // the ratio of the logo
-settings.setLogoOpacity(float) // the opacity of the logo
-settings.setLogo(url) // change the logo, url: image url, removeInvert: boolean
-settings.resetLogoImage() // reset to the default logo
-settings.setDefaultBackground(url) // change the default background, url: image filename from `background` folder
-settings.setBackgoundImage(url) // change the background, url: image url
-settings.resetBackground() // reset to the default background
-settings.positionPadding("left", integer) // left padding
-settings.positionPadding("right", integer) // right padding
-settings.positionPadding("top", integer) // top padding
-settings.positionPadding("bottom", integer) // bottom padding
-settings.positionReset() // reset the position
-
-settings.open() // open settings panel
-settings.close() // close settings panel
-settings.reset() // reset settings
-```
+Using JS events to change settings is recommended.
## Config
### General Config
diff --git a/showcase/src/components/aklive2d.js b/showcase/src/components/aklive2d.js
index e1fd86c..d9653ed 100644
--- a/showcase/src/components/aklive2d.js
+++ b/showcase/src/components/aklive2d.js
@@ -5,6 +5,7 @@ import Player from "@/components/player";
import Background from "@/components/background";
import Logo from "@/components/logo";
import Insight from "@/components/insight";
+import * as Event from "@/components/event";
import {
isWebGLSupported,
insertHTMLChild,
@@ -17,11 +18,11 @@ export default class AKLive2D {
#el = document.createElement("div")
#appEl
#queries = new URLSearchParams(window.location.search)
- #voice = new Voice()
- #music = new Music()
- #player = new Player()
- #background = new Background()
- #logo = new Logo()
+ #voice
+ #music
+ #player
+ #background
+ #logo
#insight = new Insight()
constructor(appEl) {
@@ -32,18 +33,16 @@ export default class AKLive2D {
document.addEventListener("gesturestart", e => e.preventDefault());
this.#appEl = appEl
- }
-
- init() {
- this.#logo.init(this.#appEl);
- this.#background.init(this.#appEl);
- this.#voice.init(this.#appEl);
- this.#music.init(this.#appEl);
+ this.#logo = new Logo(this.#appEl)
+ this.#background = new Background(this.#appEl)
+ this.#voice = new Voice(this.#appEl)
+ this.#music = new Music(this.#appEl)
if (isWebGLSupported()) {
- this.#player.init(this.#appEl);
+ this.#player = new Player(this.#appEl)
} else {
- (new Fallback()).init(this.#appEl)
+ new Fallback(this.#appEl)
}
+
this.#el.id = "settings-box"
this.#el.hidden = true
this.#el.innerHTML = `
@@ -63,15 +62,8 @@ export default class AKLive2D {
insertHTMLChild(this.#appEl, this.#el)
addEventListeners([
{
- event: "player-ready", handler: () => this.success()
- },
- ...this.#logo.listeners,
- ...this.#background.listeners,
- ...this.#player.listeners,
- ...this.#voice.listeners,
- ...this.#music.listeners,
- ...this.#insight.listeners,
- {
+ event: "player-ready", handler: () => this.#success()
+ }, {
id: "settings-reset", event: "click", handler: () => this.reset()
}, {
id: "settings-close", event: "click", handler: () => this.close()
@@ -79,10 +71,54 @@ export default class AKLive2D {
id: "settings-to-directory", event: "click", handler: () => {
window.location.href = '/';
}
- }
+ },
+ ...this.#logo.listeners,
+ ...this.#background.listeners,
+ ...this.#player.listeners,
+ ...this.#voice.listeners,
+ ...this.#music.listeners,
+ ...this.#insight.listeners,
])
}
+ get voice() {
+ return this.#voice
+ }
+
+ get music() {
+ return this.#music
+ }
+
+ get player() {
+ return this.#player
+ }
+
+ get background() {
+ return this.#background
+ }
+
+ get logo() {
+ return this.#logo
+ }
+
+ get events() {
+ return Event
+ }
+
+ get config() {
+ return {
+ player: this.#player.config,
+ background: this.#background.config,
+ logo: this.#logo.config,
+ music: this.#music.config,
+ voice: this.#voice.config
+ }
+ }
+
+ get configStr() {
+ return JSON.stringify(this.config, null)
+ }
+
open() {
this.#el.hidden = false;
}
@@ -96,59 +132,36 @@ export default class AKLive2D {
this.#background.reset()
this.#logo.reset()
this.#voice.reset()
+ this.#music.reset()
}
- success() {
+ #success() {
this.#music.link(this.#background)
this.#background.link(this.#music)
this.#voice.link(this.#player)
+ this.#player.success()
this.#voice.success()
this.#music.success()
this.#insight.success()
- if (this.#queries.has("settings") || this.#queries.has("aklive2d") || import.meta.env.MODE === 'development') {
+ if (this.#queries.has("aklive2d") || import.meta.env.MODE === 'development') {
this.open()
}
- this.#backCompatibility()
+ this.#registerBackCompatibilityFns()
}
- #backCompatibility() {
- window.voice = this.#voice
- window.music = this.#music
+ #registerBackCompatibilityFns() {
+ const _this = this
+ window.voice = _this.#voice
+ window.music = _this.#music
window.settings = {
- spinePlayer: this.#player.spine,
- setFPS: this.#player.setFPS,
- setLogoDisplay: this.#logo.setLogoDisplay,
- setLogo: this.#logo.setLogo,
- setLogoImage: this.#logo.setLogoImage,
- resetLogoImage: this.#logo.resetLogoImage,
- setLogoRatio: this.#logo.setLogoRatio,
- setLogoOpacity: this.#logo.setLogoOpacity,
- setBackgoundImage: this.#background.setBackgroundImage,
- currentBackground: this.#background.currentBackground,
- setDefaultBackground: this.#background.setDefaultBackground,
- setBackground: this.#background.setBackground,
- resetBackground: this.#background.resetBackground,
- loadViewport: this.#player.loadViewport,
- setScale: this.#player.setScale,
- scale: this.#player.scale,
- positionPadding: this.#player.positionPadding,
- positionReset: this.#player.positionReset,
- scaleReset: this.#player.scaleReset,
elementPosition: updateElementPosition,
- logoPadding: this.#logo.logoPadding,
- logoReset: this.#logo.logoReset,
- useStartAnimation: this.#player.useStartAnimation,
- open: this.open,
- close: this.close,
- reset: this.reset,
- setMusicFromWE: this.#music.setMusicFromWE,
- setMusic: this.#music.setMusic,
- resetMusic: this.#music.resetMusic,
- setVideo: this.#background.setVideo,
- setVideoVolume: this.#background.setVideoVolume,
- getVideoVolume: this.#background.getVideoVolume,
- setVideoFromWE: this.#background.setVideoFromWE,
- resetVideo: this.#background.resetVideo
+ open: _this.open,
+ close: _this.close,
+ reset: _this.reset,
+ ..._this.#player.backCompatibilityFns,
+ ..._this.#logo.backCompatibilityFns,
+ ..._this.#music.backCompatibilityFns,
+ ..._this.#background.backCompatibilityFns
}
}
}
\ No newline at end of file
diff --git a/showcase/src/components/background.js b/showcase/src/components/background.js
index 53b5dca..a84bfa3 100644
--- a/showcase/src/components/background.js
+++ b/showcase/src/components/background.js
@@ -16,11 +16,16 @@ export default class Background {
image: "operator_bg.png"
}
#config = {
- image: null
+ video: {
+ name: null,
+ volume: 100,
+ },
+ useVideo: false,
+ name: null
}
#musicObj
- init(el) {
+ constructor(el) {
this.#parentEl = el
this.#el.id = "background-box"
this.image = this.#default.location + this.#default.image
@@ -31,119 +36,20 @@ export default class Background {
this.#videoEl = document.getElementById("video-src")
}
- set image(v) {
- this.#el.style.backgroundImage = `url("${v}")`
- }
-
- set video(v) {
- const update = (url) => {
- this.#videoEl.src = url
- this.#videoEl.load()
- document.getElementById("custom-video-background-clear").disabled = false
- }
- if (typeof v === "object") {
- readFile(
- v,
- (blobURL) => update(blobURL)
- )
- } else {
- update(v)
- }
- }
-
- get volume() {
- return this.#videoEl.volume * 100
- }
-
- set volume(v) {
- this.#videoEl.volume = parseInt(v) / 100
- }
-
- get current() {
- return this.#config.image || this.#default.image
- }
-
- set default(v) {
- this.#default.image = v
- if (!this.#config.image) {
- this.image = this.#default.location + this.#default.image
- }
- }
-
- set custom(v) {
- const update = (url) => {
- this.#config.image = v
- this.image = url
- document.getElementById("custom-background-clear").disabled = false
- }
- if (typeof v === "object") {
- readFile(
- v,
- (blobURL) => update(blobURL)
- )
- } else {
- update(v)
- }
- }
-
- setVideoFromWE(url) {
- // Note: Back Compatibility
- this.video = url
- }
-
- get currentBackground() {
- // Note: Back Compatibility
- return this.current
- }
-
resetImage() {
document.getElementById("custom-background").value = ""
document.getElementById("custom-background-clear").disabled = true
- this.#config.image = null
+ this.#config.name = null
this.image = this.#default.location + this.#default.image
}
resetVideo() {
+ this.#config.video.name = null
this.#videoEl.src = ""
document.getElementById("custom-video-background").value = ""
document.getElementById("custom-video-background-clear").disabled = true
}
- setBackgroundImage(v) {
- // Note: Back Compatibility
- this.image = v
- }
-
- setDefaultBackground(v) {
- // Note: Back Compatibility
- this.default = v
- }
-
- setBackground(v) {
- // Note: Back Compatibility
- this.custom = v
- }
-
- resetBackground() {
- // Note: Back Compatibility
- this.resetImage()
- }
-
- setVideo(e) {
- // Note: Back Compatibility
- this.video = e.target.files[0]
- }
-
- getVideoVolume() {
- // Note: Back Compatibility
- return this.volume
- }
-
- setVideoVolume(v) {
- // Note: Back Compatibility
- this.volume = v
- }
-
reset() {
this.resetImage()
this.resetVideo()
@@ -153,6 +59,100 @@ export default class Background {
this.#musicObj = musicObj
}
+ get useVideo() {
+ return this.#config.useVideo
+ }
+
+ set useVideo(v) {
+ this.#config.useVideo = v
+ }
+
+ set image(v) {
+ this.#el.style.backgroundImage = `url("${v}")`
+ }
+
+ set video(v) {
+ const update = (url, v = null) => {
+ this.#config.video.name = {
+ isLocalFile: v !== null,
+ value: v ? v.name : url
+ }
+ this.#videoEl.src = url
+ this.#videoEl.load()
+ document.getElementById("custom-video-background-clear").disabled = false
+ }
+ if (typeof v === "object") {
+ readFile(
+ v,
+ (blobURL) => update(blobURL, v)
+ )
+ } else {
+ update(v)
+ }
+ }
+
+ get volume() {
+ return this.#config.video.volume
+ }
+
+ set volume(v) {
+ v = parseInt(v)
+ this.#config.video.volume = v
+ this.#videoEl.volume = v / 100
+ }
+
+ get current() {
+ return this.#config.name || this.#default.image
+ }
+
+ set default(v) {
+ this.#default.image = v
+ this.#musicObj.music = v
+ this.image = this.#default.location + this.#default.image
+ }
+
+ set custom(v) {
+ const update = (url, v = null) => {
+ this.#config.name = {
+ isLocalFile: v !== null,
+ value: v ? v.name : url
+ }
+ this.image = url
+ document.getElementById("custom-background-clear").disabled = false
+ }
+ if (typeof v === "object") {
+ readFile(
+ v,
+ (blobURL) => update(blobURL, v)
+ )
+ } else {
+ update(v)
+ }
+ }
+
+ get config() {
+ return {
+ default: this.#default.image,
+ ...this.#config
+ }
+ }
+
+ get backCompatibilityFns() {
+ const _this = this
+ return {
+ currentBackground: _this.current,
+ setBackgoundImage: (v) => _this.image = v,
+ setDefaultBackground: (v) => _this.default = v,
+ setBackground: (v) => _this.custom = v,
+ resetBackground: _this.resetImage,
+ setVideo: (e) => _this.video = e.target.files[0],
+ getVideoVolume: () => _this.volume,
+ setVideoVolume: (v) => _this.volume = v,
+ setVideoFromWE: (url) => _this.video = url,
+ resetVideo: _this.resetVideo
+ }
+ }
+
get HTML() {
return `
@@ -163,19 +163,29 @@ export default class Background {
-
+
-
+
+
+
+
+
+
-
-
+
+
-
+
-
+
+
+
+
+
+
@@ -204,21 +214,25 @@ export default class Background {
}, {
id: "default-background-select", event: "change", handler: e => {
this.default = e.currentTarget.value
- this.#musicObj.music = e.currentTarget.value
}
}, {
id: "custom-background", event: "change", handler: e => this.custom = e.target.files[0]
}, {
id: "custom-background-clear", event: "click", handler: () => this.resetImage()
+ }, {
+ id: "custom-background-url-apply", event: "click", handler: () => this.custom = document.getElementById("custom-background-url").value
}, {
id: "video", event: "click", handler: e => {
showRelatedHTML(e.currentTarget, "video-realted");
+ this.useVideo = e.currentTarget.checked
if (!e.currentTarget.checked) this.resetVideo()
}
}, {
id: "custom-video-background", event: "change", handler: e => this.video = e.target.files[0]
}, {
id: "custom-video-background-clear", event: "click", handler: () => this.resetVideo()
+ }, {
+ id: "custom-video-background-url-apply", event: "click", handler: () => this.video = document.getElementById("custom-video-background-url").value
}, {
id: "video-volume-slider", event: "input", handler: e => {
syncHTMLValue(e.currentTarget, "video-volume-input");
diff --git a/showcase/src/components/fallback.js b/showcase/src/components/fallback.js
index 40a34e9..ba0e84c 100644
--- a/showcase/src/components/fallback.js
+++ b/showcase/src/components/fallback.js
@@ -4,7 +4,7 @@ import '@/components/fallback.css'
export default class Fallback {
#el = document.createElement("div")
- init(parentEl) {
+ constructor(parentEl) {
alert('WebGL is unavailable. Fallback image will be used.');
const calculateScale = (width, height) => {
return { x: window.innerWidth / width, y: window.innerHeight / height };
diff --git a/showcase/src/components/helper.js b/showcase/src/components/helper.js
index f856187..ab3a3da 100644
--- a/showcase/src/components/helper.js
+++ b/showcase/src/components/helper.js
@@ -42,10 +42,10 @@ export const updateHTMLOptions = (array, id = null) => {
export const addEventListeners = (listeners) => {
listeners.forEach(listener => {
- if (typeof listener.id === "undefined") {
- document.addEventListener(listener.event, e => listener.handler(e))
- } else {
+ if (listener.id) {
document.getElementById(listener.id).addEventListener(listener.event, e => listener.handler(e))
+ } else {
+ document.addEventListener(listener.event, e => listener.handler(e))
}
})
}
diff --git a/showcase/src/components/logo.js b/showcase/src/components/logo.js
index 461b036..6d7c86b 100644
--- a/showcase/src/components/logo.js
+++ b/showcase/src/components/logo.js
@@ -27,10 +27,11 @@ export default class Logo {
hidden: this.#default.hidden,
ratio: this.#default.ratio,
opacity: this.#default.opacity,
- position: {...this.#default.position}
+ position: { ...this.#default.position },
+ name: null
}
- init(el) {
+ constructor(el) {
this.#parentEl = el
this.#el.id = "logo-box"
this.#el.innerHTML = `
@@ -43,34 +44,48 @@ export default class Logo {
this.#updateSizeOnWindowResize()
}
- #updateSizeOnWindowResize() {
- const _this = this
- const resize = () => {
- _this.#resize(_this)
- }
- window.addEventListener("resize", resize, true);
- resize()
- }
-
setImage(src, invertFilter = false) {
this.#imageEl.src = src
this.#resize()
this.#setInvertFilter(invertFilter)
}
- set image(v) {
- const update = (url) => {
- this.setImage(url, false)
- document.getElementById("logo-image-clear").disabled = false
- }
- if (typeof v === "object") {
- readFile(
- v,
- (blobURL) => update(blobURL)
- )
- } else {
- update(v)
- }
+ resetPosition() {
+ this.position = {...this.#default.position}
+ document.getElementById("logo-position-x-slider").value = this.#default.position.x
+ document.getElementById("logo-position-x-input").value = this.#default.position.x
+ document.getElementById("logo-position-y-slider").value = this.#default.position.y
+ document.getElementById("logo-position-y-input").value = this.#default.position.y
+ }
+
+ resetImage() {
+ this.#config.name = null
+ this.setImage(this.#default.location + this.#default.image, this.#default.useInvertFilter)
+ document.getElementById("logo-image-clear").disabled = true
+ }
+
+ resetOpacity() {
+ this.opacity = this.#default.opacity
+ document.getElementById("logo-opacity-slider").value = this.#default.opacity
+ document.getElementById("logo-opacity-input").value = this.#default.opacity
+ }
+
+ resetHidden() {
+ this.hidden = this.#default.hidden
+ }
+
+ resetRatio() {
+ this.ratio = this.#default.ratio
+ document.getElementById("logo-ratio-slider").value = this.#default.ratio
+ document.getElementById("logo-ratio-input").value = this.#default.ratio
+ }
+
+ reset() {
+ this.resetPosition()
+ this.resetImage()
+ this.resetRatio()
+ this.resetOpacity()
+ this.resetHidden()
}
#resize(_this, value) {
@@ -87,6 +102,38 @@ export default class Logo {
}
}
+ #updateLogoPosition() {
+ updateElementPosition(this.#imageEl, this.#config.position)
+ }
+
+ #updateSizeOnWindowResize() {
+ const _this = this
+ const resize = () => {
+ _this.#resize(_this)
+ }
+ window.addEventListener("resize", resize, true);
+ resize()
+ }
+
+ set image(v) {
+ const update = (url, v = null) => {
+ this.#config.name = {
+ isLocalFile: v !== null,
+ value: v ? v.name : url
+ }
+ this.setImage(url, false)
+ document.getElementById("logo-image-clear").disabled = false
+ }
+ if (typeof v === "object") {
+ readFile(
+ v,
+ (blobURL) => update(blobURL, v)
+ )
+ } else {
+ update(v)
+ }
+ }
+
get hidden() {
return this.#config.hidden
}
@@ -140,115 +187,61 @@ export default class Logo {
set position(v) {
if (typeof v !== "object") return;
- if (typeof v.x !== "undefined") this.#config.position.x = v.x;
- if (typeof v.y !== "undefined") this.#config.position.y = v.y;
+ if (v.x) this.#config.position.x = v.x;
+ if (v.y) this.#config.position.y = v.y;
this.#updateLogoPosition()
}
- #updateLogoPosition() {
- updateElementPosition(this.#imageEl, this.#config.position)
- }
-
- logoPadding(key, value) {
- // Note: Back Compatibility
- switch (key) {
- case "x":
- this.position = {
- x: value
+ get backCompatibilityFns() {
+ const _this = this
+ return {
+ setLogoDisplay: (v) => _this.hidden = v,
+ setLogo: _this.setImage,
+ setLogoImage: (e) => _this.image = e.target.files[0],
+ resetLogoImage: _this.resetImage,
+ setLogoRatio: (v) => _this.ratio = v,
+ setLogoOpacity: (v) => _this.opacity = v,
+ logoPadding: (key, value) => {
+ switch (key) {
+ case "x":
+ this.position = {
+ x: value
+ }
+ break;
+ case "y":
+ this.position = {
+ y: value
+ }
+ break;
+ default:
+ this.position = value
+ break;
}
- break;
- case "y":
- this.position = {
- y: value
- }
- break;
- default:
- this.position = value
- break;
+ },
+ logoReset: _this.resetPosition
}
}
- setLogoOpacity(v) {
- // Note: Back Compatibility
- this.opacity = v
- }
-
- setLogoRatio(value) {
- // Note: Back Compatibility
- this.ratio = value
- }
-
- setLogoDisplay(flag) {
- // Note: Back Compatibility
- this.hidden = flag;
- }
-
- setLogo(src, invertFilter) {
- // Note: Back Compatibility
- this.setImage(src, invertFilter)
- }
-
- setLogoImage(e) {
- // Note: Back Compatibility
- this.image = e.target.files[0]
- }
-
- resetPosition() {
- this.position = {...this.#default.position}
- document.getElementById("logo-position-x-slider").value = this.#default.position.x
- document.getElementById("logo-position-x-input").value = this.#default.position.x
- document.getElementById("logo-position-y-slider").value = this.#default.position.y
- document.getElementById("logo-position-y-input").value = this.#default.position.y
- }
-
- logoReset() {
- // Note: Back Compatibility
- this.resetPosition()
- }
-
- resetImage() {
- this.setImage(this.#default.location + this.#default.image, this.#default.useInvertFilter)
- document.getElementById("logo-image-clear").disabled = true
- }
-
- resetLogoImage() {
- // Note: Back Compatibility
- this.resetImage()
- }
-
- resetOpacity() {
- this.setLogoOpacity(this.#default.opacity)
- document.getElementById("logo-opacity-slider").value = this.#default.opacity
- document.getElementById("logo-opacity-input").value = this.#default.opacity
- }
-
- resetHidden() {
- this.hidden = this.#default.hidden
- }
-
- resetRatio() {
- this.ratio = this.#default.ratio
- document.getElementById("logo-ratio-slider").value = this.#default.ratio
- document.getElementById("logo-ratio-input").value = this.#default.ratio
- }
-
- reset() {
- this.resetPosition()
- this.resetImage()
- this.resetRatio()
- this.resetOpacity()
- this.resetHidden()
+ get config() {
+ return {
+ ...this.#config
+ }
}
get HTML() {
return `
-
-
+
+
-
+
+
+
+
+
+
@@ -297,6 +290,8 @@ export default class Logo {
id: "logo-image", event: "change", handler: e => this.image = e.target.files[0]
}, {
id: "logo-image-clear", event: "click", handler: () => this.resetImage()
+ }, {
+ id: "logo-image-url-apply", event: "click", handler: () => this.image = document.getElementById("logo-image-url").value
}, {
id: "logo-ratio-slider", event: "input", handler: e => {
syncHTMLValue(e.currentTarget, "logo-ratio-input");
diff --git a/showcase/src/components/music.js b/showcase/src/components/music.js
index cea21d2..9513408 100644
--- a/showcase/src/components/music.js
+++ b/showcase/src/components/music.js
@@ -30,60 +30,12 @@ export default class Music {
#config = {
useMusic: false,
timeOffset: 0.3,
- volume: 0.5
+ volume: 50,
+ name: null
}
#backgroundObj
- get timeOffset() {
- return this.#config.timeOffset
- }
-
- set timeOffset(value) {
- this.#config.timeOffset = value
- }
-
- get volume() {
- return this.#config.volume * 100
- }
-
- set volume(value) {
- value = value / 100
- this.#config.volume = value
- this.#audio.intro.el.volume = value
- this.#audio.loop.el.volume = value
- }
-
- get musics() {
- return this.#music.list
- }
-
- get useMusic() {
- return this.#config.useMusic
- }
-
- set useMusic(value) {
- this.#config.useMusic = value
- if (value) {
- this.#playMusic()
- } else {
- this.#stopMusic()
- }
- }
-
- get currentMusic() {
- // Note: Back Compatibility
- return this.music
- }
-
- get music() {
- return this.#music.current
- }
-
- get isUsingCustom() {
- return this.#music.isUsingCustom
- }
-
- init(el) {
+ constructor(el) {
this.#parentEl = el
this.#el.id = "music-box"
this.#el.innerHTML = `
@@ -98,13 +50,13 @@ export default class Music {
this.#music.list = Object.keys(this.#music.mapping)
this.#audio.intro.el = document.getElementById(this.#audio.intro.id)
this.#audio.loop.el = document.getElementById(this.#audio.loop.id)
- this.#audio.intro.el.volume = this.#config.volume
- this.#audio.loop.el.volume = this.#config.volume
+ this.#audio.intro.el.volume = this.#volume
+ this.#audio.loop.el.volume = this.#volume
this.#audio.intro.el.ontimeupdate = () => {
if (this.#audio.intro.el.currentTime >= this.#audio.intro.el.duration - this.#config.timeOffset) {
this.#audio.intro.el.pause()
this.#audio.loop.el.currentTime = 0
- this.#audio.loop.el.volume = this.#config.volume
+ this.#audio.loop.el.volume = this.#volume
}
}
this.#audio.loop.el.ontimeupdate = () => {
@@ -123,62 +75,16 @@ export default class Music {
this.#backgroundObj = backgroundObj
}
- changeMusic(name) {
- // Note: Back Compatibility
- this.music = name
- }
-
- set music(name) {
- if (name !== null && name !== this.#music.current) {
- this.#music.current = name
- if (this.#config.useMusic && !this.#music.isUsingCustom) {
- this.#audio.loop.el.pause()
- this.#audio.intro.el.pause()
- this.#playMusic()
- }
- getCurrentHTMLOptions("music-select", name)
- }
- }
-
- set custom(url) {
- const update = (url, type) => {
- this.#setMusic(url, type)
- document.getElementById("custom-music-clear").disabled = false
- }
- if (typeof url === "object") {
- readFile(
- url,
- (blobURL, type) => update(blobURL, type)
- )
- } else {
- update(url, url.split(".").pop())
- }
- }
-
- setMusicFromWE(url) {
- // Note: Back Compatibility
- this.custom = url
- }
-
- setMusic(e) {
- // Note: Back Compatibility
- this.custom = e.target.files[0]
- }
-
reset() {
document.getElementById("custom-music").value = ""
document.getElementById("custom-music-clear").disabled = true
this.#music.isUsingCustom = false
+ this.#config.name = null
if (this.#config.useMusic) {
this.#playMusic()
}
}
- resetMusic() {
- // Note: Back Compatibility
- this.reset()
- }
-
#setMusic(data, type) {
this.#audio.loop.el.src = data
this.#audio.loop.el.querySelector('source').type = type
@@ -200,12 +106,12 @@ export default class Music {
this.#audio.loop.el.volume = 0
this.#audio.loop.el.play()
} else {
- this.#audio.loop.el.volume = this.#config.volume
+ this.#audio.loop.el.volume = this.#volume
this.#audio.loop.el.play()
}
} else {
this.#audio.intro.el.pause()
- this.#audio.loop.el.volume = this.#config.volume
+ this.#audio.loop.el.volume = this.#volume
this.#audio.loop.el.play()
}
}
@@ -215,12 +121,116 @@ export default class Music {
this.#audio.loop.el.pause()
}
+ get timeOffset() {
+ return this.#config.timeOffset
+ }
+
+ set timeOffset(value) {
+ this.#config.timeOffset = value
+ }
+
+ get volume() {
+ return this.#config.volume
+ }
+
+ get #volume() {
+ return this.#config.volume / 100
+ }
+
+ set volume(value) {
+ this.#config.volume = value
+ this.#audio.intro.el.volume = this.#volume
+ if (this.#audio.intro.el.paused) this.#audio.loop.el.volume = this.#volume
+ }
+
+ get musics() {
+ return this.#music.list
+ }
+
+ get useMusic() {
+ return this.#config.useMusic
+ }
+
+ set useMusic(value) {
+ this.#config.useMusic = value
+ if (value) {
+ this.#playMusic()
+ } else {
+ this.#stopMusic()
+ }
+ }
+
+ get music() {
+ return this.#music.current
+ }
+
+ get isUsingCustom() {
+ return this.#music.isUsingCustom
+ }
+
+ set music(name) {
+ if (name !== null && name !== this.#music.current) {
+ this.#music.current = name
+ if (this.#config.useMusic && !this.#music.isUsingCustom) {
+ this.#audio.loop.el.pause()
+ this.#audio.intro.el.pause()
+ this.#playMusic()
+ }
+ getCurrentHTMLOptions("music-select", name)
+ }
+ }
+
+ set custom(url) {
+ const update = (url, type, v = null) => {
+ this.#config.name = {
+ isLocalFile: v !== null,
+ value: v ? v.name : url
+ }
+ this.#setMusic(url, type)
+ document.getElementById("custom-music-clear").disabled = false
+ }
+ if (typeof url === "object") {
+ readFile(
+ url,
+ (blobURL, type) => update(blobURL, type, url)
+ )
+ } else {
+ update(url, url.split(".").pop())
+ }
+ }
+
+ get currentMusic() {
+ // Note: Back Compatibility
+ return this.music
+ }
+
+ changeMusic(name) {
+ // Note: Back Compatibility
+ this.music = name
+ }
+
+ get backCompatibilityFns() {
+ const _this = this
+ return {
+ setMusicFromWE: (url) => _this.custom = url,
+ setMusic: (e) => _this.custom = e.target.files[0],
+ resetMusic: _this.reset
+ }
+ }
+
+ get config() {
+ return {
+ default: this.#music.current,
+ ...this.#config
+ }
+ }
+
get HTML() {
return `
-
-
+
+
-
+
-
+
+
+
+
+
+
@@ -270,6 +285,8 @@ export default class Music {
id: "custom-music", event: "change", handler: e => this.custom = e.target.files[0]
}, {
id: "custom-music-clear", event: "click", handler: () => this.reset()
+ }, {
+ id: "custom-music-url-apply", event: "click", handler: () => this.custom = document.getElementById("custom-music-url").value
}, {
id: "music-volume-slider", event: "input", handler: e => {
syncHTMLValue(e.currentTarget, "music-volume-input");
diff --git a/showcase/src/components/player.js b/showcase/src/components/player.js
index 627437c..7b3d8a7 100644
--- a/showcase/src/components/player.js
+++ b/showcase/src/components/player.js
@@ -32,66 +32,14 @@ export default class Player {
#config = {
fps: this.#default.fps,
useStartAnimation: true,
- padding: {...this.#default.padding},
+ usePadding: false,
+ padding: {
+ ...this.#default.padding
+ },
scale: this.#default.scale
}
- set useStartAnimation(v) {
- this.#config.useStartAnimation = v
- }
-
- get useStartAnimation() {
- return this.#config.useStartAnimation
- }
-
- get spine() {
- return this.#spine
- }
-
- set fps(v) {
- this.#config.fps = v
- this.#spine.setFps(v)
- }
-
- get fps() {
- return this.#config.fps
- }
-
- setFPS(fps) {
- // Note: Back Compatibility
- this.fps = fps
- }
-
- resetFPS() {
- this.fps = this.#default.fps
- document.getElementById("fps-slider").value = this.#default.fps
- document.getElementById("fps-input").value = this.#default.fps
- }
-
- set scale(v) {
- this.#config.scale = 1 / v
- this.#spine.setOperatorScale(1 / v)
- }
-
- setScale(v) {
- // Note: Back Compatibility
- this.scale = v
- }
-
- resetScale() {
- this.scale = this.#default.scale
- }
-
- scaleReset() {
- // Note: Back Compatibility
- this.resetScale()
- }
-
- get scale() {
- return this.#config.scale
- }
-
- init(el) {
+ constructor(el) {
this.#parentEl = el
this.#el.id = "player-box"
insertHTMLChild(this.#parentEl, this.#el)
@@ -144,7 +92,6 @@ export default class Player {
entry.mixDuration = 0.3;
widget.animationState.addAnimation(0, "Idle", true, 0);
}
- _this.success()
document.dispatchEvent(PlayerReadyEvent);
},
}
@@ -161,8 +108,33 @@ export default class Player {
updateHTMLOptions(this.#spine.skeleton.data.animations.map(e => e.name), "animation-selection")
}
- get node() {
- return this.#el
+ resetPadding() {
+ this.padding = {...this.#default.padding}
+ document.getElementById("position-padding-left-slider").value = this.#default.padding.left
+ document.getElementById("position-padding-left-input").value = this.#default.padding.left
+ document.getElementById("position-padding-right-slider").value = this.#default.padding.right
+ document.getElementById("position-padding-right-input").value = this.#default.padding.right
+ document.getElementById("position-padding-top-slider").value = this.#default.padding.top
+ document.getElementById("position-padding-top-input").value = this.#default.padding.top
+ document.getElementById("position-padding-bottom-slider").value = this.#default.padding.bottom
+ document.getElementById("position-padding-bottom-input").value = this.#default.padding.bottom
+ }
+
+ resetScale() {
+ this.scale = this.#default.scale
+ }
+
+ resetFPS() {
+ this.fps = this.#default.fps
+ document.getElementById("fps-slider").value = this.#default.fps
+ document.getElementById("fps-input").value = this.#default.fps
+ }
+
+ reset() {
+ this.resetFPS()
+ this.resetPadding()
+ this.resetScale()
+ this.#spine.play()
}
#loadViewport() {
@@ -174,9 +146,46 @@ export default class Player {
})
}
- loadViewport() {
- // Note: Back Compatibility
- this.#loadViewport()
+ get usePadding() {
+ return this.#config.usePadding
+ }
+
+ set usePadding(v) {
+ this.#config.usePadding = v
+ }
+
+ set useStartAnimation(v) {
+ this.#config.useStartAnimation = v
+ }
+
+ get useStartAnimation() {
+ return this.#config.useStartAnimation
+ }
+
+ get spine() {
+ return this.#spine
+ }
+
+ set fps(v) {
+ this.#config.fps = v
+ this.#spine.setFps(v)
+ }
+
+ get fps() {
+ return this.#config.fps
+ }
+
+ set scale(v) {
+ this.#config.scale = 1 / v
+ this.#spine.setOperatorScale(1 / v)
+ }
+
+ get scale() {
+ return this.#config.scale
+ }
+
+ get node() {
+ return this.#el
}
get padLeft() {
@@ -225,64 +234,56 @@ export default class Player {
set padding(v) {
if (typeof v !== "object") return;
- if (typeof v.left !== "undefined") this.#config.padding.left = v.left;
- if (typeof v.right !== "undefined") this.#config.padding.right = v.right ;
- if (typeof v.top !== "undefined") this.#config.padding.top = v.top;
- if (typeof v.bottom !== "undefined") this.#config.padding.bottom = v.bottom;
+ if (v.left) this.#config.padding.left = v.left;
+ if (v.right) this.#config.padding.right = v.right ;
+ if (v.top) this.#config.padding.top = v.top;
+ if (v.bottom) this.#config.padding.bottom = v.bottom;
this.#loadViewport()
}
- positionPadding(key, value) {
- // Note: Back Compatibility
- switch (key) {
- case "left":
- this.padding = {
- left: value
+ get backCompatibilityFns() {
+ const _this = this
+ return {
+ spinePlayer: _this.#spine,
+ setFPS: (fps) => _this.fps = fps,
+ loadViewport: _this.#loadViewport,
+ setScale: (v) => this.scale = v,
+ scale: _this.scale,
+ positionPadding: (key, value) => {
+ switch (key) {
+ case "left":
+ this.padding = {
+ left: value
+ }
+ break;
+ case "right":
+ this.padding = {
+ right: value
+ }
+ break;
+ case "top":
+ this.padding = {
+ top: value
+ }
+ break;
+ case "bottom":
+ this.padding = {
+ bottom: value
+ }
+ break;
+ default:
+ this.#config.padding = value
+ break;
}
- break;
- case "right":
- this.padding = {
- right: value
- }
- break;
- case "top":
- this.padding = {
- top: value
- }
- break;
- case "bottom":
- this.padding = {
- bottom: value
- }
- break;
- default:
- this.#config.padding = value
- break;
+ },
+ positionReset: _this.resetPadding,
+ scaleReset: _this.resetScale,
+ useStartAnimation: _this.useStartAnimation
}
}
- resetPadding() {
- this.padding = {...this.#default.padding}
- document.getElementById("position-padding-left-slider").value = this.#default.padding.left
- document.getElementById("position-padding-left-input").value = this.#default.padding.left
- document.getElementById("position-padding-right-slider").value = this.#default.padding.right
- document.getElementById("position-padding-right-input").value = this.#default.padding.right
- document.getElementById("position-padding-top-slider").value = this.#default.padding.top
- document.getElementById("position-padding-top-input").value = this.#default.padding.top
- document.getElementById("position-padding-bottom-slider").value = this.#default.padding.bottom
- document.getElementById("position-padding-bottom-input").value = this.#default.padding.bottom
- }
-
- positionReset() {
- // Note: Back Compatibility
- this.resetPadding()
- }
-
- reset() {
- this.resetFPS()
- this.resetPadding()
- this.resetScale()
- this.#spine.play()
+ get config() {
+ return {...this.#config}
}
get HTML() {
@@ -310,8 +311,8 @@ export default class Player {
-
-
+
+
@@ -398,6 +399,7 @@ export default class Player {
}, {
id: "position", event: "click", handler: e => {
showRelatedHTML(e.currentTarget, "position-realted");
+ this.usePadding = e.currentTarget.checked;
if (!e.currentTarget.checked) this.resetPadding();
}
}, {
diff --git a/showcase/src/components/voice.js b/showcase/src/components/voice.js
index fc26e84..c4b5cf8 100644
--- a/showcase/src/components/voice.js
+++ b/showcase/src/components/voice.js
@@ -50,7 +50,7 @@ export default class Voice {
useSubtitle: false,
useVoice: false,
useVoiceActor: false,
- voiceLang: null,
+ language: null,
subtitle: {
language: this.#default.region,
...this.#default.subtitle
@@ -59,11 +59,188 @@ export default class Voice {
}
#playerObj
- constructor() {
+ constructor(el) {
this.#default.language.voice = this.#voice.languages[0]
- this.#config.voiceLang = this.#default.language.voice
+ this.#config.language = this.#default.language.voice
this.#voice.locations = this.#getVoiceLocations()
this.#voice.list = Object.keys(this.#getVoices())
+
+ this.#parentEl = el
+ this.#el.id = "voice-box"
+ this.#el.hidden = true
+ this.#el.innerHTML = `
+
+
+
+ `
+ insertHTMLChild(this.#parentEl, this.#el)
+ }
+
+ success() {
+ const audioEndedFunc = () => {
+ this.#audio.isPlaying = false
+ this.#setCurrentSubtitle(null)
+ this.#audio.lastClickToNext = false
+ }
+ this.#audio.el.addEventListener('ended', audioEndedFunc)
+ this.#playEntryVoice()
+ this.#initNextVoiceTimer()
+ this.#playerObj.node.addEventListener('click', () => {
+ this.#audio.lastClickToNext = true
+ this.#nextVoice()
+ })
+ document.addEventListener('mousemove', () => {
+ if (this.#voice.listener.idle === -1) {
+ this.#initIdleVoiceTimer()
+ }
+ })
+ }
+
+ link(playerObj) {
+ this.#playerObj = playerObj
+ }
+
+ resetPosition() {
+ this.position = {...this.#default.subtitle}
+ document.getElementById("subtitle-padding-x-slider").value = this.#default.subtitle.x
+ document.getElementById("subtitle-padding-x-input").value = this.#default.subtitle.x
+ document.getElementById("subtitle-padding-y-slider").value = this.#default.subtitle.y
+ document.getElementById("subtitle-padding-y-input").value = this.#default.subtitle.y
+ }
+
+ reset() {
+ this.resetPosition()
+ }
+
+ #getVoiceLocations() {
+ const folders = JSON.parse(import.meta.env.VITE_VOICE_FOLDERS)
+ const customVoiceName = this.#voice.languages.filter(i => !folders.sub.map(e => e.lang).includes(i))[0]
+ folders.sub = folders.sub.map(e => {
+ return {
+ name: e.name,
+ lang: e.lang === "CUSTOM" ? customVoiceName : e.lang
+ }
+ })
+ return folders
+ }
+
+ #getVoices() {
+ return charword_table.subtitleLangs[this.#config.subtitle.language].default
+ }
+
+ #playEntryVoice() {
+ this.#playSpecialVoice("问候")
+ }
+
+ #playSpecialVoice(matcher) {
+ const voices = this.#getVoices()
+ const voiceId = Object.keys(voices).find(e => voices[e].title === matcher)
+ this.#playVoice(voiceId)
+ }
+
+ #playVoice(id) {
+ if (!this.useVoice) return
+ this.#voice.id.last = this.#voice.id.current
+ this.#voice.id.current = id
+ this.#audio.el.src = `./assets/${this.#getVoiceLocation()
+ }/${id}.ogg`
+ let startPlayPromise = this.#audio.el.play()
+ if (startPlayPromise !== undefined) {
+ startPlayPromise
+ .then(() => {
+ this.#audio.isPlaying = true
+ this.#setCurrentSubtitle(id)
+ })
+ .catch(() => {
+ return
+ })
+ }
+ }
+
+ #getVoiceLocation() {
+ const locations = this.#voice.locations
+ return `${locations.main}/${locations.sub.find(e => e.lang === this.#config.language).name}`
+ }
+
+ #setCurrentSubtitle(id) {
+ if (id === null) {
+ setTimeout(() => {
+ if (this.#audio.isPlaying) return
+ this.#toggleSubtitle(0)
+ }, 5 * 1000);
+ return
+ }
+ const subtitle = this.#getSubtitleById(id)
+ const title = subtitle.title
+ const content = subtitle.text
+ const cvInfo = charword_table.voiceLangs[this.subtitleLanguage][this.#config.language]
+ document.getElementById('voice-title').innerText = title
+ document.getElementById('voice-subtitle').innerText = content
+ document.getElementById('voice-actor-name').innerText = cvInfo.join('')
+ if (this.#audio.isPlaying) {
+ this.#toggleSubtitle(1)
+ }
+ }
+
+ #toggleSubtitle(v) {
+ this.#el.style.opacity = v ? 1 : 0
+ }
+
+ #getSubtitleById(id) {
+ const obj = charword_table.subtitleLangs[this.#config.subtitle.language]
+ let key = 'default'
+ if (obj[this.#config.language]) {
+ key = this.#config.language
+ }
+ return obj[key][id]
+ }
+
+ #getSubtitleLanguages() {
+ return Object.keys(charword_table.subtitleLangs)
+ }
+
+ #updateSubtitlePosition() {
+ updateElementPosition(this.#el, {
+ x: this.position.x,
+ y: this.position.y - 100
+ })
+ }
+
+ #initNextVoiceTimer() {
+ this.#voice.listener.next = setInterval(() => {
+ if (!this.#voice.lastClickToNext) {
+ this.#nextVoice()
+ }
+ }, this.#config.duration.next)
+ }
+
+ #nextVoice() {
+ const getVoiceId = () => {
+ const id = this.#voice.list[Math.floor((Math.random() * this.#voice.list.length))]
+ return id === this.#voice.id.last ? getVoiceId() : id
+ }
+ this.#playVoice(getVoiceId())
+ }
+
+ #initIdleVoiceTimer() {
+ this.#voice.listener.idle = setInterval(() => {
+ this.#playSpecialVoice("闲置")
+ clearInterval(this.#voice.listener.idle)
+ this.#voice.listener.idle = -1
+ }, this.#config.duration.idle)
}
set useSubtitle(show) {
@@ -143,11 +320,76 @@ export default class Voice {
set position(v) {
if (typeof v !== "object") return;
- if (typeof v.x !== "undefined") this.#config.subtitle.x = v.x;
- if (typeof v.y !== "undefined") this.#config.subtitle.y = v.y;
+ if (v.x) this.#config.subtitle.x = v.x;
+ if (v.y) this.#config.subtitle.y = v.y;
this.#updateSubtitlePosition()
}
+ set language(lang) {
+ if (this.#voice.languages.includes(lang)) {
+ this.#config.language = lang
+ } else {
+ this.#config.language = this.#default.language.voice
+ }
+ const availableSubtitleLang = this.#getSubtitleLanguages()
+ if (!availableSubtitleLang.includes(this.#config.subtitle.language)) {
+ this.#config.subtitle.language = availableSubtitleLang[0]
+ }
+ }
+
+ get language() {
+ return this.#config.language
+ }
+
+ get languages() {
+ return this.#voice.languages
+ }
+
+ get duration() {
+ return {
+ idle: this.#config.duration.idle / 60 / 1000,
+ next: this.#config.duration.next / 60 / 1000
+ }
+ }
+
+ set duration(v) {
+ if (typeof v !== "object") return;
+ if (v.idle) {
+ clearInterval(this.#voice.listener.idle)
+ if (v.idle !== 0) {
+ this.#config.duration.idle = parseInt(v.idle) * 60 * 1000
+ this.#initIdleVoiceTimer()
+ }
+ }
+ if (v.next) {
+ clearInterval(this.#voice.listener.next)
+ if (v.next !== 0) {
+ this.#config.duration.next = parseInt(v.next) * 60 * 1000
+ this.#initNextVoiceTimer()
+ }
+ }
+ }
+
+ get durationIdle() {
+ return this.duration.idle
+ }
+
+ set durationIdle(duration) {
+ this.duration = {
+ idle: duration
+ }
+ }
+
+ set durationNext(duration) {
+ this.duration = {
+ next: duration
+ }
+ }
+
+ get durationNext() {
+ return this.duration.next
+ }
+
set subtitleX(x) {
// Note: Back Compatibility
this.position = {
@@ -172,61 +414,6 @@ export default class Voice {
return this.position.y
}
- set language(lang) {
- if (this.#voice.languages.includes(lang)) {
- this.#config.voiceLang = lang
- } else {
- this.#config.voiceLang = this.#default.language.voice
- }
- const availableSubtitleLang = this.#getSubtitleLanguages()
- if (!availableSubtitleLang.includes(this.#config.subtitle.language)) {
- this.#config.subtitle.language = availableSubtitleLang[0]
- }
- }
-
- get language() {
- return this.#config.voiceLang
- }
-
- get languages() {
- return this.#voice.languages
- }
-
- get duration() {
- return {
- idle: this.#config.duration.idle / 60 / 1000,
- next: this.#config.duration.next / 60 / 1000
- }
- }
-
- set duration(v) {
- if (typeof v !== "object") return;
- if (typeof v.idle !== "undefined") {
- clearInterval(this.#voice.listener.idle)
- if (v.idle !== 0) {
- this.#config.duration.idle = parseInt(v.idle) * 60 * 1000
- this.#initIdleVoiceTimer()
- }
- }
- if (typeof v.next !== "undefined") {
- clearInterval(this.#voice.listener.next)
- if (v.next !== 0) {
- this.#config.duration.next = parseInt(v.next) * 60 * 1000
- this.#initNextVoiceTimer()
- }
- }
- }
-
- get durationIdle() {
- return this.duration.idle
- }
-
- set durationIdle(duration) {
- this.duration = {
- idle: duration
- }
- }
-
set idleDuration(duration) {
// Note: Back Compatibility
this.duration = {
@@ -239,211 +426,26 @@ export default class Voice {
return this.duration.idle
}
- set durationNext(duration) {
- this.duration = {
- next: duration
- }
- }
-
set nextDuration(duration) {
// Note: Back Compatibility
this.duration.next = duration
}
- get durationNext() {
- return this.duration.next
- }
-
get nextDuration() {
// Note: Back Compatibility
return this.duration.next
}
- init(el) {
- this.#parentEl = el
- this.#el.id = "voice-box"
- this.#el.hidden = true
- this.#el.innerHTML = `
-
-
-
- `
- insertHTMLChild(this.#parentEl, this.#el)
- }
-
- success() {
- const audioEndedFunc = () => {
- this.#audio.isPlaying = false
- this.#setCurrentSubtitle(null)
- this.#audio.lastClickToNext = false
- }
- this.#audio.el.addEventListener('ended', audioEndedFunc)
- this.#playEntryVoice()
- this.#initNextVoiceTimer()
- this.#playerObj.node.addEventListener('click', () => {
- this.#audio.lastClickToNext = true
- this.#nextVoice()
- })
- document.addEventListener('mousemove', () => {
- if (this.#voice.listener.idle === -1) {
- this.#initIdleVoiceTimer()
- }
- })
- }
-
- link(playerObj) {
- this.#playerObj = playerObj
- }
-
- #getVoiceLocations() {
- const folders = JSON.parse(import.meta.env.VITE_VOICE_FOLDERS)
- const customVoiceName = this.#voice.languages.filter(i => !folders.sub.map(e => e.lang).includes(i))[0]
- folders.sub = folders.sub.map(e => {
- return {
- name: e.name,
- lang: e.lang === "CUSTOM" ? customVoiceName : e.lang
- }
- })
- return folders
- }
-
- #getVoices() {
- return charword_table.subtitleLangs[this.#config.subtitle.language].default
- }
-
- #playEntryVoice() {
- this.#playSpecialVoice("问候")
- }
-
- #playSpecialVoice(matcher) {
- const voices = this.#getVoices()
- const voiceId = Object.keys(voices).find(e => voices[e].title === matcher)
- this.#playVoice(voiceId)
- }
-
- #playVoice(id) {
- if (!this.useVoice) return
- this.#voice.id.last = this.#voice.id.current
- this.#voice.id.current = id
- this.#audio.el.src = `./assets/${this.#getVoiceLocation()
- }/${id}.ogg`
- let startPlayPromise = this.#audio.el.play()
- if (startPlayPromise !== undefined) {
- startPlayPromise
- .then(() => {
- this.#audio.isPlaying = true
- this.#setCurrentSubtitle(id)
- })
- .catch(() => {
- return
- })
- }
- }
-
- #getVoiceLocation() {
- const locations = this.#voice.locations
- return `${locations.main}/${locations.sub.find(e => e.lang === this.#config.voiceLang).name}`
- }
-
- #setCurrentSubtitle(id) {
- if (id === null) {
- setTimeout(() => {
- if (this.#audio.isPlaying) return
- this.#toggleSubtitle(0)
- }, 5 * 1000);
- return
- }
- const subtitle = this.#getSubtitleById(id)
- const title = subtitle.title
- const content = subtitle.text
- const cvInfo = charword_table.voiceLangs[this.subtitleLanguage][this.#config.voiceLang]
- document.getElementById('voice-title').innerText = title
- document.getElementById('voice-subtitle').innerText = content
- document.getElementById('voice-actor-name').innerText = cvInfo.join('')
- if (this.#audio.isPlaying) {
- this.#toggleSubtitle(1)
- }
- }
-
- #toggleSubtitle(v) {
- this.#el.style.opacity = v ? 1 : 0
- }
-
- #getSubtitleById(id) {
- const obj = charword_table.subtitleLangs[this.#config.subtitle.language]
- let key = 'default'
- if (obj[this.#config.voiceLang]) {
- key = this.#config.voiceLang
- }
- return obj[key][id]
- }
-
- #getSubtitleLanguages() {
- return Object.keys(charword_table.subtitleLangs)
- }
-
- #updateSubtitlePosition() {
- updateElementPosition(this.#el, {
- x: this.position.x,
- y: this.position.y - 100
- })
- }
-
- #initNextVoiceTimer() {
- this.#voice.listener.next = setInterval(() => {
- if (!this.#voice.lastClickToNext) {
- this.#nextVoice()
- }
- }, this.#config.duration.next)
- }
-
- #nextVoice() {
- const getVoiceId = () => {
- const id = this.#voice.list[Math.floor((Math.random() * this.#voice.list.length))]
- return id === this.#voice.id.last ? getVoiceId() : id
- }
- this.#playVoice(getVoiceId())
- }
-
- #initIdleVoiceTimer() {
- this.#voice.listener.idle = setInterval(() => {
- this.#playSpecialVoice("闲置")
- clearInterval(this.#voice.listener.idle)
- this.#voice.listener.idle = -1
- }, this.#config.duration.idle)
- }
-
- resetPosition() {
- this.position = {...this.#default.subtitle}
- document.getElementById("subtitle-padding-x-slider").value = this.#default.subtitle.x
- document.getElementById("subtitle-padding-x-input").value = this.#default.subtitle.x
- document.getElementById("subtitle-padding-y-slider").value = this.#default.subtitle.y
- document.getElementById("subtitle-padding-y-input").value = this.#default.subtitle.y
- }
-
- reset() {
- this.resetPosition()
+ get config() {
+ return {...this.#config}
}
get HTML() {
return `