Change size directly by Controls in fabricjs? - fabricjs

I develop web aplication with fabricjs ,At present, scaleX or scaleY is changed by Controls in fabricjs, but I want to change width or height directly by Controls. How can I do that?

try this Code :
canvas.on({
'object:scaling': function(e){
var selected = e.target;
selected.set({
height: selected.height * selected.scaleY,
width: selected.width * selected.scaleX,
scaleX: 1,
scaleY: 1
});
canvas.renderAll();
}
})
set height and width after calculation of scaling and make scale 1.

Related

Phaser 3 interactive hitarea of a container

I have a container containing an image and some text;
We are facing an issue with the interactive hitarea of the container
this.image = this.add.image(0, 0, 'GreenButtonBox').setOrigin(0.5).setScale(2);
this.text = this.add.text(0, 0, "Click", { font: "bold 35px Arial", fill: '#ffffff' }).setOrigin(0.5).setScale(1);
this.container = this.add.container(400, 400, [this.image, this.text])
.setSize(this.image.width, this.image.height)
.setInteractive()
.on('pointerup', this.SubmitAnswer);
In the case above the interactive part of the button is located in the middle (maybe due to the scale of the image?
I also tried adding new Phaser.Geom.Rectangle to the interactive function as such
.setInteractive(new Phaser.Geom.Rectangle(0, 0, this.image.width, this.image.height), Phaser.Geom.Rectangle.Contains)
but the clickable area become the lower right corner of the button; wheras with
.setInteractive(new Phaser.Geom.Rectangle((this.image.width / 2) * -1, (this.image.height / 2) * -1, this.image.width, this.image.height), Phaser.Geom.Rectangle.Contains)
the clickable area becomes the uper left part of the button;
Do I need to account for the scale myself? such as
.setInteractive(new Phaser.Geom.Rectangle((this.image.width / 2) * -1, (this.image.height / 2) * -1, this.image.width * 2, this.image.height * 2), Phaser.Geom.Rectangle.Contains)
This actually works
I'm confused as to why I need to scale the image again when I'm taking the image width and height for the container width and height.
Am I missing something?
You are right, height and width won't work (they hold the original size), but you could simply use the properties displayWidth and displayHeight, this properties gives you the right size (especially after scaling), calculation is handled by phaser.
Here a small demo illustrating this:
let Scene = {
preload(){
this.load.image('ship', 'https://labs.phaser.io/assets/sprites/ship.png');
},
create(){
this.image = this.add.image(0, 0, 'ship').setOrigin(0.5)
console.info(`before Scale width: ${this.image.width}, height: ${this.image.height}, displayWidth: ${this.image.displayWidth}, displayHeight:${this.image.displayHeight}` );
this.image.setScale(2);
console.info(`after Scale width: ${this.image.width}, height: ${this.image.height}, displayWidth: ${this.image.displayWidth}, displayHeight:${this.image.displayHeight}` );
}
}
const config = {
type: Phaser.AUTO,
width:400,
height:200,
scene: Scene,
banner: false
}
const game = new Phaser.Game(config)
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>

FabricJS resizing groups and objects in them

I need to programmaticaly resize a group and it's contained elements on a fabric canvas.
By default, fabric applies scaling to objects. I can get around this easily enough by using the scalex & scaley to calculate the new height and width then I set them back to 1. This works fine for the group but I cant figure out how to set the new size for the objects contained in this group.
Eg. Before resize:
After resize:
My (typescript) code is like:
redraw() {
this.shapeGroup.set({
scaleX: 1,
scaleY: 1,
left: this.shape.origin.x,
top: this.shape.origin.y,
width: this.shape.extent.width + this.shape.line.lineWidth,
height: this.shape.extent.height + this.shape.line.lineWidth,
dirty: true
});
// const ac = this.shapeGroup.calcCoords();
this.shapeElement.set({
rx: this.shape.cornerRadius,
ry: this.shape.cornerRadius,
width: this.shape.extent.width,
height: this.shape.extent.height,
dirty: true
});
// this.shapeElement.setCoords(ac);
if (this.shape.text) {
this.textElement.set({
width: this.shapeElement.width - (this.cornerRadius * 2),
height: this.shapeElement.height - (this.cornerRadius * 2)
});
}
this.canvas.fabric.renderAll();
}
(this.shape is the underlying model of the object which is represented by 1 or more fabric objects in a group).
Has anyone done anything like this with success?
I used a similar code to resize the group items, so maybe this can lead you to a solution.
canvas.setActiveGroup(this.shapeGroup.setCoords());
var objs = canvas.getActiveGroup().getObjects();
for(i in objs){
objs[i].set({
...
});
}

how to stretch image size to match fabric.Image object

I am using fabricjs to implement image editing and I try to use a fabric.Image object as the background image of canvas to store the data. And the following is the code:
var canvas = new fabric.Canvas('canvasId');
var imageObject = new fabric.Image($originImage);
canvas.add(imageObject);
but I found the $originImage's size is much larger than canvas' size and also imageObject's size, so the canvas can only show part of the image. I want to know how to stretch the $originImage to adapt the canvas then canvas can display all of the $originImage?
Here what I have done
$canvas.width = $originImage.clientWidth;
$canvas.height = $originImage.clientHeight;
var fabricCanvas = new fabric.Canvas('canvasId');
// canvas.setFabricCanvas(fabricCanvas);
var imageObject = new fabric.Image($originImage);
// fabricCanvas.add(imageObject);
// fabricCanvas.isDrawingMode = true;
fabricCanvas.setBackgroundImage(imageObject, fabricCanvas.renderAll.bind(fabricCanvas), {
scaleX: fabricCanvas.width / $originImage.naturalWidth,
scaleY: fabricCanvas.height / $originImage.naturalHeight
});
the upper is my related code and below is the display:
it is solved, and the previous question is because I resize the $originImage first, so when I input the image src as setBackgroundImage's parameter, it can display normally.
For version 2.0 of fabricjs, they have changed the way to handle width/height compared to previous version. If you apply width/height, then it basically crop the image to that particular size compared to the original image size. In order to resize it to a proper size, you have to switch to use scaleX/scaleY like other people have suggested.
For example, I use image.fromUrl to load an image and set it to be the background:
fabric.Image.fromURL(imgUrl, function(img) {
img.set({
scaleX: currentWidth / img.width,
scaleY: currentHeight / img.height,
top: topPosition,
left: leftPosition
originX: 'left', originY: 'top'
});
canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas));
});
where currentWidth/currentHeight can be your canvas size if you want full background or they can be a specific width/height that you want (in my project, I want to do letter-boxed image instead so I set the width and height based on my letter-boxing algorithm), and top/left is the location that you want to place your image in the canvas (leave it none if you want to be full background image). Do not set any width and height since that will crop the scaled image rather than setting the image to be the exact size.
If this does not work, check the backgroundImage object from the canvas and see if its dimension and scale properties are different compared to a manual calculation. If you take a look, you will see the width and height properties are your natural image width and height, but it will have scaleX and scaleY less than 1 if your canvas size is less than the image size or more than 1 if it is bigger.
Also I see you are loading image directly into an image object. That might be different compared to load the data and set it directly using setBackgroundImage.
canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas), {
scaleX: canvas.width / $originalImg.naturalWidth,
scaleY: canvas.width / $originalImg.naturalHeight
});
use scaleX,scaleY to resize.
(function() {
var $originalImg = $('#originalImage')[0];
console.dir($originalImg)
var canvas = this.__canvas = new fabric.Canvas('c');
var img = new fabric.Image($originalImg);
canvas.setBackgroundImage(img,canvas.renderAll.bind(canvas),{
scaleX:canvas.width/$originalImg.naturalWidth,
scaleY:canvas.width/$originalImg.naturalHeight
});
})();
canvas{
border-width: 1pz;
border-style: solid;
border-color: #000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.0.0-rc.3/fabric.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<img id='originalImage'src='http://fabricjs.com/assets/pug_small.jpg'>
<canvas id='c' width=200 height=200></canvas>
I am using the following script to ensure the image is no larger than the canvas width:
var aspect = image.width / image.height; //Aspect ratio of image
new fabric.Image(image, {
scaleX: canvas.width / image.width,
scaleY: canvas.width / (image.height * aspect)
});

Fabricjs How to scale object but keep the border (stroke) width fixed

I'm developing a diagram tool based on fabricjs. Our tool has our own collection of shape, which is svg based. My problem is when I scale the object, the border (stroke) scale as well. My question is: How can I scale the object but keep the stroke width fixed. Please check the attachments.
Thank you very much!
Here is an easy example where on scale of an object we keep a reference to the original stroke and calculate a new stroke based on the scale.
var canvas = new fabric.Canvas('c', { selection: false, preserveObjectStacking:true });
window.canvas = canvas;
canvas.add(new fabric.Rect({
left: 100,
top: 100,
width: 50,
height: 50,
fill: '#faa',
originX: 'left',
originY: 'top',
stroke: "#000",
strokeWidth: 1,
centeredRotation: true
}));
canvas.on('object:scaling', (e) => {
var o = e.target;
if (!o.strokeWidthUnscaled && o.strokeWidth) {
o.strokeWidthUnscaled = o.strokeWidth;
}
if (o.strokeWidthUnscaled) {
o.strokeWidth = o.strokeWidthUnscaled / o.scaleX;
}
})
canvas {
border: 1px solid #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.4/fabric.min.js"></script>
<canvas id="c" width="600" height="600"></canvas>
There is a property called: strokeUniform
Use it like this
shape.set({stroke: '#f55b76', strokeWidth:2, strokeUniform: true })
I have found what feels like an even better solution, works really well with SVG paths.
You can override fabricjs' _renderStroke method and add ctx.scale(1 / this.scaleX, 1 / this.scaleY); before ctx.stroke(); as shown below.
fabric.Object.prototype._renderStroke = function(ctx) {
if (!this.stroke || this.strokeWidth === 0) {
return;
}
if (this.shadow && !this.shadow.affectStroke) {
this._removeShadow(ctx);
}
ctx.save();
ctx.scale(1 / this.scaleX, 1 / this.scaleY);
this._setLineDash(ctx, this.strokeDashArray, this._renderDashedStroke);
this._applyPatternGradientTransform(ctx, this.stroke);
ctx.stroke();
ctx.restore();
};
You may also need to override fabric.Object.prototype._getTransformedDimensions to adjust the bounding box to account for the difference in size.
Also a more complete implementation would probably add a fabric object property to conditionally control this change for both overridden methods.
Another way is to draw a new object on scaled and remove the scaled one.
object.on({
scaled: function()
{
// store new widht and height
var new_width = this.getScaledWidth();
var new_height = this.getScaledHeight();
// remove object from canvas
canvas.remove(this);
// add new object with same size and original options like strokeWidth
canvas.add(new ...);
}
});
Works perfect for me.

How to set fixed width and height?

How to set fixed width and height on fancy box? the fancy box works fine but not the width/height.
The width should be 953px and the height 100%.
script:
<script type="text/javascript">
$(document).ready(function(){
$(".fancybox").fancybox();
});
$(".fancybox").fancybox({
padding : 0
});
</script>
You can set width and height for fancy box directly :
$(".fancybox").fancybox({
'overlayShow': false,
'frameWidth': 500, // set the width
'frameHeight': 100, // set the height
'type': 'iframe', // tell the script to create an iframe
'autoDimensions':false,
'autoSize':false
});
or
$('.fancybox').fancybox({
autoDimensions: false,
height: 300,
width: 400
});

Resources