I can't get the Font size on my canvas to work [duplicate] - node.js

Is there a way to only change the font size of a canvas context without having to know/write the font family.
var ctx = document.getElementById("canvas").getContext("2d");
ctx.font = '20px Arial'; //Need to speficy both size and family...
Note:
ctx.fontSize = '12px'; //doesn't exist so won't work...
ctx.style.fontSize = '20 px' //doesn't exist so won't work...
//we are changing the ctx, not the canvas itself
Other note: I could do something like: detect where 'px' is, remove what's before 'px' and replace it by my font size. But I'd like something easier than that if possible.

Here is an easier and cleaner way of changing the font size that will work regardless if you are using font-variant or font-weight or not.
First some RegEx to match and return the current font size
const match = /(?<value>\d+\.?\d*)/;
Set an absolute size, assuming your new font size is 12px
ctx.font = ctx.font.replace(match, 12);
Set a relative size, increase by 5:
const newSize = parseFloat(ctx.font.match(match).groups.value + 5);
ctx.font = ctx.font.replace(match, newSize);
Working example:
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
// Match on digit at least once, optional decimal, and optional digits
// Named group `value`
const match = /(?<value>\d+\.?\d*)/;
const setFontSize = (size) => ctx.font = ctx.font.replace(match, size);
const adjustFontSize = (amount) => {
const newSize = parseFloat(ctx.font.match(match).groups.value + amount);
return ctx.font = ctx.font.replace(match, newSize);
}
// Default font is 12px sans-serif
console.log( setFontSize(18) ); // 18px sans-serif
console.log( adjustFontSize(2) ); // 20px sans-serif
console.log( adjustFontSize(-5) ); // 15px sans-serif
<canvas id="canvas"></canvas>

Update: (from comments) There is no way around specifying font. The Canvas' font is modeled after the short-hand version of font in CSS.
However, there is always a font set on the canvas (or a font type) so what you can do is to first extract the current font by using it like this:
var cFont = ctx.font;
Then replace size arguments and set it back (note that there might be a style parameter there as well).
A simple split for the sake of example:
var fontArgs = ctx.font.split(' ');
var newSize = '12px';
ctx.font = newSize + ' ' + fontArgs[fontArgs.length - 1]; /// using the last part
You will need support for style if needed (IIRC it comes first if used).
Notice that font-size is fourth parameter, so this will not work if you will have/not have font-variant(bold,italic,oblique), font-variant(normal, small-caps) and font-weight(bold etc).

A cleaner way to not worry about scraping every other parameter:
canvas.style.font = canvas.getContext("2d").font;
canvas.style.fontSize = newVal + "px";
canvas.getContext("2d").font = canvas.style.font;
Explanation:
As mentioned in the first answer, the canvas context will always have a font. So canvas.getContext("2d").font might return something like "10px sans-serif". To not scrape this string we can use the Canvas DOM element (or any other element) to parse the full font spec and populate the other font fields. Which means that after setting:
canvas.style.font = "10px sans-serif";
canvas.style.fontSize = "26px";
the canvas.style.font will become "26px sans-serif". We can then pass this font spec back to the canvas context.

To correctly answer your question, there is no way to change the font size of a canvas context without having to know/write the font family.

try this (using jquery):
var span = $('<span/>').css('font', context.font).css('visibility', 'hidden');
$('body').append(span);
span[0].style.fontWeight = 'bold';
span[0].style.fontSize = '12px';
//add more style here.
var font = span[0].style.font;
span.remove();
return font;

Related

Text in canvas being wiped out with a video canvas underneath

