commit 47d80a88df5ebbc10675eeb2d78bd0582cbcd132 Author: Alexander Bass Date: Sun Apr 23 15:51:14 2023 -0400 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fa60821 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.sass-cache +art +node_modules +target \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..c040407 --- /dev/null +++ b/README.md @@ -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. diff --git a/makefile b/makefile new file mode 100644 index 0000000..d751cd1 --- /dev/null +++ b/makefile @@ -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/ diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..ae2b9ef --- /dev/null +++ b/package-lock.json @@ -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" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..13b3345 --- /dev/null +++ b/package.json @@ -0,0 +1,10 @@ +{ + "name": "corkboard", + "version": "1.0.0", + "description": "", + "author": "Alexander Bass", + "license": "GPLv3", + "dependencies": { + "rollup": "^2.78.1" + } +} \ No newline at end of file diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..a65830b --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,9 @@ +export default { + input: 'source/js/main.js', + output: [ + { + file: 'target/js/main.js', + format: 'iife' + } + ] +}; diff --git a/source/assets/cork.png b/source/assets/cork.png new file mode 100644 index 0000000..20ef451 Binary files /dev/null and b/source/assets/cork.png differ diff --git a/source/assets/note.png b/source/assets/note.png new file mode 100644 index 0000000..46d61ed Binary files /dev/null and b/source/assets/note.png differ diff --git a/source/assets/page.png b/source/assets/page.png new file mode 100644 index 0000000..e67dd33 Binary files /dev/null and b/source/assets/page.png differ diff --git a/source/assets/tack.png b/source/assets/tack.png new file mode 100644 index 0000000..5eb1202 Binary files /dev/null and b/source/assets/tack.png differ diff --git a/source/index.html b/source/index.html new file mode 100644 index 0000000..3fbf528 --- /dev/null +++ b/source/index.html @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/js/boardObject.js b/source/js/boardObject.js new file mode 100644 index 0000000..8b1381e --- /dev/null +++ b/source/js/boardObject.js @@ -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); + }); + + } +} \ No newline at end of file diff --git a/source/js/main.js b/source/js/main.js new file mode 100644 index 0000000..5a028b0 --- /dev/null +++ b/source/js/main.js @@ -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! +
+ Try dragging everything around and writing in the notes. also click the tacks! +
+
+
+ Back to Alexanderbass.com? +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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); + + +}; \ No newline at end of file diff --git a/source/js/objects/note.js b/source/js/objects/note.js new file mode 100644 index 0000000..8ed3c97 --- /dev/null +++ b/source/js/objects/note.js @@ -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`; + } + +} \ No newline at end of file diff --git a/source/js/objects/notespawner.js b/source/js/objects/notespawner.js new file mode 100644 index 0000000..8a69d0c --- /dev/null +++ b/source/js/objects/notespawner.js @@ -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`; + } + +} \ No newline at end of file diff --git a/source/js/objects/page.js b/source/js/objects/page.js new file mode 100644 index 0000000..dd5ee1f --- /dev/null +++ b/source/js/objects/page.js @@ -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`; + } + +} \ No newline at end of file diff --git a/source/js/pageWidget.js b/source/js/pageWidget.js new file mode 100644 index 0000000..81959fb --- /dev/null +++ b/source/js/pageWidget.js @@ -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` : {}; + } + + +} \ No newline at end of file diff --git a/source/js/pointing.js b/source/js/pointing.js new file mode 100644 index 0000000..f2948a8 --- /dev/null +++ b/source/js/pointing.js @@ -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); + } + + }); +}); diff --git a/source/js/utility.js b/source/js/utility.js new file mode 100644 index 0000000..94ba0d4 --- /dev/null +++ b/source/js/utility.js @@ -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]; +} \ No newline at end of file diff --git a/source/js/widgets/paragraph.js b/source/js/widgets/paragraph.js new file mode 100644 index 0000000..dc2fddb --- /dev/null +++ b/source/js/widgets/paragraph.js @@ -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; + + } + +} \ No newline at end of file diff --git a/source/js/widgets/tack.js b/source/js/widgets/tack.js new file mode 100644 index 0000000..f5aebde --- /dev/null +++ b/source/js/widgets/tack.js @@ -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) + ); + + } + + +} \ No newline at end of file diff --git a/source/js/widgets/textbox.js b/source/js/widgets/textbox.js new file mode 100644 index 0000000..b7ce6dc --- /dev/null +++ b/source/js/widgets/textbox.js @@ -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; + + } + +} \ No newline at end of file diff --git a/source/scss/main.scss b/source/scss/main.scss new file mode 100644 index 0000000..695bd81 --- /dev/null +++ b/source/scss/main.scss @@ -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; +} \ No newline at end of file