FabricJS - Create rectangle using 4 points (tl,tr, bl, br)? - javascript

Creating a rectangle in FabricJs is straightforward using top, left, width and height values.
After moving, scaling and rotating the rectangle around, one can get the rectangle's definitive coordinate using the aCoords of the object, returning bl, br, tl, tr x and y coordinates of the rectangles four edges.
How can one recreate the same rectangle using only these four coordinates, which also include rotation and scaling?
I was only able to recreate the rectangle after it has been moved and scaled, but not with rotations.
This above image is a screenshot of the following jsFiddle: https://jsfiddle.net/7neukojd/32/
As you can see, the left side is the original rectangle (blue), which has been scaled, moved and rotated. The right side rectangle (red) is me, trying to copy it using only the bl, br, tl and tr coordinates.

Thanks to asturur, this issue has been solved with the following code
let rectObj = canvas1.getObjects()[0];
const tl = rectObj.aCoords.tl;
const tr = rectObj.aCoords.tr;
const bl = rectObj.aCoords.bl;
let rectCopy = new fabric.Rect({
left: rectObj.aCoords.tl.x,
top: rectObj.aCoords.tl.y,
width: (new fabric.Point(tl.x, tl.y).distanceFrom(tr)),
height: (new fabric.Point(tl.x, tl.y).distanceFrom(bl)),
angle: fabric.util.radiansToDegrees(Math.atan2(tr.y - tl.y, tr.x - tl.x)),
fill: "red",
})
Source: https://github.com/fabricjs/fabric.js/discussions/6834#discussioncomment-314599

Related

PIXI JS - I want to change Width/height and Position of Rectangle with Mouse

var rectangle = new Graphics();
rectangle.beginFill(0, 0.001);
rectangle.lineStyle(1, 0xFF0000);
rectangle.drawRect(5, 5, 200, 100);
rectangle.interactive = true;
rectangle.buttonMode = true;
rectangle.on('pointerdown', onDragStart)
.on('pointerup', onDragEnd)
.on('pointerupoutside', onDragEnd)
.on('pointermove', onDragMove);
app.viewport.addChild(rectangle);
I'm using this code to create a rectangle and adding it to rectangle. This is the onDragMove Function
function onDragMove() {
if (this.dragging) {
const newPosition = this.data.getLocalPosition(this.parent);
this.width = newPosition.x;
this.height = newPosition.y;
}
}
This is basically changing its width and height, but I also want to Drag the Rectangle when clicked inside the rectangle and dragged on the screen. Any suggestions how can I achieve this?
Borders to change width/height
clicking inside rectangle to drag on the screen.
I created working example of dragging and resizing: https://www.pixiplayground.com/#/edit/SbpTjm7WKXbL51kmiG89W - please check.
when you click near bottom border of rectangle (+/- 20 px) then you will start resizing it.
when you click in any other place inside rectangle then you will drag it
Basing on this example i think you can implement resizing of other borders of rectangle etc :)
One note: please look at function drawRectangle :
function drawRectangle(rectangle)
{
rectangle.clear();
rectangle.beginFill(0, 0.001);
rectangle.lineStyle(1, 0xFF0000);
rectangle.drawRect(rectangle.myRectanglePosition[0], rectangle.myRectanglePosition[1], rectangle.myRectanglePosition[2], rectangle.myRectanglePosition[3]);
}
^ the rectangle.clear(); is called to redraw the PIXI.Graphics object. So on each frame we clear and draw it again on specific position with specific width & height.

SVG: Scale one element of group from specified anchor point

OK, so I have a group of four elements rotating 90 degrees as I want them to, around an origin point in the middle of the four elements.
I would like to scale the top left block before and after spinning as well, outward from said origin point, but I'm having much difficulty doing so.
Here is a fiddle for my sample (read: overly simplified) progress so far:
http://jsfiddle.net/Vac2Q/2843/
The fiddle's javascript:
/* create an svg drawing */
var draw = SVG('drawing')
/* draw rectangle */
var dial = draw.circle(60)
.move(125,125)
.fill('#0099ff')
var rect_yellow = draw.rect(50,50)
.move(100,100)
.fill('gold')
var rect_blue = draw.rect(50,50)
.move(160,100)
.fill('navy')
var rect_black = draw.rect(50,50)
.move(160,160)
.fill('black')
var rect_green = draw.rect(50,50)
.move(100,160)
.fill('green')
var blades = draw.group()
.add(rect_yellow)
.add(rect_blue)
.add(rect_black)
.add(rect_green)
.back()
var angle = 0
var rotation = 90
var spin = document.getElementById('spin')
var spun = 0
/* make rectangle jump and change color on mouse over */
spin.addEventListener('click', function() {
/* calculate new ending orientation for blades */
angle += rotation
var new_rotate = angle
/* rotate the blade group */
blades.animate(1000, '>')
.rotate(new_rotate, 155, 155)
++spun
})
And here is a slightly more glamorous example of what I'm trying to do re: scaling:
The first issue is being able to determine which blade is in the top left position after a given rotation. The second issue is scaling itself; I've gotten the blade to scale, but then it goes crazy and moves off the screen at the same time. I'm not sure how to get it to scale properly from the specified origin point (the middle of the center circle).
You can use the .after() function to chain animations.
I'm not sure if I am using svg.js correctly, but here's what I did:
var rects = [rect_yellow, rect_green, rect_black, rect_blue];
// define the animations
var enlarge_blade = function() {
rects[spun % 4].animate(250, '<')
.scale(1.25, 1.25)
.translate(-38,-38);
};
function spin_anim() {
rects[spun % 4].animate(250, '>')
.scale(1, 1)
.translate(0,0)
.after(rotate_blades);
};
var rotate_blades = function() {
blades.animate(1000, '>')
.rotate(angle, 155, 155)
.after(function() {
++spun;
enlarge_blade();
title.text('spun ' + spun + ' times');
});
};
// Pre-enlarge the first (yellow) rect
enlarge_blade();
/* make rectangle jump and change color on mouse over */
spin.addEventListener('click', function() {
angle += rotation
spin_anim();
})
Demo here
I see you're using svg.js. I don't know how it treats transforms internally. But in any case. To scale an element, you typically use its center point as a reference. Therefore you should find the center point of each rect and scale it using that point. (I assume svg.js performs the required translation internally).

