Skip to content

Commit

Permalink
Fixes touch device interactions and re-organizes component dependencies.
Browse files Browse the repository at this point in the history
  • Loading branch information
Danziger committed Dec 31, 2023
1 parent f1c73a1 commit 8591b51
Show file tree
Hide file tree
Showing 10 changed files with 348 additions and 136 deletions.
75 changes: 53 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,47 +49,74 @@ Known Issues
TODOs & Bugfixes
----------------

- Configure GitHub actions.
### Bugs

- Update dependencies.

- Add an option to send drawings to me (to Supabase, maybe).

- Show artist picture?

- Add "Hiring?" and attribution inside the menu too.

- Use a `<template>` for `.content__warningHeader`
- Adjust side paddings to be included in the header links and button.

- Add a placeholder for the page logo in case it doesn't load.
- Hide "Hiring?" label in screenshot mode.

- Review aria tags and code organization.
- Close menu when enabling focus mode.

- Adjust side paddings to be included in the header links and button.
- Hide (custom) cursor while resizing.

- Improve performance on large high-res screens. See:
- https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Optimizing_canvas
- https://github.com/karellodewijk/canvas-webgl
- https://github.com/jagenjo/Canvas2DtoWebGL
- https://www.reddit.com/r/webgl/comments/sc4024/is_webgl_fast_than_canvas_api_for_drawing_a_bunch/
- Consider breaking down the canvas into smaller ones (tiles).
- Consider adding other brush shapes, like any other standard drawing app (e.g https://www.youtube.com/watch?v=3GqUM4mEYKA)

<br />


Other Ideas & Features
----------------------
### JS Paint Features

- Image upload. See https://medium.com/@agbales/how-to-make-8-bit-art-with-javascript-eea808a70ba2

- Move cursor position to a corner.

- Multiple types and sizes of brushes (square, circle).

- Consider a non-pixel-art version of the app where you can use a regular "pen" brush.

- Custom colors.

- Screenshot mode with multiple sizes.
- Multitouch on mobile.
- Undo/redo.

- Resize and keep canvas.

- Multitouch on mobile.

- Block trajectory in X/Y or diagonal axis.
- Image upload. See https://medium.com/@agbales/how-to-make-8-bit-art-with-javascript-eea808a70ba2

- Fill tool.
- Multiple sizes (or shapes) of brushes.
- Close menu when enabling focus mode.
- Toggle cursor type.
- Animate attribution tag.

<br />


### Other Features

- Add an option to send drawings to me (to Supabase, maybe).

- Show artist picture.

- Add "Hiring?" and attribution inside the menu too.

- Toggle cursor type (native (default + pen), custom, both). Consider using 2 settings, one for regular interaction and the other one for drawing.

- Consider removing vibration toggle (it can be done from the phone).

<br />


### Tech Debt

- Update dependencies.

- Use a `<template>` for `.content__warningHeader`.

- Review aria tags and code organization.

<br />

Expand All @@ -109,6 +136,10 @@ Some other sites I've built

🎰 Circular slot machine mobile-first SPA built with JavaScript, CSS variables and Emojis!

- **[Stars.js](https://danziger.github.io/starsjs/)**

✨ Generate a rotating galaxy in a canvas.

- **[Job.js](https://danziger.github.io/jobjs/)**

💼 Sharable image summaries for your open positions: Share your jobs as an image on LinkedIn and Instagram to get a visibility boost and more applicants!
Expand Down
115 changes: 89 additions & 26 deletions src/app/components/app/app.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { initializeLinks } from '../link/link.utils';
import { TORINO_VIDEOS } from '../torino/torino.constants';
import { Nav } from '../nav/nav.component';

import { AppActions } from './app.constants';

export class App {

// CSS classes:
Expand All @@ -15,15 +17,22 @@ export class App {
static C_SHOW_FALLBACK = 'app--showFallback';
static C_SHOW_SCREENSHOT = 'app--showScreenshot';

// Paint Actions:
actionHandlers = {};

// Elements:
root = document.body;

// Components:
nav = null;
footer = null;
// Paint Controller:
jsPaint = null;
ruler = null;
cursor = null;

// UI Components:
uiControls = {
nav: null,
footer: null,
ruler: null,
cursor: null,
};

constructor() {
const { root } = this;
Expand All @@ -49,6 +58,8 @@ export class App {
document.addEventListener('touchstart', disableFocus);
}

this.handleAction = this.handleAction.bind(this);

if (IS_BROWSER_SUPPORTED) {
initializeLinks();
}
Expand All @@ -61,34 +72,50 @@ export class App {
}

init() {
const { root } = this;

// TODO: Need something to manage dependecy injection...
const {
uiControls,
handleAction: onAction,
} = this;

if (HAS_CURSOR) {
this.root.classList.add(App.C_HAS_ACTIVE_HOVER);
const nav = new Nav({ onAction });

this.cursor = new Cursor();
}
const footer = new Footer({ onAction });

const ruler = this.ruler = new Ruler(); // TODO: Pass root?
const ruler = new Ruler({ onAction });

const jsPaint = this.jsPaint = new JsPaint({
cursor: this.cursor,
ruler,
const cursor = new Cursor({
onAction,
enabled: HAS_CURSOR,
});

this.nav = new Nav({ jsPaint, ruler, cursor: this.cursor });

// TODO: Pass down jsPaint and cursor:
this.footer = new Footer((color) => {
jsPaint.setColor(color);

root.style.setProperty('--c-current', color);
});
if (HAS_CURSOR) {
// TODO: Should the addition or removal of this be triggered from within Cursor?
this.root.classList.add(App.C_HAS_ACTIVE_HOVER);
}

// TODO: Get rid of this unworthy code...
jsPaint.footer = this.footer;
// TODO: Also pass handlers instead?
this.jsPaint = new JsPaint({ uiControls });

this.actionHandlers = {
[AppActions.ADD_GLOBAL_CLASS]: this.handleAddGlobalClass.bind(this),
[AppActions.REMOVE_GLOBAL_CLASS]: this.handleRemoveGlobalClass.bind(this),

// TODO: Just pass a ref to jsPaint instead to all UI components:
[AppActions.MAGIC_DRAWING]: this.jsPaint.magicDrawing.bind(this.jsPaint),
[AppActions.DISABLE]: this.jsPaint.disable.bind(this.jsPaint),
[AppActions.ENABLE]: this.jsPaint.enable.bind(this.jsPaint),
[AppActions.CLEAR]: this.jsPaint.reset.bind(this.jsPaint),
[AppActions.DOWNLOAD]: this.jsPaint.download.bind(this.jsPaint),
[AppActions.CHANGE_COLOR]: this.handleColorChange.bind(this),

[AppActions.CHANGE_RULER_MODE]: this.handleRulerModeChange.bind(this),
[AppActions.CHANGE_CURSOR_MODE]: this.handleCursorModeChange.bind(this),
};

uiControls.nav = nav;
uiControls.footer = footer;
uiControls.ruler = ruler;
uiControls.cursor = cursor;
}

showFallback() {
Expand All @@ -107,6 +134,7 @@ export class App {
document.querySelector('.content__regularHeader').setAttribute('aria-hidden', true);
document.querySelector('.content__warningHeader').removeAttribute('hidden');
document.querySelector('.content__warningHeader').removeAttribute('aria-hidden');

// Load one video:
const torinoVideo = TORINO_VIDEOS[Math.floor(Math.random() * TORINO_VIDEOS.length)];

Expand Down Expand Up @@ -138,4 +166,39 @@ export class App {
this.root.classList.remove(App.C_SHOW_SCREENSHOT);
}

handleAction(type, payload) {
const actionHandler = this.actionHandlers[type];

if (actionHandler) actionHandler(payload);
}

handleAddGlobalClass() {
// TODO: TO avoid changing this.body.classList from each component...
console.log(!!this);
}

handleRemoveGlobalClass() {
// TODO: TO avoid changing this.body.classList from each component...
console.log(!!this);
}

// handleToggleGlobalClass

handleColorChange(hexColor) {
this.jsPaint.setColor(hexColor);
this.root.style.setProperty('--c-current', hexColor); // TODO: This should be updated by calling setColor
}

handleRulerModeChange(openRuler) {
if (openRuler) {
this.uiControls.ruler.open();
} else {
this.uiControls.ruler.close();
}
}

handleCursorModeChange(cursorMode) {
this.uiControls.cursor.setMode(cursorMode);
}

}
23 changes: 23 additions & 0 deletions src/app/components/app/app.constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export const AppActions = {

ADD_GLOBAL_CLASS: 'ADD_GLOBAL_CLASS',

REMOVE_GLOBAL_CLASS: 'REMOVE_GLOBAL_CLASS',

DISABLE: 'DISABLE',

ENABLE: 'ENABLE',

CLEAR: 'CLEAR',

DOWNLOAD: 'DOWNLOAD',

MAGIC_DRAWING: 'MAGIC_DRAWING',

CHANGE_COLOR: 'CHANGE_COLOR',

CHANGE_RULER_MODE: 'CHANGE_RULER_MODE',

CHANGE_CURSOR_MODE: 'CHANGE_CURSOR_MODE',

};
14 changes: 8 additions & 6 deletions src/app/components/footer/footer.component.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { AppActions } from '../app/app.constants';

export class Footer {

// CSS selectors:
Expand All @@ -21,13 +23,13 @@ export class Footer {
attributionByName = document.querySelector(Footer.S_ATTRIBUTION_BY_NAME);
attributionSurname = document.querySelector(Footer.S_ATTRIBUTION_SURNAME);

// Callbacks:
onActionClicked;
// Callback:
onAction;

constructor(onActionClicked) {
this.onActionClicked = onActionClicked;
constructor({ onAction }) {
this.onAction = onAction;

if (onActionClicked) {
if (onAction) {
this.addEventListeners();
this.show();
} else {
Expand All @@ -46,7 +48,7 @@ export class Footer {

target.classList.add(Footer.C_CURRENT);

this.onActionClicked(window.getComputedStyle(target).getPropertyValue('--bg').trim());
this.onAction(AppActions.CHANGE_COLOR, window.getComputedStyle(target).getPropertyValue('--bg').trim());
});

}
Expand Down
5 changes: 5 additions & 0 deletions src/app/components/footer/footer.styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@
margin: 0 0 0 auto;
transform: translate(0, 3px);
white-space: nowrap;

.app--hasActiveHover &:hover {
animation: shake .5s;
animation-iteration-count: infinite;
}
}

&__hiring {
Expand Down
Loading

0 comments on commit 8591b51

Please sign in to comment.