This commit is contained in:
Alexander Bass 2023-04-23 15:51:14 -04:00
commit 47d80a88df
23 changed files with 567 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
.sass-cache
art
node_modules
target

5
README.md Normal file
View file

@ -0,0 +1,5 @@
# Corkboard
This project was an experiment in making a physical looking website.
It consists of sticky notes that can be typed on dragged around and removed.
It allows more functionality that was not implemented, like adding traditional webpage aspects of navigation on paper and such.

19
makefile Normal file
View file

@ -0,0 +1,19 @@
debug: clean build js_debug
clean:
-rm -rf target
-rm -rf .sass-cache
build: javascript
- mkdir target
sass source/scss/main.scss target/style.css
-mkdir target/assets
cp source/assets/* target/assets/
cp source/index.html target/index.html
javascript:
npx rollup -c
js_debug:
-mkdir target/js
cp -r source/js/* target/js/

59
package-lock.json generated Normal file
View file

@ -0,0 +1,59 @@
{
"name": "taxtheft",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "taxtheft",
"version": "1.0.0",
"license": "UNLICENSED",
"dependencies": {
"rollup": "^2.78.1"
}
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/rollup": {
"version": "2.78.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.78.1.tgz",
"integrity": "sha512-VeeCgtGi4P+o9hIg+xz4qQpRl6R401LWEXBmxYKOV4zlF82lyhgh2hTZnheFUbANE8l2A41F458iwj2vEYaXJg==",
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=10.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
}
},
"dependencies": {
"fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"optional": true
},
"rollup": {
"version": "2.78.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.78.1.tgz",
"integrity": "sha512-VeeCgtGi4P+o9hIg+xz4qQpRl6R401LWEXBmxYKOV4zlF82lyhgh2hTZnheFUbANE8l2A41F458iwj2vEYaXJg==",
"requires": {
"fsevents": "~2.3.2"
}
}
}
}

10
package.json Normal file
View file

@ -0,0 +1,10 @@
{
"name": "corkboard",
"version": "1.0.0",
"description": "",
"author": "Alexander Bass",
"license": "GPLv3",
"dependencies": {
"rollup": "^2.78.1"
}
}

9
rollup.config.js Normal file
View file

@ -0,0 +1,9 @@
export default {
input: 'source/js/main.js',
output: [
{
file: 'target/js/main.js',
format: 'iife'
}
]
};

BIN
source/assets/cork.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 KiB

BIN
source/assets/note.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

BIN
source/assets/page.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
source/assets/tack.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

13
source/index.html Normal file
View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<script src="./js/main.js" type="module" charset="utf-8"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="./style.css">
<noscript> This page requires javascript to function. For those of you who have it disabled: don't fret; there's nothing here except a cool corkboard demo <a href="http://alexanderbass.com">Back?</a></noscript>
</head>
<body>
</body>
</html>

71
source/js/boardObject.js Normal file
View file

@ -0,0 +1,71 @@
import { Mouse } from './pointing.js';
export class BoardObject {
constructor(x, y, width, height, className, margins = { left: 0, right: 0, top: 0, bottom: 0 }) {
this.el = document.createElement('div');
this.el.className = className;
this.width = width;
this.height = height;
this.margins = margins;
this.updatePosition(x, y);
this.grabbable = false;
this.widgets = [];
}
updatePosition(x, y) {
this.x = x;
this.y = y;
this.el.style.left = `${x}px`;
this.el.style.top = `${y}px`;
}
nudgePosition(dx, dy) {
this.x = this.x + dx;
this.y = this.y + dy;
this.el.style.left = `${this.x}px`;
this.el.style.top = `${this.y}px`;
}
addWidget(widget) {
widget.setParent(this);
widget.resize();
this.el.appendChild(widget.el);
this.widgets.push(widget);
}
addWidgets(widgetList) {
for (const widget of widgetList) {
this.addWidget(widget);
}
}
removeWidget(widget) {
this.widgets = this.widgets.filter(function (currentWidget) {
return currentWidget !== widget;
});
widget.el.remove();
}
registerGrabEvent() {
this.grabbable = true;
this.el.classList.add("grabable");
this.el.addEventListener("mousedown", event => {
if (!(event.target == this.el || event.target.clickThrough)) return;
this.el.parentNode.append(this.el);
Mouse.isDown = true;
Mouse.dragging = this;
const x = event.screenX - this.x;
const y = event.screenY - this.y;
Mouse.offset = [x, y];
});
this.el.addEventListener("touchstart", event => {
if (!(event.target == this.el || event.target.clickThrough)) return;
this.el.parentNode.append(this.el);
Mouse.isDown = true;
Mouse.dragging = this;
console.log("touchstart", Mouse.dragging);
});
}
}

69
source/js/main.js Normal file
View file

@ -0,0 +1,69 @@
import { randomNumber } from './utility.js';
import { Note } from "./objects/note.js";
import { Page } from "./objects/page.js";
import { Tack } from './widgets/tack.js';
import { Textbox } from './widgets/textbox.js';
import { Paragraph } from './widgets/paragraph.js';
import { NoteSpawner } from './objects/notespawner.js';
globalThis.onload = function () {
const noteBox = document.body;
for (let i = 0; i < 5; i++) {
const noteSize = 300;
const x = randomNumber(0, document.documentElement.scrollWidth - noteSize);
const y = randomNumber(0, document.documentElement.scrollHeight - noteSize);
const note = new Note(x, y, randomNumber(-180, 180), noteSize, randomNumber(-15, 15), "./assets/note.png");
note.registerGrabEvent();
note.addWidgets([new Tack, new Textbox("Welcome")]);
noteBox.append(note.el);
}
for (let i = 0; i < 1; i++) {
const noteSize = 300;
const x = randomNumber(0, document.documentElement.scrollWidth - 1004);
const y = randomNumber(0, document.documentElement.scrollHeight - 720);
const note = new Page(x, y, 720, 1004, undefined, "./assets/page.png");
note.registerGrabEvent();
note.addWidget(new Tack);
note.addWidget(new Paragraph(
`
Hello and welcome to the Corkboard!
<br>
Try dragging everything around and writing in the notes. <i> also click the tacks!</i>
<br>
<br>
<br>
Back to <a href="https://alexanderbass.com/show">Alexanderbass.com</a>?
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
P.S. yes, I do know that sticky notes don't need tacks as they're sticky, but this wouldn't be half as fun without the falling notes.
`
));
noteBox.append(note.el);
}
const noteSpawner = new NoteSpawner();
noteSpawner.addWidget(new Paragraph("Create New Note", 50, 50));
noteBox.append(noteSpawner.el);
};

19
source/js/objects/note.js Normal file
View file

@ -0,0 +1,19 @@
import { BoardObject } from "../boardObject.js";
export class Note extends BoardObject {
constructor(x, y, noteHue = 0, noteSize = 300, rotation = 0, url) {
const margins = { left: 12, right: 25, top: 35, bottom: 50 };
super(x, y, noteSize, noteSize, "note", margins);
this.el.style.filter = `hue-rotate(${noteHue}deg) `;
this.el.style.background = `url(${url})`;
this.el.style.width = `${this.width}px`;
this.el.style.height = `${this.height}px`;
this.el.style.backgroundSize = `contain`;
this.rotate(rotation);
}
rotate(rotation) {
this.rotation = rotation;
this.el.style.rotate = `${rotation}deg`;
}
}

View file

@ -0,0 +1,50 @@
import { BoardObject } from "../boardObject.js";
import { randomNumber } from "../utility.js";
import { Note } from "./note.js";
import { Tack } from "../widgets/tack.js";
import { Textbox } from "../widgets/textbox.js";
import { Mouse } from "../pointing.js";
const inspiration = ["Bababooey", "Hello there!", "Sticky!", "Noted.", "Look behind you...",
"I'm in your notepad",
"51.4354123 -68.691168",
"help a beaver is gnawing at my leg!",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""];
const noteBox = document.body;
export class NoteSpawner extends BoardObject {
constructor(noteHue = 0, noteSize = 300, rotation = 5) {
const margins = { left: 12, right: 25, top: 35, bottom: 50 };
const y = window.innerHeight - 200;
// console.log(window);
super(0, y, noteSize, noteSize, "note", margins);
this.el.style.filter = `hue-rotate(${noteHue}deg) `;
this.el.style.background = `url("./assets/note.png")`;
this.el.style.width = `${this.width}px`;
this.el.style.height = `${this.height}px`;
this.el.style.backgroundSize = `contain`;
this.el.classList.add("clickable");
this.rotate(rotation);
this.el.addEventListener("mousedown", event => {
const noteSize = 300;
const x = event.screenX - noteSize / 2;
const y = event.screenY - noteSize / 2;
const note = new Note(x, y, randomNumber(-180, 180), noteSize, randomNumber(-15, 15), "./assets/note.png");
note.registerGrabEvent();
note.addWidgets([new Tack, new Textbox(inspiration[randomNumber(0, inspiration.length)])]);
noteBox.append(note.el);
Mouse.isDown = true;
Mouse.dragging = note;
Mouse.offset = [noteSize / 2, noteSize / 2];
});
}
rotate(rotation) {
this.rotation = rotation;
this.el.style.rotate = `${rotation}deg`;
}
}

19
source/js/objects/page.js Normal file
View file

@ -0,0 +1,19 @@
import { BoardObject } from "../boardObject.js";
export class Page extends BoardObject {
constructor(x, y, width, height, rotation = 0, url) {
const margins = { left: 86, right: 10, top: 83, bottom: 0 };
super(x, y, width, height, "note", margins);
this.el.style.width = `${width}px`;
this.el.style.height = `${height}px`;
this.el.style.backgroundImage = `url(${url})`;
this.el.style.backgroundSize = `contain`;
this.rotate(rotation);
}
rotate(rotation) {
this.rotation = rotation;
this.el.style.rotate = `${rotation}deg`;
}
}

39
source/js/pageWidget.js Normal file
View file

@ -0,0 +1,39 @@
export class pageWidget {
constructor(elementType, className, size = { respectMargins: false, fillWidth: true, fillHeight: true, centerWidth: false, centerHeight: false, width: null, height: null }, clickThrough = false) {
this.size = size;
this.el = document.createElement(elementType);
this.el.clickThrough = clickThrough;
this.el.className = className;
parent = {};
}
setParent(parent) {
this.parent = parent;
}
resize() {
const parent = this.parent;
const elStyle = this.el.style;
const size = this.size;
elStyle.position = "absolute";
if (this.size.respectMargins) {
this.size.fillWidth ? elStyle.width = `${parent.width - parent.margins.left - parent.margins.right}px` : {};
this.size.fillHeight ? elStyle.height = `${parent.height - parent.margins.top - parent.margins.bottom}px` : {};
elStyle.position = "absolute";
elStyle.top = `${parent.margins.top}px`;
elStyle.right = `${parent.margins.right}px`;
elStyle.left = `${parent.margins.left}px`;
elStyle.bottom = `${parent.margins.bottom}px`;
} else {
this.size.fillWidth ? elStyle.width = `${parent.width}px` : {};
this.size.fillHeight ? elStyle.height = `${parent.height}px` : {};
}
size.centerWidth ? elStyle.left = `${(parent.width - size.width) / 2}px` : {};
size.centerHeight ? elStyle.top = `${(parent.height - size.height) / 2}px` : {};
size.width ? elStyle.width = `${size.width}px` : {};
size.height ? elStyle.height = `${size.height}px` : {};
}
}

32
source/js/pointing.js Normal file
View file

@ -0,0 +1,32 @@
export let Mouse = { isDown: false, dragging: null, offset: [0, 0] };
globalThis.addEventListener("load", () => {
document.addEventListener('mouseup', () => {
Mouse.isDown = false;
Mouse.dragging = null;
});
document.addEventListener("touchend", event => {
Mouse.isDown = false;
Mouse.dragging = null;
console.log("touchend");
});
document.addEventListener('mousemove', (event) => {
event.preventDefault();
if (Mouse.isDown && Mouse.dragging.el) {
const x = event.screenX - Mouse.offset[0];
const y = event.screenY - Mouse.offset[1];
Mouse.dragging.updatePosition(x, y);
}
});
document.addEventListener('touchmove', (event) => {
console.log("touchmove");
if (Mouse.isDown && Mouse.dragging.el) {
const x = event.touches[0].screenX - Mouse.offset[0];
const y = event.touches[0].screenY - Mouse.offset[1];
Mouse.dragging.updatePosition(x, y);
}
});
});

21
source/js/utility.js Normal file
View file

@ -0,0 +1,21 @@
export function randomNumber(min, max) {
return Math.floor(Math.random() * (max - min) + min);
}
export function pxToPercent(x, y) {
const [screenWidth, screenHeight] = getScreenDimensions();
const left = 100 * x / screenWidth;
const top = 100 * y / screenHeight;
return [left, top];
}
// Input, Screen Percentage. Output, Screen Pixels
function convertToPixels(left, top) {
}
function getScreenDimensions() {
const width = window.innerWidth;
const height = window.innerHeight;
return [width, height];
}

View file

@ -0,0 +1,17 @@
import { pageWidget } from "../pageWidget.js";
export class Paragraph extends pageWidget {
constructor(text, fontSize = 25, lineSpacing = 27) {
super("p", "paragraph", { respectMargins: true, fillWidth: true, fillHeight: false, centerWidth: false, centerHeight: false, width: null, height: 8 }, true);
// this.el.spellcheck = false;
this.el.style.color = "black";
this.el.style.paddingTop = "5px";
this.el.style.overflowWrap = "anywhere";
this.el.style.lineHeight = `${lineSpacing}px`;
this.el.style.fontSize = `${fontSize}px`;
this.el.style.userSelect = "none";
this.el.innerHTML = text;
}
}

35
source/js/widgets/tack.js Normal file
View file

@ -0,0 +1,35 @@
import { pageWidget } from "../pageWidget.js";
export class Tack extends pageWidget {
constructor(tackHue) {
super("div", "tack", { respectMargins: false, fillWidth: false, fillHeight: false, centerWidth: true, centerHeight: false, width: 45, height: 45 }, false);
this.el.style.filter = `hue-rotate(${tackHue}deg)`;
this.el.classList.add("clickable");
this.el.addEventListener("mousedown", () => {
this.click();
}, false);
}
click() {
this.parent.el.parentNode.append(this.el);
this.parent.removeWidget(this);
let velocity = 0;
let finished = false;
let frame = () => {
this.parent.nudgePosition(0, velocity);
velocity = velocity + 1.5;
if (this.parent.y > screen.height) {
this.parent.el.remove();
finished = true;
return;
}
requestAnimationFrame(frame);
};
if (!finished) (
requestAnimationFrame(frame)
);
}
}

View file

@ -0,0 +1,11 @@
import { pageWidget } from "../pageWidget.js";
export class Textbox extends pageWidget {
constructor(text = "") {
super("textarea", "textbox", { respectMargins: true, fillWidth: true, fillHeight: true, centerWidth: false, centerHeight: false, width: null, height: null }, true);
this.el.spellcheck = false;
this.el.value = text;
}
}

65
source/scss/main.scss Normal file
View file

@ -0,0 +1,65 @@
$text-color: white;
$tackImage: url(assets/tack.png);
$corkImage: url(assets/cork.png);
* {
box-sizing: border-box;
}
body {
background-image: $corkImage;
background-size: 11%;
background-repeat: repeat;
color: $text-color;
font-family: "Arial";
overflow-y: hidden;
}
.grabable:hover {
cursor: move;
}
.clickable:hover {
cursor: pointer;
}
.note {
position: absolute;
background-size: contain;
background-size: cover;
background-size: 100%;
display: block;
textarea:focus {
outline: none;
border: none;
}
textarea {
position: relative;
display: block;
overflow: hidden;
overflow-y: hidden;
font-family: 'Comic Sans MS', cursive;
font-size: xxx-large;
resize: none;
background-color: transparent;
border: none;
}
.tack {
background-image: $tackImage;
background-size: contain;
}
}
a {
text-decoration: none;
}
a:hover {
text-decoration: underline;
}