1
0
basics/stfx/ui/html/generic.html
2025-06-25 17:35:39 +01:00

160 lines
3.8 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<title>Generic STFX UI</title>
<style>
* {
box-sizing: border-box;
}
body {
display: grid;
grid-template-areas: "header" "params";
grid-template-rows: 3em 1fr;
margin: 0;
padding: 0;
width: 100vw;
max-width: 100vw;
height: 100vh;
max-height: 100vh;
overflow: hidden;
background: linear-gradient(#FFF, #EEE, #CCC);
color: #000;
text-align: center;
word-wrap: break-word;
font-family: Bahnschrift, 'DIN Alternate', 'Alte DIN 1451 Mittelschrift', 'D-DIN', 'OpenDin', 'Clear Sans', 'Barlow', 'Abel', 'Franklin Gothic Medium', system-ui, sans-serif;
/* no text selectable by default */
user-select: none;
}
output {
font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, 'DejaVu Sans Mono', monospace;
font-weight: normal;
color: #000;
}
#header {
grid-area: header;
display: flex;
justify-content: center;
}
#params {
grid-area: params;
display: flex;
align-content: space-around;
justify-content: space-evenly;
flex-wrap: wrap;
padding: 1rem;
gap: 1rem;
}
.param-range {
display: grid;
width: 3.5rem;
grid-template-areas: "dial" "name";
grid-template-rows: 3.5rem 1fr;
}
.param-range-value {
position: relative;
grid-area: dial;
width: 3.5rem;
height: 3.5rem;
}
.param-range-dial {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
}
.param-range-text {
position: absolute;
top: 30%;
left: 0;
width: 100%;
}
.param-range-units {
position: absolute;
top: 60%;
left: 0;
width: 100%;
color: #666;
font-size: 0.85rem;
}
.param-range-name {
grid-area: name;
}
</style>
</head>
<body>
<h1 id="header">{name}</h1>
<section id="params">
<template @foreach>
<label class="param-range" @if="${d => d.$type == 'ParamRange'}">
<div class="param-range-name">{name}</div>
<script>
function move(data, dx, dy) {
data.rangeUnit = Math.min(1, Math.max(0, data.rangeUnit - dy/250));
}
function press(data, count) {
if (count == 2) data.value = data.defaultValue;
}
</script>
<div class="param-range-value" $move="${move}" $press="${press}">
<canvas class="param-range-dial" $update="${drawDial}"></canvas>
<output class="param-range-text">{text}</output><br>
<div class="param-range-units">{textUnits}</div>
</div>
</label>
</template>
</section>
<script>
function drawDial(data, canvas) {
let scale = window.devicePixelRatio;
let pixels = canvas.offsetWidth*scale;
if (canvas.width != pixels) canvas.width = pixels;
if (canvas.height != pixels) canvas.height = pixels;
let context = canvas.getContext('2d');
context.resetTransform();
context.clearRect(0, 0, pixels, pixels);
context.scale(pixels/2, pixels/2);
context.translate(1, 1);
context.beginPath();
context.ellipse(0, 0, 1, 1, 0, 0, Math.PI*2);
context.fillStyle = '#FFF';
context.fill();
context.beginPath();
context.ellipse(0, 0, 0.9, 0.9, 0, Math.PI*-1.25, Math.PI*(-1.249 + data.rangeUnit*1.499));
context.strokeStyle = '#000';
context.lineWidth = 0.2;
context.lineCap = 'round';
context.stroke();
}
</script>
<script src="cbor.min.js"></script>
<script src="matsui-bundle.min.js"></script>
<script>
let state = Matsui.replace(document.body, {name: "..."});
state.trackMerges(merge => {
window.parent.postMessage(CBOR.encode(merge), '*');
});
addEventListener('message', e => {
state.merge(CBOR.decode(e.data));
});
window.parent.postMessage(CBOR.encode("ready"), '*');
</script>
</body>
</html>