Split-flap message board
HTML
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport"
content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="./main.css"/>
<script src="./main.js"></script>
</head>
<body>
<div>
<textarea id="message"
rows="6" cols="80"
maxlength="2000">The apparition
of these faces in the crowd:
Petals on a wet, black bough.</textarea>
</div>
<div>
<button id="run" type="button">Run</button>
</div>
<br/>
<div id="display-container"></div>
</body>
</html>
CSS
:root {
/* these are overridden by our JS code */
--msg-font-size: 16px;
--glyph-split-gap: 2px;
}
body {
font-family: helvetica;
}
#display-container {
font-size: var(--msg-font-size);
display: none;
background-color: #121212;
width: fit-content;
border: solid rgba(90,90,90,0.7) 2px;
border-radius: 3px;
}
.display-line {
display: flex;
flex-flow: row wrap;
justify-content: flex-start;
align-content: flex-start;
width: fit-content;
gap: 5px;
padding: 3px;
}
.display-cell {
display: flex;
justify-content: center;
align-items: center;
border: solid rgba(15,15,15,0.5) 1px;
box-shadow: inset 2px 2px 2px rgba(35,35,35,0.7),
inset -2px -2px 2px rgba(70,70,70,0.3);
position: relative;
background: #1c1c1c;
width: 1em;
height: 1.2em;
}
/* draws a horizontal line through
the middle of each display cell,
as an alternative to using the
`.glyph-split-gap` class
.display-cell::after {
content: "";
position: absolute;
left: 0;
right: 0;
top: 50%;
height: 1px;
background: black;
transform: translateY(-50%);
z-index: 0;
}
*/
.glyph-top {
background: linear-gradient(
to bottom,
white 50%,
transparent 50%
);
background-clip: text;
color: transparent;
z-index: 10;
}
.glyph-bottom {
background: linear-gradient(
to bottom,
transparent 50%,
white 50%
);
background-clip: text;
color: transparent;
position: absolute;
z-index: 10;
}
/* see the JS code for how this is used */
.glyph-split-gap {
top: var(--glyph-split-gap);
}
JavaScript
/*
Split-flap display emulation.
https://en.wikipedia.org/wiki/Split-flap_display
Fakes a flap flipping by displaying the top half of
a letter slightly before the lower half.
Inspired by these two examples:
- https://gist.github.com/veltman/f2b2a06d4ffa62f4d39d5ebac5ceeef0
- https://github.com/paulcuth/departure-board
See https://github.com/northcoder-repo/split-flap-demo
*/
const msgFontSize = '20px';
/* gap between bottom and */
/* top halves of glyphs */
/* set to 'none' for no gap, */
/* or a valid CSS value */
/* ('2px', '5%', etc.) */
const glyphSplitGap = '1px';
// speed of transitions (in milliseconds):
const flapPause = 60; // top half of glyph to bottom half
const glyphPause = 180; // pause between glyph sequence
const cascade = 90; // speed of flow from one cell to next
const maxMessageLength = 500;
// show a gap between top and
// bottom halves of glyphs:
const letterGap = true;
let message = '';
// any char in the message which is not in the `characters`
// list must be replaced by a character which is in the list,
// otherwise the search for a match would never end.
const replacementChar = '☒';
// All the valid characters which can be displayed:
const characters = ' ' // space character (should be first in list!)
+ '1234567890'
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' // uppercase only!
+ ',.?;:/\'"!&()-’'
//+ '@#$%^_[{]}=+|~*' // less commmonly used
+ replacementChar; // Do not remove (must be last in list!)
// ------------------- s e t - u p ------------------------- //
// build a rectangular display grid matching the shape
// of the message to be displayed (as wide as the longest
// line in the message).
function createDisplayLine(lineDiv, charsPerLine) {
for (let i = 0; i < charsPerLine; i++) {
const glyphDiv = document.createElement("div");
glyphDiv.classList.add("display-cell");
const top =...