Skip to content

Commit cb09602

Browse files
committed
Refactor MultiCanvas
Signed-off-by: martinRenou <[email protected]>
1 parent eaa052a commit cb09602

File tree

1 file changed

+71
-54
lines changed

1 file changed

+71
-54
lines changed

src/widget.ts

Lines changed: 71 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Distributed under the terms of the Modified BSD License.
33

44
import {
5-
DOMWidgetModel, DOMWidgetView, ISerializers, Dict, ViewList, unpack_models
5+
DOMWidgetModel, DOMWidgetView, ISerializers, Dict, unpack_models
66
} from '@jupyter-widgets/base';
77

88
import {
@@ -431,6 +431,8 @@ class MultiCanvasModel extends DOMWidgetModel {
431431
_canvases: [],
432432
sync_image_data: false,
433433
image_data: null,
434+
width: 700,
435+
height: 500,
434436
};
435437
}
436438

@@ -445,45 +447,74 @@ class MultiCanvasModel extends DOMWidgetModel {
445447
initialize(attributes: any, options: any) {
446448
super.initialize(attributes, options);
447449

448-
this.on('change:_canvases', this.updateListeners.bind(this));
450+
this.canvas = document.createElement('canvas');
451+
this.ctx = getContext(this.canvas);
452+
453+
this.resizeCanvas();
454+
455+
this.on_some_change(['width', 'height'], this.resizeCanvas, this);
456+
this.on('change:_canvases', this.updateCanvasModels.bind(this));
449457
this.on('change:sync_image_data', this.syncImageData.bind(this));
450-
this.updateListeners();
458+
459+
this.updateCanvasModels();
451460
}
452461

453-
private updateListeners() {
462+
get canvasModels(): CanvasModel[] {
463+
return this.get('_canvases');
464+
}
465+
466+
private updateCanvasModels() {
454467
// TODO: Remove old listeners
455-
for (const canvasModel of this.get('_canvases')) {
456-
canvasModel.on('new-frame', this.syncImageData, this);
468+
for (const canvasModel of this.canvasModels) {
469+
canvasModel.on('new-frame', this.updateCanvas, this);
457470
}
471+
472+
this.updateCanvas();
458473
}
459474

460-
private async syncImageData() {
461-
if (!this.get('sync_image_data')) {
462-
return;
475+
private updateCanvas() {
476+
this.ctx.clearRect(0, 0, this.get('width'), this.get('height'));
477+
478+
for (const canvasModel of this.canvasModels) {
479+
this.ctx.drawImage(canvasModel.canvas, 0, 0);
463480
}
464481

465-
// Draw on a temporary off-screen canvas.
466-
const offscreenCanvas = document.createElement('canvas');
467-
offscreenCanvas.width = this.get('width');
468-
offscreenCanvas.height = this.get('height');
469-
const ctx = getContext(offscreenCanvas);
482+
this.forEachView((view: MultiCanvasView) => {
483+
view.updateCanvas();
484+
});
470485

471-
for (const canvasModel of this.get('_canvases')) {
472-
ctx.drawImage(canvasModel.canvas, 0, 0);
486+
this.syncImageData();
487+
}
473488

474-
// Also update the sub-canvas image-data
475-
const bytes = await toBytes(canvasModel.canvas);
489+
private resizeCanvas() {
490+
this.canvas.setAttribute('width', this.get('width'));
491+
this.canvas.setAttribute('height', this.get('height'));
492+
}
476493

477-
canvasModel.set('image_data', bytes);
478-
canvasModel.save_changes();
494+
private async syncImageData() {
495+
if (!this.get('sync_image_data')) {
496+
return;
479497
}
480498

481-
const bytes = await toBytes(offscreenCanvas);
499+
const bytes = await toBytes(this.canvas);
482500

483501
this.set('image_data', bytes);
484502
this.save_changes();
485503
}
486504

505+
private forEachView(callback: (view: MultiCanvasView) => void) {
506+
for (const view_id in this.views) {
507+
this.views[view_id].then((view: MultiCanvasView) => {
508+
callback(view);
509+
});
510+
}
511+
}
512+
513+
canvas: HTMLCanvasElement;
514+
ctx: CanvasRenderingContext2D;
515+
516+
views: Dict<Promise<MultiCanvasView>>;
517+
487518
static model_name = 'MultiCanvasModel';
488519
static model_module = MODULE_NAME;
489520
static model_module_version = MODULE_VERSION;
@@ -496,48 +527,34 @@ class MultiCanvasModel extends DOMWidgetModel {
496527
export
497528
class MultiCanvasView extends DOMWidgetView {
498529
render() {
499-
this.container = document.createElement('div');
500-
this.container.style.position = 'relative';
501-
502-
this.el.appendChild(this.container);
530+
this.ctx = getContext(this.el);
503531

504-
this.canvas_views = new ViewList<CanvasView>(this.createCanvasView, this.removeCanvasView, this);
505-
this.updateCanvasViews();
532+
this.resizeCanvas();
533+
this.model.on_some_change(['width', 'height'], this.resizeCanvas, this);
506534

507-
this.model.on('change:_canvases', this.updateCanvasViews.bind(this));
535+
this.updateCanvas();
508536
}
509537

510-
private updateCanvasViews() {
511-
this.canvas_views.update(this.model.get('_canvases'));
538+
clear() {
539+
this.ctx.clearRect(0, 0, this.el.width, this.el.height);
512540
}
513541

514-
private createCanvasView(canvasModel: CanvasModel, index: number) {
515-
// The following ts-ignore is needed due to ipywidgets's implementation
516-
// @ts-ignore
517-
return this.create_child_view(canvasModel).then((canvasView: CanvasView) => {
518-
const canvasContainer = document.createElement('div');
519-
520-
canvasContainer.style.zIndex = index.toString();
521-
522-
if (index == 0) {
523-
// This will enforce the container to respect the children size.
524-
canvasContainer.style.position = 'relative';
525-
canvasContainer.style.float = 'left';
526-
} else {
527-
canvasContainer.style.position = 'absolute';
528-
}
529-
530-
canvasContainer.appendChild(canvasView.el);
531-
this.container.appendChild(canvasContainer);
542+
updateCanvas() {
543+
this.clear();
544+
this.ctx.drawImage(this.model.canvas, 0, 0);
545+
}
532546

533-
return canvasView;
534-
});
547+
private resizeCanvas() {
548+
this.el.setAttribute('width', this.model.get('width'));
549+
this.el.setAttribute('height', this.model.get('height'));
535550
}
536551

537-
private removeCanvasView(canvasView: CanvasView) {
538-
this.container.removeChild(canvasView.el);
552+
get tagName(): string {
553+
return 'canvas';
539554
}
540555

541-
private container: HTMLDivElement;
542-
private canvas_views: ViewList<CanvasView>;
556+
el: HTMLCanvasElement;
557+
ctx: CanvasRenderingContext2D;
558+
559+
model: MultiCanvasModel;
543560
}

0 commit comments

Comments
 (0)