Initial Release
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
::-webkit-scrollbar {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
background: black;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
p,
|
||||
a {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: white;
|
||||
font-size: 1em;
|
||||
line-height: 1.5em;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
div.background {
|
||||
position: fixed;
|
||||
bottom: 32px;
|
||||
background-image: url('/about/banner_large.webp');
|
||||
background-size: cover;
|
||||
image-rendering: pixelated;
|
||||
width: 900px;
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
div.foreground {
|
||||
position: relative;
|
||||
backdrop-filter: blur(2px);
|
||||
margin: auto;
|
||||
margin-top: 32px;
|
||||
width: 480px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
div.foreground div.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 16px;
|
||||
border-bottom: 1px solid #fff;
|
||||
padding: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.foreground div.article {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.foreground div.article p.header {
|
||||
margin-bottom: 8px;
|
||||
width: fit-content;
|
||||
font-weight: 600;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
div.foreground div.article p.content {
|
||||
margin-bottom: 16px;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
/* Mobile */
|
||||
@media only screen and (max-width: 600px) {
|
||||
div.foreground {
|
||||
transform: none;
|
||||
box-sizing: border-box;
|
||||
margin-top: 0;
|
||||
inset: 0;
|
||||
padding: 16px;
|
||||
width: 100%;
|
||||
max-width: unset;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
img.background {
|
||||
bottom: 10%;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<p class="element-header">Introduction</p>
|
||||
<p class="element-paragraph">
|
||||
If you're reading this... I haven't written the article yet :P
|
||||
</p>
|
||||
<p class="element-paragraph">
|
||||
Luctus montes donec mauris. Vehicula suspendisse condimentum massa fringilla at conubia.
|
||||
Senectus laoreet praesent in, porttitor sollicitudin luctus orci scelerisque egestas malesuada faucibus a.
|
||||
Arcu imperdiet libero imperdiet nulla libero natoque eu fusce ultricies vehicula nisi.
|
||||
Torquent lacus sodales inceptos dignissim sed, mauris feugiat erat.
|
||||
Phasellus at tincidunt sociosqu potenti risus aliquam non litora.
|
||||
Commodo mi fermentum, id arcu dictum convallis.
|
||||
Vehicula convallis, proin fames molestie potenti congue penatibus feugiat porttitor.
|
||||
Habitant justo lacinia, curabitur nascetur!
|
||||
Dolor nunc accumsan imperdiet penatibus?
|
||||
Amet cursus faucibus dui vestibulum nascetur quam.
|
||||
</p>
|
||||
<div class="element-divider"></div>
|
||||
|
||||
<p class="element-header">Prerequisites</p>
|
||||
<p class="element-paragraph">
|
||||
Praesent augue ligula amet nisl. Litora lectus consectetur aliquet.
|
||||
Sociis iaculis dolor nostra pulvinar felis condimentum netus.
|
||||
Suscipit enim habitant sed eros etiam, ut odio sed.
|
||||
Fringilla quam tempus nisl natoque parturient dolor curabitur tellus.
|
||||
Lacus conubia quis inceptos tristique ultricies cum consequat.
|
||||
Consequat phasellus proin dolor molestie nec quam cursus mollis vel?
|
||||
Eros tempor sollicitudin tortor litora nunc.
|
||||
Sem scelerisque semper id ac auctor fusce quis erat imperdiet convallis donec eget!
|
||||
Pellentesque tempor nunc ipsum imperdiet quam ipsum habitant feugiat.
|
||||
</p>
|
||||
|
||||
<p class="element-subheader">Hosting</p>
|
||||
<p class="element-paragraph">
|
||||
Odio nibh penatibus erat sociosqu eget iaculis. Ante curabitur bibendum ligula?
|
||||
Netus aliquet cursus interdum pharetra. Magnis elementum nunc luctus eu nam sit habitant etiam vestibulum in.
|
||||
Lacus nisl ullamcorper molestie! Dignissim, vulputate porta pulvinar duis posuere.
|
||||
</p>
|
||||
|
||||
<p class="element-subheader">Domains</p>
|
||||
<p class="element-paragraph">
|
||||
Curae; laoreet cum duis turpis, feugiat per integer.
|
||||
Sit in blandit himenaeos. Penatibus fringilla imperdiet, luctus dapibus elementum.
|
||||
Felis senectus pulvinar a natoque risus.
|
||||
</p>
|
||||
|
||||
<p class="element-subheader">Credentials</p>
|
||||
<p class="element-paragraph">
|
||||
Mauris habitasse nulla accumsan fermentum fermentum.
|
||||
Magnis amet amet risus senectus in curae;.
|
||||
Elementum ac montes sagittis vivamus malesuada elementum senectus ultrices scelerisque fusce.
|
||||
In nulla platea.
|
||||
</p>
|
||||
|
||||
<div class="element-divider"></div>
|
||||
|
||||
<p class="element-header">Setup DNS</p>
|
||||
<p class="element-paragraph">
|
||||
Nisi vitae vehicula etiam nam auctor, accumsan purus ridiculus molestie.
|
||||
Ante malesuada condimentum aliquam etiam auctor arcu porttitor parturient nullam accumsan bibendum?
|
||||
Vitae arcu at mattis. Volutpat himenaeos class vitae elit, luctus sociosqu semper?
|
||||
Praesent taciti lobortis tortor laoreet magnis tortor sagittis porta facilisi vivamus dictum scelerisque.
|
||||
Consectetur elit enim malesuada commodo proin facilisi sagittis!
|
||||
Malesuada quam duis non elit himenaeos sem.
|
||||
Luctus suspendisse senectus mollis aliquet aliquam pellentesque conubia condimentum suspendisse aptent litora potenti.
|
||||
Curabitur sociis lacus turpis, laoreet ligula consequat luctus eu auctor auctor sagittis.
|
||||
Turpis nibh feugiat lorem donec quis bibendum.
|
||||
Himenaeos sapien interdum aptent.
|
||||
</p>
|
||||
|
||||
<p class="element-header">Setup Linux</p>
|
||||
<p class="element-paragraph">
|
||||
Convallis pharetra vitae tincidunt phasellus bibendum porta, praesent dictum aliquet volutpat.
|
||||
Lobortis semper eget consectetur elementum elit hendrerit natoque mauris taciti ante ac augue.
|
||||
Ullamcorper viverra vestibulum vehicula nisi metus vestibulum curabitur vehicula et.
|
||||
Phasellus ligula vitae posuere penatibus ullamcorper lorem phasellus metus.
|
||||
Scelerisque ac lobortis gravida primis?
|
||||
Lacus porttitor ullamcorper quisque viverra molestie.
|
||||
Lacinia bibendum facilisi tempor cubilia felis eros!
|
||||
Etiam blandit lacinia leo urna.
|
||||
</p>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 339 B |
@@ -0,0 +1,152 @@
|
||||
/* Poppins Thin */
|
||||
@font-face {
|
||||
font-style: normal;
|
||||
font-weight: 100;
|
||||
src: url(/fonts/poppins/PoppinsThin.woff2);
|
||||
font-family: 'Poppins';
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-style: italic;
|
||||
font-weight: 100;
|
||||
src: url(/fonts/poppins/PoppinsThinItalic.woff2);
|
||||
font-family: 'Poppins';
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
/* Poppins ExtraLight */
|
||||
@font-face {
|
||||
font-style: normal;
|
||||
font-weight: 200;
|
||||
src: url(/fonts/poppins/PoppinsExtraLight.woff2);
|
||||
font-family: 'Poppins';
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-style: italic;
|
||||
font-weight: 200;
|
||||
src: url(/fonts/poppins/PoppinsExtraLightItalic.woff2);
|
||||
font-family: 'Poppins';
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
/* Poppins Light */
|
||||
@font-face {
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: url(/fonts/poppins/PoppinsLight.woff2);
|
||||
font-family: 'Poppins';
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
src: url(/fonts/poppins/PoppinsLightItalic.woff2);
|
||||
font-family: 'Poppins';
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
/* Poppins Regular */
|
||||
@font-face {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(/fonts/poppins/PoppinsRegular.woff2);
|
||||
font-family: 'Poppins';
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: url(/fonts/poppins/PoppinsItalic.woff2);
|
||||
font-family: 'Poppins';
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
/* Poppins Medium */
|
||||
@font-face {
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: url(/fonts/poppins/PoppinsMedium.woff2);
|
||||
font-family: 'Poppins';
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
src: url(/fonts/poppins/PoppinsMediumItalic.woff2);
|
||||
font-family: 'Poppins';
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
/* Poppins SemiBold */
|
||||
@font-face {
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: url(/fonts/poppins/PoppinsSemiBold.woff2);
|
||||
font-family: 'Poppins';
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
src: url(/fonts/poppins/PoppinsSemiBoldItalic.woff2);
|
||||
font-family: 'Poppins';
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
/* Poppins Bold */
|
||||
@font-face {
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url(/fonts/poppins/PoppinsBold.woff2);
|
||||
font-family: 'Poppins';
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: url(/fonts/poppins/PoppinsBoldItalic.woff2);
|
||||
font-family: 'Poppins';
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
/* Poppins ExtraBold */
|
||||
@font-face {
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
src: url(/fonts/poppins/PoppinsExtraBold.woff2);
|
||||
font-family: 'Poppins';
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-style: italic;
|
||||
font-weight: 800;
|
||||
src: url(/fonts/poppins/PoppinsExtraBoldItalic.woff2);
|
||||
font-family: 'Poppins';
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
/* Poppins Black */
|
||||
@font-face {
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
src: url(/fonts/poppins/PoppinsBlack.woff2);
|
||||
font-family: 'Poppins';
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-style: italic;
|
||||
font-weight: 900;
|
||||
src: url(/fonts/poppins/PoppinsBlackItalic.woff2);
|
||||
font-family: 'Poppins';
|
||||
font-display: swap;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/* Terminus Regular */
|
||||
@font-face {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(/fonts/terminus/TerminusRegular.woff2);
|
||||
font-family: 'Terminus';
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: url(/fonts/terminus/TerminusItalic.woff2);
|
||||
font-family: 'Terminus';
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
/* Terminus Bold */
|
||||
@font-face {
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url(/fonts/terminus/TerminusBold.woff2);
|
||||
font-family: 'Terminus';
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: url(/fonts/terminus/TerminusBoldItalic.woff2);
|
||||
font-family: 'Terminus';
|
||||
font-display: swap;
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,173 @@
|
||||
:root {
|
||||
--animation-duration: 200ms;
|
||||
--color-accent: hsl(236, 100%, 80%);
|
||||
--color-background: hsl(0, 0%, 0%);
|
||||
--color-font-primary: hsl(0, 0%, 95%);
|
||||
--color-font-secondary: hsl(0, 0%, 65%);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
background-color: var(--color-background);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
p,
|
||||
a,
|
||||
span,
|
||||
button {
|
||||
display: block;
|
||||
transition: var(--animation-duration) ease-in-out color;
|
||||
margin: 0;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
color: var(--color-font-primary);
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-size: 1em;
|
||||
line-height: 1.5em;
|
||||
font-family: 'Poppins', monospace;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
button:hover,
|
||||
button:focus-visible,
|
||||
a[href]:hover,
|
||||
a[href]:focus-visible {
|
||||
cursor: pointer;
|
||||
color: var(--color-accent);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div.layout-wrapper {
|
||||
margin: auto;
|
||||
width: 1024px;
|
||||
}
|
||||
|
||||
.layout-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
max-width: 1024px;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
div.layout-navigation {
|
||||
display: flex;
|
||||
margin-top: 16px;
|
||||
padding: 0 32px;
|
||||
}
|
||||
|
||||
div.layout-navigation div.section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
div.layout-navigation div.section a {
|
||||
padding: 0 16px;
|
||||
letter-spacing: 2px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
div.layout-navigation div.section span {
|
||||
padding: 0 16px;
|
||||
color: var(--color-font-secondary);
|
||||
font-size: x-small;
|
||||
}
|
||||
|
||||
div.layout-navigation div.divider {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
|
||||
p.layout-message {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
width: 100%;
|
||||
color: var(--color-font-secondary);
|
||||
font-size: x-small;
|
||||
line-height: 2em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.layout-footer {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
padding: 0 64px;
|
||||
}
|
||||
|
||||
div.layout-footer img.barcode {
|
||||
border-right: 1px solid var(--color-font-secondary);
|
||||
padding: 8px 16px 8px 0;
|
||||
image-rendering: pixelated;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
div.layout-footer div.tooltip {
|
||||
display: grid;
|
||||
flex-basis: 100%;
|
||||
min-width: 400px;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
div.layout-footer div.tooltip p.header {
|
||||
font-weight: bold;
|
||||
}
|
||||
div.layout-footer div.tooltip p.description {
|
||||
height: 3em;
|
||||
overflow: hidden;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
div.layout-footer div.tooltip a.redirect {
|
||||
opacity: 0;
|
||||
padding-top: 8px;
|
||||
pointer-events: none;
|
||||
color: var(--color-font-secondary);
|
||||
}
|
||||
|
||||
/* Mobile */
|
||||
@media only screen and (max-width: 600px) {
|
||||
div.layout-wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.layout-navigation {
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
canvas.layout-display {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
div.layout-footer {
|
||||
display: grid;
|
||||
justify-items: center;
|
||||
padding: 0 16px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.layout-footer div.tooltip {
|
||||
min-width: 0;
|
||||
}
|
||||
div.layout-footer div.tooltip p.description {
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
div.layout-footer img.barcode {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,391 @@
|
||||
const elemHeader = document.querySelector<HTMLParagraphElement>('p.header')!
|
||||
const elemDescription = document.querySelector<HTMLParagraphElement>('p.description')!
|
||||
const elemRedirect = document.querySelector<HTMLAnchorElement>('a.redirect')!
|
||||
|
||||
function createImage(src: string): HTMLImageElement {
|
||||
const i = document.createElement('img')
|
||||
i.src = src
|
||||
return i
|
||||
}
|
||||
|
||||
const slides = new Array<{
|
||||
image: HTMLImageElement
|
||||
url?: string | null
|
||||
tooltip?: string
|
||||
header: string
|
||||
description: string
|
||||
}>({
|
||||
image: createImage('/index/slide-1.gif'),
|
||||
header: 'Tech, Chunibyo & Other Delusions',
|
||||
url: '/secret-picross.html',
|
||||
tooltip: 'Picross',
|
||||
description:
|
||||
'Welcome to my personal homepage, use the buttons at the top of the page to visit other sections of the site.',
|
||||
})
|
||||
|
||||
const CODE_VERTEX = `
|
||||
attribute vec2 p;
|
||||
varying vec2 uv;
|
||||
void main(){
|
||||
uv = p * 0.5 + 0.5;
|
||||
gl_Position = vec4(p, 0, 1);
|
||||
}
|
||||
`
|
||||
|
||||
const CODE_SHADER = `
|
||||
precision highp float;
|
||||
varying vec2 uv;
|
||||
uniform vec2 res;
|
||||
uniform sampler2D texA;
|
||||
uniform sampler2D texB;
|
||||
uniform float transition, degauss, time, barrel, scan, mask, chroma, bloom, vig, noise, flicker, jitter, bright, sat, corner;
|
||||
|
||||
float rand(vec2 co){
|
||||
return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453);
|
||||
}
|
||||
|
||||
vec2 barrelD(vec2 u, float k) {
|
||||
vec2 c = u - 0.5;
|
||||
return u + c * dot(c, c) * k;
|
||||
}
|
||||
|
||||
vec3 sampleChroma(sampler2D t, vec2 u) {
|
||||
float ca = chroma * (1. + dot(u - .5, u - .5) * 2.);
|
||||
float r = texture2D(t, barrelD(u + vec2(ca, 0), barrel)).r;
|
||||
float g = texture2D(t, barrelD(u, barrel)).g;
|
||||
float b = texture2D(t, barrelD(u - vec2(ca, 0), barrel)).b;
|
||||
return vec3(r, g, b);
|
||||
}
|
||||
|
||||
vec3 phosphorBloom(sampler2D t, vec2 u) {
|
||||
vec3 glow = vec3(0.0);
|
||||
float w = 0.0;
|
||||
|
||||
// two rings — tight inner glow + wide outer halo
|
||||
for (int x = -4; x <= 4; x++) {
|
||||
for (int y = -4; y <= 4; y++) {
|
||||
vec2 off = vec2(float(x), float(y)) / res * 3.5;
|
||||
vec3 s = texture2D(t, barrelD(u + off, barrel)).rgb;
|
||||
|
||||
// only bright pixels contribute — threshold at 0.6 luma
|
||||
float lum = dot(s, vec3(0.299, 0.587, 0.114));
|
||||
float thresh = max(lum - 0.55, 0.0) * 2.5;
|
||||
s *= thresh;
|
||||
|
||||
float wt = exp(-dot(vec2(x, y), vec2(x, y)) * 0.18);
|
||||
glow += s * wt;
|
||||
w += wt;
|
||||
}
|
||||
}
|
||||
glow /= w;
|
||||
|
||||
// wide halo pass at 3x the offset
|
||||
vec3 halo = vec3(0.0);
|
||||
float hw = 0.0;
|
||||
for (int x = -3; x <= 3; x++) {
|
||||
for (int y = -3; y <= 3; y++) {
|
||||
vec2 off = vec2(float(x), float(y)) / res * 10.0;
|
||||
vec3 s = texture2D(t, barrelD(u + off, barrel)).rgb;
|
||||
float lum = dot(s, vec3(0.299, 0.587, 0.114));
|
||||
float thresh = max(lum - 0.7, 0.0) * 4.0;
|
||||
s *= thresh;
|
||||
float wt = exp(-dot(vec2(x, y), vec2(x, y)) * 0.25);
|
||||
halo += s * wt;
|
||||
hw += wt;
|
||||
}
|
||||
}
|
||||
halo /= hw;
|
||||
|
||||
// phosphor tint — warm yellow-green inner, cooler blue outer halo
|
||||
glow *= vec3(1.1, 1.05, 0.85);
|
||||
halo *= vec3(0.85, 0.95, 1.15);
|
||||
|
||||
return glow * bloom + halo * bloom * 0.4;
|
||||
}
|
||||
|
||||
vec3 shadowMask(vec2 u) {
|
||||
float px = mod(u.x * res.x, 3.);
|
||||
vec3 m = vec3(step(px, 1.), step(1., px) * step(px, 2.), step(2., px));
|
||||
return mix(vec3(1.), m * 1.6 + .15, mask * .5);
|
||||
}
|
||||
|
||||
float scanlineW(vec2 u) {
|
||||
return mix(1., max(0., sin(u.y * res.y * 3.14159)) * .5 + .5, scan);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 u = uv;
|
||||
u.x += sin(u.y * 200. + time * 3.) * (jitter * .5) + sin(u.y * 50. + time * 7.) * jitter;
|
||||
|
||||
// degauss
|
||||
vec2 centered = u - 0.5;
|
||||
float dist = length(centered);
|
||||
float wavefront = degauss * 0.9;
|
||||
float ring = exp(-pow((dist - wavefront) * 12.0, 2.0));
|
||||
float push = ring * 0.08 * sin(degauss * 3.14159);
|
||||
u += normalize(centered + 0.0001) * push;
|
||||
|
||||
vec2 bd = barrelD(u, barrel);
|
||||
if (bd.x < 0. || bd.x > 1. || bd.y < 0. || bd.y > 1.) {
|
||||
gl_FragColor = vec4(0, 0, 0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
vec2 cr = vec2(corner * res.y / res.x, corner);
|
||||
vec2 e = smoothstep(vec2(0.), cr, bd) * smoothstep(vec2(1.), vec2(1.) - cr, bd);
|
||||
if (e.x * e.y < .01) {
|
||||
gl_FragColor = vec4(0, 0, 0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
// chroma boost at ring
|
||||
float ca_boost = chroma + ring * 0.03 * sin(degauss * 3.14159);
|
||||
|
||||
// old texture
|
||||
float rA = texture2D(texA, barrelD(u + vec2(ca_boost, 0.), barrel)).r;
|
||||
float gA = texture2D(texA, barrelD(u, barrel)).g;
|
||||
float bA = texture2D(texA, barrelD(u - vec2(ca_boost, 0.), barrel)).b;
|
||||
|
||||
// new texture
|
||||
float rB = texture2D(texB, barrelD(u + vec2(ca_boost, 0.), barrel)).r;
|
||||
float gB = texture2D(texB, barrelD(u, barrel)).g;
|
||||
float bB = texture2D(texB, barrelD(u - vec2(ca_boost, 0.), barrel)).b;
|
||||
|
||||
// wipe: pixels closer to center than wavefront show new texture
|
||||
float wipe = smoothstep(wavefront - 0.05, wavefront + 0.05, dist);
|
||||
vec3 col = mix(vec3(rB, gB, bB), vec3(rA, gA, bA), wipe);
|
||||
|
||||
// rainbow tint at ring
|
||||
col += ring * 0.4 * vec3(
|
||||
sin(degauss * 6.28 + 0.0),
|
||||
sin(degauss * 6.28 + 2.09),
|
||||
sin(degauss * 6.28 + 4.18)
|
||||
) * sin(degauss * 3.14159);
|
||||
|
||||
vec3 bl = mix(phosphorBloom(texB, u), phosphorBloom(texA, u), wipe);
|
||||
col += bl;
|
||||
|
||||
|
||||
float lum = dot(col, vec3(.299, .587, .114));
|
||||
col = mix(col, bl, bloom * (lum * lum * .5 + .1));
|
||||
col *= shadowMask(bd); col *= scanlineW(bd);
|
||||
|
||||
float v = 1. - dot(bd - .5, bd - .5) * vig * 2.; col *= clamp(v, 0., 1.);
|
||||
float fl = 1. - flicker * .5 + flicker * sin(time * 73. + bd.y * 100.) * .5; col *= fl;
|
||||
col += (rand(bd + fract(time * .1)) - .5) * noise;
|
||||
col *= bright;
|
||||
|
||||
float l2 = dot(col, vec3(.299, .587, .114)); col = mix(vec3(l2), col, sat);
|
||||
col = pow(max(col, vec3(0.)), vec3(1. / 2.2));
|
||||
col *= e.x * e.y;
|
||||
gl_FragColor = vec4(clamp(col, 0., 1.), 0);
|
||||
}
|
||||
`
|
||||
|
||||
const canvas = document.querySelector<HTMLCanvasElement>('canvas')!
|
||||
const W = canvas.clientWidth
|
||||
const H = canvas.clientHeight
|
||||
let degauss = 0,
|
||||
noise = 0,
|
||||
slideIndex = -1,
|
||||
slideCounter = 0
|
||||
|
||||
canvas.width = W
|
||||
canvas.height = H
|
||||
|
||||
const gl = canvas.getContext('webgl')!
|
||||
const program = gl.createProgram()
|
||||
|
||||
function createShader(shaderType: number, shaderCode: string) {
|
||||
const shader = gl.createShader(shaderType)!
|
||||
gl.shaderSource(shader, shaderCode)
|
||||
gl.compileShader(shader)
|
||||
return shader
|
||||
}
|
||||
|
||||
gl.attachShader(program, createShader(gl.VERTEX_SHADER, CODE_VERTEX))
|
||||
gl.attachShader(program, createShader(gl.FRAGMENT_SHADER, CODE_SHADER))
|
||||
gl.linkProgram(program)
|
||||
gl.useProgram(program)
|
||||
|
||||
const bf = gl.createBuffer()
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, bf)
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), gl.STATIC_DRAW)
|
||||
|
||||
const ap = gl.getAttribLocation(program, 'p')
|
||||
gl.enableVertexAttribArray(ap)
|
||||
gl.vertexAttribPointer(ap, 2, gl.FLOAT, false, 0, 0)
|
||||
|
||||
const U = {
|
||||
transition: gl.getUniformLocation(program, 'transition'),
|
||||
degauss: /**/ gl.getUniformLocation(program, 'degauss'),
|
||||
time: /*---*/ gl.getUniformLocation(program, 'time'),
|
||||
res: /*----*/ gl.getUniformLocation(program, 'res'),
|
||||
texA: /*---*/ gl.getUniformLocation(program, 'texA'),
|
||||
texB: /*---*/ gl.getUniformLocation(program, 'texB'),
|
||||
barrel: /*-*/ gl.getUniformLocation(program, 'barrel'),
|
||||
scan: /*---*/ gl.getUniformLocation(program, 'scan'),
|
||||
mask: /*---*/ gl.getUniformLocation(program, 'mask'),
|
||||
chroma: /*-*/ gl.getUniformLocation(program, 'chroma'),
|
||||
bloom: /*--*/ gl.getUniformLocation(program, 'bloom'),
|
||||
vig: /*----*/ gl.getUniformLocation(program, 'vig'),
|
||||
noise: /*--*/ gl.getUniformLocation(program, 'noise'),
|
||||
flicker: /**/ gl.getUniformLocation(program, 'flicker'),
|
||||
jitter: /*-*/ gl.getUniformLocation(program, 'jitter'),
|
||||
bright: /*-*/ gl.getUniformLocation(program, 'bright'),
|
||||
sat: /*----*/ gl.getUniformLocation(program, 'sat'),
|
||||
corner: /*-*/ gl.getUniformLocation(program, 'corner'),
|
||||
}
|
||||
|
||||
const textureA = gl.createTexture()
|
||||
const textureCanvasA = document.createElement('canvas')
|
||||
const textureContextA = textureCanvasA.getContext('2d')!
|
||||
{
|
||||
textureCanvasA.width = W
|
||||
textureCanvasA.height = H
|
||||
gl.bindTexture(gl.TEXTURE_2D, textureA)
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
|
||||
}
|
||||
|
||||
const textureB = gl.createTexture()
|
||||
const textureCanvasB = document.createElement('canvas')
|
||||
const textureContextB = textureCanvasB.getContext('2d')!
|
||||
{
|
||||
textureCanvasB.width = W
|
||||
textureCanvasB.height = H
|
||||
gl.bindTexture(gl.TEXTURE_2D, textureB)
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
|
||||
}
|
||||
|
||||
// function renderSMPTE() {
|
||||
// const canvas = textureCanvasA
|
||||
// const ctx = textureContextA
|
||||
|
||||
// canvas.width = W
|
||||
// canvas.height = H
|
||||
|
||||
// const bars = [
|
||||
// ['#c0c0c0', '#c0c000', '#00c0c0', '#00c000', '#c000c0', '#c00000', '#0000c0'],
|
||||
// ['#0000c0', '#101010', '#c000c0', '#101010', '#00c0c0', '#101010', '#c0c0c0'],
|
||||
// ['#001b4b', '#ffffff', '#2e006a', '#0b0b0b', '#0b0b0b', '#0b0b0b'],
|
||||
// ]
|
||||
// let bw = W / 7
|
||||
// bars[0].forEach((c, i) => {
|
||||
// ctx.fillStyle = c
|
||||
// ctx.fillRect(i * bw, 0, bw, H * 0.675)
|
||||
// })
|
||||
// bars[1].forEach((c, i) => {
|
||||
// ctx.fillStyle = c
|
||||
// ctx.fillRect(i * bw, H * 0.675, bw, H * 0.125)
|
||||
// })
|
||||
// bw = W / 6
|
||||
// bars[2].forEach((c, i) => {
|
||||
// ctx.fillStyle = c
|
||||
// ctx.fillRect(i * bw, H * 0.75, bw, H * 0.25)
|
||||
// })
|
||||
// ctx.fillStyle = '#fff'
|
||||
// ctx.fillRect(W * 0.625, H * 0.8, W * 0.25, H * 0.12)
|
||||
|
||||
// ctx.font = 'bold 24px monospace'
|
||||
// ctx.fillStyle = '#000'
|
||||
// ctx.fillText('PLEASE STAND BY', W * 0.65, H * 0.875)
|
||||
|
||||
// gl.activeTexture(gl.TEXTURE0)
|
||||
// gl.bindTexture(gl.TEXTURE_2D, textureA)
|
||||
// gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true)
|
||||
// gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas)
|
||||
// }
|
||||
|
||||
function renderFrame(t: number) {
|
||||
gl.uniform1f(U.time, t)
|
||||
gl.uniform2f(U.res, W, H)
|
||||
gl.uniform1f(U.degauss, degauss)
|
||||
gl.uniform1i(U.texA, 0)
|
||||
gl.uniform1i(U.texB, 1)
|
||||
gl.uniform1f(U.barrel, /*-*/ 0.25)
|
||||
gl.uniform1f(U.scan, /*---*/ 0.25)
|
||||
gl.uniform1f(U.mask, /*---*/ 1)
|
||||
gl.uniform1f(U.chroma, /*-*/ 0.0025)
|
||||
gl.uniform1f(U.bloom, /*--*/ 0.5)
|
||||
gl.uniform1f(U.vig, /*----*/ 1)
|
||||
gl.uniform1f(U.noise, /*--*/ noise)
|
||||
gl.uniform1f(U.flicker, /**/ 0.045)
|
||||
gl.uniform1f(U.jitter, /*-*/ 0.00075)
|
||||
gl.uniform1f(U.bright, /*-*/ 0.8)
|
||||
gl.uniform1f(U.sat, /*----*/ 1.0)
|
||||
gl.uniform1f(U.corner, /*-*/ 0.1)
|
||||
|
||||
gl.viewport(0, 0, W, H)
|
||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4)
|
||||
requestAnimationFrame(renderFrame)
|
||||
}
|
||||
|
||||
async function nextSlide(init = false) {
|
||||
const duration = 1000
|
||||
const nextIndex = slideCounter++ % slides.length
|
||||
if (nextIndex === slideIndex) return
|
||||
slideIndex = nextIndex
|
||||
|
||||
const next = slides[nextIndex]
|
||||
elemHeader.textContent = next.header
|
||||
elemDescription.textContent = next.description
|
||||
|
||||
if (next.url) {
|
||||
elemRedirect.href = next.url
|
||||
elemRedirect.style.opacity = '1'
|
||||
elemRedirect.style.pointerEvents = 'all'
|
||||
} else {
|
||||
elemRedirect.style.opacity = '0'
|
||||
elemRedirect.style.pointerEvents = 'none'
|
||||
}
|
||||
elemRedirect.textContent = `Discover ${next.tooltip || 'More'} >>>`
|
||||
|
||||
function tween(from: number, to: number, ms: number, fn: (v: number) => void) {
|
||||
return new Promise<void>((resolve) => {
|
||||
const start = performance.now()
|
||||
function tick() {
|
||||
const t = Math.min((performance.now() - start) / ms, 1)
|
||||
const e = t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t // easing
|
||||
fn(from + (to - from) * e)
|
||||
if (t < 1) requestAnimationFrame(tick)
|
||||
else resolve()
|
||||
}
|
||||
requestAnimationFrame(tick)
|
||||
})
|
||||
}
|
||||
|
||||
textureContextB.drawImage(next.image!, 0, 0, W, H)
|
||||
gl.activeTexture(gl.TEXTURE1)
|
||||
gl.bindTexture(gl.TEXTURE_2D, textureB)
|
||||
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true)
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textureCanvasB)
|
||||
|
||||
if (init) {
|
||||
tween(0, 0.02, duration, (t) => (noise = t))
|
||||
setInterval(nextSlide, duration + 8000)
|
||||
}
|
||||
await tween(0, 1, duration, (t) => (degauss = t))
|
||||
|
||||
textureContextA.drawImage(next.image!, 0, 0, W, H)
|
||||
gl.activeTexture(gl.TEXTURE0)
|
||||
gl.bindTexture(gl.TEXTURE_2D, textureA)
|
||||
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true)
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textureCanvasA)
|
||||
|
||||
degauss = 0
|
||||
}
|
||||
|
||||
// Startup Sequence (Blank Screen)
|
||||
slides[0].image.onload = () => {
|
||||
nextSlide(true)
|
||||
requestAnimationFrame(renderFrame)
|
||||
}
|
||||
|
||||
gl.activeTexture(gl.TEXTURE0)
|
||||
gl.bindTexture(gl.TEXTURE_2D, textureA)
|
||||
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true)
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas)
|
||||
@@ -0,0 +1,11 @@
|
||||
const m = [
|
||||
/* Sun */ 'Even God rested on the seventh day — are you treating yourself well?',
|
||||
/* Mon */ "If it's been done before then you know you can do it too! And if they got close, you can get even closer!",
|
||||
/* Tue */ "It's Tetris Tuesday! Play <a target='_blank' href='https://tetr.io'>TETR.IO</a>!",
|
||||
/* Wed */ 'Bored? Read about the Nintendo 3DS system software. What, too nerdy?',
|
||||
/* Thu */ "Make sure you clean your keyboard, there's so much nasty gunk in there.",
|
||||
/* Fri */ 'Did you eat something sweet today? Everyone deserves a treat, especially you sweetheart!',
|
||||
/* Sat */ 'Your abandoned projects miss you... Maybe give them a visit?',
|
||||
]
|
||||
|
||||
document.querySelector('p#motd')!.innerHTML = m[new Date().getDay()]
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 475 B |
@@ -0,0 +1,83 @@
|
||||
<style>
|
||||
body {
|
||||
background: black;
|
||||
}
|
||||
|
||||
/* Layout */
|
||||
div.noscript-layout {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 16px;
|
||||
box-sizing: border-box;
|
||||
margin: auto;
|
||||
padding: 16px 0;
|
||||
width: 100%;
|
||||
max-width: 1024px;
|
||||
}
|
||||
|
||||
div.noscript-wrapper {
|
||||
position: relative;
|
||||
margin: 16px;
|
||||
}
|
||||
|
||||
main.noscript-container {
|
||||
background-color: #202020;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
/* Elements */
|
||||
p.noscript-message {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: #f0f0f0;
|
||||
font-family: monospace;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Effects */
|
||||
div.noscript-corner {
|
||||
position: absolute;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
div.noscript-corner:nth-child(1) {
|
||||
top: -16px;
|
||||
left: -16px;
|
||||
border-top: 1px solid #606060;
|
||||
border-left: 1px solid #606060;
|
||||
}
|
||||
|
||||
div.noscript-corner:nth-child(2) {
|
||||
top: -16px;
|
||||
right: -16px;
|
||||
border-top: 1px solid #606060;
|
||||
border-right: 1px solid #606060;
|
||||
}
|
||||
|
||||
div.noscript-corner:nth-child(3) {
|
||||
bottom: -16px;
|
||||
left: -16px;
|
||||
border-bottom: 1px solid #606060;
|
||||
border-left: 1px solid #606060;
|
||||
}
|
||||
|
||||
div.noscript-corner:nth-child(4) {
|
||||
right: -16px;
|
||||
bottom: -16px;
|
||||
border-right: 1px solid #606060;
|
||||
border-bottom: 1px solid #606060;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="noscript-layout">
|
||||
<div class="noscript-wrapper">
|
||||
<div class="noscript-corner"></div>
|
||||
<div class="noscript-corner"></div>
|
||||
<div class="noscript-corner"></div>
|
||||
<div class="noscript-corner"></div>
|
||||
<main class="noscript-container">
|
||||
<p class="noscript-message"><b>NOTICE:</b> JavaScript is required to view this site.</p>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,74 @@
|
||||
body {
|
||||
margin: 0;
|
||||
background: black;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body::before {
|
||||
position: fixed;
|
||||
mix-blend-mode: multiply;
|
||||
inset: 0;
|
||||
background: repeating-linear-gradient(
|
||||
to bottom,
|
||||
rgba(255, 0, 0, 0.5) 0px,
|
||||
rgba(0, 255, 0, 0.25) 2px,
|
||||
rgba(0, 0, 255, 0.5) 4px
|
||||
);
|
||||
pointer-events: none;
|
||||
content: '';
|
||||
}
|
||||
|
||||
span,
|
||||
a,
|
||||
p {
|
||||
vertical-align: bottom;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 1.25em;
|
||||
color: #f0f0f0;
|
||||
font-size: 1.25em;
|
||||
line-height: 1.25em;
|
||||
font-family: 'Terminus', sans-serif;
|
||||
}
|
||||
|
||||
span {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
a:hover,
|
||||
a:focus-visible {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div.layout-content {
|
||||
box-sizing: border-box;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.animation-blink {
|
||||
animation: kf-blink 500ms infinite step-start;
|
||||
}
|
||||
|
||||
@keyframes kf-blink {
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.animation-scrollin {
|
||||
animation: kf-scrollin 1s forwards linear;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
text-wrap-mode: nowrap;
|
||||
}
|
||||
|
||||
@keyframes kf-scrollin {
|
||||
0% {
|
||||
width: 0%;
|
||||
}
|
||||
|
||||
100% {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
const container = document.querySelector<HTMLDivElement>('div.layout-content')!
|
||||
const messages = [
|
||||
'ERROR: REALITY NOT FOUND',
|
||||
'YOU HAVE FALLEN OUT OF YOUR UNIVERSE.',
|
||||
'<br>',
|
||||
'DONT PANIC! WE CAN SENSE YOU!',
|
||||
'WE ARE BRINGING YOU INTO OURS FOR NOW.',
|
||||
'<br>',
|
||||
'REDIRECTING IN:',
|
||||
'3...',
|
||||
'2...',
|
||||
'1...',
|
||||
'<br>',
|
||||
]
|
||||
|
||||
let timer = setInterval(tick, 1000)
|
||||
let line = 0
|
||||
|
||||
function tick() {
|
||||
// Render Messages
|
||||
container.innerHTML = messages
|
||||
.filter((_, i) => line > i)
|
||||
.map((_, i) => {
|
||||
const me = line - 1 === i
|
||||
const styleP = me ? 'animation-scrollin' : ''
|
||||
const styleS = me ? 'inline-block' : 'none'
|
||||
return `<p class="${styleP}">${messages[i]} <span class="animation-blink" style="display: ${styleS}">_</span> </p>`
|
||||
})
|
||||
.join('')
|
||||
|
||||
// Messages complete, redirect to homepage!
|
||||
if (line === messages.length) {
|
||||
window.location.replace('/')
|
||||
clearInterval(timer)
|
||||
}
|
||||
|
||||
line++
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
:root {
|
||||
--transition-time: 200ms;
|
||||
--animation-time: 200ms;
|
||||
--color-background: #1d1e2e;
|
||||
--color-text-default: #f0f0f0;
|
||||
--color-text-hint: #d0d0d0;
|
||||
--color-disabled: #212335;
|
||||
--color-inactive: #2d2f47;
|
||||
--color-active: #a1a8ff;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background-color: var(--color-background);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
p,
|
||||
a {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: var(--color-text-default);
|
||||
font-weight: normal;
|
||||
font-size: 1em;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
cursor: pointer;
|
||||
margin: 0 8px;
|
||||
border: 2px solid var(--color-inactive);
|
||||
border-radius: 8px;
|
||||
padding: 4px 8px;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
a:hover,
|
||||
a:focus-visible {
|
||||
border-color: var(--color-active);
|
||||
}
|
||||
|
||||
.text-tip {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
.text-hidden {
|
||||
color: var(--color-text-hint);
|
||||
}
|
||||
|
||||
/* Layout */
|
||||
|
||||
div.layout-container {
|
||||
box-sizing: border-box;
|
||||
margin: auto;
|
||||
padding: 32px 16px;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
div.layout-header {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.picross-row {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
div.layout-content {
|
||||
display: grid;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
/* visually centered */
|
||||
margin-left: -32px;
|
||||
padding: 32px 0;
|
||||
}
|
||||
|
||||
p.picross-hint {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
color: var(--color-text-hint);
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
button.picross-button {
|
||||
transition: var(--animation-time) ease-in-out background-color;
|
||||
border: none;
|
||||
background-color: var(--color-inactive);
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
button.state-active {
|
||||
background-color: var(--color-active) !important;
|
||||
}
|
||||
|
||||
button.state-ignore {
|
||||
background-color: var(--color-disabled) !important;
|
||||
}
|
||||
|
||||
div.layout-footer {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
p.layout-timer {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
/* Certificate */
|
||||
|
||||
div.layout-document {
|
||||
position: relative;
|
||||
width: 11in;
|
||||
height: 8.5in;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
div.layout-document span {
|
||||
position: absolute;
|
||||
background-color: white;
|
||||
color: red;
|
||||
}
|
||||
|
||||
div.layout-document span#name {
|
||||
top: 3in;
|
||||
left: 3.75in;
|
||||
width: 3.5in;
|
||||
font-size: 2em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.layout-document span#time {
|
||||
top: 4in;
|
||||
left: 6in;
|
||||
}
|
||||
|
||||
div.layout-document span#date {
|
||||
top: 3.67in;
|
||||
left: 4.8in;
|
||||
padding: 0 32px;
|
||||
}
|
||||
|
||||
div.layout-document img#background {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@media print {
|
||||
@page {
|
||||
size: letter landscape;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.no-print {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
const elemTimer = document.querySelector<HTMLParagraphElement>('p.layout-timer')!
|
||||
const elemCheck = document.querySelector<HTMLButtonElement>('a#action-send')!
|
||||
const soundSet = document.querySelector<HTMLAudioElement>('audio#audio-set')!
|
||||
const soundDel = document.querySelector<HTMLAudioElement>('audio#audio-del')!
|
||||
let secondsElapsed = 0
|
||||
|
||||
function timestamp() {
|
||||
const f = (s: number) => s.toString().padStart(2, '0')
|
||||
const h = Math.floor(secondsElapsed / 3600)
|
||||
const m = Math.floor((secondsElapsed % 3600) / 60)
|
||||
const s = Math.floor(secondsElapsed % 60)
|
||||
return `Time Spent: ${f(h)}:${f(m)}:${f(s)}`
|
||||
}
|
||||
|
||||
setInterval(() => {
|
||||
secondsElapsed++
|
||||
elemTimer.textContent = timestamp()
|
||||
}, 1000)
|
||||
|
||||
document.querySelector<HTMLDivElement>('div.layout-content')!.addEventListener(
|
||||
// Prevent annoying context menu popup whenever you miss a grid tile by 1 pixel
|
||||
'contextmenu',
|
||||
(e) => e.preventDefault(),
|
||||
)
|
||||
|
||||
document.querySelectorAll('button.picross-button').forEach((e) => {
|
||||
// Initialize Buttons
|
||||
e.addEventListener('click', () => {
|
||||
e.classList.remove('state-ignore')
|
||||
e.classList.toggle('state-active') ? soundSet.play() : soundDel.play()
|
||||
})
|
||||
e.addEventListener('contextmenu', (ev) => {
|
||||
ev.preventDefault()
|
||||
e.classList.remove('state-active')
|
||||
e.classList.toggle('state-ignore')
|
||||
soundDel.play()
|
||||
})
|
||||
})
|
||||
|
||||
elemCheck.addEventListener('click', () => {
|
||||
// Calculate Answer
|
||||
let given = 0
|
||||
const rows = document.querySelectorAll<HTMLDivElement>('div.picross-row')
|
||||
|
||||
rows.forEach((childRow, rowIndex) => {
|
||||
let childIndex = 0
|
||||
for (const child of childRow.children) {
|
||||
if (child.nodeName === 'BUTTON') {
|
||||
if (child.classList.contains('state-active')) {
|
||||
given += 1 << ((rowIndex - 3) * 9 + childIndex)
|
||||
}
|
||||
childIndex++
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (given !== 2560660350) {
|
||||
alert('Nope! Try Again')
|
||||
return
|
||||
}
|
||||
|
||||
// Render Certificate
|
||||
const time = timestamp()
|
||||
const name = prompt('Congratulations you won! Please enter the name for your certificate:', 'Anon')
|
||||
|
||||
document.querySelector<HTMLDivElement>('div.layout-document')!.style.display = 'block'
|
||||
document.querySelector<HTMLSpanElement>('span#date')!.textContent = new Date().toDateString()
|
||||
document.querySelector<HTMLSpanElement>('span#time')!.textContent = time
|
||||
document.querySelector<HTMLSpanElement>('span#name')!.textContent = name
|
||||
print()
|
||||
document.location.assign('/')
|
||||
})
|
||||
@@ -0,0 +1,67 @@
|
||||
const soundActive = document.querySelector<HTMLAudioElement>('audio#active')!
|
||||
const soundBounce = document.querySelector<HTMLAudioElement>('audio#bounce')!
|
||||
const canvas = document.querySelector<HTMLCanvasElement>('canvas#sprites')!
|
||||
|
||||
if (canvas && soundActive && soundBounce) {
|
||||
const C = {
|
||||
TEXTURE_WIDTH: 68,
|
||||
TEXTURE_HEIGHT: 78,
|
||||
TEXTURE_SCALE: 0.5,
|
||||
RENDER_WIDTH: 256,
|
||||
RENDER_HEIGHT: 256,
|
||||
GRAVITY: 0.67,
|
||||
BOUNCE: 0.75,
|
||||
JUMP: 8,
|
||||
COUNT: 3,
|
||||
}
|
||||
const TEXTURE = new Image()
|
||||
const SPRITES = new Array()
|
||||
const OFFSET = (C.RENDER_WIDTH - C.COUNT * (C.TEXTURE_WIDTH * C.TEXTURE_SCALE)) / C.COUNT
|
||||
|
||||
for (let i = 0; i < C.COUNT; i++) {
|
||||
SPRITES.push({
|
||||
x: OFFSET / 2 + OFFSET * i + C.TEXTURE_WIDTH * C.TEXTURE_SCALE * i,
|
||||
v: 0,
|
||||
y: 0,
|
||||
})
|
||||
}
|
||||
|
||||
const ctx = canvas.getContext('2d')!
|
||||
TEXTURE.src = document.querySelector<HTMLLinkElement>('link[rel=texture')!.href
|
||||
canvas.style.imageRendering = 'pixelated'
|
||||
canvas.style.width = `${C.RENDER_WIDTH}px`
|
||||
canvas.style.height = `${C.RENDER_HEIGHT}px`
|
||||
canvas.width = C.RENDER_WIDTH
|
||||
canvas.height = C.RENDER_HEIGHT
|
||||
soundBounce.playbackRate = 2
|
||||
soundBounce.volume = 0.5
|
||||
|
||||
canvas.addEventListener('click', () => {
|
||||
for (let i = 0; i < C.COUNT; i++) {
|
||||
SPRITES[i].v += C.JUMP + Math.random() * (C.JUMP / 2)
|
||||
}
|
||||
})
|
||||
|
||||
setInterval(() => {
|
||||
ctx.clearRect(0, 0, C.RENDER_WIDTH, C.RENDER_HEIGHT)
|
||||
for (let i = 0; i < SPRITES.length; i++) {
|
||||
let oy = C.RENDER_HEIGHT - C.TEXTURE_HEIGHT * C.TEXTURE_SCALE
|
||||
let s = SPRITES[i]
|
||||
s.v -= C.GRAVITY
|
||||
s.y += s.v
|
||||
|
||||
if (s.y < 0) {
|
||||
s.y = 0
|
||||
s.v = -s.v * C.BOUNCE
|
||||
if (Math.abs(s.v) < 0.8) {
|
||||
s.v = 0
|
||||
} else {
|
||||
soundBounce.currentTime = 0
|
||||
soundBounce.play()
|
||||
}
|
||||
}
|
||||
|
||||
ctx.drawImage(TEXTURE, s.x, oy - s.y, C.TEXTURE_WIDTH * C.TEXTURE_SCALE, C.TEXTURE_HEIGHT * C.TEXTURE_SCALE)
|
||||
}
|
||||
}, 1000 / 60)
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
:root {
|
||||
--accent: #4d58ff;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: black;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
a,
|
||||
p {
|
||||
display: inline-block;
|
||||
font-family: 'Poppins', sans-serif;
|
||||
line-height: 1.5em;
|
||||
font-weight: 400;
|
||||
color: #f0f0f0;
|
||||
padding: 0 16px;
|
||||
margin: 0;
|
||||
text-align: justify;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Layout */
|
||||
|
||||
div.layout-section {
|
||||
width: 100%;
|
||||
max-width: 640px;
|
||||
margin: 16px auto;
|
||||
padding: 16px;
|
||||
box-sizing: border-box;
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
img.article-banner {
|
||||
width: 100%;
|
||||
image-rendering: pixelated;
|
||||
aspect-ratio: 3.4;
|
||||
}
|
||||
|
||||
/* Effects */
|
||||
|
||||
.effect-caution {
|
||||
position: relative;
|
||||
border: 4px solid var(--accent);
|
||||
background-color: black;
|
||||
margin-bottom: 12px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.effect-caution::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: -16px;
|
||||
bottom: -16px;
|
||||
box-sizing: border-box;
|
||||
filter: brightness(0.5);
|
||||
background: repeating-linear-gradient(45deg, var(--accent) 0, var(--accent) 8px, black 8px, black 16px);
|
||||
clip-path: polygon(calc(100% - 11px) 0, 100% 0, 100% 100%, 0 100%, 0 calc(100% - 11px), calc(100% - 11px) calc(100% - 11px));
|
||||
}
|
||||
|
||||
.effect-rhombus {
|
||||
position: relative;
|
||||
font-weight: 500;
|
||||
padding: 0 16px;
|
||||
z-index: 1;
|
||||
clip-path: polygon(0 0, calc(100% - 8px) 0, 100% 100%, 8px 100%);
|
||||
}
|
||||
|
||||
.effect-rhombus::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
box-sizing: border-box;
|
||||
background: var(--accent);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
/* Mobile */
|
||||
@media only screen and (max-width: 600px) {
|
||||
div.layout-section {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.effect-caution::before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 359 B |
Reference in New Issue
Block a user