I am using JavaScript and trying to make a skew effect on a div.
First, take a look at this video: http://www.youtube.com/watch?v=ny5Uy81smpE (0:40-0:60 should be enough). The video shows some nice transformations (skew) when you move the window. What I want to do is the same thing: to skew a div when I move it.
Currently I just have a plain simple div:
<div id="a" style="background: #0f0; position: absolute; left: 0px; top: 0px;"></div>
I have done a simple skew transformation using the CSS3's transform property, but my implementation is buggy. Are there good tutorials or maths sites or resources that describe the logic behind this? I know JavaScript and CSS well enough to implement, if I just knew the logic and maths. I tried reading FreeWins source code, but I am not good in C.
I am accepting any resourceful answers or pseudo code. My dragging system is part of a bigger system, thus, now that I post some real code, it does not work without giving you the entire system (that I can not do at this point). So, you can't run this code as is. The code I use is this (slightly modified though) to demonstrate my idea:
/**
* The draggable object.
*/
Draggable = function(targetElement, options) {
this.targetElement = targetElement;
// Initialize drag data.
this.dragData = {
startX: null,
startY: null,
lastX: null,
lastY: null,
offsetX: null,
offsetY: null,
lastTime: null,
occuring: false
};
// Set the cursor style.
targetElement.style.cursor = 'move';
// The element to move.
this.applyTo = options.applyTo || targetElement;
// Event methods for "mouse down", "up" and "move".
// Mouse up and move are binded to window.
// We can attach and deattach "move" and "up" events as needed.
var me = this;
targetElement.addEventListener('mousedown', function(event) {
me.onMouseDown.call(me, event);
}, false);
this.mouseUp = function(event) {
me.onMouseUp.call(me, event);
};
this.mouseMove = function(event) {
me.onMouseMove.call(me, event);
};
};
/**
* The mouse down event.
* @param {Object} event
*/
Draggable.prototype.onMouseDown = function(event) {
// New drag event.
if (this.dragData.occuring === false) {
this.dragData.occuring = true;
this.dragData.startX = this.dragData.lastX = event.clientX;
this.dragData.startY = this.dragData.lastY = event.clientY;
this.dragData.offsetX = parseInt(this.applyTo.style.left, 10) - event.clientX;
this.dragData.offsetY = parseInt(this.applyTo.style.top, 10) - event.clientY;
this.dragData.lastTime = (new Date()).getTime();
// Mouse up and move events.
var me = this;
window.addEventListener('mousemove', this.mouseMove, false);
window.addEventListener('mouseup', this.mouseUp, false);
}
};
/**
* The mouse movement event.
* @param {Object} event
*/
Draggable.prototype.onMouseMove = function(event) {
if (this.dragData.occuring === true) {
// He is dragging me now, we move if there is need for that.
var moved = (this.dragData.lastX !== event.clientX || this.dragData.lastY !== event.clientY);
if (moved === true) {
var element = this.applyTo;
// The skew animation. :)
var skew = (this.dragData.lastX - event.clientX) * 1;
var limit = 25;
if (Math.abs(skew) > limit) {
skew = limit * (skew > 0 ? 1 : -1);
}
var transform = 'translateX(' + (event.clientX + this.dragData.offsetX - parseInt(element.style.left, 10)) + 'px)';
transform += 'translateY(' + (event.clientY + this.dragData.offsetY - parseInt(element.style.top, 10)) + 'px)';
transform += 'skew(' + skew + 'deg)';
element.style.MozTransform = transform;
element.style.webkitTransform = transform;
this.dragData.lastX = event.clientX;
this.dragData.lastY = event.clientY;
this.dragData.lastTime = (new Date()).getTime();
}
}
};
/**
* The mouse up event.
* @param {Object} event
*/
Draggable.prototype.onMouseUp = function(event) {
this.dragData.occuring = false;
var element = this.applyTo;
// Reset transformations.
element.style.MozTransform = '';
element.style.webkitTransform = '';
// Save the new position.
element.style.left = (this.dragData.lastX + this.dragData.offsetX) + 'px';
element.style.top = (this.dragData.lastY + this.dragData.offsetY) + 'px';
// Remove useless events.
window.removeEventListener('mousemove', this.mouseMove, false);
window.removeEventListener('mousemove', this.mouseUp, false);
};
Currently my dragging system is buggy and simple. I need more information on the logic that I should be applying.
Wow, the idea rocks. :) I've cleaned your code a bit, and solved the problems with initialization. Now it works fine for me on Firefox and Chrome (even though you said it shouldn't).
A few notes:
top
and left
positions during initialization (getBoundingClientRect)this.dragData
and element.style
for shortness and faster executiondragData
can be initialized as an empty object. It's fine in javascript. You can add properties later.options
should be conditionally initialized as an empty object, so that you can take zero optionsmoved
and dragData.occuring
were totally useless because of the event managementpreventDefault
is needed in order not to select text during draggingz-indexes
to be the active element always visibleHave fun!
/**
* The draggable object.
*/
Draggable = function(targetElement, options) {
this.targetElement = targetElement;
// we can take zero options
options = options || {};
// Initialize drag data.
// @props: startX, startY, lastX, lastY,
// offsetX, offsetY, lastTime, occuring
this.dragData = {};
// Set the cursor style.
targetElement.style.cursor = 'move';
// The element to move.
var el = this.applyTo = options.applyTo || targetElement;
// Event methods for "mouse down", "up" and "move".
// Mouse up and move are binded to window.
// We can attach and deattach "move" and "up" events as needed.
var me = this;
targetElement.addEventListener('mousedown', function(event) {
me.onMouseDown.call(me, event);
}, false);
this.mouseUp = function(event) {
me.onMouseUp.call(me, event);
};
this.mouseMove = function(event) {
me.onMouseMove.call(me, event);
};
// initialize position, so it will
// be smooth even on the first drag
var position = el.getBoundingClientRect();
el.style.left = position.left + "px";
el.style.top = position.top + "px";
el.style.position = "absolute";
if (el.style.zIndex > Draggable.zindex)
Draggable.zindex = el.style.zIndex + 1;
};
Draggable.zindex = 0;
/**
* Sets the skew and saves the position
* @param {Number} skew
*/
Draggable.prototype.setSkew = function(skew) {
var data = this.dragData;
var style = this.applyTo.style;
// Set skew transformations.
data.skew = skew;
style.MozTransform = skew ? 'skew(' + skew + 'deg)' : '';
style.webkitTransform = skew ? 'skew(' + skew + 'deg)' : '';
// Save the new position.
style.left = (data.lastX + data.offsetX) + 'px';
style.top = (data.lastY + data.offsetY) + 'px';
}
/**
* The mouse down event.
* @param {Object} event
*/
Draggable.prototype.onMouseDown = function(event) {
var data = this.dragData;
// New drag event.
var style = this.applyTo.style;
data.startX = data.lastX = event.clientX;
data.startY = data.lastY = event.clientY;
data.offsetX = parseInt(style.left, 10) - event.clientX;
data.offsetY = parseInt(style.top, 10) - event.clientY;
style.zIndex = Draggable.zindex++;
data.lastTime = (new Date()).getTime();
// Mouse up and move events.
window.addEventListener('mousemove', this.mouseMove, false);
window.addEventListener('mouseup', this.mouseUp, false);
event.preventDefault(); // prevent text selection
};
/**
* The mouse movement event.
* @param {Object} event
*/
Draggable.prototype.onMouseMove = function(event) {
// He is dragging me now
var me = this;
var data = me.dragData;
var element = me.applyTo;
var clientX = event.clientX;
var clientY = event.clientY;
data.moving = true;
// The skew animation. :)
var skew = (data.lastX - clientX) * 1;
var limit = 25;
if (Math.abs(skew) > limit) {
skew = limit * (skew > 0 ? 1 : -1);
}
var style = element.style;
var left = parseInt(style.left, 10);
var top = parseInt(style.top, 10);
var transform =
'translateX(' + (clientX + data.offsetX - left) + 'px)' +
'translateY(' + (clientY + data.offsetY - top) + 'px)' +
'skew(' + skew + 'deg)';
style.MozTransform = transform;
style.webkitTransform = transform;
data.lastX = clientX;
data.lastY = clientY;
data.lastTime = (new Date()).getTime();
// here is the cooldown part in order
// not to stay in disorted state
var pre = skew > 0 ? 1 : -1;
clearInterval(data.timer);
data.timer = setInterval(function() {
var skew = data.skew - (pre * 10);
skew = pre * skew < 0 ? 0 : skew;
me.setSkew(skew);
if (data.moving || skew === 0)
clearInterval(data.timer);
}, 20);
data.moving = false;
};
/**
* The mouse up event.
* @param {Object} event
*/
Draggable.prototype.onMouseUp = function(event) {
this.setSkew('');
// Remove useless events.
window.removeEventListener('mousemove', this.mouseMove, false);
window.removeEventListener('mousemove', this.mouseUp, false);
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With