move styles and scripts into directories
This commit is contained in:
parent
7341f49b5a
commit
28eb19ff41
8 changed files with 33 additions and 33 deletions
303
script/run.ts
Normal file
303
script/run.ts
Normal file
|
@ -0,0 +1,303 @@
|
|||
/** one of the six document sections */
|
||||
export type Pane = 'hello' | 'id' | 'activities' | 'links' | 'friends' | 'six';
|
||||
|
||||
export const allPanes: Pane[] =
|
||||
['hello', 'id', 'activities', 'links', 'friends', 'six'];
|
||||
|
||||
namespace Cube {
|
||||
|
||||
/** location on the cube in space */
|
||||
export type Face = 'front' | 'top' | 'back' | 'bottom' | 'left' | 'right';
|
||||
|
||||
/**
|
||||
* - for front, left, right: up is up
|
||||
* - for back: up is down (lol)
|
||||
* - for top: up is away from you
|
||||
* - for bottom: up is towards you
|
||||
*/
|
||||
export type Orientation = 'up' | 'left' | 'down' | 'right';
|
||||
|
||||
export type Place = [Face, Orientation];
|
||||
|
||||
|
||||
function table<A extends string, B = A>(m: Record<A, B>): (x: A) => B {
|
||||
return x => m[x];
|
||||
}
|
||||
|
||||
|
||||
const doCwO =
|
||||
table<Orientation>({up: 'right', right: 'down', down: 'left', left: 'up'});
|
||||
|
||||
const doCcwO =
|
||||
table<Orientation>({up: 'left', left: 'down', down: 'right', right: 'up'});
|
||||
|
||||
|
||||
export type RotateXY = 'up' | 'left' | 'down' | 'right';
|
||||
export type RotateZ = 'cw' | 'ccw';
|
||||
export type Rotation = RotateXY | RotateZ;
|
||||
|
||||
// if you rotate the cube "up" (along the x axis), face f becomes up(f)
|
||||
// et cetera
|
||||
const moveF: Record<Rotation, (f: Face) => Face> = {
|
||||
up: table({
|
||||
front: 'top', top: 'back', back: 'bottom',
|
||||
bottom: 'front', left: 'left', right: 'right'
|
||||
}),
|
||||
down: table({
|
||||
front: 'bottom', top: 'front', back: 'top',
|
||||
bottom: 'back', left: 'left', right: 'right'
|
||||
}),
|
||||
left: table({
|
||||
front: 'left', top: 'top', back: 'right',
|
||||
bottom: 'bottom', left: 'back', right: 'front'
|
||||
}),
|
||||
right: table({
|
||||
front: 'right', top: 'top', back: 'left',
|
||||
bottom: 'bottom', left: 'front', right: 'back'
|
||||
}),
|
||||
cw: table({
|
||||
front: 'front', back: 'back', left: 'top',
|
||||
right: 'bottom', top: 'right', bottom: 'left'
|
||||
}),
|
||||
ccw: table({
|
||||
front: 'front', back: 'back', left: 'bottom',
|
||||
right: 'top', top: 'left', bottom: 'right'
|
||||
})
|
||||
};
|
||||
|
||||
const moveO: Record<Rotation, (f: Face, o: Orientation) => Orientation> = {
|
||||
up(f, o) {
|
||||
switch (f) {
|
||||
case 'left': return doCcwO(o);
|
||||
case 'right': return doCwO(o);
|
||||
default: return o;
|
||||
}
|
||||
},
|
||||
down(f, o) {
|
||||
switch (f) {
|
||||
case 'left': return doCwO(o);
|
||||
case 'right': return doCcwO(o);
|
||||
default: return o;
|
||||
}
|
||||
},
|
||||
left(f, o) {
|
||||
switch (f) {
|
||||
case 'top': return doCwO(o);
|
||||
case 'bottom': return doCcwO(o);
|
||||
case 'left':
|
||||
case 'back': return doCwO(doCwO(o));
|
||||
default: return o;
|
||||
}
|
||||
},
|
||||
right(f, o) {
|
||||
switch (f) {
|
||||
case 'top': return doCcwO(o);
|
||||
case 'bottom': return doCwO(o);
|
||||
case 'right':
|
||||
case 'back': return doCcwO(doCcwO(o));
|
||||
default: return o;
|
||||
}
|
||||
},
|
||||
cw(f, o) { return f == 'back' ? doCcwO(o) : doCwO(o); },
|
||||
ccw(f, o) { return f == 'back' ? doCwO(o) : doCcwO(o); }
|
||||
};
|
||||
|
||||
export function applyMoves(p: Place, ms: Rotation[]): Place {
|
||||
return ms.reduce(([f, o], m) => [moveF[m](f), moveO[m](f, o)], p);
|
||||
}
|
||||
|
||||
/** the sequence of movements to put this place on the front */
|
||||
export function toFrontUpright([f, o]: Place): [RotateXY[], RotateZ[]] {
|
||||
const toFront: (f: Face) => RotateXY[] =
|
||||
table<Face, RotateXY[]>({
|
||||
front: [], top: ['down'], back: ['left', 'left'],
|
||||
bottom: ['up'], left: ['right'], right: ['left']
|
||||
});
|
||||
|
||||
const toUpright: (o: Orientation) => RotateZ[] =
|
||||
table<Orientation, RotateZ[]>({
|
||||
up: [], left: ['cw'], down: ['cw', 'cw'], right: ['ccw']
|
||||
});
|
||||
|
||||
const directions = toFront(f);
|
||||
const rotations = toUpright(applyMoves([f, o], directions)[1]);
|
||||
return [directions, rotations];
|
||||
}
|
||||
|
||||
|
||||
const movementToTransform = table<Rotation, string>({
|
||||
up: 'rotateX(.25turn)', down: 'rotateX(-.25turn)',
|
||||
left: 'rotateY(-.25turn)', right: 'rotateY(.25turn)',
|
||||
cw: 'rotateZ(.25turn)', ccw: 'rotateZ(-.25turn)'
|
||||
});
|
||||
|
||||
/** the css `transform` value corresponding to this sequence of movements */
|
||||
export function movementsToTransform(ms: Rotation[]): string {
|
||||
return ms.length > 0 ? ms.map(movementToTransform).join(' ') : 'none';
|
||||
}
|
||||
|
||||
|
||||
const faceToTransform = table<Face, string>({
|
||||
front: '', top: 'rotateX(.25turn)',
|
||||
back: 'rotateX(.5turn)', bottom: 'rotateX(-.25turn)',
|
||||
left: 'rotateY(-.25turn)', right: 'rotateY(.25turn)'
|
||||
});
|
||||
|
||||
const orientationToTransform = table<Orientation, string>({
|
||||
up: '', left: 'rotateZ(-.25turn)',
|
||||
down: 'rotateZ(-.5turn)', right: 'rotateZ(.25turn)'
|
||||
});
|
||||
|
||||
/**
|
||||
* the css `transform` value that will bring the given place to the
|
||||
* front and upright
|
||||
*/
|
||||
export function placeToTransform([f, o]: Place): string {
|
||||
const ft = faceToTransform(f);
|
||||
const ot = orientationToTransform(o);
|
||||
return ft || ot ? `${ft} ${ot}` : 'none';
|
||||
}
|
||||
|
||||
|
||||
/** a map of where each pane is on the cube */
|
||||
export type Conf = Record<Pane, Place>;
|
||||
|
||||
/** initial cube configuration with `hello` at the front */
|
||||
let current: Conf = {
|
||||
hello: ['front', 'up'], id: ['left', 'up'],
|
||||
activities: ['back', 'down'], links: ['right', 'up'],
|
||||
friends: ['bottom', 'up'], six: ['top', 'up'],
|
||||
};
|
||||
// the back face is 'down' so it has the same visual orientation as other side
|
||||
// faces
|
||||
|
||||
/** apply the css transforms to each pane element */
|
||||
export function applyConfiguration(): void {
|
||||
for (const pane of allPanes) {
|
||||
const element = document.getElementById(pane)!;
|
||||
const place = current[pane];
|
||||
|
||||
element.style.setProperty('--base-transform', placeToTransform(place));
|
||||
element.style.pointerEvents = place[0] == 'front' ? 'auto' : 'none';
|
||||
}
|
||||
}
|
||||
|
||||
export function move(c: Conf, ...ms: Rotation[]): Conf {
|
||||
let res: Partial<Conf> = {};
|
||||
for (const pane of allPanes) { res[pane] = applyMoves(c[pane], ms) }
|
||||
return res as Conf;
|
||||
}
|
||||
|
||||
|
||||
export function animateMoveWith(ds: RotateXY[], rs: RotateZ[]): void {
|
||||
const outer = document.getElementById('outer')!;
|
||||
const cube = document.getElementById('cube')!;
|
||||
|
||||
cube.dataset.moving = 'true';
|
||||
cube.style.transition = '0.4s cubic-bezier(.4, -0.29, .43, 1.26)';
|
||||
outer.style.transition = `0.4s 0.25s cubic-bezier(.48, 0, .44, 1.07)`;
|
||||
|
||||
function transitionListener(elem: HTMLElement): () => void {
|
||||
function handler(e: Event) { if (e.target == elem) { finish(); } }
|
||||
|
||||
elem.addEventListener('transitionend', handler);
|
||||
return () => elem.removeEventListener('transitionend', handler);
|
||||
}
|
||||
|
||||
let removeOuter = () => {};
|
||||
let removeCube = () => {};
|
||||
if (rs.length > 0) {
|
||||
removeOuter = transitionListener(outer);
|
||||
cube.style.transform = movementsToTransform(ds);
|
||||
outer.style.transform = movementsToTransform(rs);
|
||||
} else if (ds.length > 0) {
|
||||
removeCube = transitionListener(cube);
|
||||
cube.style.transform = movementsToTransform(ds);
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
|
||||
function finish() {
|
||||
removeOuter(); removeCube();
|
||||
|
||||
delete cube.dataset.moving;
|
||||
outer.style.transition = cube.style.transition = 'none';
|
||||
outer.style.transform = cube.style.transform = 'none';
|
||||
|
||||
current = move(current, ...ds, ...rs);
|
||||
applyConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
export function animateMoveTo(pane: Pane): void {
|
||||
animateMoveWith(...toFrontUpright(current[pane]));
|
||||
history.replaceState(null, '🦎', `#${pane}`);
|
||||
}
|
||||
|
||||
export function squashCube() {
|
||||
for (const pane of allPanes) {
|
||||
const elem = document.getElementById(pane)!
|
||||
elem.style.setProperty('--base-transform', 'none');
|
||||
}
|
||||
}
|
||||
|
||||
export function instantMoveTo(pane: Pane): void {
|
||||
current = move(current, ...toFrontUpright(current[pane]).flat());
|
||||
applyConfiguration();
|
||||
}
|
||||
|
||||
export function LEFT() { animateMoveWith(['left'], []); }
|
||||
export function RIGHT() { animateMoveWith(['right'], []); }
|
||||
export function UP() { animateMoveWith(['up'], []); }
|
||||
export function DOWN() { animateMoveWith(['down'], []); }
|
||||
export function CW() { animateMoveWith([], ['cw']); }
|
||||
export function CCW() { animateMoveWith([], ['ccw']); }
|
||||
|
||||
}
|
||||
|
||||
|
||||
namespace Flat {
|
||||
|
||||
export function fadeTo(newPane: Pane): void {
|
||||
for (const pane of allPanes) {
|
||||
const here = pane == newPane;
|
||||
document.getElementById(pane)!.dataset.selected = `${here}`;
|
||||
if (here) { history.replaceState(null, '🦎', `#${pane}`); }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
let reducedMotion =
|
||||
matchMedia(`(prefers-reduced-motion: reduce),
|
||||
(max-height: 649px), (max-width: 649px)`);
|
||||
|
||||
function switchTo(pane: Pane): void {
|
||||
if (reducedMotion.matches) { Cube.instantMoveTo(pane); }
|
||||
else { Cube.animateMoveTo(pane); }
|
||||
Flat.fadeTo(pane);
|
||||
}
|
||||
|
||||
|
||||
function setup(): void {
|
||||
const here = location.hash.slice(1) || 'hello';
|
||||
|
||||
for (const pane of allPanes) {
|
||||
const box = document.getElementById(`b-${pane}`) as HTMLInputElement;
|
||||
box.addEventListener('change', () => {
|
||||
if (box.checked) { switchTo(pane); }
|
||||
});
|
||||
|
||||
if (pane == here) {
|
||||
Cube.instantMoveTo(pane);
|
||||
Flat.fadeTo(pane);
|
||||
box.checked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', setup);
|
||||
|
||||
export {};
|
66
script/shuffle.ts
Normal file
66
script/shuffle.ts
Normal file
|
@ -0,0 +1,66 @@
|
|||
function group<A>(subject: A[], keepTogether: A[][]): A[][] {
|
||||
type Value = {array: A[], added: boolean};
|
||||
|
||||
let groups: Map<A, Value> = new Map;
|
||||
for (const xs of keepTogether) {
|
||||
let value = {array: xs, added: false};
|
||||
for (const x of xs) { groups.set(x, value); }
|
||||
}
|
||||
|
||||
let res = [];
|
||||
|
||||
for (const x of subject) {
|
||||
let group = groups.get(x);
|
||||
if (group?.added) { continue; }
|
||||
else if (group) {
|
||||
group.added = true;
|
||||
res.push(group.array);
|
||||
} else {
|
||||
res.push([x]);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
function shuffle<A>(subject: A[]): A[] {
|
||||
let res = Array.from(subject);
|
||||
|
||||
for (let i = 0; i < res.length - 2; ++i) {
|
||||
const j = i + Math.floor(Math.random() * (res.length - i));
|
||||
if (i != j) {
|
||||
const k = res[i]!;
|
||||
res[i] = res[j]!;
|
||||
res[j] = k;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
function groupedShuffle<A>(subject: A[], keepTogether: A[][]): A[] {
|
||||
let groups = group(subject, keepTogether);
|
||||
return shuffle(groups).flat();
|
||||
}
|
||||
|
||||
function shuffleAll() {
|
||||
let groups = [group('myno', 'abyss')];
|
||||
|
||||
for (const elem of Array.from(document.getElementsByClassName('shuffle'))) {
|
||||
let shuffled = groupedShuffle(Array.from(elem.children), groups);
|
||||
|
||||
elem.innerHTML = '';
|
||||
for (const child of shuffled) {
|
||||
elem.appendChild(child);
|
||||
}
|
||||
}
|
||||
|
||||
function group(...xs: string[]) {
|
||||
let elements = xs.map(x => document.getElementById(x));
|
||||
return elements.every(x => x) ? elements as HTMLElement[] : [];
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', shuffleAll);
|
||||
|
||||
export {};
|
Loading…
Add table
Add a link
Reference in a new issue