280 lines
9.5 KiB
JavaScript
280 lines
9.5 KiB
JavaScript
import { useState, useEffect } from 'preact/hooks'
|
|
import '@/components/settings.css'
|
|
import { subscribe, unsubscribe, publish } from '@/libs/events'
|
|
|
|
const getPercentage = (value) => parseInt(value.replace("%", ""))
|
|
const defaultBackgroundImage = getComputedStyle(document.body).backgroundImage
|
|
|
|
export default function Settings({
|
|
spinePlayer, setShowSettings, hidden, logoEl
|
|
}) {
|
|
const defaultFps = 60
|
|
const defaultRatio = 61.8
|
|
const defaultOpacity = import.meta.env.VITE_OPACITY
|
|
const defaultShowLogo = false
|
|
const defaultInvertFilter = import.meta.env.VITE_INVERT_FILTER === "true"
|
|
const [defaultLogoImage, setDefaultLogoImage] = useState(null)
|
|
const defaultPadLeft = getPercentage(`${import.meta.env.VITE_VIEWPORT_LEFT}%`)
|
|
const defaultPadRight = getPercentage(`${import.meta.env.VITE_VIEWPORT_RIGHT}%`)
|
|
const defaultPadTop = getPercentage(`${import.meta.env.VITE_VIEWPORT_TOP}%`)
|
|
const defaultPadBottom = getPercentage(`${import.meta.env.VITE_VIEWPORT_BOTTOM}%`)
|
|
const defaultViewport = {
|
|
debugRender: false,
|
|
padLeft: `${defaultPadLeft}%`,
|
|
padRight: `${defaultPadRight}%`,
|
|
padTop: `${defaultPadTop}%`,
|
|
padBottom: `${defaultPadBottom}%`,
|
|
x: 0,
|
|
y: 0,
|
|
}
|
|
|
|
const [fps, setFps] = useState(defaultFps)
|
|
const [ratio, setRatio] = useState(defaultRatio)
|
|
const [opacity, setOpacity] = useState(defaultOpacity)
|
|
const [padLeft, setPadLeft] = useState(defaultPadLeft)
|
|
const [padRight, setPadRight] = useState(defaultPadRight)
|
|
const [padTop, setPadTop] = useState(defaultPadTop)
|
|
const [padBottom, setPadBottom] = useState(defaultPadBottom)
|
|
|
|
const [showLogo, setShowLogo] = useState(defaultShowLogo)
|
|
const [hidePositionSettings, setHidePositionSettings] = useState(true)
|
|
const [backgroundClearDisabled, setBackgroundClearDisabled] = useState(true)
|
|
const [logoClearDisabled, setLogoClearDisabled] = useState(true)
|
|
|
|
const [isPlaying, setIsPlaying] = useState(true)
|
|
|
|
const resize = (value) => {
|
|
logoEl.width = window.innerWidth / 2 * (value || ratio) / 100
|
|
}
|
|
|
|
const readFile = (e, onload, callback) => {
|
|
const file = e.target.files[0]
|
|
if (!file) return
|
|
const reader = new FileReader()
|
|
reader.readAsDataURL(file);
|
|
reader.onload = readerEvent => onload(readerEvent)
|
|
callback()
|
|
}
|
|
|
|
const setFPS = (value) => {
|
|
setFps(value)
|
|
spinePlayer.setFps(value)
|
|
}
|
|
|
|
const setLogoDisplay = (flag) => {
|
|
setShowLogo(flag)
|
|
logoEl.hidden = flag;
|
|
}
|
|
|
|
const setLogoImage = (e) => {
|
|
readFile(
|
|
e,
|
|
(readerEvent) => {
|
|
const content = readerEvent.target.result;
|
|
logoEl.src = content
|
|
resize()
|
|
setLogoInvertFilter(false)
|
|
},
|
|
() => setLogoClearDisabled(false)
|
|
)
|
|
}
|
|
|
|
const resetLogoImage = () => {
|
|
logoEl.src = defaultLogoImage
|
|
resize()
|
|
setLogoInvertFilter(defaultInvertFilter)
|
|
setLogoClearDisabled(true)
|
|
}
|
|
|
|
const setLogoRatio = (value) => {
|
|
setRatio(value)
|
|
resize(value)
|
|
}
|
|
|
|
const setLogoOpacity = (value) => {
|
|
setOpacity(value)
|
|
logoEl.style.opacity = value / 100
|
|
}
|
|
|
|
const setLogoInvertFilter = (flag) => {
|
|
if (!flag) {
|
|
logoEl.style.filter = "invert(0)"
|
|
} else {
|
|
logoEl.style.filter = "invert(1)"
|
|
}
|
|
}
|
|
|
|
const setBackground = (e) => {
|
|
readFile(
|
|
e,
|
|
(readerEvent) => {
|
|
const content = readerEvent.target.result;
|
|
document.body.style.backgroundImage = `url("${content}")`
|
|
},
|
|
() => setBackgroundClearDisabled(false)
|
|
)
|
|
}
|
|
|
|
const resetBackground = () => {
|
|
document.body.style.backgroundImage = defaultBackgroundImage
|
|
setBackgroundClearDisabled(true)
|
|
}
|
|
|
|
const positionPadding = (key, value) => {
|
|
switch (key) {
|
|
case "left":
|
|
setPadLeft(value)
|
|
spinePlayer.updateViewport({
|
|
...defaultViewport,
|
|
padLeft: `${value}%`,
|
|
})
|
|
break;
|
|
case "right":
|
|
setPadRight(value)
|
|
spinePlayer.updateViewport({
|
|
...defaultViewport,
|
|
padRight: `${value}%`,
|
|
})
|
|
break;
|
|
case "top":
|
|
setPadTop(value)
|
|
spinePlayer.updateViewport({
|
|
...defaultViewport,
|
|
padTop: `${value}%`,
|
|
})
|
|
break;
|
|
case "bottom":
|
|
setPadBottom(value)
|
|
spinePlayer.updateViewport({
|
|
...defaultViewport,
|
|
padBottom: `${value}%`,
|
|
})
|
|
break;
|
|
}
|
|
}
|
|
|
|
const positionReset = () => {
|
|
setPadLeft(defaultPadLeft)
|
|
setPadRight(defaultPadRight)
|
|
setPadTop(defaultPadTop)
|
|
setPadBottom(defaultPadBottom)
|
|
spinePlayer.updateViewport(defaultViewport)
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (logoEl) {
|
|
resize()
|
|
setLogoInvertFilter(defaultInvertFilter)
|
|
setLogoOpacity(defaultOpacity)
|
|
setDefaultLogoImage(logoEl.src)
|
|
}
|
|
}, [logoEl])
|
|
|
|
useEffect(() => {
|
|
window.addEventListener("contextmenu", e => e.preventDefault());
|
|
document.addEventListener("gesturestart", e => e.preventDefault());
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
window.addEventListener("resize", resize, true);
|
|
return () => {
|
|
window.removeEventListener("resize", resize, true);
|
|
}
|
|
}, [])
|
|
|
|
return (
|
|
<div class="website-settings" hidden={hidden}>
|
|
<div>
|
|
<label for="fps">FPS</label>
|
|
<input type="range" min="1" max="60" value={fps} step="1"
|
|
onChange={(e) => setFPS(e.target.value)}
|
|
/>
|
|
<input type="number" id="fps_input" min="1" max="60" name="fps" value={fps}
|
|
onChange={(e) => setFPS(e.target.value)}
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label for="operator_logo">Operator Logo</label>
|
|
<input type="checkbox" name="operator_logo" checked={!showLogo} onClick={(e) => setLogoDisplay(!e.target.checked)} />
|
|
<div hidden={showLogo}>
|
|
<div>
|
|
<label for="logo_image">Logo Image (Store Locally)</label>
|
|
<input type="file" onChange={(e) => setLogoImage(e)} />
|
|
<button type="button" disabled={logoClearDisabled} onClick={() => resetLogoImage()}>Clear</button>
|
|
</div>
|
|
<div>
|
|
<label for="logo_ratio">Logo Ratio</label>
|
|
<input type="range" min="0" max="100" step="0.1" value={ratio} onChange={(e) => setLogoRatio(e.target.value)} />
|
|
<input type="number" name="logo_ratio" value={ratio} onChange={(e) => setLogoRatio(e.target.value)} />
|
|
</div>
|
|
<div>
|
|
<label for="logo_opacity">Logo Opacity</label>
|
|
<input type="range" min="0" max="100" step="1" id="logo_opacity_slider" value={opacity} onChange={(e) => setLogoOpacity(e.target.value)} />
|
|
<input type="number" id="logo_opacity_input" name="logo_opacity" value={opacity} onOpacity={(e) => setLogoOpacity(e.target.value)} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<label for="background_image">Background Image (Store Locally)</label>
|
|
<input type="file" onChange={(e) => setBackground(e)} />
|
|
<button type="button" disabled={backgroundClearDisabled} onClick={() => resetBackground()}>Clear</button>
|
|
</div>
|
|
<div>
|
|
<label for="position">Position</label>
|
|
<input type="checkbox" name="position" checked={!hidePositionSettings} onClick={(e) => setHidePositionSettings(!e.target.checked)} />
|
|
<div hidden={hidePositionSettings}>
|
|
<div>
|
|
<label for="position_padding_left">Padding Left</label>
|
|
<input type="range" min="-100" max="100" value={padLeft} onChange={(e) => positionPadding("left", e.target.value)} />
|
|
<input type="number" name="position_padding_left" value={padLeft} onChange={(e) => positionPadding("left", e.target.value)} />
|
|
</div>
|
|
<div>
|
|
<label for="position_padding_right">Padding Right</label>
|
|
<input type="range" min="-100" max="100" value={padRight} onChange={(e) => positionPadding("right", e.target.value)} />
|
|
<input type="number" name="position_padding_right" value={padRight} onChange={(e) => positionPadding("right", e.target.value)} />
|
|
</div>
|
|
<div>
|
|
<label for="position_padding_top">Padding Top</label>
|
|
<input type="range" min="-100" max="100" value={padTop} onChange={(e) => positionPadding("top", e.target.value)} />
|
|
<input type="number" name="position_padding_top" value={padTop} onChange={(e) => positionPadding("top", e.target.value)} />
|
|
</div>
|
|
<div>
|
|
<label for="position_padding_bottom">Padding Bottom</label>
|
|
<input type="range" min="-100" max="100" value={padBottom} onChange={(e) => positionPadding("bottom", e.target.value)} />
|
|
<input type="number" name="position_padding_bottom" value={padBottom} onChange={(e) => positionPadding("bottom", e.target.value)} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<button type="button" disabled={isPlaying}
|
|
onClick={() => {
|
|
spinePlayer.play()
|
|
setIsPlaying(true)
|
|
}}
|
|
>Play
|
|
</button>
|
|
<button type="button" disabled={!isPlaying}
|
|
onClick={() => {
|
|
spinePlayer.pause()
|
|
setIsPlaying(false)
|
|
}}
|
|
>
|
|
Pause
|
|
</button>
|
|
<button type="button" onClick={() => {
|
|
setFPS(defaultFps)
|
|
setLogoDisplay(defaultShowLogo)
|
|
resetLogoImage()
|
|
setLogoRatio(defaultRatio)
|
|
setLogoOpacity(defaultOpacity)
|
|
resetBackground()
|
|
positionReset()
|
|
spinePlayer.play()
|
|
}}>
|
|
Reset
|
|
</button>
|
|
<button type="button" onClick={() => setShowSettings(false)}>Close</button>
|
|
</div>
|
|
</div>
|
|
)
|
|
} |