d3.js svg image: scale from center of rectangle

I'm using d3 to populate a cartesian plane with a bunch of svg:image elements spread out over different coordinates.
I'd like to add mouserover and mouseout logic that zooms the image the mouse is over in and lightens the opacity of the others. I'm filtering my selection on mouseover to only select the desired element and everything is working great, except my scaling logic doesn't seem to get the desired effect. The images expand downward and to the right rather than in the outward from the diagonal center.
Here's what I've tried:
transform: scale(1.5) Which expands, but also totally shifts the image's position
transform: translate(-(width/2), -(height/2)) combined with scale, which does the same but from a different starting position
Changing the x and y coords to ones adjusted for half widths and heights, which has the same effect.
Is there no text-anchor equivalent for image elements with which I could set an "anchor point" to scale from? I'm not sure what the html svg parlance is, but I guess I'm thinking of something similar to the anchor points a lot of vector editors have.
Current approach, mouseover handler:
function fade(dir){
return function(d){
var others = svg.selectAll("image.movie_cover")
.filter(function(g,i){
return g != d
})
.transition().duration(800)
.style("opacity",.3);
var single = svg.selectAll("image.movie_cover")
.filter(function(g,i){
return g === d;
})
.transition().duration(900)
.attr("transform", "translate(-40,-40) scale(1.4)")
var title = keys[coords.indexOf(d)];
var url = "/static/eshk/"+hash+"_images/" + title + ".jpg";
tt.transition()
.duration(200)
.style("opacity", .9);
tt.html(title)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
}
}
Using this method, the images move inconsistent distances despite all being the same size.
Set up: A 50 x 50 box at 200, 200. It needs to transition to a 100 x 100. It is 50 larger and wider, so needs to move back and up 25, eg 175, 175. Replace hard coded values with functions that look up the current width on mouse hover to calculate the exact values.
d3.select('svg').append('rect');
rect = d3.select('rect');
rect.attr({
height: 50,
width: 50,
x: 200,
y: 200,
color: 'steelblue'
})
.transition()
.attr({
width: 100,
height: 100,
x: 175,
y: 175
});
This could also be done without modifying width or position attributes:
images.on("mouseover", function(d, i) {
var selection = d3.select(this);
var offsetX = parseFloat(selection.attr("x"))+
parseFloat(selection.attr("width")/2.0);
var offsetY = parseFloat(selection.attr("y"))+
parseFloat(selection.attr("height")/2.0);
selection.attr({
transform:"translate("+offsetX+ ","+offsetY+") "+
"scale(1.2) "+
"translate(-"+offsetX+",-"+offsetY+ ")"
});
});
And on mouse out, you'd just set the transform to null to remove it.
Basically, this is just translating the center point to the origin, scaling around that, and translating back to the correct position. Remember that transforms are applied in reverse order (right to left).
I think you were on the right track with the use of translate with scale, but translating back and forth from the origin is what allows it to work while remaining centered at the original location.

Raphael.js centered text on path

What I have:
Text along a path made out of circle. It uses Raphael.js and a function called textOnPath (found here: Raphael JS Text Along path ):
var pathTest = r.path(getCircletoPath(286, 322, radius)).attr({stroke:"#b9b9b9"});
textOnPath(message, pathTest, fontSize, fontSpacing, kerning, kerning, point, textFill, fontNormal, fontFamily);
JSFiddle: http://jsfiddle.net/zorza/62hDH/1/
What I need:
The text to be centered on top of the circle.
My approach:
Try to calculate where the text should start depending on the arc size and text width. I tried to calculate the text width by creating it's invisible clone with text() function and get it's BBox width.
It doesn't quite work and the results vary depending on the web browser, font used and number of letters and spaces:
var length = r.text(100,400,message)
.attr({"font-size":fontSize,'opacity': 0, 'font-family': fontFamily})
.getBBox()
.width;
var point = (Math.PI*radius - length*fontSpacing)/2;
JSFiddle: http://jsfiddle.net/zorza/k8vBy/3/
Could anyone point me in the right direction?
The easiest way, IMHO, is to create additional helper path that is raised by half of text size. http://jsfiddle.net/p84VQ/
Also, I find it a bit more convenient to define a circle and then get points at specified angle:
var Circle = function(cx, cy, r) {
return function (a) {
return {
x: cx + r*Math.sin(Math.PI*-a/180),
y: cy - r*Math.cos(Math.PI*-a/180)
}
}
};

Right way to rotate things with a mouse in Fabric.js

This is the example:
http://jsbin.com/UHENoKi/11/
Here I use simple formula to find the angle between two points (vectors):
But as you could see at JSBin - something is broken. Where did I make a mistake?
UPDATE
Working example:
http://jsbin.com/UHENoKi/13/edit?js,output
The angle to rotate by is being calculated based on the 0,0 origin, but the square is rotating around its own center at 100, 100, so they don't match. If you move the square to 0,0, it feels OK:
var rect = new fabric.Rect({
fill: '#00FFAB',
top: 0,
left: 0,
width: 100,
height: 100,
selectable: false
});
Rather than translating to canvas coordinates in toLocal, translate into the coordinate space of the square.

Categories