Note: Minimal working example here. Click on change text button, and you can see the text will appear for one frame and disappear.
I wanted to add an overlay to my video, so I stacked two canvases and filled text to the transparent top canvas.
However, the text does not stick. It disappears.
To test if I was filling the text correctly, I tried using a black canvas (no video) underneath.
I just needed to fillText once and the text stayed.
However, with the video canvas underneath, the text won't stick unless I draw text in requestAnimationFrame, which I believe keeps drawing the same text in every frame, which is unnecessary.
The text canvas is supposed to be separate from the video canvas. Why is this getting wiped out without requestAnimationFrame?
How can I fix it?
Minimal working example here, but the code is also shown below.
import "./styles.css";
import { useEffect, useRef, useState } from "react";
export default function App() {
const inputStreamRef = useRef();
const videoRef = useRef();
const canvasRef = useRef();
const overlayCanvasRef = useRef();
const [text, setText] = useState("Hello");
function drawTextWithBackground() {
const ctx = overlayCanvasRef.current.getContext("2d");
ctx.clearRect(
0,
0,
overlayCanvasRef.current.width,
overlayCanvasRef.current.height
);
/// lets save current state as we make a lot of changes
ctx.save();
/// set font
const font = "50px monospace";
ctx.font = font;
/// draw text from top - makes life easier at the moment
ctx.textBaseline = "top";
/// color for background
ctx.fillStyle = "#FFFFFF";
/// get width of text
const textWidth = ctx.measureText(text).width;
// Just note that this way of "measuring" height is not accurate.
// You can measure height of a font by using a temporary div / span element and
// get the calculated style from that when font and text is set for it.
const textHeight = parseInt(font, 10);
const textXPosition = canvasRef.current.width / 2 - textWidth / 2;
const textYPosition = canvasRef.current.height - textHeight * 3;
ctx.save();
ctx.globalAlpha = 0.4;
/// draw background rect assuming height of font
ctx.fillRect(textXPosition, textYPosition, textWidth, textHeight);
ctx.restore(); // this applies globalAlpha to just this?
/// text color
ctx.fillStyle = "#000";
ctx.fillText(text, textXPosition, textYPosition);
/// restore original state
ctx.restore();
}
function updateCanvas() {
const ctx = canvasRef.current.getContext("2d");
ctx.drawImage(
videoRef.current,
0,
0,
videoRef.current.videoWidth,
videoRef.current.videoHeight
);
// QUESTION: Now the text won't stick unless I uncomment this below,
// which runs drawTextWithBackground in requestAnimationFrame.
// Previosuly, with just a black canvas underneath, I just needed to fillText once
// and the text stayed.
// The text canvas is supposed to be separate. Why is this getting wiped out without requestAnimationFrame?
// drawTextWithBackground();
requestAnimationFrame(updateCanvas);
}
const CAMERA_CONSTRAINTS = {
audio: true,
video: {
// the best (4k) resolution from camera
width: 4096,
height: 2160
}
};
const enableCamera = async () => {
inputStreamRef.current = await navigator.mediaDevices.getUserMedia(
CAMERA_CONSTRAINTS
);
videoRef.current.srcObject = inputStreamRef.current;
await videoRef.current.play();
// We need to set the canvas height/width to match the video element.
canvasRef.current.height = videoRef.current.videoHeight;
canvasRef.current.width = videoRef.current.videoWidth;
overlayCanvasRef.current.height = videoRef.current.videoHeight;
overlayCanvasRef.current.width = videoRef.current.videoWidth;
requestAnimationFrame(updateCanvas);
};
useEffect(() => {
enableCamera();
});
return (
<div className="App">
<video
ref={videoRef}
style={{ visibility: "hidden", position: "absolute" }}
/>
<div style={{ position: "relative", width: "90vw" }}>
<canvas
style={{ width: "100%", top: 0, left: 0, position: "static" }}
ref={canvasRef}
width="500"
height="400"
/>
<canvas
style={{
width: "100%",
top: 0,
left: 0,
position: "absolute"
}}
ref={overlayCanvasRef}
width="500"
height="400"
/>
</div>
<button
onClick={() => {
setText(text + "c");
drawTextWithBackground();
}}
>
change text
</button>
</div>
);
}
Very simple fix
The canvas is being cleared due to how React updates state.
The fix
The simplest fix is to use just the one canvas and draw the text onto the canvas used to display the video.
From the working example change the two functions updateCanvas and drawTextWithBackground as follows.
Note I have removed all the state saving in drawTextWithBackground as the canvas state is lost due to react anyway so why use the very expensive state stack functions.
function drawTextWithBackground(ctx) {
const can = ctx.canvas;
const textH = 50;
ctx.font = textH + "px monospace";
ctx.textBaseline = "top";
ctx.textAlign = "center";
ctx.fillStyle = "#FFFFFF66"; // alpha 0.4 as part of fillstyle
ctx.fillStyle = "#000";
const [W, X, Y] = [ctx.measureText(text).width, can.width, can.height - textH * 3];
ctx.fillRect(X - W / 2, Y, W, textH);
ctx.fillText(text, X, Y);
}
function updateCanvas() {
const vid = videoRef.current;
const ctx = canvasRef.current.getContext("2d");
ctx.drawImage(vid, 0, 0, vid.videoWidth, vid.videoHeight);
drawTextWithBackground(ctx);
requestAnimationFrame(updateCanvas);
}
when you update the canvas from the video, all of the pixels in the canvas are updated with pixels from the video. Think of it like a wall:
you painted "here's my text" on the wall.
Then you painted the whole wall blue. (your text is gone, and the wall is just blue).
If you paint the whole wall blue, and then repaint "here's my text", you'd still see your text on the blue wall.
When you redraw the canvas, you have to re-draw everything you want to appear on the canvas. If the video "covers" your text, you've got to redraw the text.
working example at https://github.com/dougsillars/chyronavideo which draws a chyron (the bar at the bottom of a news story) on each frame of the canvas.

