Compare commits

...

10 Commits

Author SHA1 Message Date
Luca 75e2e10fad Update bauchbinde by fl0rp
continuous-integration/drone/push Build is passing Details
2021-12-24 23:21:32 +01:00
Luca 1364fd793b Fix using Date.now() where date object is required
continuous-integration/drone/push Build is passing Details
2021-12-24 18:04:05 +01:00
Luca eec9d73e61 Replace `new Date()` with `Date.now()` where appropriate
continuous-integration/drone/push Build is passing Details
2021-12-24 15:48:07 +01:00
Stefan Schwarz fdd6674e15 infobeamer: use actual date
continuous-integration/drone/push Build is passing Details
2021-12-23 23:53:02 +01:00
Florian Sorg 48e4a254da optimize infobeamer layout
continuous-integration/drone/push Build is passing Details
2021-12-23 23:43:55 +01:00
Luca a009efe016 Fix infobeamer
continuous-integration/drone/push Build is passing Details
2021-12-23 23:13:12 +01:00
Florian Sorg b62b21df15 add default value for --scrollTop
continuous-integration/drone/push Build is passing Details
2021-12-23 23:08:42 +01:00
Florian Sorg 260560e753 implement parallax effect for dots
continuous-integration/drone/push Build is passing Details
2021-12-23 22:59:19 +01:00
Stefan Schwarz e35730d75c fix indent
continuous-integration/drone/push Build is passing Details
2021-12-23 21:53:57 +01:00
Stefan Schwarz eb30a69ab6 infobeamer beta
continuous-integration/drone/push Build is passing Details
2021-12-23 21:40:48 +01:00
11 changed files with 226 additions and 368 deletions

View File

