FabricJS Group selection / bounding box after child resize - javascript

I'm making a flowchart editor with FabricJS and I'm having troubles with my group bounding / selection box and/or the positioning of the child withing. I'm not sure what's happening.
My items are a fabric.Group (activity) with a fabric.Rect and fabric.Text inside it. The problem occurs when I change the text and want to change the size of the Rect with it. The text is changed from outside the canvas, so not with IText.
It works like this:
for(var i in finly.dossiertypes.workflow.activities ) {
if( finly.dossiertypes.workflow.activities[i].activityId === activityId ) {
var activity = finly.dossiertypes.workflow.activities[i];
var rect = activity._objects[0];
var text = activity._objects[1];
/**
* Set text
*/
text.set({
'text': name
}).setCoords();
/**
* Get width from text object
*/
var width = text.width + (finly.dossiertypes.workflow.activityPadding * 2);
/**
* Set rect width
*/
rect.set({
'width': width
}).setCoords();
/**
* Caculate group bounds
*/
activity._calcBounds( true );
break;
}
}
What happens is that the rect is shifting to the right outside the selectable group bounding. As you can see, the mousepointer suggests where the group is. The right side of the rect is not selectable, since it is outside of the group.
I've tried several things, like setting the width of the group. Could anyone help me with this one?

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.

how to make a div match a dynamically created google chart height

I have a page that dynamically loads a google timeline chart onto a div. The timeline increases in height as you add items on to it. If the chart is smaller than the div, it will be visible with empty space at the bottom. If the chart is larger, it will show scrollbars on the side.
What I would like is for the container div to always show the full chart or, another way, I would like the container div height to dynamically match the height of the chart.
I have tried to approximate the average size of each chart line and then adjust the div height when I load the chart with:
$("#gantt_chart").height(data['num_contents']*46);
But, although that somehow works, it's far from perfect because the approximation on height starts to accumulate with each additional line and the empty space at the bottom of the div increases which is not an elegant solution at all.
How would I ensure that the container div always shows the full chart?
I hope this is clear enough.
Many thanks.
The chart determines its height by either checking the height option passed into the draw call, or by checking the container height if the height option is not specified. I would suggest using the option instead of changing the div height via jQuery. I find the best method for dynamically calculating the chart height is to take the height of a row (typically 41px) times the number of rows, plus some padding for the top and bottom:
var height = data.getNumberOfRows() * 41 + 30;
Though in the doc they have shown to adjust height of div from where we are specifying the id of timeline but I recommend you to have set the height from the internal method.
// set the height for padding(padding height)
var pH = 40;
// get total height of rows (row height)
var rH = dataTable.getNumberOfRows() * 15;
// total chart height (chart height)
var cH = rH + pH;
// Now set this chart height to the timeline via option object
// apart from height, other attributes are just optional
var options = {
height: chartHeight,
colors: ['#339900', '#e6e600', '#B00000'],
tooltip: {
isHtml: true
},
timeline: {
colorByRowLabel: false
},
avoidOverlappingGridLines: true
};
Been trying to do the same thing myself. The top answer wasn’t exactly right and caused an inner scroll bar to appear at times.
Here’s what worked for me:
var barLabelFontSize = 12; //default font size for bar labels
try{barLabelFontSize = options.timeline.barLabelStyle.fontSize; //bar height is dependent on the bar label's font size that you set...
}catch(e){}
var barHeight = barLabelFontSize * 1.196; //found in google api as: this.PU = 1.916 * this.UF.fontSize;
var barMargin = barLabelFontSize * 0.75; //found in google api as: this.Rfa = .75 * this.UF.fontSize;
var rowHeight = barHeight + barMargin * 2; //found in google api as: b.height = 2 * this.Rfa + this.PU (+additional complication for grouped bars, if anyone wants to try work that out it was: b.height = 2 * this.Rfa + this.PU * a.SI.length + this.Qfa * (a.SI.length - 1); (where Qfa is 0.583 * font size, and a.SI.length is the number of grouped bars displayed on the row)
var chartAreaHeight = rowHeight * dataTable.getNumberOfRows();
var axisHeight = 24; //worked out by querying: document.getElementById('timelineChart') getElementsByTagName('svg')[0].getBBox().height;
var whiteSpaceHeight = 28; //trail-and-error to find how much whitespace was required to stop a scroll bar appearing, 27 works too, but I rounded up to play it safe
var chartHeight = chartAreaHeight + axisHeight + whiteSpaceHeight;
You can also reverse engineer this to set the height of the bars using the bar label font size.

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).

