I am new to interactjs and I am trying to make the star drop on top of the tree (see jsfiddle) if it is snapped inside the dropzone. How would I do this?
Javascript:
interact('.draggable')
.draggable({
onmove: function (event) {
var target = event.target,
x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx,
y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
target.style.webkitTransform =
target.style.transform =
'translate(' + x + 'px, ' + y + 'px)';
target.setAttribute('data-x', x);
target.setAttribute('data-y', y);
},
onend: function (event) {
var textEl = event.target.querySelector('p');
textEl && (textEl.textContent =
'moved a distance of '
+ (Math.sqrt(event.dx * event.dx +
event.dy * event.dy)|0) + 'px');
}
})
.inertia(true)
.restrict({
drag: "parent",
endOnly: true,
elementRect: { top: 0, left: 0, bottom: 1, right: 1 }
});
// enable draggables to be dropped into this
interact('.tree').dropzone({
// only accept elements matching this CSS selector
accept: '#star',
// Require a 75% element overlap for a drop to be possible
overlap: 0.75,
// listen for drop related events:
ondropactivate: function (event) {
// add active dropzone feedback
event.target.classList.add('drop-active');
},
ondragenter: function (event) {
var draggableElement = event.relatedTarget,
dropzoneElement = event.target;
// feedback the possibility of a drop
dropzoneElement.classList.add('drop-target');
draggableElement.classList.add('can-drop');
$('.tree:not(.drop-target)').find('img').animate({
opacity: .5,
height: "160px"
});
},
ondragleave: function (event) {
var draggableElement = event.relatedTarget,
dropzoneElement = event.target;
// remove the drop feedback style
event.target.classList.remove('drop-target');
event.relatedTarget.classList.remove('can-drop');
$('.tree:not(.drop-target)').find('img').animate({
opacity: 1,
height: "186px"
});
},
ondrop: function (event) {
//Dropped event
},
ondropdeactivate: function (event) {
// remove active dropzone feedback
event.target.classList.remove('drop-active');
event.target.classList.remove('drop-target');
}
})
.snap({
mode: 'anchor',
grid: { x: 20, y: 20 },
range: Infinity
});
Library:
http://interactjs.io/
Fiddle:
http://jsfiddle.net/hpq7rpnh/2/
The above won't work with the updated API. I forked a pen of Taye (the author of interact.js) and made it working.
See a possible solution here:https://codepen.io/eljefedelrodeodeljefe/pen/vybegM
if I see this right, one needs to set the target on dragenter, in any case the solution with the new API is, imo, much more verbose from an integrator POV.
event.draggable.draggable({
snap: {
targets: [dropCenter]
}
});
If there are still people struggling with this, this worked well for me!
https://github.com/taye/interact.js/issues/79
interact('.draggable')
.draggable({
onmove: function (event) {
var target = event.target,
x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx,
y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
target.style.webkitTransform =
target.style.transform =
'translate(' + x + 'px, ' + y + 'px)';
target.setAttribute('data-x', x);
target.setAttribute('data-y', y);
},
onend: function (event) {
var textEl = event.target.querySelector('p');
textEl && (textEl.textContent =
'moved a distance of '
+ (Math.sqrt(event.dx * event.dx +
event.dy * event.dy)|0) + 'px');
}
})
.inertia(true)
.restrict({
drag: "parent",
endOnly: true,
elementRect: { top: 0, left: 0, bottom: 1, right: 1 }
});
interact('.draggable').snap({
mode: 'anchor',
anchors: [],
range: Infinity,
elementOrigin: { x: 0.5, y: 2 },
endOnly: true
});
// enable draggables to be dropped into this
interact('.tree').dropzone({
// only accept elements matching this CSS selector
accept: '#star',
// Require a 75% element overlap for a drop to be possible
overlap: 0.75,
// listen for drop related events:
ondropactivate: function (event) {
// add active dropzone feedback
event.target.classList.add('drop-active');
},
ondragenter: function (event) {
var draggableElement = event.relatedTarget,
dropzoneElement = event.target;
// feedback the possibility of a drop
clearInterval(interval); // stop star rotation
dropzoneElement.classList.add('drop-target');
draggableElement.classList.add('can-drop');
$('.tree:not(.drop-target)').find('img').animate({
opacity: .5,
height: "160px"
});
var dropRect = interact.getElementRect(event.target),
dropCenter = {
x: dropRect.left + dropRect.width / 2,
y: dropRect.top + dropRect.height / 2
};
event.draggable.snap({
anchors: [ dropCenter ]
});
},
ondragleave: function (event) {
var draggableElement = event.relatedTarget,
dropzoneElement = event.target;
// remove the drop feedback style
event.target.classList.remove('drop-target');
event.relatedTarget.classList.remove('can-drop');
$('.tree:not(.drop-target)').find('img').animate({
opacity: 1,
height: "186px"
});
event.draggable.snap(false);
},
ondrop: function (event) {
//Dropped event
},
ondropdeactivate: function (event) {
// remove active dropzone feedback
event.target.classList.remove('drop-active');
event.target.classList.remove('drop-target');
}
})
//Start star rotation
var angle = 0;
var interval = setInterval(function(){
angle+=1;
$("#star img").rotate(angle);
},50)
Fiddle:
http://jsfiddle.net/hpq7rpnh/3/
The only thing I couldn't figure out is that the star gets on the small tree top position if you drag it to another tree while it's small.
Related
Hello I'm searching "Object Layout library" its working on JavaScript.
I want to implement function for my web app like these feature below (look at this link).
https://imgur.com/mJq3HiV
I already searched information but I couldn't find a good module. Do you know a library something like that?
Okay, you are looking for interact.js here is the npm link.
Read the docs to learn, see the snippet bellow to see if is that what you want.
function dragMoveListener(event) {
var target = event.target
// keep the dragged position in the data-x/data-y attributes
var x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx
var y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy
// translate the element
target.style.webkitTransform =
target.style.transform =
'translate(' + x + 'px, ' + y + 'px)'
// update the posiion attributes
target.setAttribute('data-x', x)
target.setAttribute('data-y', y)
}
window.onload = () => {
interact('.resize-drag')
.resizable({
// resize from all edges and corners
edges: { left: true, right: true, bottom: true, top: true },
listeners: {
move(event) {
var target = event.target
var x = (parseFloat(target.getAttribute('data-x')) || 0)
var y = (parseFloat(target.getAttribute('data-y')) || 0)
// update the element's style
target.style.width = event.rect.width + 'px'
target.style.height = event.rect.height + 'px'
// translate when resizing from top or left edges
x += event.deltaRect.left
y += event.deltaRect.top
target.style.webkitTransform = target.style.transform =
'translate(' + x + 'px,' + y + 'px)'
target.setAttribute('data-x', x)
target.setAttribute('data-y', y)
target.textContent = Math.round(event.rect.width) + '\u00D7' + Math.round(event.rect.height)
}
},
modifiers: [
// keep the edges inside the parent
interact.modifiers.restrictEdges({
outer: 'parent'
}),
// minimum size
interact.modifiers.restrictSize({
min: { width: 100, height: 50 }
})
],
inertia: true
})
.draggable({
listeners: {
// call this function on every dragmove event
move: dragMoveListener,
// call this function on every dragend event
end(event) {
var textEl = event.target.querySelector('p')
textEl && (textEl.textContent =
'moved a distance of ' +
(Math.sqrt(Math.pow(event.pageX - event.x0, 2) +
Math.pow(event.pageY - event.y0, 2) | 0))
.toFixed(2) + 'px')
}
},
inertia: true,
modifiers: [
interact.modifiers.restrictRect({
restriction: 'parent',
endOnly: true
})
]
})
}
.resize-drag {
background-color: #29e;
color: white;
font-size: 20px;
font-family: sans-serif;
border-radius: 8px;
padding: 20px;
touch-action: none;
width: 120px;
/* This makes things *much* easier */
box-sizing: border-box;
}
<script src="https://cdn.jsdelivr.net/npm/interactjs/dist/interact.min.js"></script>
<div class="resize-drag">
Resize from any edge or corner
</div>
How can I jump a resized div to next row when the resize is constrained on the parent div like below (fullcalendar like thing).
Update.This code is dependent on InteractJS (http://interactjs.io/)
interact(resizeDiv)
.resizable({
edges: { left: true, right: true, bottom: false, top: false },
restrictEdges: {
endOnly: true,
outer: '.month'
},
restrictSize: {
min: { width: 80, height: 3 },
max: {height: 3}
},
inertia: true,
})
.on('resizemove', function (event) {
var target = event.target,
x = (parseFloat(target.getAttribute('data-x')) || 0),
y = (parseFloat(target.getAttribute('data-y')) || 0);
target.style.width = event.rect.width + 'px';
target.style.height = event.rect.height + 'px';
x += event.deltaRect.left;
y += event.deltaRect.top;
target.style.webkitTransform = target.style.transform =
'translate(' + x + 'px,' + y + 'px)';
target.setAttribute('data-x', x);
target.setAttribute('data-y', y);
target.textContent = Math.round(event.rect.width) + '\u00D7' + Math.round(event.rect.height);
var onTopElement = document.elementFromPoint(event.clientX, event.clientY);
let calendarArea = document.getElementById('calendarArea');
console.log(event.clientY, target.offsetTop);
})
Example - Fullcalendar:
Fullcalendar logic is simple, it will create a new div on next row rather than using the same div which you are resizing and apply resize(for fullcalendar, it uses JQuery resize event) event on the new div. I think its technically impossible to use the same div(same dom element) on the two rows which you are trying to do.
My moveMask function in the code below is called x amount of times for each HTML element with the class .layout-box.
I'm struggling to think of a way of re-writing my code so that it only runs the functions once, each time an event listener is invoked.
I understand this might not be a simple fix, but if I could get some some idea of the direction I need to be heading in, then I'd really appreciate it.
// Store SVG container element as variable
var layoutBox = document.querySelectorAll(".layout-box"),
xPos = 0,
yPos = 0,
elOver = true,
clicked = false,
rect,
coords,
split = document.querySelectorAll('.words h2, .words h3, .words p, .words li');
// GSAP tweens/functions that require access to coordinates
function moveMask(valueX, valueY) {
[].slice.call(layoutBox).forEach(function(el, i) {
console.log("test");
var img = el.querySelectorAll('.img');
// Mask move animation
var tlMove = new TimelineMax({
paused: true
});
tlMove.set(img, {
webkitClipPath: '40px at ' + valueX + 'px ' + valueY + 'px'
});
el.animationMaskMove = tlMove;
// Mask click animation
var tlClick = tlLeave = new TimelineMax({
paused: true
});
tlClick.to(img, 0.8, {
webkitClipPath: '500px at ' + valueX + 'px ' + valueY + 'px',
});
tlClick.to(img, 0.4, {
'-webkit-filter': 'grayscale(0%)',
filter: 'grayscale(0%)'
}, '-=0.8');
el.animationMaskClick = tlClick;
tlClick.progress(1).progress(0); // Forces an initial render of this tween so that it's cached for its 2nd usage
// Mask leave animation
var tlLeave = new TimelineMax({
paused: true
});
tlLeave.to(img, 0.2, {
webkitClipPath: '0 at ' + valueX + 'px ' + valueY + 'px',
'-webkit-filter': 'grayscale(100%)',
filter: 'grayscale(100%)'
});
el.animationMaskLeave = tlLeave;
tlLeave.progress(1).progress(0);
// Mask slow leave animation
var tlLeaveSlow = new TimelineMax({
paused: true
});
tlLeaveSlow.to(img, 0.6, {
ease: Bounce.easeOut,
webkitClipPath: '0 at ' + valueX + 'px ' + valueY + 'px',
'-webkit-filter': 'grayscale(100%)',
filter: 'grayscale(100%)'
});
el.animationMaskSlowLeave = tlLeaveSlow;
tlLeaveSlow.progress(1).progress(0);
});
}
// Force initial run with temporary coordinates (so tweens can be cached)
moveMask(-100, -100);
// GSAP tweens/functions that don't access to coordinates
[].slice.call(layoutBox).forEach(function(el, i) {
console.log("test2");
var svgContainer = el.querySelectorAll('.svg-container');
var svgContainerLeft = el.querySelectorAll('.left');
var svgContainerBottom = el.querySelectorAll('.bottom');
var img = el.querySelectorAll('.img');
var mask = el.querySelectorAll('.mask');
var text = el.querySelectorAll('.text');
var elSplit = el.querySelectorAll('.words');
// Container 3d push effect
var tlContainer3d = new TimelineMax({
paused: true
});
tlContainer3d.to(svgContainer, 0.3, {
x: "-=6px",
y: "+=6px"
});
el.animationContainer3d = tlContainer3d;
tlContainer3d.progress(1).progress(0);
// Container 3d push effect left
var tlContainer3dLeft = new TimelineMax({
paused: true
});
tlContainer3dLeft.to(svgContainerLeft, 0.3, {
width: "4px"
});
el.animationContainer3dLeft = tlContainer3dLeft;
tlContainer3dLeft.progress(1).progress(0);
// Container 3d push effect bottom
var tlContainer3dBottom = new TimelineMax({
paused: true
});
tlContainer3dBottom.to(svgContainerBottom, 0.3, {
height: "4px",
right: "+=6px"
});
el.animationContainer3dBottom = tlContainer3dBottom;
tlContainer3dBottom.progress(1).progress(0);
// Img move animation
var tlImgMove = new TimelineMax({
paused: true
});
tlImgMove.to(img, 0.3, {
scale: 1,
ease: Sine.easeOut,
'-webkit-filter': 'grayscale(0%)',
filter: 'grayscale(0%)'
}, 0.05);
el.animationImgMove = tlImgMove;
tlImgMove.progress(1).progress(0);
// Mask click text animation
var tlText = new TimelineMax({
paused: true
})
tlText.to(elSplit, .3, {
autoAlpha: 0
});
el.animationTextClick = tlText;
tlText.progress(1).progress(0);
});
// Loop through boxes and assign event listeners
for (var x = 0; x < layoutBox.length; x++) {
layoutBox[x].addEventListener("mousemove", function(e) {
if (elOver) { // Only run if mousedown hasn't been triggered
// Get coordinates of container
rect = this.getBoundingClientRect();
xPos = e.pageX - rect.left;
yPos = e.pageY - rect.top - window.scrollY;
// Add coordinates to array and pass in to moveMask function
coords = [xPos, yPos];
moveMask.apply(null, coords);
this.animationMaskMove.play();
}
});
layoutBox[x].addEventListener("mousedown", function(e) {
this.animationContainer3d.play();
this.animationContainer3dLeft.play();
this.animationContainer3dBottom.play();
this.animationMaskClick.play();
this.animationTextClick.play();
clicked = true;
elOver = false;
});
layoutBox[x].addEventListener("mouseleave", function(e) {
this.animationContainer3d.reverse();
this.animationContainer3dLeft.reverse();
this.animationContainer3dBottom.reverse();
this.animationTextClick.timeScale(1.25).reverse();
// If clicked then run slow animation
if (clicked) {
this.animationMaskSlowLeave.play();
} else {
this.animationMaskLeave.play();
}
clicked = false;
elOver = true;
});
}
Edit:
// Store SVG container element as variable
var layoutBox = document.querySelectorAll(".layout-box"),
elOver = true,
clicked = false,
xPos = 0,
yPos = 0,
rect,
coords;
// GSAP tweens/functions that require access to coordinates
function moveMask(el, valueX, valueY) {
console.log("test");
var img = el.querySelectorAll('.img');
// Mask move animation
var tlMove = new TimelineMax({
paused: true
});
tlMove.set(img, {
webkitClipPath: '40px at ' + valueX + 'px ' + valueY + 'px'
});
el.animationMaskMove = tlMove;
// Mask click animation
var tlClick = tlLeave = new TimelineMax({
paused: true
});
tlClick.to(img, 0.8, {
webkitClipPath: '500px at ' + valueX + 'px ' + valueY + 'px',
});
tlClick.to(img, 0.4, {
'-webkit-filter': 'grayscale(0%)',
filter: 'grayscale(0%)'
}, '-=0.8');
el.animationMaskClick = tlClick;
tlClick.progress(1).progress(0); // Forces an initial render of this tween so that it's cached for its 2nd usage
// Mask leave animation
var tlLeave = new TimelineMax({
paused: true
});
tlLeave.to(img, 0.2, {
webkitClipPath: '0 at ' + valueX + 'px ' + valueY + 'px',
'-webkit-filter': 'grayscale(100%)',
filter: 'grayscale(100%)'
});
el.animationMaskLeave = tlLeave;
tlLeave.progress(1).progress(0);
// Mask slow leave animation
var tlLeaveSlow = new TimelineMax({
paused: true
});
tlLeaveSlow.to(img, 0.6, {
ease: Bounce.easeOut,
webkitClipPath: '0 at ' + valueX + 'px ' + valueY + 'px',
'-webkit-filter': 'grayscale(100%)',
filter: 'grayscale(100%)'
});
el.animationMaskSlowLeave = tlLeaveSlow;
tlLeaveSlow.progress(1).progress(0);
}
// Force initial run with temporary coordinates (so tweens can be cached)
[].forEach.call(layoutBox, function(el, i) { moveMask(el, -100, -100) });
// GSAP tweens/functions that don't access to coordinates
[].forEach.call(layoutBox, function(el, i) {
console.log("test2");
var svgContainer = el.querySelectorAll('.svg-container');
var svgContainerLeft = el.querySelectorAll('.left');
var svgContainerBottom = el.querySelectorAll('.bottom');
var img = el.querySelectorAll('.img');
var mask = el.querySelectorAll('.mask');
var text = el.querySelectorAll('.text');
var elSplit = el.querySelectorAll('.words');
// Container 3d push effect
var tlContainer3d = new TimelineMax({
paused: true
});
tlContainer3d.to(svgContainer, 0.3, {
x: "-=6px",
y: "+=6px"
});
el.animationContainer3d = tlContainer3d;
tlContainer3d.progress(1).progress(0);
// Container 3d push effect left
var tlContainer3dLeft = new TimelineMax({
paused: true
});
tlContainer3dLeft.to(svgContainerLeft, 0.3, {
width: "4px"
});
el.animationContainer3dLeft = tlContainer3dLeft;
tlContainer3dLeft.progress(1).progress(0);
// Container 3d push effect bottom
var tlContainer3dBottom = new TimelineMax({
paused: true
});
tlContainer3dBottom.to(svgContainerBottom, 0.3, {
height: "4px",
right: "+=6px"
});
el.animationContainer3dBottom = tlContainer3dBottom;
tlContainer3dBottom.progress(1).progress(0);
// Img move animation
var tlImgMove = new TimelineMax({
paused: true
});
tlImgMove.to(img, 0.3, {
scale: 1,
ease: Sine.easeOut,
'-webkit-filter': 'grayscale(0%)',
filter: 'grayscale(0%)'
}, 0.05);
el.animationImgMove = tlImgMove;
tlImgMove.progress(1).progress(0);
// Mask click text animation
var tlText = new TimelineMax({
paused: true
})
tlText.to(elSplit, .3, {
autoAlpha: 0
});
el.animationTextClick = tlText;
tlText.progress(1).progress(0);
});
// Event listener functions
function myMouseMove(e) {
if (elOver) { // Only run if mousedown hasn't been triggered
// Get coordinates of container
rect = this.getBoundingClientRect();
xPos = e.pageX - rect.left;
yPos = e.pageY - rect.top - window.scrollY;
// Add coordinates to array and pass in to moveMask function
coords = [xPos, yPos];
moveMask.call(null, coords);
this.animationMaskMove.play();
}
}
function myMouseDown(e) {
this.animationContainer3d.play();
this.animationContainer3dLeft.play();
this.animationContainer3dBottom.play();
this.animationMaskClick.play();
this.animationTextClick.play();
clicked = true;
elOver = false;
};
function myMouseLeave(e) {
this.animationContainer3d.reverse();
this.animationContainer3dLeft.reverse();
this.animationContainer3dBottom.reverse();
this.animationTextClick.timeScale(1.25).reverse();
// If clicked then run slow animation
if (clicked) {
this.animationMaskSlowLeave.play();
} else {
this.animationMaskLeave.play();
}
clicked = false;
elOver = true;
};
// Loop through boxes and assign event listener
for (var x = 0; x < layoutBox.length; x++) {
layoutBox[x].addEventListener("mousemove", myMouseMove);
//layoutBox[x].addEventListener("mousedown", this, myMouseDown);
//layoutBox[x].addEventListener("mouseleave", this, myMouseLeave);
};
I've tried using jquery's built in draggable and I've tried using custom drag functions with no avail. Both have their respected issues and I will try to highlight both of them.
Basically, I am trying to allow the dragging of an element that is on a scaled div container. The following methods work okay on a scaled element that is less than around 2. But if you go any higher than that, we see some issues.
Any help would be appreciated. Thank you for your time.
HTML
<div id="container">
<div id="dragme">Hi</div>
</div>
Method 1 (Jquery draggable function)
I've tried the jquery draggable function as you can see in this jsfiddle example.
The problems I found in this example are the following:
Biggest concern: The droppable container does not change when it is scaled up. So if the element is being dragged over part of the scaled container that isn't a part of it's original size, it will fail.
When you click to drag a div, it teleports a little bit away from the mouse and is not a seamless drag.
JS
var percent = 2.5;
$("#dragme").draggable({
zIndex: 3000,
appendTo: 'body',
helper: function (e, ui) {
var draggable_element = $(this),
width = draggable_element.css('width'),
height = draggable_element.css('height'),
text = draggable_element.text(),
fontsize = draggable_element.css('font-size'),
textalign = draggable_element.css('font-size');
return $('<div id="' + draggable_element.id + '" name="' + draggable_element.attr('name') + '" class="text">' + text + '</div>').css({
'position': 'absolute',
'text-align': textalign,
'background-color': "red",
'font-size': fontsize,
'line-height': height,
'width': width,
'height': height,
'transform': 'scale(' + percent + ')',
'-moz-transform': 'scale(' + percent + ')',
'-webkit-transform': 'scale(' + percent + ')',
'-ms-transform': 'scale(' + percent + ')'
});
},
start: function (e, ui) {
$(this).hide();
},
stop: function (e, ui) {
$(this).show();
}
});
$("#container").droppable({
drop: function (event, ui) {
var formBg = $(this),
x = ui.offset.left,
y = ui.offset.top,
drag_type = ui.draggable.attr('id');
var element_top = (y - formBg.offset().top - $(ui.draggable).height() * (percent - 1) / 2) / percent,
element_left = (x - formBg.offset().left - $(ui.draggable).width() * (percent - 1) / 2) / percent;
$(ui.draggable).css({
'top': element_top,
'left': element_left
});
}
});
Method 2 - Custom drag function
I've tried using a custom drag function but it unusable after around a 2 scale.
jsfiddle on a scale(2) - Looks like the draggable div is having a seizure.
jsfiddle on a scale(2.5) - The draggable div flys away when you try to drag it.
JS
(function ($) {
$.fn.drags = function (opt) {
opt = $.extend({
handle: "",
cursor: "move"
}, opt);
if (opt.handle === "") {
var $el = this;
} else {
var $parent = this;
var $el = this.find(opt.handle);
}
return $el.css('cursor', opt.cursor).on("mousedown", function (e) {
if (opt.handle === "") {
var $drag = $(this).addClass('draggable');
} else {
$(this).addClass('active-handle')
var $drag = $parent.addClass('draggable');
}
var
drg_h = $drag.outerHeight(),
drg_w = $drag.outerWidth(),
pos_y = $drag.offset().top + drg_h - e.pageY,
pos_x = $drag.offset().left + drg_w - e.pageX;
follow = function (e) {
$drag.offset({
top: e.pageY + pos_y - drg_h,
left: e.pageX + pos_x - drg_w
})
};
$(window).on("mousemove", follow).on("mouseup", function () {
$drag.removeClass('draggable');
$(window).off("mousemove", follow);
});
e.preventDefault(); // disable selection
}).on("mouseup", function () {
if (opt.handle === "") {
$(this).removeClass('draggable');
} else {
$(this).removeClass('active-handle');
$parent.removeClass('draggable');
}
});
}
})(jQuery);
$("#dragme").drags({}, function (e) {});
Here are a few of my findings to make sure dragging on a scaled container works for method one. The only caveat is to make sure you have var percent as the scaled percentage declared before any of these actions happen.
First, use this code at the top of your javascript. This wil help making sure that the droppable area works with a sacled container.
$.ui.ddmanager.prepareOffsets = function( t, event ) { var i, j, m = $.ui.ddmanager.droppables[ t.options.scope ] || [], type = event ? event.type : null, list = ( t.currentItem || t.element ).find( ":data(ui-droppable)" ).addBack(); droppablesLoop: for ( i = 0; i < m.length; i++ ) { if ( m[ i ].options.disabled || ( t && !m[ i ].accept.call( m[ i ].element[ 0 ], ( t.currentItem || t.element ) ) ) ) { continue; } for ( j = 0; j < list.length; j++ ) { if ( list[ j ] === m[ i ].element[ 0 ] ) { m[ i ].proportions().height = 0; continue droppablesLoop; } } m[ i ].visible = m[ i ].element.css( "display" ) !== "none"; if ( !m[ i ].visible ) { continue; } if ( type === "mousedown" ) { m[ i ]._activate.call( m[ i ], event ); } m[ i ].offset = m[ i ].element.offset(); m[ i ].proportions({ width: m[ i ].element[ 0 ].offsetWidth * percent, height: m[ i ].element[ 0 ].offsetHeight * percent }); } };
Here are a few functions that are necessary to fix the drag so it works on a scaled container.
function dragFix(event, ui) { var changeLeft = ui.position.left - ui.originalPosition.left, newLeft = ui.originalPosition.left + changeLeft / percent, changeTop = ui.position.top - ui.originalPosition.top, newTop = ui.originalPosition.top + changeTop / percent; ui.position.left = newLeft; ui.position.top = newTop; }
function startFix(event, ui) { ui.position.left = 0; ui.position.top = 0; var element = $(this); }
You will want this if you want to enable the element to be resizable on a scaled container.
function resizeFix(event, ui) { var changeWidth = ui.size.width - ui.originalSize.width, newWidth = ui.originalSize.width + changeWidth / percent, changeHeight = ui.size.height - ui.originalSize.height, newHeight = ui.originalSize.height + changeHeight / percent; ui.size.width = newWidth; ui.size.height = newHeight; }
To make an element draggable, I use the following function.
$("ELEMENT").resizable({ minWidth: - ($(this).width()) * 10, minHeight: - ($(this).height()) * 10, resize: resizeFix, start: startFix });
$("ELEMENT").draggable({ cursor: "move", start: startFix, drag: dragFix }); }
A similar problem is mentioned here: jquery - css "transform:scale" affects '.offset()' of jquery
It seems the problem arises from the fact that jQuery fails to return exact size for scaled elements and therefore failing setting right offset values to the element.
To solve this, he is suggesting first setting scale to 1 and setting offset and then again resetting scale value.
But this alone does not solve the problem here. Since mouse position is taken while it is scaled, position values should also be divided by scale value.
Here is an edited version of code:
var scl = 2.5;
var
drg_h = $drag.outerHeight(),
drg_w = $drag.outerWidth(),
pos_y = $drag.offset().top/scl + drg_h - e.pageY/scl,
pos_x = $drag.offset().left/scl + drg_w - e.pageX/scl;
follow = function(e) {
var size = {
top:e.pageY/scl + pos_y - drg_h+scl*2,
left:e.pageX/scl + pos_x - drg_w+scl*2
};
$drag.parent().css("transform","scale(1)");
$drag.offset(size);
$drag.parent().css("transform","scale("+scl+")");
};
Note: I only replaced scale value for transform tag, since I am using chrome. You can also replace all instances or instead you can use a different class with 1 scale value.
JSFiddle is also here.
Here is an example of simple drag with scaling, however, in prue dom.
<style>
#dragme {
position:absolute;
border:1px solid red;
background:pink;
left:10px;
top:20px;
width:100px;
height:200px;
}
#container {
transform: scale(2,2) translate(100px,100px);
position:relative;
border:1px solid green;
background:grey;
width:200px;
height:300px;
}
</style>
<body>
<div id="container">
<div id="dragme">Hi</div>
</div>
<script>
var dragme=document.getElementById("dragme");
var container=document.getElementById("container");
dragme.onmousedown=function Drag(e){
this.ini_X = this.offsetLeft-e.clientX/2;
this.ini_Y = this.offsetTop-e.clientY/2;
container.onmousemove = move;
container.onmouseup = release;
return false;
}
function move(e){
e.target.style.left = e.clientX/2 + e.target.ini_X + 'px';
e.target.style.top = e.clientY/2 + e.target.ini_Y + 'px';
}
function release(){
container.onmousemove=container.onmouseup=null;
}
</script>
</body>
I am using InteractJs and I almost have it working but when I put the star on a tree that was small before it goes to the top of the small tree.
So when you drag the star on a tree the other trees get smaller. When you drag it to another tree the trees get normal height again but the star goes to the top of the small tree height that it used to be.
interact('.draggable')
.draggable({
onmove: function (event) {
var target = event.target,
x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx,
y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
target.style.webkitTransform =
target.style.transform =
'translate(' + x + 'px, ' + y + 'px)';
target.setAttribute('data-x', x);
target.setAttribute('data-y', y);
},
onend: function (event) {
var textEl = event.target.querySelector('p');
textEl && (textEl.textContent =
'moved a distance of '
+ (Math.sqrt(event.dx * event.dx +
event.dy * event.dy)|0) + 'px');
}
})
.inertia(true)
.restrict({
drag: "parent",
endOnly: true,
elementRect: { top: 0, left: 0, bottom: 1, right: 1 }
});
interact('.draggable').snap({
mode: 'anchor',
anchors: [],
range: Infinity,
elementOrigin: { x: 0.5, y: 2 },
endOnly: true
});
// enable draggables to be dropped into this
interact('.tree').dropzone({
// only accept elements matching this CSS selector
accept: '#star',
// Require a 75% element overlap for a drop to be possible
overlap: 0.75,
// listen for drop related events:
ondropactivate: function (event) {
// add active dropzone feedback
event.target.classList.add('drop-active');
},
ondragenter: function (event) {
var draggableElement = event.relatedTarget,
dropzoneElement = event.target;
// feedback the possibility of a drop
clearInterval(interval); // stop star rotation
dropzoneElement.classList.add('drop-target');
draggableElement.classList.add('can-drop');
$('.tree:not(.drop-target)').find('img').animate({
opacity: .5,
height: "160px"
});
var dropRect = interact.getElementRect(event.target),
dropCenter = {
x: dropRect.left + dropRect.width / 2,
y: dropRect.top + dropRect.height / 2
};
event.draggable.snap({
anchors: [ dropCenter ]
});
},
ondragleave: function (event) {
var draggableElement = event.relatedTarget,
dropzoneElement = event.target;
// remove the drop feedback style
event.target.classList.remove('drop-target');
event.relatedTarget.classList.remove('can-drop');
$('.tree:not(.drop-target)').find('img').animate({
opacity: 1,
height: "186px"
});
event.draggable.snap(false);
},
ondrop: function (event) {
//Dropped event
},
ondropdeactivate: function (event) {
// remove active dropzone feedback
event.target.classList.remove('drop-active');
event.target.classList.remove('drop-target');
}
})
//Start star rotation
var angle = 0;
var interval = setInterval(function(){
angle+=1;
$("#star img").rotate(angle);
},50)
jsfiddle: http://jsfiddle.net/hpq7rpnh/4/
Library: http://interactjs.io/