How to install Google Font on nodejs-canvas?

I'm trying to get Google Fonts dynamically with nodejs-canvas and I'm not sure what is the best way to do that.
I understand it needs to be with: "registerFont" method.
This is the code I have now:
const { registerFont, createCanvas, loadImage } = require('canvas')
//registerFont('comicsans.ttf', { family: 'Comic Sans' })
const canvas = createCanvas(200, 200)
const ctx = canvas.getContext('2d')
let name = req.query.name;
// Write "Awesome!"
ctx.font = '30px Impact'
ctx.rotate(0.1)
ctx.fillText(name, 50, 100)
// Draw line under text
var text = ctx.measureText(name)
ctx.strokeStyle = 'rgba(0,0,0,0.5)'
ctx.beginPath()
ctx.lineTo(50, 102)
ctx.lineTo(50 + text.width, 102)
ctx.stroke()
//console.log(canvas.toDataURL());
res.write('<img style="border:1px solid #000;" src="' + canvas.toDataURL() + '" />');
res.end();
Using PT Mono as an example this is how you do it.
First go to the font directly in this case PT Mono.
Next click the "download family" link in the top right. That will give you a zip file which contains the TTF font.
Now copy the ttf file into your node application directory and use registerFont in your file like this.
registerFont('PtMono-Regular.ttf', { family: 'PT Mono' });
As per the documentation make sure you register your fonts before creating the canvas.
Then to use your font on your canvas you'd just use the following to set the font for the context.
ctx.font = "16px 'PT Mono'";
Full Example using our google font just modifying the original example in the node-canvs repo.
const { registerFont, createCanvas } = require('canvas');
registerFont('PtMono-Regular.ttf', { family: 'PT Mono' });
const canvas = createCanvas(500, 500);
const ctx = canvas.getContext('2d');
ctx.font = '16px 'PT Mono'"'
ctx.fillText('This is the font.', 250, 10);

KonvaJS, positioning editable text inputs

I need to position text inputs at various places on a KonvaJS layer. I found the following code at https://konvajs.github.io/docs/sandbox/Editable_Text.html and I'm trying to understand the textPosition, stageBox, and areaPosition vars in this code. I want my stage centered in the browser window, but when I do that, the textarea (activated on dblclick) pops up way off to the left. I can't get a console readout of the x/y coordinates, so I can't visualize how the positioning works &, thus, how to change it. Can anyone explain, or point me in the right direction?
var text_overlay = new Konva.Layer();
stage.add(text_overlay);
var textNode = new Konva.Text({
text: 'Some text here',
x: 20,
y: 50,
fontSize: 20
});
text_overlay.add(textNode);
text_overlay.draw();
textNode.on('dblclick', () => {
// create textarea over canvas with absolute position
// first we need to find its position
var textPosition = textNode.getAbsolutePosition();
var stageBox = stage.getContainer().getBoundingClientRect();
var areaPosition = {
x: textPosition.x + stageBox.left,
y: textPosition.y + stageBox.top
};
// create textarea and style it
var textarea = document.createElement('textarea');
document.body.appendChild(textarea);
textarea.value = textNode.text();
textarea.style.position = 'absolute';
textarea.style.top = areaPosition.y + 'px';
textarea.style.left = areaPosition.x + 'px';
textarea.style.width = textNode.width();
textarea.focus();
textarea.addEventListener('keydown', function (e) {
// hide on enter
if (e.keyCode === 13) {
textNode.text(textarea.value);
text_overlay.draw();
document.body.removeChild(textarea);
}
});
})
// add the layer to the stage
stage.add(text_overlay);
UPDATE: I solved part of the problem--the textarea showing up way out of position. You need to use 2 divs in the HTML file instead of one, like so:
<div id="containerWrapper" align="center"><div id="container"></div></div>
Thanks to Frens' answer on Draw border edges of the Konvajs container Stage in html for that one!

