feat: improve html/js (similar style, add animations, center columns)

This commit is contained in:
Mark 2025-10-29 14:55:10 +01:00
parent 5bb76c99d3
commit 87836948ba

View File

@ -4,6 +4,7 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="dark"> <meta name="color-scheme" content="dark">
<link rel="icon" href="./favicon.ico">
<title>ulm öpnv abfahrten</title> <title>ulm öpnv abfahrten</title>
<style> <style>
.dbflex { .dbflex {
@ -38,7 +39,7 @@
<body> <body>
<a id="nojslink" href="./nojs" style="position:fixed"><br>no javascript? use the nojs version</a> <a id="nojslink" href="./nojs" style="position:fixed"><br>no javascript? use the nojs version</a>
<div style="background:darkviolet; width: 100%; height:4px;" id="untilNextUpdateProgressBarBackground"><span style="display:block; background:purple; width:100%; height: 100%;" id="untilNextUpdateProgressBar"></span></div> <div style="background:darkviolet; width: 100%; height:4px;" id="untilNextUpdateProgressBarBackground"><span style="display:block; background:purple; width:100%; height: 100%;" id="untilNextUpdateProgressBar"></span></div>
<table style="width:99vw; height: 72vh; max-height: 72vh; display: block; overflow: hidden;" id="routesTable"></table> <table style="width:99vw; height: 72vh; max-height: 72vh; display: block; overflow-y: hidden;" id="routesTable"></table>
<div style="width:99vw; height: 24vh; max-height: 24vh; display:block; overflow: hidden;" id="bahnhofPart"></div> <div style="width:99vw; height: 24vh; max-height: 24vh; display:block; overflow: hidden;" id="bahnhofPart"></div>
<script> <script>
// https://stackoverflow.com/questions/951021/what-is-the-javascript-version-of-sleep // https://stackoverflow.com/questions/951021/what-is-the-javascript-version-of-sleep
@ -60,7 +61,12 @@ function getDirectionColor(direction) {
} }
} }
function getRouteColumn(route) { function resizeRouteColumns() {
let minWidthPerColumn = (80 / (routesTable.childElementCount)) + "vw";
for (let child of routesTable.children) child.style["min-width"] = minWidthPerColumn;
}
function getRouteColumn(route, first) {
const desiredId = "routeColumn" + route; const desiredId = "routeColumn" + route;
const column = document.getElementById(desiredId); const column = document.getElementById(desiredId);
if (column) { if (column) {
@ -70,8 +76,11 @@ function getRouteColumn(route) {
const newColumn = document.createElement("table"); const newColumn = document.createElement("table");
const columnHeaderRow = document.createElement("tr"); const columnHeaderRow = document.createElement("tr");
const columnHeaderData = document.createElement("td"); const columnHeaderData = document.createElement("td");
newColumn.style.marginLeft = "auto";
newColumn.style.marginRight = "auto";
columnHeaderData.innerHTML = "<small><small>Linie</small></small> "; columnHeaderData.innerHTML = "<small><small>Linie</small></small> ";
columnHeaderData.append(route); columnHeaderData.append(route);
columnHeaderData.style.whiteSpace = "nowrap";
columnHeaderData.style.fontSize = "3em"; columnHeaderData.style.fontSize = "3em";
columnHeaderData.style.textDecoration = "underline gray"; columnHeaderData.style.textDecoration = "underline gray";
columnHeaderRow.appendChild(columnHeaderData); columnHeaderRow.appendChild(columnHeaderData);
@ -88,21 +97,20 @@ function getRouteColumn(route) {
} }
} }
routesTable.insertBefore(newColumnWrapper, beforeWhich); routesTable.insertBefore(newColumnWrapper, beforeWhich);
let columnWidthPercent = (99 / routesTable.childElementCount) + "vw"; fadeInColumn(newColumnWrapper, first);
for (const column of routesTable.children) {
column.style.width = columnWidthPercent;
}
return newColumn; return newColumn;
} }
} }
function getTripRow(route, vehicle, scheduledTime) { function getTripRow(route, vehicle, scheduledTime, first) {
const desiredId = "tripRow" + route + "#" + vehicle + "#" + scheduledTime; const desiredId = "tripRow" + route + "#" + vehicle + "#" + scheduledTime;
const tripRow = document.getElementById(desiredId); const tripRow = document.getElementById(desiredId);
if (tripRow) { if (tripRow) {
tripRowsUsed.set(desiredId, tripRow);
tripRowsUnused.delete(desiredId);
return tripRow; return tripRow;
} else { } else {
const newTripRow = document.createElement("tr"); const newTripRow = document.createElement("tr");
const column = getRouteColumn(route); const column = getRouteColumn(route, first);
let isFirst = true; let isFirst = true;
let beforeWhich = null; let beforeWhich = null;
for (row of column.children) { for (row of column.children) {
@ -117,12 +125,17 @@ function getTripRow(route, vehicle, scheduledTime) {
} }
newTripRow.id = desiredId; newTripRow.id = desiredId;
column.insertBefore(newTripRow, beforeWhich); column.insertBefore(newTripRow, beforeWhich);
fadeInRow(newTripRow, first);
tripRowsUsed.set(desiredId, newTripRow);
tripRowsUnused.delete(desiredId);
return newTripRow; return newTripRow;
} }
} }
var currentSeq = 0; var currentSeq = 0;
var resUniAll = []; var resUniAll = [];
var tripRowsUsed = new Map();
var tripRowsUnused = new Map();
var secondsUntilNextUpdate = 0; var secondsUntilNextUpdate = 0;
async function fetchUniAllRepeated(firstTime) { async function fetchUniAllRepeated(firstTime) {
@ -165,11 +178,10 @@ async function fetchUniAll(first) {
const resUniAllText = await (await fetch("./uni/all")).text(); const resUniAllText = await (await fetch("./uni/all")).text();
const prevResUniAll = resUniAll; const prevResUniAll = resUniAll;
resUniAll = []; resUniAll = [];
tripRowsUnused = tripRowsUsed;
tripRowsUsed = new Map();
var resUniThis = {}; var resUniThis = {};
const now = new Date(); const now = new Date();
for (const departure of prevResUniAll) {
departure.elem.remove();
}
for (const resUniAllLine of resUniAllText.split("\n")) { for (const resUniAllLine of resUniAllText.split("\n")) {
if (resUniAllLine.startsWith("#")) { if (resUniAllLine.startsWith("#")) {
const furtherSplitLine = resUniAllLine.substring(1).split("#"); const furtherSplitLine = resUniAllLine.substring(1).split("#");
@ -180,7 +192,8 @@ async function fetchUniAll(first) {
} }
} else if (resUniAllLine.startsWith("-")) { } else if (resUniAllLine.startsWith("-")) {
resUniThis.elem = document.createElement("td"); resUniThis.elem = document.createElement("td");
getTripRow(resUniThis.route, resUniThis.vehicle, resUniThis.scheduledTime).replaceChildren(resUniThis.elem); resUniThis.tripRow = getTripRow(resUniThis.route, resUniThis.vehicle, resUniThis.scheduledTime, first);
resUniThis.tripRow.replaceChildren(resUniThis.elem);
resUniAll.push(resUniThis); resUniAll.push(resUniThis);
resUniThis = {}; resUniThis = {};
} else if (resUniAllLine.startsWith("R")) { } else if (resUniAllLine.startsWith("R")) {
@ -199,22 +212,22 @@ async function fetchUniAll(first) {
resUniThis[resUniAllLine.substring(0, 1)] = resUniAllLine.substring(1); resUniThis[resUniAllLine.substring(0, 1)] = resUniAllLine.substring(1);
} }
} }
let columnsContainer = document.getElementById("routesTable").firstChild;
if (columnsContainer) {
for (const column of columnsContainer.children) {
if (column.childElementCount <= 1) {
column.remove();
let columnWidthPercent = (99 / routesTable.childElementCount) + "vw";
for (const column of routesTable.children) {
column.style.width = columnWidthPercent;
}
}
}
}
let nojslink = document.getElementById("nojslink"); let nojslink = document.getElementById("nojslink");
if (nojslink) { if (nojslink) {
nojslink.remove(); nojslink.remove();
} }
let removeRows = new Array();
for (const tripRow of tripRowsUnused.values()) {
removeRows.push(fadeThenRemoveRow(tripRow));
}
(async function() {
for (let promise of removeRows) await promise;
for (let child of routesTable.children) {
if (child.firstChild.childElementCount <= 1) {
fadeThenRemoveColumn(child);
}
}
})();
await rebuildDepartureElements(); await rebuildDepartureElements();
await updateBahnhofAll(); await updateBahnhofAll();
} }
@ -238,6 +251,61 @@ async function rebuildDepartureElements() {
resUniThis.elem.children[2].style.color = "hsl(" + Math.round(100 * timeScaledP) + "," + Math.round(100 - 100 * timeScaledP) + "%,50%)"; resUniThis.elem.children[2].style.color = "hsl(" + Math.round(100 * timeScaledP) + "," + Math.round(100 - 100 * timeScaledP) + "%,50%)";
} }
} }
async function fadeThenRemoveColumn(child) {
let width = child.getBoundingClientRect().width;
for (let p = 1; p <= 20; ++p) {
let q = 1 - 3*p*p/400-2*p*p*p/8000;
child.style.opacity = Math.floor(100*q) + "%";
await sleep(20);
}
for (let p = 0; p < 17; ++p) {
let q = 1 - 3*p*p/400-2*p*p*p/8000;
child.style.maxWidth = Math.floor(width*q) + "px";
await sleep(20);
}
child.style.maxWidth = "0px";
await sleep(20);
child.remove();
resizeRouteColumns();
}
async function fadeInColumn(child, first) {
resizeRouteColumns();
let width = child.getBoundingClientRect().width;
child.style.opacity = "0%";
if (!first) {
child.style.maxWidth = "0px";
for (let p = 2; p < 20; ++p) {
let q = 3*p*p/400-2*p*p*p/8000;
child.style.maxWidth = Math.floor(width*q) + "px";
await sleep(20);
}
child.style.removeProperty("max-width");
}
for (let p = 0; p < 20; ++p) {
let q = 3*p*p/400-2*p*p*p/8000;
child.style.opacity = Math.floor(100*q) + "%";
await sleep(20);
}
await sleep(20);
child.style.removeProperty("opacity");
}
async function fadeThenRemoveRow(child) {
let width = child.getBoundingClientRect().width;
for (let p = 1; p < 17; ++p) {
let q = 1 - 3*p*p/400-2*p*p*p/8000;
child.style.fontSize = q + "em";
await sleep(20);
}
child.remove();
}
async function fadeInRow(child, first) {
for (let p = first ? 20 : 1; p <= 20; ++p) {
let q = 3*p*p/400-2*p*p*p/8000;
child.style.fontSize = q + "em";
await sleep(20);
}
}
</script> </script>
</body> </body>
</html> </html>