Skip to content

Commit dae61c2

Browse files
committed
Redo implemented
1 parent 82c120f commit dae61c2

File tree

4 files changed

+68
-10
lines changed

4 files changed

+68
-10
lines changed

src/Frame.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ export default class Frame {
3838
return this.activePath.push(point);
3939
}
4040

41+
addPath(path: Array<[number, number]>) {
42+
this.activePath = path;
43+
this.paths.push(path);
44+
}
45+
4146
destroy() {
4247
this.activePath = undefined;
4348
this.paths = undefined;

src/Layer.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export default class Layer {
2828

2929
color: string = chroma.random().hex();
3030
width: number = 5;
31-
fill: boolean = true;
31+
fill: boolean = false;
3232
cycles: number = 0;
3333

3434
playing: boolean = false;
@@ -143,6 +143,16 @@ export default class Layer {
143143
}
144144

145145
pointTransform(x, y) {
146+
try {
147+
// Apple sucks, and there's a proportional term added to the position of a
148+
// pointer, to compensate for their stupid scrolling behaviors.
149+
// Actually, they double suck because they claim "parity" between iPad OS
150+
// Safari and Desktop Safari such that they have identical user-agents
151+
// which means we have to create this stupid touch event on all platforms.
152+
document.createEvent("TouchEvent");
153+
y -= 60 * y / this.canvas.height
154+
} catch (e) {}
155+
146156
return [
147157
((x-this.canvas.width/2) * 1/this.scale) * Math.cos(-this.rotation) -
148158
((y-this.canvas.height/2) * 1/this.scale) * Math.sin(-this.rotation),
@@ -176,9 +186,10 @@ export default class Layer {
176186
this.frames.splice(at, 1);
177187
}
178188

179-
undo(at = this.frame) {
180-
this.frames[at].paths.pop();
189+
undo(frameIndex = this.frame) {
190+
const path = this.frames[frameIndex].paths.pop();
181191
this.render();
192+
return path;
182193
}
183194

184195
next() {
@@ -232,22 +243,21 @@ export default class Layer {
232243
this._drawing = true;
233244
this.canvas.style.zIndex = '100';
234245
this.frames[this.frame].beginPath([x-this.offset[0], y-this.offset[1]]);
246+
this.last_pos = [x, y];
235247
}
236248

237249
handleMove = ({ clientX, clientY, pointerType }) => {
238250
if (pointerType === 'touch' || !this.drawing) return;
239251
const [x, y] = this.pointTransform(clientX, clientY);
240252

241-
this.last_pos = [x, y];
242-
243253
// As user is drawing, we immediately render the new path
244254
this.ctx.beginPath();
245255
this.ctx.moveTo(...this.last_pos);
246256
this.ctx.lineTo(x, y);
247257
this.ctx.stroke();
248258

249259
this.frames[this.frame].lineTo([x-this.offset[0], y-this.offset[1]]);
250-
260+
this.last_pos = [x, y];
251261
this.render();
252262
}
253263

src/Scene.ts

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,17 @@ export default class Scene {
202202
}
203203

204204
handleKeyDown = e => {
205-
if ((e.metaKey || e.ctrlKey) && e.which === 37 || e.which === 39) e.preventDefault();
205+
if ((e.metaKey || e.ctrlKey) && (e.which === 89 || e.which === 37 || e.which === 39)) e.preventDefault();
206+
207+
// e.preventDefault();
208+
// e.stopPropagation();
209+
// e.stopImmediatePropagation();
206210

207211
switch (e.which) {
212+
// cmd+z, undo
208213
case 90: e.metaKey && this.undo(); break;
214+
// cmd+y, redo
215+
case 89: e.metaKey && this.redo(); break;
209216
case 32: this.play(); break;
210217
case 39:
211218
e.metaKey || e.ctrlKey ? this.layers[this.active_layer].rotation += 0.1 : this.next()
@@ -229,7 +236,8 @@ export default class Scene {
229236
this.history_offset < this.history.length ? this.history.length - this.history_offset : 0,
230237
[
231238
this.layers[this.active_layer].id,
232-
this.layers[this.active_layer].currentFrame.id
239+
this.layers[this.active_layer].currentFrame.id,
240+
null
233241
]
234242
);
235243

@@ -262,6 +270,8 @@ export default class Scene {
262270
}
263271

264272
undo() {
273+
if (this.history_offset <= 0) return;
274+
265275
this.history_offset = Math.max(0, this.history_offset - 1);
266276
const [layerId, frameId] = this.history[this.history_offset];
267277

@@ -272,12 +282,38 @@ export default class Scene {
272282
if (this.layers[layerIndex].id !== this.layers[this.active_layer].id) this.active_layer = layerIndex;
273283
// move all layers if frame is navigated to
274284
if (this.layers[layerIndex].frame !== frameIndex) this.layers.forEach(l => l.goTo(l.frame + frameDelta));
275-
this.layers[layerIndex].undo(frameIndex);
285+
// The layer action that was undone
286+
const layerPath = this.layers[layerIndex].undo(frameIndex);
287+
// add a third value to the `history` tuple, for `redo` action
288+
this.history[this.history_offset][2] = layerPath;
289+
276290
this.onlayeractivate && this.onlayeractivate(this.layers[this.active_layer]);
277291
}
278292

279293
redo() {
294+
if (this.history_offset === this.history.length) return;
295+
296+
const historyItem = this.history[this.history_offset];
297+
const [layerId, frameId, path] = historyItem;
298+
299+
this.history_offset = Math.min(this.history.length, this.history_offset + 1);
300+
301+
const layerIndex = this.layers.findIndex(l => l.id === layerId);
302+
const frameIndex = this.layers[layerIndex].frames.findIndex(f => f.id === frameId);
303+
const frameDelta = frameIndex - this.layers[layerIndex].frame;
304+
305+
if (!path) throw new Error(`${layerIndex}:${frameIndex}: No path found for redo action!`);
280306

307+
if (this.layers[layerIndex].id !== this.layers[this.active_layer].id) this.active_layer = layerIndex;
308+
// move all layers if frame is navigated to
309+
if (this.layers[layerIndex].frame !== frameIndex) this.layers.forEach(l => l.goTo(l.frame + frameDelta));
310+
311+
this.layers[layerIndex].frames[frameIndex].addPath(path);
312+
// unset saved path (as its now back in the frame)
313+
historyItem[2] = null;
314+
315+
this.onlayeractivate && this.onlayeractivate(this.layers[this.active_layer]);
316+
this.layers[layerIndex].render();
281317
}
282318

283319
play() {

src/index.scss

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ html, body {
33
padding: 0;
44
height: 100vh;
55
width: 100vw;
6+
user-select: none;
67
}
78

89
body {
@@ -11,6 +12,8 @@ body {
1112
height: 100vh;
1213
width: 100vw;
1314
overflow: hidden;
15+
user-select: none;
16+
pointer-events: none;
1417
}
1518

1619
#root > header {
@@ -29,6 +32,7 @@ body {
2932
align-items: center;
3033
-webkit-appearance: none;
3134
height: 30px;
35+
pointer-events: all;
3236

3337
& + button {
3438
border-radius: 0;
@@ -56,9 +60,10 @@ main {
5660
top: 0;
5761
height: 100vh;
5862
width: 100vw;
63+
user-select: none;
5964

6065
&.active {
61-
pointer-events: auto;
66+
pointer-events: all;
6267
}
6368
}
6469
}
@@ -71,6 +76,7 @@ aside:first-of-type {
7176
max-width: 100px;
7277
z-index: 10;
7378
opacity: 0.5;
79+
pointer-events: all;
7480

7581
transform: translate3d(-50%, 0, 0);
7682
transition: transform 150ms ease, opacity 150ms ease;
@@ -137,6 +143,7 @@ aside:last-of-type {
137143
z-index: 10;
138144
opacity: 0.5;
139145
user-select: none;
146+
pointer-events: all;
140147

141148
transform: translate3d(80%, 0, 0);
142149
transition: transform 250ms ease, opacity 150ms ease;

0 commit comments

Comments
 (0)