I have a draggable element, which is also resizable and rotatable. These rotations are handled by CSS transformations, however, when an element is rotated, it makes the draggable feature spin out of control.
Heres a (updated) fiddle: Click me
What I think happens is that when an element is rotated, it's height and width obviously stay the same, just at an angle, however, jQuery doesn't account for the rotation, making it think that the element is in it's normal horizontal way, which results in the "bugg" shown in the fiddle above.
In a wild goose chase for the answer, I read somewhere that this would do the trick:
refreshPositions: true,
But it didn't work. Neither did destroying the draggable function on the element and then reinitiating it. Is there a way to fix this, so the containment will function normally, thus making jQuery recognise the rotation?
Thanks.
One option is to handle the containment yourself. Here is one possible way to do that.
I am using getBoundingClientRect to get the height and width of the rotated element. Using these values I can get an idea of where the dragged element resides in relation to it's parent container and force it to stay within those bounds.
var boundingContainer, boundingDraggable, prevLeft, prevTop;
$(".draggable").draggable({
classes: {
"ui-draggable-dragging": "highlight-draggable"
},
start: function(event, ui) {
boundingDraggable = ui.helper[0].getBoundingClientRect();
boundingContainer = ui.helper.closest('#draggableContainer')[0].getBoundingClientRect();
},
drag: function(event, ui) {
if(ui.offset.left <= boundingContainer.left){
if(ui.position.left < prevLeft){
ui.position.left = prevLeft;
}
}
if(ui.offset.top <= boundingContainer.top){
if(ui.position.top < prevTop){
ui.position.top = prevTop;
}
}
if(ui.offset.left+boundingDraggable.width >= boundingContainer.right){
if(ui.position.left > prevLeft){
ui.position.left = prevLeft;
}
}
if(ui.offset.top+boundingDraggable.height >= boundingContainer.bottom){
if(ui.position.top > prevTop){
ui.position.top = prevTop;
}
}
prevLeft = ui.position.left;
prevTop = ui.position.top;
}
});
Fiddle
Related
I used jquery.panzoom plugin in order to zoom and pan jsPlumb diagrams and every things is working find but when i zoom and drag an element this one goes far away from the pointer!
Does someone facing the same issue? can some one help me with this?
Thanks
see this
http://jsfiddle.net/a4v2guvt/
$panzoom.on('panzoomzoom', function (e, panzoom, scale) {
jsPlumb.setZoom(scale);
});
To fix this i used jQueryUI/draggable instead of builtin one:
var currentScale = 1;
$container.find(".diagram .item").draggable({
start: function(e){
var pz = $container.find(".panzoom");
//we need current scale factor to adjust coordinates of dragging element
currentScale = pz.panzoom("getMatrix")[0];
$(this).css("cursor","move");
pz.panzoom("disable");//disable panzoom to avoid double offset
},
drag:function(e,ui){
//fix scale issue
ui.position.left = ui.position.left/currentScale;
ui.position.top = ui.position.top/currentScale;
if($(this).hasClass("jsplumb-connected"))
{
plumb.repaint($(this).attr('id'),ui.position);
}
},
stop: function(e,ui){
var nodeId = $(this).attr('id');
plumb.repaint(nodeId,ui.position);
$(this).css("cursor","");
//enable panzoom back
$container.find(".panzoom").panzoom("enable");
}
});
Look at this demo
I'm currently trying to apply a containment to both the resize and the drag of a component. The drag accepts the containment bounds perfectly but the resize decides to break out and not follow the specified boundaries that I have set - anything i'm doing wrong here?
See the following jsFiddle (http://jsfiddle.net/cyVYq/30/);
$(".Event3").draggable({
axis: "x",
containment: "#2"
}).resizable({
axis: "x",
handles: "e, w",
containment: "#2"
})
Many Thanks,
You're trying to contain the resizable in an element that isn't its parent (#2)
Try setting the container to its parent .TimelineContainer:
$(".Event3").resizable({
axis: "x",
handles: "e, w",
containment: ".TimelineContainer"
})
Fiddle
Update:
From the JQuery-UI documentation it appears that the intended use of the containment option is to be used within the confines of a parent element. It doesn't seem possible to use this option the way you intend. Try something like:
$(".Event3").resizable({
axis: "x",
handles: "e, w",
containment: ".TimelineContainer",
resize: function(event, ui) {
// Define the dimensions of this element
var left = ui.position.left;
var width = ui.size.width;
var right = left + width;
// Define my constraints based on the element #2
var constraintLeft = $('#2').position().left;
var constraintWidth = $('#2').width();
var constraintRight = constraintLeft + constraintWidth;
// if resizable has gone further left than the constraint
if(left < constraintLeft) {
var left = parseInt($(this).css('left'));
// measure the difference between this left and the constraint left
var difference = constraintLeft - left;
// make up the difference
$(this).css('left', constraintLeft);
// make sure your width stays the same
$(this).width($(this).width() - difference);
// cancel the event
$(this).resizable('widget').trigger('mouseup');
}
// if resizable has gone further right than the constraint
if( right > constraintRight) {
// change the width to fit between the constraint right and this left
$(this).width(constraintRight - left);
// cancel the event
$(this).resizable('widget').trigger('mouseup');
}
}
});
Where you manually define the boundaries of the resizable and then you call a mouseup when the resizable has reached those boundaries. Here's the fiddle to play around with:
Fiddle
Edit#1:
Since I asked this problem, I made a script which allows the elements to move together, but with problems. :(
$(this).append(
$('<div class="elem '+classes+'" style="width: 210px; height: 30px" data-modby="'+ui.draggable.attr("data-for")+'"><p>'+text+'</p></div>')
.resizable({
grid: 10,
maxHeight: 120,
maxWidth: 600,
minHeight: 30,
minWidth: 210,
containment: ".box-section"
})
.draggable({
grid: [10,10],
containment: ".box-section",
scroll: false,
helper: "original",
start: function(event, ui) {
posTopArray = [];
posLeftArray = [];
if ($(this).hasClass("r-active")) {
$(".elem.r-active").each(function(i) {
thiscsstop = $(this).css('top');
if (thiscsstop == 'auto') thiscsstop = 0;
thiscssleft = $(this).css('left');
if (thiscssleft == 'auto') thiscssleft = 0;
posTopArray[i] = parseInt(thiscsstop);
posLeftArray[i] = parseInt(thiscssleft);
});
}
begintop = $(this).offset().top;
beginleft = $(this).offset().left;
},
drag: function(event, ui) {
var topdiff = $(this).offset().top - begintop;
var leftdiff = $(this).offset().left - beginleft;
if ($(this).hasClass("r-active")) {
$(".elem.r-active").each(function(i) {
$(this).css('top', posTopArray[i] + topdiff);
$(this).css('left', posLeftArray[i] + leftdiff);
});
}
}
})
);
The actual problem is the moved elements have to stay in the containment box (called .box-section and if I have a single element it works fine. Also if i have two elements has not the same width, if i pick the sorter one and drag, I can pull out the longer of the container.
Also, if I move them fast (like a ninja) they will slip, and they won't be in the same grid they used to be.
30 percent solved since:
I'm going to make a new thingy which can drag and drop items to a box, and resize them.
My problem is that, I can't make them move together. But with some rules.
Rule one: They must move based on a 10x10px grid.
Rule two: They can't move outside their frame.
(Resize is an issue which i simply can't imagine, so I don't care about it, resize will be later)
I made a fiddle for it here: http://jsfiddle.net/9fueY/ you can see this.
In the right side, you can see inputs and a and a checkbox (and on hover a button).
The checkbox is the draggable object, drag it to the image. Now you have some text or a star appeared on the left.
If you type anything to the inputs, it refreshes the text.
OnHover the right boxes you can see a button. By clicking on it, will make a clone of the first element. Drag it inside.
If you click to a right-side box, it will glow blue. Click to two boxes on the right, makes all the two textfields glow blue in the image.
Now this is the case when i want them to move together. :D
What's your opinion, what should I do with this?
Thank you very much. - Répás
There is a plugin i developed for dragging elements together. Please do use it if it makes sense.
https://github.com/someshwara/MultiDraggable
I'm a bit stumped here. I am developing a feedback utility that will allow the user to "draw" boxes on a web page to highlight problem areas. Right now I have an overlay DIV that fills the screen and jQuery allows you to draw red outlined DIVs by clicking and dragging.
Here is the JS:
{
var $feedbackOverlay = jQuery('#feedbackOverlay');
var $original = { top: 0, left:0 };
$feedbackOverlay.bind('mousedown', function (e)
{
jQuery('<div id="currentHighlight"></div>')
.css('width', '1px')
.css('height', '1px')
.css('border', 'solid 3px #ff0000')
.css('border-radius', '5px')
.css('position', 'absolute')
.css('left', e.pageX)
.css('top', e.pageY)
.css('z-index', '8000001')
.appendTo('body');
$original = { top: e.pageY, left: e.pageX };
});
$feedbackOverlay.bind('mousemove', function (e)
{
var $currentHighlight = jQuery('#currentHighlight');
if ($currentHighlight.length > 0)
{
var $pos = { top: e.pageY, left: e.pageX };
if($pos.top < $original.top) $currentHighlight.css('top', $pos.top);
if ($pos.left < $original.left) $currentHighlight.css('left', $pos.left);
$currentHighlight.height(Math.abs($pos.top - $original.top));
$currentHighlight.width(Math.abs($pos.left - $original.left));
}
});
$feedbackOverlay.bind('mouseup', function (e)
{
var $currentHighlight = jQuery('#currentHighlight');
$currentHighlight.removeAttr('id');
});
var $feedbackInstructions = jQuery('#feedbackInstructions');
$feedbackInstructions.fadeIn(1000, function ()
{
setTimeout(function ()
{
$feedbackInstructions.fadeOut(1000);
}, 3000);
});
$feedbackOverlay.height(jQuery(document).height());
});
Here is a jsFiddle for the above:
http://jsfiddle.net/Chevex/RSYTq/
The problem is that I can't drag the boxes up or left. The first click puts the top left corner where the mouse clicked. After that subsequent dragging will change the width of the box. Letting go of the mouse completes the box and you may then start drawing another one. If you try to drag the DIV left or up while drawing it's width will remain at 0 but won't go negative.
Here you can find working solution: http://jsfiddle.net/RSYTq/34/
Something like this will get you closer to what you want: http://jsfiddle.net/RSYTq/18/
Doesn't quite handle move up and to the left and then switching to moving down and to the right quite right yet but it gives you the idea.
There's no such thing a a negative width - these are not coorindinates. You need to reposition and recalculate the corner positions relative to the corner that's not being moved.
Sounds like you need to check if the click origin (x,y) is > than the current mouse position, and then swap which one you use for the CSS top-left.
You would need to track the original start point somewhere (variables, data attributes on #currentHighlight, wherever you want), and check for width or height < 0. When so, set the #currentHighlight left/top CSS to be offset by original + (e.pageX - $currentHighlight.position().left) (for example). Then set the #currentHighlight width/height to the same difference (but positive: (e.pageX - $currentHighlight.position().left) * -1).
I have a jQuery UI draggable element. It's extremely simple. It's just a div (container) with another div inside (draggable piece) set to a grid. The problem is after I move the element one time I can't go back to the first point. If i change the grid size it works, but I need it to work on this grid as it's matching some element below it
Relevant JS:
$('<div class="slider_wrap"><div class="slider"></div></div>').appendTo('#chart');
$('#chart .slider')
.draggable({
containment:'parent',
grid:[distanceBetweenPoints,0],
opacity: 0.25
})
.bind('mousedown', function(e, ui){
// bring target to front
$(e.target.parentElement).append( e.target );
})
.bind('drag', function(e, ui){
// update coordinates manually, since top/left style props don't work on SVG
e.target.setAttribute('x', ui.position.left);
})
.bind('dragstop',function(e, ui){
//a = true offset of slider piece
var a = ui.position.left + distanceBetweenPoints;
var b = containerWidth;
var c = thePoints.length;
var d = b / c;
var x = a / d;
//Since the points are in an array which starts at 0, not 1, we -1 from the currentPoint
console.log(x)
var currentPoint = Math.round(x)-1;
thisPointIndex = currentPoint;
chart.series[0].data[currentPoint].select(true);
});
Any ideas?
Example:
http://jsbin.com/ucebar
You're using a fractional grid size, for example 39.7 px. So, with each drag, the div gets offset a pixel to the left. This means that position zero quickly becomes unavailable:
That is: at point 1, ui.position.left will be 38 pixels or less.
Since moving the minimum jump (39.7px) -- towards point 0 -- will take the div outside the bounding rectangle, that move is not allowed.
And, using the nearest integer for grid size will quickly result in misalignment between the grid and the data points.
One way around all this is to:
Delete the grid:[distanceBetweenPoints,0], parameter.
Instead, snap the div upon drag stop, like so:
/*--- Snap to nearest grid.
*/
var gridPos = Math.round (
Math.round (ui.position.left / distanceBetweenPoints)
* distanceBetweenPoints
);
var delta = gridPos - ui.position.left;
var newOffset = $(this).offset ().left + delta;
$(this).offset ( {left: newOffset} );
See it in action at jsBin.
I didnt have time to work out a real solution, but I found that if you drag and drop the slider moves slightly more to the left each time. The reason it can't go back into first place is that after the first drop, there is not enough room anymore. Good luck!
I was able to solve it with the following:
.draggable({
...
drag : function(event, ui) {
ui.position.left = Math.round(ui.position.left / distance_between_points) * distance_between_points;
}
});