@ -33,12 +33,20 @@ body {
scroll, scroll,
fixed; fixed;
background-size: background-size:
cover, auto,
cover, cover,
100% 100%, 100% 100%,
cover; cover;
background-repeat: no-repeat; background-repeat:
background-position: center; repeat-y,
no-repeat,
no-repeat,
no-repeat;
background-position:
50% calc(50% + 1px * 0.5 * var(--scrollTop, 0)),
50% 50%,
50% 50%,
50% 50%;
color: $color-text; color: $color-text;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -60,6 +68,21 @@ body {
radial-gradient(transparent, transparent, rgba(0, 0, 0, 0.5)); radial-gradient(transparent, transparent, rgba(0, 0, 0, 0.5));
} }
&::before {
content: '';
display: block;
position: fixed;
opacity: 0.3;
top: 0;
left: 0;
height: 100%;
width: 100%;
pointer-events: none;
background-image: url('layout/fg_pixels.gif');
background-repeat: repeat-y;
background-position: 50% calc(50% + -1.4px * var(--scrollTop));
}
@media (min-width: $small) { @media (min-width: $small) {
background-image: background-image:
url('layout/bg_pixels.gif'), url('layout/bg_pixels.gif'),
@ -288,6 +311,9 @@ table {
.box-content { .box-content {
padding: $gap-vertical $gap-horizontal; padding: $gap-vertical $gap-horizontal;
flex-grow: 1; flex-grow: 1;
flex-shrink: 1;
text-overflow: ellipsis;
overflow: hidden;
} }
.box-footer { .box-footer {
padding: ($gap-vertical / 2) $gap-horizontal; padding: ($gap-vertical / 2) $gap-horizontal;
@ -315,3 +341,31 @@ table {
.overflow-x-auto { .overflow-x-auto {
overflow-x: auto; overflow-x: auto;
} }
.infobeamer {
h1 {
margin: $gap-vertical 0;
}
main {
max-width: 1700px;
}
.box-grid {
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: minmax(10px, 1fr);
max-height: 75vh;
}
}
.progress {
@include aberration(0.1em);
border: 1px solid $color-box-frame;
height: 1em;
.fill {
background: $color-box-frame;
height: 100%;
}
}

View File

@ -1,6 +1,6 @@
{{ $currentPage := . }} {{ $currentPage := . }}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="{{ .Site.Language.Lang }}"> <html lang="{{ .Site.Language.Lang }}" style="--scrollTop: 0;">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0"> <meta name="viewport" content="width=device-width,initial-scale=1.0">
@ -10,6 +10,7 @@
<link rel="icon" href="/franconianNet.svg" sizes="any" type="image/svg+xml"> <link rel="icon" href="/franconianNet.svg" sizes="any" type="image/svg+xml">
{{ $style := resources.Get "style.scss" | resources.ToCSS (dict "outputStyle" "compressed") }} {{ $style := resources.Get "style.scss" | resources.ToCSS (dict "outputStyle" "compressed") }}
<link rel="stylesheet" href="{{ $style.RelPermalink }}"> <link rel="stylesheet" href="{{ $style.RelPermalink }}">
<script src="/layout/scroll.js"></script>
</head> </head>
<body> <body>
<header> <header>

Binary file not shown.

Binary file not shown.

View File

@ -53,8 +53,6 @@
async function init() { async function init() {
const root = document.querySelector('html'); const root = document.querySelector('html');
textEl = document.querySelector('.text');
textEl.innerHTML = '';
if (window.location.search) { if (window.location.search) {
const pairs = window.location.search.slice(1).split('&').map(x => x.split('=')); const pairs = window.location.search.slice(1).split('&').map(x => x.split('='));
@ -126,7 +124,7 @@
} else { } else {
headline = talk.title; headline = talk.title;
if (talk.persons) { if (talk.persons) {
speaker = talk.persons.map(person => person.public_name).join(' / '); speaker = talk.persons.map(person => person.public_name).join(', ');
} }
} }
} }
@ -135,164 +133,28 @@
return false; return false;
} }
if (speaker && headline) { const headlineEl = document.querySelector('.headline');
headline += ','; headlineEl.innerHTML = '';
} const speakerEl = document.querySelector('.speaker');
speakerEl.innerHTML = '';
const headlineEl = document.createElement('span'); console.log('headline', headline);
headlineEl.classList.add('headline'); console.log('speaker', speaker);
const speakerEl = document.createElement('span');
speakerEl.classList.add('speaker');
if (headline) { if (headline) {
Array.from(headline).forEach(letter => { headlineEl .innerText = headline;
const letterEl = document.createElement('span');
letterEl.classList.add('letter');
letterEl.innerText = letter;
headlineEl.appendChild(letterEl);
})
} }
if (speaker) { if (speaker) {
Array.from(speaker).forEach(letter => { speakerEl.innerText = speaker;
const letterEl = document.createElement('span');
letterEl.classList.add('letter');
letterEl.innerText = letter;
speakerEl.appendChild(letterEl);
})
}
textEl.appendChild(headlineEl);
textEl.appendChild(document.createTextNode(' '));
textEl.appendChild(speakerEl);
const mainTilesEl = document.querySelector('.background .main-tiles');
mainTilesEl.innerHTML = '';
for (let i = 0; i < 16; i++) {
const tile = document.createElement('div');
tile.classList.add('tile', 'large');
mainTilesEl.appendChild(tile);
}
const secondaryTilesEl = document.querySelector('.background .secondary-tiles');
secondaryTilesEl.innerHTML = '';
for (let i = 0; i < 17; i++) {
const tile = document.createElement('div');
tile.classList.add('tile');
if (i === 0 || Math.random() > 0.5) {
tile.classList.add('medium');
} else {
tile.classList.add('small');
}
secondaryTilesEl.appendChild(tile);
} }
return true; return true;
} }
async function animate() { async function animate() {
if (autoIntro ? isFirstRun : isIntro) { document.querySelector('.container').classList.add('visible');
await Promise.all([
slideIn(),
fadeInText(),
]);
} else {
await Promise.all([
fadeIn(),
fadeInText(),
]);
}
await new Promise(r => setTimeout(r, holdDuration * 1000)); await new Promise(r => setTimeout(r, holdDuration * 1000));
await Promise.all([ document.querySelector('.container').classList.remove('visible');
fadeOut(),
fadeOutText(),
])
}
async function slideIn() {
const tiles = document.querySelectorAll('.tile');
for (const tile of tiles) {
tile.style.animationName = `slide-in-${Math.floor(Math.random() * 10)}`;
tile.style.animationDelay = `${Math.random() * 0.4}s`;
tile.style.animationDuration = `${0.8 + Math.random() * 0.4}s`;
tile.style.animationIterationCount = '1';
tile.style.animationTimingFunction = 'ease-in';
}
await new Promise(r => setTimeout(r, 600));
for (const tile of tiles) {
tile.classList.add('visible');
}
await new Promise(r => setTimeout(r, 1000));
}
async function fadeIn() {
const tiles = document.querySelectorAll('.tile');
for (const tile of tiles) {
tile.style.animationName = `fade-in`;
tile.style.animationDelay = `${Math.random() * 0.4}s`;
tile.style.animationDuration = `${0.8 + Math.random() * 0.4}s`;
tile.style.animationIterationCount = '1';
tile.style.animationTimingFunction = 'ease-in';
}
await new Promise(r => setTimeout(r, 600));
for (const tile of tiles) {
tile.classList.add('visible');
}
await new Promise(r => setTimeout(r, 1000));
}
async function fadeOut() {
const tiles = document.querySelectorAll('.tile');
for (const tile of tiles) {
tile.style.animationName = `fade-out`;
tile.style.animationDelay = `${Math.random() * 0.4}s`;
tile.style.animationDuration = `${0.8 + Math.random() * 0.4}s`;
tile.style.animationIterationCount = '1';
tile.style.animationTimingFunction = 'ease-in';
}
await new Promise(r => setTimeout(r, 600));
for (const tile of tiles) {
tile.classList.remove('visible');
}
await new Promise(r => setTimeout(r, 1000));
}
async function fadeInText() {
const letters = document.querySelectorAll('.letter');
for (const letter of letters) {
letter.style.animationName = `fade-in`;
letter.style.animationDelay = `${Math.random() * 0.4}s`;
letter.style.animationDuration = `${0.8 + Math.random() * 0.4}s`;
letter.style.animationIterationCount = 1;
letter.style.animationTimingFunction = 'ease-in';
}
await new Promise(r => setTimeout(r, 600));
for (const tile of letters) {
tile.classList.add('visible');
}
await new Promise(r => setTimeout(r, 1000));
}
async function fadeOutText() {
const letters = document.querySelectorAll('.letter');
for (const letter of letters) {
letter.style.animationName = `fade-out`;
letter.style.animationDelay = `${Math.random() * 0.4}s`;
letter.style.animationDuration = `${0.8 + Math.random() * 0.4}s`;
letter.style.animationIterationCount = '1';
letter.style.animationTimingFunction = 'ease-in';
}
await new Promise(r => setTimeout(r, 600));
for (const tile of letters) {
tile.classList.remove('visible');
}
await new Promise(r => setTimeout(r, 1000));
} }
async function cycle() { async function cycle() {

View File

@ -1,239 +1,60 @@
@font-face { @font-face {
font-family: Orbitron; font-family: Changa;
src: url('Orbitron-VariableFont_wght.ttf'); font-weight: 700;
src: url('Changa-SemiBold.ttf');
}
@font-face {
font-family: SpaceMono;
src: url('SpaceMono-Regular.ttf');
} }
:root { :root {
--large-tile-color: #02fae0;
--medium-tile-color: #fff900;
--small-tile-color: #0bcb60;
--text-color: #252826;
--bottom: 9vw;
--left: 12.5vw;
--width: 67.5vw;
--top: auto;
}
:root.theme-main {
--large-tile-color: #6800e7;
--medium-tile-color: #fff;
--small-tile-color: #05b9ec;
--text-color: #fff; --text-color: #fff;
--header-text-color: #252826;
--frame-color: #fff;
--bottom: 7vw;
--left: 25vw;
--width: 50vw;
--top: auto;
} }
body { body {
margin: 0; margin: 0;
background-color: #000; background-color: #444;
font-family: Orbitron, sans-serif; font-family: SpaceMono, monospace;
font-size: 2vw; font-size: 1.7vw;
line-height: 2vw;
color: var(--text-color); color: var(--text-color);
/*color: #0e1c23;*/
text-transform: uppercase;
letter-spacing: 0.1vw;
} }
.headline { .headline {
font-weight: 900; text-transform: lowercase;
font-family: Changa, sans-serif;
font-weight: 700;
background: var(--frame-color);
color: var(--header-text-color);
text-align: center;
line-height: 1.3;
padding: 0 1vw;
font-size: 1.25em;
} }
.speaker { .speaker {
font-size: 0.8em; padding: 0.5vw 1vw 0.7vw;
} }
.container { .container {
transition: opacity 0.3s ease 0s;
position: fixed; position: fixed;
bottom: var(--bottom); bottom: var(--bottom);
left: var(--left); left: var(--left);
width: var(--width); width: var(--width);
top: var(--top); top: var(--top);
} border: 0.1vw solid var(--frame-color);
.background {
z-index: -20;
width: 100%;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.text {
margin-left: 6.25%;
padding: 1vw 1.5vw;
}
.tiles {
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
align-items: stretch;
justify-content: stretch;
}
.main-tiles {
margin-left: 6.25%;
transform: translateY(-100%);
}
.tile {
flex: 1;
position: relative;
opacity: 0;
}
.tile::before {
content: '';
position: absolute;
display: block;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.tile.large::before {
background-color: var(--large-tile-color);
width: 100%;
height: 100%;
box-shadow: 1px 0 0 0 var(--large-tile-color);
}
.tile.medium::before {
background-color: var(--medium-tile-color);
width: 50%;
padding-top: 50%;
}
.tile.small::before {
background-color: var(--small-tile-color);
width: 35%;
padding-top: 35%;
}
.letter {
opacity: 0; opacity: 0;
background: rgba(0, 0, 0, 0.5);
} }
.visible { .visible {
opacity: 1; opacity: 1;
} }
@keyframes slide-in-0 {
0% {
transform: translate(50vw, -100vw) scale(10);
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes slide-in-1 {
0% {
transform: translate(-40vw, -20vw) scale(2);
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes slide-in-2 {
0% {
transform: translate(50vw, 20vw) scale(5);
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes slide-in-3 {
0% {
transform: translate(80vw, -50vw) scale(3);
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes slide-in-4 {
0% {
transform: translate(-30vw, -50vw) scale(7.5);
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes slide-in-5 {
0% {
transform: translate(-30vw, 50vw) scale(4);
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes slide-in-6 {
0% {
transform: translate(5vw, 30vw) scale(2.5);
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes slide-in-7 {
0% {
transform: translate(-70vw, 100vw) scale(4);
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes slide-in-8 {
0% {
transform: translate(-4vw, 2vw) scale(2);
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes slide-in-9 {
0% {
transform: translate(2vw, -5vw) scale(2);
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes fade-in {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes fade-out {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}

View File

@ -2,17 +2,14 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Intro</title> <title>Bauchbinde</title>
<link rel="stylesheet" href="./assets/style.css"> <link rel="stylesheet" href="./assets/style.css">
<script src="./assets/script.js"></script> <script src="./assets/script.js"></script>
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<div class="text"></div> <div class="headline"></div>
<div class="background"> <div class="speaker"></div>
<div class="secondary-tiles tiles"></div>
<div class="main-tiles tiles"></div>
</div>
</div> </div>
</body> </body>
</html> </html>

View File

@ -0,0 +1,122 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>
Infobeamer
</title>
<link rel="stylesheet" href="/style.css">
<script>
main().then(() => console.log("loaded"));
function parseDuration(duration) {
if (!/^\d+:\d+$/.test(duration)) {
return 0;
}
const [hours, minutes] = duration.split(":");
return (hours * 60 * 60 + minutes * 60) * 1000;
}
function sanitize(unsafe) {
const element = document.createElement("div");
element.innerHTML = unsafe;
return element.innerText;
}
function render(talk) {
const now = Date.now();
const max = talk.end - talk.start;
let value = 0;
if (talk.start < now && talk.end > now) {
value = talk.end - now;
value = max - value
} else if (talk.end < now) {
value = max;
}
return `
<li class="box">
<h2 class="box-header">${sanitize(talk.title)}</h2>
<div class="box-content clamp-height">
<p class="date">
Speakers: ${sanitize(talk.persons.join(", ") || "---")}<br>
Stage: ${sanitize(talk.room)}<br>
Time: ${sanitize(talk.start_string)}<br>
Duration: ${sanitize(talk.duration)}<br>
</p>
<p>${sanitize(talk.abstract)}</p>
</div>
<div class="box-footer">
<div class="progress"><div class="fill" style="width: ${value / max * 100}%"></div></div>
</div>
</li>`;
}
async function main() {
const resp = await fetch("https://static.rc3.world/schedule/everything.json");
const schedule = await resp.json();
const now = Date.now();
const upcoming = [];
for (const day of schedule.schedule.conference.days) {
for (const [channel, talks] of Object.entries(day.rooms)) {
for (const talk of talks) {
const talk_end = Date.parse(talk.date) + parseDuration(talk.duration);
if (talk_end > now) {
const parsed = {
start: Date.parse(talk.date),
date_string: talk.date,
end: talk_end,
start_string: talk.start,
duration: talk.duration,
room: talk.room,
title: talk.title,
abstract: talk.abstract,
persons: talk.persons.map(person => person.public_name),
};
upcoming.push(parsed);
break;
}
}
}
}
let content = "";
upcoming.sort((a, b) => a.end - b.end);
for (const talk of upcoming.slice(0, 6)) {
content += render(talk);
}
const time = new Date();
document.getElementById("list").innerHTML = content;
const hours = `${time.getHours()}`.padStart(2, '0');
const minutes = `${time.getMinutes()}`.padStart(2, '0');
document.getElementById("time").innerText = `${hours}:${minutes}`;
setTimeout(main, (60-time.getSeconds())*1000+500);
}
</script>
</head>
<body class="infobeamer">
<header>
<nav class="nav nav-main">
<a class="nav-logo" href="/">
<img src="/franconianNet.svg"
alt="Logo of franconian.net">
franconian.net
</a>
<a class="nav-link" id="time"></a>
</nav>
</header>
<main>
<h1>running / upcoming</h1>
<ul class="box-grid" id="list">
</ul>
</main>
</body>
</html>

BIN
static/layout/fg_pixels.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 765 KiB

1
static/layout/scroll.js Normal file
View File

@ -0,0 +1 @@
window.addEventListener('scroll', e => document.documentElement.style.setProperty('--scrollTop', window.scrollY));