Object.getWidth() and Object.getHeight have been renamed to getScaledWidth and getScaledHeight.
The reason behind it is that they were confusion on what they were supposed to give back.
If you use the accessors optional mixin, you get them back but they will just be getters for .width and .height
Fabric.PathGroup has been removed. The class existed as container able to render the imported SVGs.
Since now fabric.Group can handle it, the class has been removed from the codebase.
To convert a PathGroup to a Group from an old cobdebase we have to add a function to fabric so that the process can go smooth.
fabric.PathGroup = { }; fabric.PathGroup.fromObject = function(object, callback) { var originalPaths = object.paths; delete object.paths; if (typeof originalPaths === 'string') { fabric.loadSVGFromURL(originalPaths, function (elements) { var pathUrl = originalPaths; var group = fabric.util.groupSVGElements(elements, object, pathUrl); group.type = 'group'; object.paths = originalPaths; callback(group); }); } else { fabric.util.enlivenObjects(originalPaths, function(enlivenedObjects) { enlivenedObjects.forEach(function(obj) { obj._removeTransformMatrix(); }) var group = new fabric.Group(enlivenedObjects, object); group.type = 'group'; object.paths = originalPaths; callback(group); }); } };This should do the trick.
Fabric.Image got some changes.
Lets' s start with the easy ones.
alignX, alignY, meetOrSlice
are gone.
Old objects carrying those 3 properties will evntually restore it at no use.
This feature was introduced by myself to support:
preserveAspectRatio
attribute from SVGs. It turned out that outside SVGs no one had ever a clue of how using it and other than the reference
example SVG no other SVG ever used it. A complete useless thing. I would be happy to be proven wrong anyway
.
Instead of it we introduced cropX
, cropY
attributes. Those 2 new attributes togheter with the classic
width and height can still emulate the full functionality of SVG's preserveAspectRatio attribute and let you do more.
So the image svg parser got bigger to translate preserveAspectRatio
into a combination of scaleX, scaleY, width, height, cropX, cropY
,
but you can do more. More what?
You can actually crop pictures!. There is no interface for it and maybe there will be not, but you can do that with the normal events.
So a fundamental behaviour has changed: before 2.0 release, altering a fabric.Image width and height property would make it bigger or smaller, exactly as scaleX
and scaleY would do. This was a side effect of drawImage htmlCanvas api. Now for images drawImage is used in the 9 parameter form:
ctx.drawImage(element, cropX, cropY, width, height, dx, dy, dw, dh)
.
So cropX and cropY represent how much are you cropping from the top left corner for the image, in pixels, no scales involved.
Width and height properties are influencing how much of the image are you using. A crop rectangle.
Due to changes in how width and height are handled, the old JSON will not load properly if the image dimensions have been modified.
What follow is a dropin replacement for fabric.Image.fromObject
to fix the difference:
/** * Creates an instance of fabric.Image from its object representation * @static * @param {Object} object Object to create an instance from * @param {Function} callback Callback to invoke when an image instance is created */ fabric.Image.fromObject = function(object, callback) { fabric.util.loadImage(object.src, function(img, error) { if (error) { callback && callback(null, error); return; } fabric.Image.prototype._initFilters.call(object, object.filters, function(filters) { object.filters = filters || []; fabric.Image.prototype._initFilters.call(object, [object.resizeFilter], function(resizeFilters) { object.resizeFilter = resizeFilters[0]; if (typeof object.version === 'undefined') { var elWidth = img.naturalWidth || img.width; var elHeight = img.naturalHeight || img.height; var scaleX = (object.scaleX || 1) * object.width / elWidth; var scaleY = (object.scaleY || 1) * object.height / elHeight; object.width = elWidth; object.height = elHeight; object.scaleX = scaleX; object.scaleY = scaleY; } var image = new fabric.Image(img, object); callback(image); }); }); }, null, object.crossOrigin); };
Image.applyFilters got sync!
the applyFilter functions of fabric.Image on the version 1.x is:
1) create a new canvas to hosted the filtered image 2) copy the current image on that canvas 3) for each filter do the following: a) getImageData from the canvas b) processImageData c) write everything on the canvas d) rinse, repeat. 4) a the end convert the canvas in dataUrl 5) create a new Image element and load the dataUrlSo the function, after blocking the main thread for many many milliseconds, sometimes thousands, start an async process to load the image back on an imageElement, with pain of the callback managment, and no benefit of it, since it was heavily blocking.
1) create a new canvas to host the filtered image if you do not have one. 2) copy the current image on that canvas, getImageData from the canvas. 3) for each filter do the following: a) processImageData 4) At the end putImageData on the canvas. 5) Keep that canvas as drawing element since is ok 6) If you filter again, the canvas is cleaned and reused.If you ever built a slider to preview brightness in real time with the old code, every sliding, every mousemove, you were creating a new canvas element, stop using it at the next tick, memory went up for all canvases created, at some point the GC would kick in and stop you for some time. The DataURL took time, even considering that everything was happening in your memory, to a dataurl operation from a grid of pixel is involved a PNG compression, a reading and a PNG decompression from the image element, a painting over the Bitmap. Is not for free. Even without WEBGL the new canvas filtering is faster. So important, all the examples you find around ( i corrected all that i could find ):
img.applyFilters(canvas.renderAll.bind(canvas));are out dated and will throw error. You have just to do:
img.applyFilters(); canvas.requestRenderAll(); // or the old canvas.renderAll() if you prefer or need it // Optionally you can pass a filters array to applyFilter // if you want to apply something that is not the image filter chain. img.applyFilters([ new fabric.Image.filters.Contrast({ contrast: 15 })]);Another breaking change is that the
.resizeFilters
is no more an array of resize filter.
Is a single resizeFilter that you can use when the object is scaled on the canvas.
image.resizeFilter = new fabric.Image.filters.ResizeFilter({type: 'hermite'});Resize filters are not ready in WEBGL yet.
Canvas.renderall
is a sync operation, blocking the main thread for a decent amount of milliseconds.
When dragging around object for each mouse mouse move fired you were getting a renderAll, also when adding may objects with a for loop, you were getting a renderAll for each
object if you did not stop the rendering with renderOnAddRemove = false
.
Additionally some methods were rendering to display the canvas changes, others were not.
In this release we tried to do things better.
We suggest to replace Canvas.renderAll
with Canvas.requestRenderAll
, and this has been already done for all the actions that involve
mouse operations.
requestRenderAll ad renderAll interact in this way:
/** * Append a renderAll request to next animation frame. * a boolean flag will avoid appending more. * @return {fabric.Canvas} instance * @chainable */ requestRenderAll: function () { if (!this.isRendering) { this.isRendering = fabric.util.requestAnimFrame(this.renderAndResetBound); } return this; }, /** * Function created to be instance bound at initialization * used in requestAnimationFrame rendering * @return {fabric.Canvas} instance * @chainable */ renderAndReset: function() { this.isRendering = 0; this.renderAll(); }, /** * Renders the canvas * @return {fabric.Canvas} instance * @chainable */ renderAll: function () { var canvasToDrawOn = this.contextContainer; if (this.isRendering) { fabric.window.cancelAnimationFrame(this.isRendering); } this.renderCanvas(canvasToDrawOn, this._objects); return this; },
Canvas.requestRenderAll
is going to append a render to the next frame ( a sort of dynamic setTimeout ) and save in this.rendering
the handler to that request. When the times come, fabric zeros the handler and renders with renderAll
.if(!this.isRendering)
).renderOnAddRemove = false
:
Collection.add ( Group, Canvas, ActiveSelection classes ) Collection.insertAt ( Group, Canvas, ActiveSelection classes ) Collection.remove ( Group, Canvas, ActiveSelection classes ) StaticCanvas.setViewportTransform StaticCanvas.clear StaticCanvas.sendToBack StaticCanvas.sendBackwards StaticCanvas.bringToFront StaticCanvas.bringForward StaticCanvas.moveTo StaticCanvas._centerObject ( all the center related function, viewport or not)Those methods fire a render and they just do it:
IText.enterEditing StaticCanvas.setDimensionsThe property
renderOnAddRemove
looks misnamed at least. This property already existed, and in version 1 it was covering
just add , remove and insertAt. As a fabricjs developer and without requestAnimationFrame and without caching, using some methods
that would force a render was very frustrating for me, and i ended up always writing crazy functions to do what the methods to but without
rendering. So i builded up the idea that this was something that a developer wanted to control.