Open Siyfion opened 10 years ago
I'll mark it as possible feature for now. We'll see if anyone else needs this.
+1 on this
+1 in order to make fabricjs a "responsive app" more easy.
+1 :+1:
+1 on laying the groundwork for an easily responsive fabricjs backed canvas
+1
+1
+1
+1
Alrighty, marking this as feature.
+1 on this
Is there any deadline of this feature? eagerly waiting for this feature.... Thank you so much for consideration...
+1
Can't you simply style the canvas to make it responsive?
#canvas {
position: absolute;
top: 0;
right: 0;
left: 0;
width: 100% !important;
height: 100% !important;
}
.canvas-container {
position: relative;
width: 100% !important;
height: 0 !important;
padding-bottom: 100%; // the canvas height as a percentage of its width
}
.upper-canvas {
width: 100% !important;
height: 100% !important;
}
Of course, this doesn't change the width of the canvas, it scales it. Thoughts?
The objects would need re-drawn & re-positioned as would the invisible interaction layer, plus we need to handle the proportions of the canvas and it's objects something like this (not tried) http://stackoverflow.com/questions/19937397/show-canvas-in-fullscreen-in-fabric-js/20008922#20008922
Just realized my solution wouldn't work on a StaticCanvas.
Take a look on #1505
+1
+1
+1
+1
+1
+1
i see we have lot of +1. I'm going in the process to make the upper canvas retina enabled. Once i see how the event position change with a css transform, and once i found a nice way to find the css transform for ie10+, then i think we can do this.
+1
+1
+1
looks like this is very +1d.
Please ping me again here if in a couple of days i do not draft a proposal for how it could work.
In our app we use a Knockout to get the fabric canvas onto the screen. I've extended our KO binding handler so that it can manage the fabric instance and support it being dynamically resized. Essentially when it sees that its area has changed a new fabric instance is created. The old instance has its JSON data saved out, along with information about the viewport transform. That's then fed into the new instance which is displayed then displayed on screen. That has worked pretty well but I'm sure there are some holes. Supporting some sort of CSS transform would be much more straightforward but browser quirks with how touch, mouse, pen, etc events get detected in the scaled DOM element may not be fun.
@IanYates can you provide more information ?
+1, How are things going on lastest version ?
+1, How are things going on lastest version ?
a solution in version 2. 4.2-b http://fabricjs.com/docs/fabric.js.html#line7130
it works in my code, set width and height 100% :
+1 - using setDimensions with cssOnly works but the controls will not scale well
The reason behind not supporting this feature is that we need to poll the dom to know how much the container is big and adapt the canvas accordingly. Once the canvas is adapted you may want to zoom the content or not to zoom it, but those behavior are not written in any kind of neutral logic and are heavily dependent on the scope of your project.
We could provide a method called adaptToContainer
in order to do so, but this would cover only the 100% case and not any percentage.
We can make a demo with the new resize event for the browsers where this is implemented and with the window.resize that is usually where those things happen.
What do you think?
The original request canvas.setWidth(100%)
can still work, the problem is that this 100% is one shot and won't persist with following resizes.
I'm using a combination of setDimensions and setZoom now, this will scale the controls and everything just as needed. As you said, this is very userland specific, I will provide a jsfiddle to demonstrate the behaviour. Maybe the method adaptToContainer could do this when called with a specific width/height.
I implemented this like this:
window.onresize = (event) => {
fitResponsiveCanvas();
};
function fitResponsiveCanvas() {
// canvas dimensions
let canvasSize = {
width: 1200,
height: 700
};
// canvas container dimensions
let containerSize = {
width: document.getElementById('canvas-container').offsetWidth,
height: document.getElementById('canvas-container').offsetHeight
};
let scaleRatio = Math.min(containerSize.width / canvasSize.width, containerSize.height / canvasSize.height);
canvas.setWidth(containerSize.width);
canvas.setHeight(containerSize.height);
//set canvas zoom aspect
canvas.setZoom(scaleRatio)
}
<div id="canvas-container">
<canvas id="canvas"></canvas>
</div>
The only thing you have to do is add some css to your container:
#canvas-container {
height: 80vh;
width: 100%;
}
+1
+1 - This should be a possibility in 2021. Everything on the web is responsive now, including complex applications.
is already a possibility, just that you have to handle it. Put a resize event on your canvas container and change the size of it. Is a couple of lines of cose
I've not found this in the official documentation. If you're referring to the code above, looks good and I'd implement it in a heartbeat if it was official. Making a complex drawing library responsive in this way makes me wonder about why it's not in the library and the documentation to begin with. As such I assumed it would create unforseen problems and my application has to be stable above responsive. For example, I'm worried 'setZoom' as a means to get it to be responsive will mess up the .toJson since the zoom will be reverted to default on reload and load of json.
From your response I gather FabricJS sees this as the accepted solution to this problem?
Yes is an accepted solution for me. I use it too. FabricJS does not handle the canvas as an application, or at least tries to not do it where different frameworks or generic code solutions are available.
The canvas is in pixels and so is not really responsive per se.
There are a couple of improvements you can add to the solution above ( that i did not remember was here ).
window.onresize = (event) => {
fitResponsiveCanvas();
};
function fitResponsiveCanvas() {
// canvas dimensions
let canvasSize = {
width: 1200,
height: 700
};
// canvas container dimensions
let containerSize = {
width: document.getElementById('canvas-container').offsetWidth,
height: document.getElementById('canvas-container').offsetHeight
};
canvas.setDimensions(containerSize);
// how you want to handle your zoom is really application dependant.
let scaleRatio = Math.min(containerSize.width / canvasSize.width, containerSize.height / canvasSize.height);
canvas.setZoom(scaleRatio)
}
Thanks! I'll implement it this way.
This is still a pain in 2021. I have a drawing canvas inside the main canvas. setDimension and setzoom work well on the same device. The drawing rect. has proper dimensions in all the devices.
But the inner objects don't scale when open different resolutions.
It mostly depends how do you want to handle your zoom level on the canvas and apply it on bigger/smaller canvases.
The fact that you want the inner object to scale, it means that you are implementing a kind of fit to page
behaviour that does not exist in fabricJS since there is no a page.
If there is page is a construct of the application.
I am stuck in a problem, don't know if this is related to the current thread.
I have created a rectangle inside the canvas like this:
The grey part is the canvas there is a small rectangle inside. This is the code I have written:
// THIS METHOD WHEN THE COMPONENT IS INITIALIZED
// THAT IS VERY FIRST TIME
// htmlCanvas = Canvas from HTML file
initCanvas(htmlCanvas) {
this.mockupDrawableAreaCanvasSize = size;
this.mainCanvasDivRef = htmlCanvas;
fabric.textureSize = 4096;
fabric.charWidthsCache = {};
fabric.Object.prototype.borderScaleFactor = 2;
fabric.Object.prototype.objectCaching = false;
fabric.Object.prototype.noScaleCache = true;
fabric.Object.prototype.lockScalingFlip = true;
fabric.Object.prototype.hasRotatingPoint = true;
fabric.Object.prototype.transparentCorners = false;
fabric.Object.prototype.cornerColor = "rgb(255,255,255)";
fabric.Object.prototype.cornerSize = 8;
fabric.Object.prototype.cornerStrokeColor = "#48B774";
fabric.Object.prototype.borderColor = "#48B774";
fabric.Object.prototype.fill = "#FFFFFF";
fabric.Object.prototype.cornerStyle = "rect";
fabric.Object.prototype.borderOpacityWhenMoving = .5;
fabric.Object.prototype.snapAngle = 90;
fabric.Object.prototype.snapThreshold = 5;
fabric.Object.prototype.charWidthsCache = {};
this.canvas = new fabric.Canvas(htmlCanvas.nativeElement, this.defaultOption);
this.canvas.backgroundColor = '#e8e8e8';
this.canvas.uniScaleTransform = true;
this.canvas.requestRenderAll()
// -------
const {
width,
height
} = this.getDimensions();
this.drawingBoard = new fabric.Rect({
excludeFromExport: false,
hasControls: false,
height,
selectable: false,
borderColor: "transparent",
strokeWidth: 0,
stroke: 'transparent',
fill: '#ffffff',
width,
preserveObjectStacking: false,
absolutePositioned: true,
clip_id: 'io_main_canvas',
originX: 'center',
originY: 'center',
lockUniScaling: true,
});
}
// Here mockupDrawableAreaCanvasSize = {width: 1080, height: 1080}
getDimensions() {
let containerWidth: any = parseInt(this.mockupDrawableAreaCanvasSize.width, 10);
let containerHeight: any = parseInt(this.mockupDrawableAreaCanvasSize.height, 10);
const footer: any = document.querySelector('.footer-toolbar');
const container = document.querySelector('.upper-canvas');
const lowerCanvasEl = container && window.getComputedStyle(container, null);
let canvasHeight = parseInt(lowerCanvasEl.height, 10) - 1.5 * parseInt(footer.offsetHeight, 10);
const canvasWidth = parseInt(lowerCanvasEl.width, 10);
// Complete fit
if (containerWidth <= canvasWidth && containerHeight <= canvasHeight) {
return {
width: containerWidth,
height: containerHeight
}
}
if (canvasWidth < containerWidth) {
containerWidth = canvasWidth - 80;
canvasHeight = (containerWidth / (containerWidth / containerHeight)) - 6 * parseInt(footer.offsetHeight, 10)
}
let width = (containerWidth / containerHeight) * canvasHeight;
return {
width,
height: canvasHeight
}
}
This code works well on all the screens meaning the drawingboard rect has the right position + size on every resolution. The problem is with the content(objects) inside the this.drawingBoard.
I add the object to the canvas and centralize the object.
The objects on the smaller screens go very right corner, as shown in the image, as shown in the below snap
I am into this problem for the last 3 days, tried n number of solutions like- Grouping, centering the group, and Ungrouping also didn't work perfectly Second I tried repositioning the object by reducing some pixels that didn't even help.
finding no help on the internet. Can you please lead me to something, I know I have missed something but finding myself clueless about that?
I am expecting the objects to be at the same place where I added them initially.
@asturur
This solution worked well for me,
const ratioX = this.drawingBoard.width / originalWidth;
const ratioY = this.drawingBoard.height / originalHeight;
o.left *= ratioX;
o.top *= ratioY;
o.scaleX *= ratioX;
o.scaleY *= ratioY;
o.setCoords();
But still, there are some pixels on the left!
the best way to do in my opinion is to group everything, calculate % position of the group, scale the group by ratio, apply the same % position and ungroup.
Is the most generic but also the one that works ok most of the time.
Hi @asturur calculate % position of the group
can you please guide me on this little bit more? any example or something
group.left = this.drawingBoard.left;
group.top = this.drawingBoard.top;
group.width = this.drawingBoard.getScaledWidth();
group.height = this.drawingBoard.getScaledHeight();
this.canvas.add(group);
I tried this and it seems to be working. But on lower sized devices the canvas objects goes down
Would it not be nice to also be able to set the canvas height or width to a percentage value? I know this means that internally an event would need to take place on window resize to update the width and height, but is it not worth it?
Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.