Skip to content

Commit

Permalink
split project into components
Browse files Browse the repository at this point in the history
  • Loading branch information
went2 committed Jul 6, 2023
1 parent a69e9e8 commit 343e447
Show file tree
Hide file tree
Showing 9 changed files with 223 additions and 180 deletions.
170 changes: 0 additions & 170 deletions pixelEditor.ts

This file was deleted.

6 changes: 5 additions & 1 deletion src/App.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import {
PixelEditor,
} from "./components";

import { draw, fill, rectangle, pick } from "./utils/drawHelpers";

import { EditorState } from "./types";
import Picture from "./models/Picture";
import { historyUpdateState } from "./models/reducers";

const initialState: EditorState = {
tool: "draw",
Expand Down Expand Up @@ -41,7 +45,7 @@ function startPixelEditor({
const app = new PixelEditor(state, {
tools,
controls,
dispatch(action) {
dispatch(action: { undo: boolean; picture: Picture }) {
state = historyUpdateState(state, action);
app.syncState(state);
},
Expand Down
3 changes: 3 additions & 0 deletions src/components/PictureCanvas.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import elt from "../utils/createElement";
import { Position } from "../types";
import Picture from "../models/Picture";
import { drawPicture } from "../utils/drawHelpers";

// A component holds canvas that only knows current picture
// It adds mouse and touch events handlers when constructs
Expand All @@ -18,6 +20,7 @@ class PictureCanvas {
ontouchstart: (event: TouchEvent) => this.touch(event, pointerDown),
}) as HTMLCanvasElement;
if (scale) this.scale = scale;
this.picture = picture;
this.syncState(picture);
}

Expand Down
5 changes: 3 additions & 2 deletions src/components/PixelEditor.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import elt from "../utils/createElement";
import PictureCanvas from "./PictureCanvas";
import { UIComponent, EditorState, EditorConfig, Position } from "../types";

class PixelEditor implements UIComponent {
public state: EditorState;
Expand All @@ -11,10 +12,10 @@ class PixelEditor implements UIComponent {
const { tools, controls, dispatch } = config;
this.state = state;

this.canvas = new PictureCanvas(state.picture, (pos) => {
this.canvas = new PictureCanvas(state.picture, (pos: Position) => {
const tool = tools[this.state.tool];
const onMove = tool(pos, this.state, dispatch);
if (onMove) return (pos) => onMove(pos, this.state);
if (onMove) return (pos: Position) => onMove(pos, this.state);
});

this.controls = controls.map((Control) => new Control(state, config));
Expand Down
42 changes: 42 additions & 0 deletions src/models/Picture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// model for a frame of canvas
class Picture {
public width: number;
public height: number;
public pixels: string[]; // array to store colors

constructor(width: number, height: number, pixels: string[]) {
this.width = width;
this.height = height;
this.pixels = pixels;
}
public static empty(width: number, height: number, color: string) {
const pixels = new Array(width * height).fill(color);
return new Picture(width, height, pixels);
}

public getPixel(x: number, y: number) {
return this.pixels[x + y * this.width];
}

public draw(pixels: Pixel[]) {
const copy = this.pixels.slice();
for (const { x, y, color } of pixels) {
copy[x + y * this.width] = color;
}
return new Picture(this.width, this.height, copy);
}
}

// store coordinates of color
export class Pixel {
public x: number;
public y: number;
public color: string; // like "#000000"
constructor(x: number, y: number, color: string) {
this.x = x;
this.y = y;
this.color = color;
}
}

export default Picture;
24 changes: 24 additions & 0 deletions src/models/reducers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { EditorState } from "../types";
import Picture from "./Picture";

// undo state reducer
export function historyUpdateState(
state: EditorState,
action: { undo: boolean; picture: Picture }
) {
if (action.undo == true) {
if (state.done.length == 0) return state;
return Object.assign({}, state, {
picture: state.done[0],
done: state.done.slice(1),
doneAt: 0,
});
} else if (action.picture && state.doneAt < Date.now() - 1000) {
return Object.assign({}, state, action, {
done: [state.picture, ...state.done],
doneAt: Date.now(),
});
} else {
return Object.assign({}, state, action);
}
}
9 changes: 2 additions & 7 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import Picture from "./models/Picture";

// UIs are modeled as components.
// Each component creates corresponding HTML elements
// and expose sysnState() to outside world
Expand All @@ -24,13 +26,6 @@ export interface EditorConfig {
dispatch: any;
}

// store coordinates of color
export class Pixel {
public x: number;
public y: number;
public color: string; // like "#000000"
}

export interface Position {
x: number;
y: number;
Expand Down
Loading

0 comments on commit 343e447

Please sign in to comment.