Valentino Ru
Valentino Ru

Reputation: 5052

fabric.js custom size of rendered image via toDataUrl()

I am implementing a whiteboard in an existing application, which is optimized for huge touch-screens (NEC display). The canvas itself is located in a widget, which is resizable, meaning the visible part of the canvas can vary from something of 400x500 px up to fullscreen size. However, only the viewport gets adjusted, the canvas is always fullscreen size in the beackround (and aligned to the top left corner of the widget), so the objects do not get distorted in size and ratio when changing de size of the widget. This means, when I render an image via canvas.toDataUrl('png'), the image is always the same size.

Now we want to add a feature that the canvas itself gets scrollable, for easiness lets say its dimension is 3x3 fullscreens, meaning if you need more place, you can scroll one screen-size to the left or right resp. top or bottom. This is not difficult, but now we come to my question:

Is it possible to adjust the render-function, so that the image is not rendered from the whole canvas size, but rather the size of the "bounding box" of all elements resp. only selected elements? Here is an image to explain:

enter image description here

In this picture, the black square represents the acutal size of the canvas (and therefor the image which gets rendered), but I am searching a way to only render the red square (lets say all elements plus a padding of x pixels).

A really hacky way would be the following, but I am not sure if there is an easier way:

  1. create an invisible "imageRenderCanvas"
  2. on render, calculate the top and left most corner and the right and bottom most corner, calculate the size of this "bounding box" and add 2 x padding to each side and set this size to the imageRenderCanvas
  3. put (selected) objects at position 0+padding / 0+padding
  4. render the canvas as image

Is there any other way of doing this?

Upvotes: 0

Views: 643

Answers (1)

Valentino Ru
Valentino Ru

Reputation: 5052

For anyone who's interested, I've come up with the following solution. It's basically exactly what I sketched above, but would still be curious if there's an easier way.

renderSelection() {
    const selectedObject = fabric.util.object.clone(this.canvas.getActiveObject());
    const defaultPadding = 100;
    this.dynamicCanvas = new fabric.Canvas('#dynamicCanvas', {
        preserveObjectStacking: true
    });

    if ( selectedObject.type !== 'activeSelection') {
        this.renderSingleSelectionDynamicCanvas(selectedObject, defaultPadding);
    } else {
        this.renderMultiSelectionCanvas(selectedObject, defaultPadding);
    }

    const renderedImage = this.dynamicCanvas.toDataURL('image/png');
}

renderSingleSelectionDynamicCanvas(selectedObject: any, defaultPadding: number) {
    const totalWidth = selectedObject.width * selectedObject.scaleX;
    const totalHeight = selectedObject.height * selectedObject.scaleY;
    this.dynamicCanvas.setWidth(totalWidth + 2 * defaultPadding);
    this.dynamicCanvas.setHeight(totalHeight + 2 * defaultPadding);
    selectedObject.set({
        left: defaultPadding,
        top: defaultPadding
    });
    this.dynamicCanvas.add(selectedObject);
    this.dynamicCanvas.renderAll();
}

renderMultiSelectionCanvas(multiSelectionObject: any, defaultPadding: number) {
    const clonedObjects: Array<any> = [];
    multiSelectionObject.forEachObject( obj => {
        clonedObjects.push(fabric.util.object.clone(obj));
    });

    const group = new fabric.Group(clonedObjects, {
        left: defaultPadding,
        top: defaultPadding,
        width: multiSelectionObject.width,
        height: multiSelectionObject.height,
        originX: multiSelectionObject.originX,
        originY: multiSelectionObject.originY,
        scaleX: multiSelectionObject.scaleX,
        scaleY: multiSelectionObject.scaleY
    });
    const totalWidth = multiSelectionObject.width + 2 * defaultPadding;
    const totalHeight = multiSelectionObject.height + 2 * defaultPadding;
    this.dynamicCanvas.setWidth(totalWidth);
    this.dynamicCanvas.setHeight(totalHeight);
    this.dynamicCanvas.add(group);
    this.dynamicCanvas.renderAll();
}

Upvotes: 1

Related Questions