Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Position and Drag An Element With A Transform Applied

I have a javascript code that works perfectly for dragging object...but when I scaled the body down to 0.5...

transform:scale(0.5);

the position of mouse and the object dragged is not the same. How can I fix this? or is this possible?... thank you.

Heres a fiddle : http://jsfiddle.net/Xcb8d/65/

like image 568
user3204806 Avatar asked Apr 10 '14 00:04

user3204806


People also ask

What does CSS transform do?

The transform CSS property lets you rotate, scale, skew, or translate an element. It modifies the coordinate space of the CSS visual formatting model.

What is transformOrigin?

The transformOrigin property allows you to change the position on transformed elements. 2D transformed element can change the x- and y-axis of the element. 3D transformed element can also change the z-axis of the element. Note: This property must be used together with the transform property.

What is CSS webkit transform?

The CSS -webkit-transform property enables web authors to transform an element in two-dimensional (2D) or three-dimensional (3D) space. For example, you can rotate elements, scale them, skew them, and more.


2 Answers

This was pretty interesting and makes me rethink all the locations I simply used offsetHeight and offsetWidth without knowing that if a transformation was applied to the element, these readonly properties in JavaScript would return incorrect.

The "trick" to this is the clientHeight/offsetHeight will not report their transformed properties correctly. In order to get the correct sizing information from the element you need to call getBoundingClientRect(). You then can calculate the correct pixel information of the scaled element, allowing you then to perform the correct positioning.

Retrieving the bounding rectangle allows you to get the pixel information off the viewport, you then can compare this information to the clientHeight within the browser to determine the scaled offset height, and position.

I modified some of the event wire ups just for testing. Also I added another class to produce a quarter sized object just to prove it works regardless of scale.

CSS:

html,
body {
  width:100%;
  height:100%;
}

.half-size
{
   transform:scale(0.5);
  -moz-transform:scale(0.5);
  -webkit-transform:scale(0.5);
}
.quarter-size
{
   transform:scale(0.25);
  -moz-transform:scale(0.25);
  -webkit-transform:scale(0.25);
}

#draggable-element {
  width:100px;
  height:100px;
  background-color:#666;
  color:white;
  padding:10px 12px;
  cursor:move;
  position:relative; /* important (all position that's not `static`) */
  display:block;
}

JavaScript:

var selected = null, // Object of the element to be moved
    x_pos = 0, y_pos = 0, // Stores x & y coordinates of the mouse pointer
    x_elem = 0, y_elem = 0; // Stores top, left values (edge) of the element

var elem_height = 0;
var elem_width = 0;

// Will be called when user starts dragging an element
function _drag_init(elem) {
    // Store the object of the element which needs to be moved
    selected = elem;

    var boundingRectangle = selected.getBoundingClientRect();

    y_elem = (selected.offsetHeight - (boundingRectangle.bottom - boundingRectangle.top)) / 2;
    x_elem = (selected.offsetWidth - (boundingRectangle.right - boundingRectangle.left)) / 2

    half_elem_height = (boundingRectangle.bottom - boundingRectangle.top) / 2;
    half_elem_width = (boundingRectangle.right - boundingRectangle.left) /2;

    document.addEventListener('mousemove', _move_elem, false);
    document.addEventListener('mouseup', _destroy, false);    
};

// Will be called when user dragging an element
function _move_elem(e) 
{
  x_pos = e.clientX;
  y_pos = e.clientY;

  selected.style.left = ((x_pos - x_elem) - half_elem_width) + 'px';
  selected.style.top = ((y_pos - y_elem) - half_elem_height) + 'px';    
}
// Destroy the object when we are done and remove event binds
function _destroy() {
  selected = null;
  document.removeEventListener('mousemove', _move_elem);
  document.removeEventListener('mouseup', _destroy);
}

// Bind the functions...
document.getElementById('draggable-element').onmousedown = function () {
    _drag_init(this);
};

HTML:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
</head>
<body> 
<div id="draggable-element" class='half-size'>Drag me!</div>    
</body>
</html>

Click below for a live preview

http://jsbin.com/moyadici/1/edit (I prefer jsBin over jsFiddle for its live updates)

I did modify some of the event wire ups just for my initial testing. Also I broke the transform into a style so I could try other transforms. Tests look correct when rendering the 'quarter-size'. This just proves out it works regardless of scale and you don't need a magic number for your position calculations.

like image 119
cgatian Avatar answered Oct 18 '22 04:10

cgatian


Not a real answer , but too long for a comment and because it is still jumpy, maybe not on first try ...


In Firefox, using pointer-events, you could try to restrain the area that will catch the mouse.

create an inside element right in center wich has the size of the element itself once scaled.

Your fiddle says scale(0.5) a square of 100px, so lets draw a square of 50px right in the middle with a pseudo element.

Set pointer-events to none to the element and reset it back to normal for the pseudo element. Now we have a square of 50px that will accept the mouse. once the element scaled down to 50px , we still have this area reacting. (the square only looks smaller, but keeping using same space.

To finalize your fiddle test, let's add to body : transform-origin:top left;, now we should be abble to drag this square.

Firefox test :http://jsfiddle.net/Xcb8d/78/

Chrome test : http://jsfiddle.net/Xcb8d/79/ (negative margins added )

after a few clicks, it really gets jumpy of limits :)


hope it gives some hints.


reading this : http://css-tricks.com/get-value-of-css-rotation-through-javascript/

I thought , we could get the transform value from body and update calculation within the function , so we can modify scale value without touching script

. rookie test : http://jsfiddle.net/Xcb8d/82/

ex: function updated from original fiddle

// Will be called when user dragging an element
function _move_elem(e) {
var el = window.document.body;
var st = window.getComputedStyle(el, null);
var tr = st.getPropertyValue("transform") ;

var values = tr.split('(')[1];
    values = values.split(')')[0];
    values = values.split(',');

var a = values[0];
var b = values[1];
var c = values[2];
var d = values[3];
    x_pos = document.all ? window.event.clientX : e.pageX;
    y_pos = document.all ? window.event.clientY : e.pageY;
    if (selected !== null) {
        selected.style.left = ((x_pos / a) - x_elem) + 'px';
        selected.style.top = ((y_pos /  d) - y_elem) + 'px';
    }
}
like image 32
G-Cyrillus Avatar answered Oct 18 '22 05:10

G-Cyrillus