Jquery Position not honoring on first call

I am trying to make my autocomplete menu open above the input box if there is not enough space below the input box to display the menu. The code works fine except for on the initial render.
This means that it always displays at the bottom when:
1. Start searching
2. Click in the field and fire the search for the existing text in field
I have it output the position.my and position.at contents and they both are correct for "above" placement but it still displays below the input box.
I have the function called resize that is binded to window scroll and resize also. The moment you scroll the page, the menu gets positioned correctly. My suspect is that it is positioning before fully rendering.
Code
_renderMenu function hook
// Autocomplete _renderMenu function
$(autocomplete_object)._renderMenu = function( ul, item ) {
var that = this;
jQuery.each( items, function( index, item ) {
that._renderItemData( ul, item );
});
// Make sure the menu is now shown to calculate heights and etc (menu is now rendered, position rendering next)
jQuery(ul).show();
autocomplete.resize(ul, options);
autocomplete.create_dropdown_handlers(ul, options);
}
Resize Function
// Resize function
function resize( ul, options ) {
var height;
// If the height of the results is smaller than the space available, set the height to the results height
var ul_height = 0;
jQuery(ul).find('li').each(function(i, element){
ul_height += jQuery(element).height();
});
// Make the height the full height available below the input box but above the window cut off
// Move the dropdown above the input box if there is not enough room below the input box
var $parent = jQuery("#" + options.name);
var padding = 25; // arbitrary number to prevent dropdown from hitting the window border in either direction
var bottom_distance = autocomplete.getViewportHeight() - ($parent.offset().top + $parent.height()) - padding;
var bottom_limit = 200;
var ul_position = {
my: "left top",
at : "left bottom",
of: $parent,
collision: 'none'
};
height = bottom_distance;
if (bottom_distance < bottom_limit) {
var top_distance = $parent.offset().top - padding;
height = top_distance;
// ----- It is getting here fine! -----
ul_position.my = "left bottom";
ul_position.at = "left top";
}
// We have room to show the entire dropdown results without a scrollbar
if (ul_height < height) {
height = 'auto';
}
// Position below or above parent depending on space
jQuery(ul).position(ul_position);
jQuery(ul).css({height: height == 'auto' ? height : height + 'px'});
}
TLDR:
Jquery position is set to show above input field, but it still shows below?
I ended up having to update the autocomplete object's position value as well as the ul position. I believe the issue was that the initial render would inherit the autocomplete's position variable (Which defaults to showing below the input box).
Here is the new line:
// In the resize function after jQuery(ul).position(ul_position);
$parent.autocomplete("option", "position", ul_position); // Now the rendering is correct!
Resize function addition
function resize (ul, options) {
...
calculate the height and position requirements
...
jQuery(ul).position(ul_position);
$parent.autocomplete("option", "position", ul_position); // <-- Addition to honor position for rendering
}

Making a Javascript game, Having a little problem with scrolling

I have a #wrapper div and a #grid div nested inside. currently I can scroll around with this function below.
getCursorPos : function(){
// set the empty cursor object
var cursor = {};
//get the offset from the left of the grid container
var grid
//offset loop
$(function getCursorPos(){
grid = $('#grid').offset();
setTimeout(getCursorPos, game.loopSpeed);
});
//continuosly get the position
var that = this;
$(document).mousemove(function(e){
//if game mode is menu exit
if(game.mode === 'menu'){
return;
}
// NOTE: this looks a litle over done but don't remove anything
// its like this because javascript uses floating points
// and so in order to line up to the nearest hunderedth I
// had to make the cursor and div position intergers by
// muliplying by ten. one the two are added I reduced them
// and rounded them.
that.x = Math.round(((e.pageX * 10) - (grid.left * 10)) / 10);
that.y = Math.round(((e.pageY * 10) - (grid.top * 10)) / 10);
});
},
the problem is that the mouse coordinates only update when the mouse moves. is there any way to get the coordinates with out moving the mouse?
You always have the latest up-to-date coordinates of the mouse from the last mouse move, clarify why those are not useful to you.

Categories