Skip to content

Commit 9e173db

Browse files
committed
Testing save/open
1 parent b4fe947 commit 9e173db

File tree

3 files changed

+95
-21
lines changed

3 files changed

+95
-21
lines changed

src/pg/inputPixelEditor/__examples__/basic/basic.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,11 @@
3737
<button part="modeEllipseOutline">Ellipse Outline</button>
3838
</div>
3939
<div part="debug"></div>
40+
<div>
41+
<button part="save">Save</button>
42+
<button part="open">Open</button>
43+
</div>
44+
<pre part="output">
45+
46+
</pre>
4047
</div>

src/pg/inputPixelEditor/__examples__/basic/basic.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ export default class XPgInputPixelEditorBasic extends HTMLElement {
3030
@Part() $modeEllipse: HTMLButtonElement;
3131
@Part() $modeEllipseOutline: HTMLButtonElement;
3232

33+
@Part() $save: HTMLButtonElement;
34+
@Part() $open: HTMLButtonElement;
35+
@Part() $output: HTMLPreElement;
36+
3337
connectedCallback() {
3438
this.$width.value = '10';
3539
this.$height.value = '10';
@@ -68,6 +72,14 @@ export default class XPgInputPixelEditorBasic extends HTMLElement {
6872
this.$invert.addEventListener('click', () => {
6973
this.$input.invert();
7074
});
75+
this.$save.addEventListener('click', async () => {
76+
const json = await this.$input.save();
77+
this.$output.textContent = JSON.stringify(json, null, 4);
78+
});
79+
this.$open.addEventListener('click', () => {
80+
const json = this.$output.textContent;
81+
this.$input.open(json as any);
82+
});
7183
}
7284

7385
handleChange(e: CustomEvent) {

src/pg/inputPixelEditor/inputPixelEditor.ts

Lines changed: 76 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,21 @@ type Layer = {
6363
export: boolean
6464
}
6565

66+
interface FileOptions {
67+
history?: boolean
68+
}
69+
70+
interface File {
71+
width: number
72+
height: number
73+
transparent: boolean
74+
colors: Color[]
75+
layers: Layer[]
76+
data: number[][][]
77+
undo?: History[]
78+
redo?: History[]
79+
}
80+
6681
function toColor([r, g, b, a]: Color) {
6782
return `rgba(${r}, ${g}, ${b}, ${a})`;
6883
}
@@ -158,28 +173,9 @@ export default class PgInputPixelEditor extends HTMLElement {
158173
}
159174

160175
render(changes) {
161-
if (changes.width || changes.height || changes.size) {
176+
if (changes.width || changes.height || changes.size || changes.transparent) {
162177
this.#init();
163178
}
164-
if (changes.transparent) {
165-
const totalSize = this.size + this.gridSize;
166-
const actualWidth = this.width * totalSize - this.gridSize;
167-
const actualHeight = this.height * totalSize - this.gridSize;
168-
if (this.transparent) {
169-
for (let y = 0; y < this.height; y++) {
170-
for (let x = 0; x < this.width; x++) {
171-
this.#baseLayerContext.fillStyle = WHITE;
172-
this.#baseLayerContext.fillRect(x * totalSize, y * totalSize, this.size + 1, this.size + 1);
173-
this.#baseLayerContext.fillStyle = '#DDD';
174-
this.#baseLayerContext.fillRect(x * totalSize + 5, y * totalSize, 5, 5);
175-
this.#baseLayerContext.fillRect(x * totalSize, y * totalSize + 5, 5, 5);
176-
}
177-
}
178-
} else {
179-
this.#baseLayerContext.clearRect(0, 0, actualWidth, actualHeight);
180-
}
181-
this.#updateGrid();
182-
}
183179
}
184180

185181
#reset = true;
@@ -189,10 +185,24 @@ export default class PgInputPixelEditor extends HTMLElement {
189185
const actualHeight = this.height * totalSize - this.gridSize;
190186
this.$canvas.width = actualWidth;
191187
this.$canvas.height = actualHeight;
188+
this.#context.clearRect(0, 0, actualWidth, actualHeight);
192189
[this.#baseLayer, this.#baseLayerContext] = createLayer(actualWidth, actualHeight);
193190
[this.#editLayer, this.#editLayerContext] = createLayer(actualWidth, actualHeight);
194191
[this.#noEditLayer, this.#noEditLayerContext] = createLayer(actualWidth, actualHeight);
195192
[this.#previewLayer, this.#previewLayerContext] = createLayer(actualWidth, actualHeight);
193+
if (this.transparent) {
194+
for (let y = 0; y < this.height; y++) {
195+
for (let x = 0; x < this.width; x++) {
196+
this.#baseLayerContext.fillStyle = WHITE;
197+
this.#baseLayerContext.fillRect(x * totalSize, y * totalSize, this.size + 1, this.size + 1);
198+
this.#baseLayerContext.fillStyle = '#DDD';
199+
this.#baseLayerContext.fillRect(x * totalSize + 5, y * totalSize, 5, 5);
200+
this.#baseLayerContext.fillRect(x * totalSize, y * totalSize + 5, 5, 5);
201+
}
202+
}
203+
} else {
204+
this.#baseLayerContext.clearRect(0, 0, actualWidth, actualHeight);
205+
}
196206
if (this.#reset) {
197207
this.#layer = 0;
198208
this.#layers = [{
@@ -209,6 +219,7 @@ export default class PgInputPixelEditor extends HTMLElement {
209219
} else {
210220
this.#redraw();
211221
}
222+
this.#updateGrid();
212223
}
213224

214225
#redraw() {
@@ -665,7 +676,7 @@ export default class PgInputPixelEditor extends HTMLElement {
665676
return;
666677
}
667678
iterateGrid(this.#data[this.#layer], (x, y) => {
668-
this.#data[this.#layer][y][x] = this.#data[this.#layer] [y][x] === 0 ? 1 : 0;
679+
this.#data[this.#layer][y][x] = this.#data[this.#layer][y][x] === 0 ? 1 : 0;
669680
});
670681
this.#setPixelAll();
671682
}
@@ -766,4 +777,48 @@ export default class PgInputPixelEditor extends HTMLElement {
766777
inputModeEllipseOutline() {
767778
this.#inputMode = InputMode.EllipseOutline;
768779
}
780+
781+
async save(options: FileOptions = {}): Promise<File> {
782+
const file: File = {
783+
width: this.width,
784+
height: this.height,
785+
transparent: this.transparent,
786+
colors: this.#colors,
787+
layers: this.#layers,
788+
data: this.#data
789+
};
790+
if (options.history === true) {
791+
file.undo = this.#undoHistory;
792+
file.redo = this.#redoHistory;
793+
}
794+
// Trim data
795+
for (let l = 0; l < file.data.length; l++) {
796+
for (let y = file.data[l].length - 1; y >= 0; y--) {
797+
if (y >= this.height) {
798+
file.data[l].pop();
799+
continue;
800+
}
801+
for (let x = file.data[l][y].length - 1; x >= 0; x--) {
802+
if (x >= this.width) {
803+
file.data[l][y].pop();
804+
}
805+
}
806+
}
807+
}
808+
// Output
809+
return file;
810+
}
811+
812+
async open(json: File) {
813+
const errors: string[] = [];
814+
// Validate 6 properties exist
815+
const keys = Object.keys(json);
816+
const required = ['width', 'height', 'transparent', 'colors', 'layers', 'data'];
817+
required.forEach((key) => {
818+
if (!keys.includes(key)) {
819+
errors.push(`JSON key '${key}' required.`);
820+
}
821+
});
822+
}
823+
769824
}

0 commit comments

Comments
 (0)