Detect Graphical Regions in an Image on a Web Page

I've been beating around the bush a lot, so I'll explain my problem here and hope with the whole picture, somebody has some ideas. With the following image:
I need to detect a mouseover on the blobs over her eyes and mouth, and solve this problem in a general form. The model and blobs are on two different layers, so I can produce one image with only the blobs, and one with only the model, and somehow synchronise a virtual cursor over the blobs while it actually hovers over the model.
I can also make the blobs polygons, for hit testing, but I think a colour hit test would be much easier. If I hit blue, I am on her mouth and I show lipstick images; if I hit pink, I'm over her eyes, and display eye makeup images.
What are the suggestions and conversation of the learned ones here?
The simpler way to do it would be to load the layer image in a canvas, then get all its pixel data. When the mouse is hovering the model image, find out what color is currently selected and if it is different from a previous one, trigger an event to indicate that the selection has changed.
Here is an example, feel free to toy with it; but be aware that it doesn't handle all cases:
what if the layer and the model image are not the same size
what if the layer and the model image are not the same width/height ratio
what if you want to use some alpha channel (the example doesn't take it into account)
$(function() {
/* we load all the image data first */
var imageData = null;
var layerImage = new Image();
layerImage.onload = function() {
var canvas = document.createElement("canvas");
canvas.width = this.width;
canvas.height = this.height;
context = canvas.getContext('2d');
context.drawImage(this, 0, 0, canvas.width, canvas.height);
imageData = context.getImageData(0, 0, canvas.width, canvas.height).data;
};
/* it's easier to set the image data for example as base64 data */
layerImage.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAOwgAADsIBFShKgAAAABp0RVh0U29mdHdhcmUAUGFpbnQuTkVUIHYzLjUuMTFH80I3AAAA5klEQVR4Xu3WMQ7CMAwF0IxcgCtw/xsGdWApItWHKorLQ8qESe2HbWjNiwABAgQIECBAgAABAgQIECBAgMAUgd4e/ehMSeTMh4wK2j/nqPjt/TNzm3IXgEFb64CdgBG44hKcsmg8pKDAvbf+7SlY7nvK3xb/+lx5BAA/jMCGpwOqC/z9CFT/ApfP/9Z6T87yBaUJJsVvsen9y8cDMAJ2gCWY7IHll1qaYFL84a9AelnF+CFwxYLSnAGMBFLNivE6QAcMBCq2dJqzETACRuCzQDpPFePTv9riCRAgQIAAAQIECBC4nsATagY67TVyuhAAAAAASUVORK5CYII=";
var pColor = null;
/* on mouse over the model image */
$("#model").mousemove(function(event) {
/* we correct the offset */
var offset = $(this).offset();
var relX = event.pageX - offset.left;
var relY = event.pageY - Math.round(offset.top);
/* and get the pixel values at this place (note we are not keeping the alpha channel; it's your decision whether or not it is valuable */
var pixelIndex = relY * layerImage.width + relX;
var dataIndex = pixelIndex * 4;
var color = [imageData[dataIndex], imageData[dataIndex + 1], imageData[dataIndex + 2]];
if (pColor == null) {
/* we trigger when first entering the image */
$(this).trigger("newColor", {
message: "Initial layer color",
data: color
});
} else if (pColor[0] != color[0] || pColor[1] != color[1] || pColor[2] != color[2]) {
/* we trigger if the new position is a new color in the layer image */
$(this).trigger("newColor", {
message: "Changed layer color",
data: color
});
}
pColor = color;
});
/* some small help to convert rgb to css colors */
function rgb2hex(red, green, blue) {
var rgb = blue | (green << 8) | (red << 16);
return '#' + (0x1000000 + rgb).toString(16).slice(1)
}
/* there you have the new layer color event management; for the example sake we change the color of some text */
$("#model").on("newColor", function(event, eventData) {
$("#selector").css("color", rgb2hex(eventData.data[0], eventData.data[1], eventData.data[2]));
});
});
img {
border: 1px solid silver
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<body>
<h4>Model image</h4>
<img id="model" src="data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAIAAAAlC+aJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjExR/NCNwAAAtdJREFUaEPtmS2PwkAQhhGQEASEBIECFAJBgoCAxpDgsWBxSP4lPwGJRCLv5m4uk6Xt7s5He9dLFst2+z7vfG3bxsc//zX+uf6PBPDXEUwRSBEwOpBSyGig+fIUAbOFxg3KjMD1em3EfqvV6vl8GkW7l5cJsF6vY/p//i8RQwbwer22221U5W63K/T4fr93u1263LdMFB8ZAEc96BsOhwER5WIIAMB+NO92u4lMKlw8m80oFJaMEgCg/ZADdvW0A2GMRiPdtgKAEu13tUJG4c667sQFoBap8yl8FdiPDIqyZgFQ9oerU83mlrV0ExaAJfshdKfTiSMLg8BZKR5k4ewPS4TpNplMOLKqAqD88YngSwxjVAVgyR+O8bSmKoCKumeerVoAkZe6xQnA45vCGH7rfGuIFbVRBUDgvO2bCefzWXEj4I8PDtyXOYzQ0cVi4WMonAmWSR8HIDWbzUZ33grXNKifTqcA3Ov1FNUfB3AdhdOv4h6+S0D6fr/HWLVaLd1jBgsAFNChV5RLAVpKelDfbrd16lk1QCLczLZjoPEW6SiMG4FAdUphXO/tCSkD8GEwi9uVDvZLny6gZg6HQ8YvDYBr23g8DnR9319S6XjH5XIJGw4Gg7fxZwwiFDfsyGfQSQeRvnci1ghE+XXz1d3W7bb5WVF3ABpzvpZVdwDM+8CYqyOAmzPRx6l6ARyPx3w/CNd9jQDgNEHq+RO6LgCgvt/vA8Dlcol2tjLnQPRmhW00n+W4rNPpPB6P6J5cAHofqnhlSffIAIDT8/k8n+jNZhO8l6qPHOYKPxlJ3+Wj1swpqJRzKPc06n6JIOf4GBmz1U778kpWxJmvQ9EjEOQergEX1KegcEnIAHAvPgaIRgDpMwO/jjUA/N1hJT3Hia7iL64c4KtRfP/4mkQrq9r3rVUngEBMUgQYCZtqIGRSSqGUQgwHUgoZTSrrQ3KhjN8oYiN/+PJPqpb83Htu7qcAAAAASUVORK5CYII="
/>
<p>You are pointing at some <strong><span id="selector">color</span></strong>
</p>
<hr/>
<h4>Layer image (reference only, not displayed in page)</h4>
<img id="layer" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAOwgAADsIBFShKgAAAABp0RVh0U29mdHdhcmUAUGFpbnQuTkVUIHYzLjUuMTFH80I3AAAA5klEQVR4Xu3WMQ7CMAwF0IxcgCtw/xsGdWApItWHKorLQ8qESe2HbWjNiwABAgQIECBAgAABAgQIECBAgMAUgd4e/ehMSeTMh4wK2j/nqPjt/TNzm3IXgEFb64CdgBG44hKcsmg8pKDAvbf+7SlY7nvK3xb/+lx5BAA/jMCGpwOqC/z9CFT/ApfP/9Z6T87yBaUJJsVvsen9y8cDMAJ2gCWY7IHll1qaYFL84a9AelnF+CFwxYLSnAGMBFLNivE6QAcMBCq2dJqzETACRuCzQDpPFePTv9riCRAgQIAAAQIECBC4nsATagY67TVyuhAAAAAASUVORK5CYII="
/>
</body>
If you can have both images (the one with the blob and the one without), I think you can do this using HTML5 canvas.
draw the image normally
draw the blob image beneath the master image so it is invisible
copy the blob to a Canvas
onMouseOver, retrieve pixel data (R,G,B and alpha) for the Canvas at the appropriate coordinates
profit
Twist: you might be able to do this with only one image and its alpha channel, if you don't need it for anything else - give the pixels a full opacity (A=255) everywhere except in blobs 1, 2 and 3, which will have opacity equal to 255-(1,2,3...). You can't have too many different blobs or the transparency will become noticeable. Haven't tried, but it should work. Given the likely compressibility of a "blob-only" image, a pair of images (one without transparency, one also without transparency and with only N+1 colours, PNG compressed) should yield better results.
More-or-less-pseudo code with two images, using jQuery (can be done without):
var image = document.getElementById('mainImage')
var blobs = document.getElementById('blobImage');
// Create a canvas
canvas = $('<canvas/>')[0];
canvas.width = image.width;
canvas.height = image.height;
// IMPORTANT: for this to work, this script and blobImage.src must be both
// in the same security domain, or you'll get "this operation is insecure"
canvas.getContext('2d').drawImage(blobs, 0, 0, image.width, image.height);
// Now wait for it.
$('#mainImage').mouseover(function(event) {
// TO DO: offset clientX, clientY by margin on mainImage
var ctx = canvas.getContext('2d');
// Get one pixel
var pix = ctx.getImageData(event.clientX, event.clientY, 1, 1);
// Retrieve the red component
var red = pix.data[0];
if (red > 128) {
// ... do something for red
}
});
You could use SVG graphics to layer over the image.
My example uses an ellipse but you could use a polygons just as easily.
You could use the colour like you stated in your question or add an extra property to the svg element. The example uses onclick but mouseover works as well.
example js:
function svg_clicked(objSVG)
{
alert(objSVG.style.fill);
alert(objSVG.getAttribute('data-category'));
}
example svg:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<ellipse cx="110" cy="80" rx="100" ry="50" style="fill:red;" onclick="svg_clicked(this);" data-category="lipstick" />
</svg>
Here's a fiddle (move the mouse over the O's in the picture)
It still works if you make the svg element transparent (using fill:transparent).
You can change the overlay to a colour or outline quickly for testing.
I highly recommend the time tested method.
The easiest way to both create blobs and to detect if the mouse is over them is to use svg graphics on top of the other image. SVG supports mouseover events and allows vector shapes which are going to give you far greater precision than using <map> or <area>.
I found this question that might also shed some light on where I am coming from: Hover only on non-transparent part of image. Read the second answer down because it will likely be prefered in your situation.
The svg elements on your image would be transparent (or whatever you would want), and you could easily detect the mouse over events.
The library from that question is called raphael. Hopefully this proves to be useful.

Is there a way to crop only the design from a the canvas and ignoring all the white/transparent space?

I have a canvas built using fabricJS with the dimension of 600x500. I have added an image to this canvas which is of size 200x300 and also a text element just below it.
$canvasObj.toDataURL();
exports the whole canvas area including the white spaces surrounding the design on the canvas.
Is there a way to get the cropped output of the design on the canvas alone instead of all the whitespace?
This can be done by cloning objects to a group, getting the group boundingRect, and then passing the boundingRect parameters to toDataUrl() function (see fiddle).
e.g.
// make a new group
var myGroup = new fabric.Group();
canvas.add(myGroup);
// ensure originX/Y 'center' is being used, as text uses left/top by default.
myGroup.set({ originX: 'center', originY: 'center' });
// put canvas things in new group
var i = canvas.getObjects().length;
while (i--) {
var objType = canvas.item(i).get('type');
if (objType==="image" || objType==="text" || objType==="itext" || objType==="rect") {
var clone = fabric.util.object.clone(canvas.item(i));
myGroup.addWithUpdate(clone).setCoords();
// remove original lone object
canvas.remove(canvas.item(i));
}
}
canvas.renderAll();
// get bounding rect for new group
var i = canvas.getObjects().length;
while (i--) {
var objType = canvas.item(i).get('type');
if (objType==="group") {
var br = canvas.item(i).getBoundingRect();
}
}
fabric.log('cropped png dataURL: ', canvas.toDataURL({
format: 'png',
left: br.left,
top: br.top,
width: br.width,
height: br.height
}));
p.s. I should probably mention that i've not worked with image types, so i just guessed that it's called 'image'..

Resources