Am trying to achieve this (built using webflow) animation and interaction when hovering on an element but am not able to do so. I've found this answer here but when I tried to refactor it with on hover function I still couldn't make it work.
Here's what I've tried.
// Maximum offset for image
var maxDeltaX = 50,
maxDeltaY = 50,
viewportWidth = 0,
viewportHeight = 0,
mouseX = 0,
mouseY = 0,
translateX = 0,
translateY = 0;
// Bind mousemove event to document
jQuery('.image-content-right').on('mousemove', function(e) {
// Get viewport dimensions
viewportWidth = document.documentElement.clientWidth,
viewportHeight = document.documentElement.clientHeight;
// Get relative mouse positions to viewport
// Original range: [0, 1]
// Should be in the range of -1 to 1, since we want to move left/right
// Transform by multipling by 2 and minus 1
// Output range: [-1, 1]
mouseX = e.pageX / viewportWidth * 2 - 1,
mouseY = e.pageY / viewportHeight * 2 - 1;
// Calculate how much to transform the image
translateX = mouseX * maxDeltaX,
translateY = mouseY * maxDeltaY;
jQuery('.cyan').css('transform', 'translate(' + translateX + 'px, ' + translateY + 'px)');
jQuery('.small-cyan').css('transform', 'translate(' + translateX + 'px, ' + translateY + 'px)');
jQuery('.small-darktangirine').css('transform', 'translate(' + translateX + 'px, ' + translateY + 'px)');
}).hover(function() {
jQuery('.cyan').css('transform', 'translate(' + translateX + 'px, ' + translateY + 'px)');
jQuery('.small-cyan').css('transform', 'translate(' + translateX + 'px, ' + translateY + 'px)');
jQuery('.small-darktangirine').css('transform', 'translate(' + translateX + 'px, ' + translateY + 'px)');
})
It's a little bit clunky and not as smooth as what I want to achieve and also I would want it to go back to its original position when not hovered.
I'm not too sure how you'd really do much more to make that function a little smoother really considering its really depending on how often jQuery itself would execute its events. For now maybe I'd consider splitting all the code within your jQuery event declaration into their own respective functions. It'll be a lot easier and cleaner for you to work on :)
function animateElementOnMouseMove() {
// your translate code
}
function animateElementOnMouseHover() {
// your initial hover animation code
}
$('.image-content-right').on('mousemove', animateElementOnMouseMove)
.on('hover', animateElementOnMouseHover);
for it to return back to the position you had it at before you could either save an original untranslated position of each of the elements OR, you could save each of the translations into a count variable, then "undo" the translations after the element has become unfocused.
like:
var elementTranslateCountX = 0;
var elementTranslateCountY = 0;
// ON TRANSLATE
elementTranslateCountX += translateX;
elementTranslateCountY += translateY;
By the looks and feel of the webflow thing (if I understand your goal correctly) you want to be able to move your object by the full maxDeltaX/Y within the hover area. If that's the case, your math needs some adjustments: you need to define an origin (the center of the moving object most likely) and normalize to [-1, 1] the hover area around the origin. Placing the object in the dead center of the hover box simplifies calculations. I'm posting the code in a snippet, but it should be run on a full page because the coordinates are not correctly calculated. Funnily enough, if I run it on codepen, it works as expected on Chrome, but not on Safari. To avoid this issue you should wrap everything in a parent div and calculate coordinates relative to it
const page = document.getElementById("page-id");
const area = document.getElementById("area-id");
const box = document.getElementById("box-id");
// we want to move the object by 50px at most
const maxDeltaX = 50;
const maxDeltaY = 50;
let pageBox = page.getBoundingClientRect();
let pageTopLeft = {
x: pageBox.x,
y: pageBox.y
};
let areaBox = area.getBoundingClientRect();
let areaRange = {
w: areaBox.width / 2.0,
h: areaBox.height / 2.0
};
let boxBox = box.getBoundingClientRect();
let transformOrigin = {
x: boxBox.x + (boxBox.width / 2.0),
y: boxBox.y + (boxBox.height / 2.0)
};
// multipliers allow the full delta displacement within the hover area range
let multX = maxDeltaX / areaRange.w;
let multY = maxDeltaY / areaRange.h;
area.addEventListener("mousemove", onMove);
area.addEventListener("mouseleave", onLeave);
window.addEventListener("resize", onResize);
// mouse coords are computed wrt the wrapper top left corner and their distance from the object center is normalized
function onMove(e) {
let dx = (((e.clientX - pageTopLeft.x) - transformOrigin.x));
let dy = (((e.clientY - pageTopLeft.y) - transformOrigin.y));
box.style.transform = "translate3d(" + (dx * multX) + "px, " + (dy * multY) + "px, 0)";
/*
// or you can add some fancy rotation as well lol
let rotationDeg = Math.atan2(dy,dx) * (180/Math.PI);
let rotationString = "rotate(" + rotationDeg + "deg)";
box.style.transform = "translate3d(" + (dx * multX) + "px, " + (dy * multY) + "px, 0) " + rotationString;
*/
}
function onLeave(e) {
box.style.transform = "translate3d(0, 0, 0)";
}
function onResize(e) {
// redefine all the "let" variables
}
* {
margin: 0;
padding: 0;
}
.page {
position: relative;
width: 100%;
height: 100vh;
background-color: #ddd;
display: grid;
place-items: center;
}
.hover-area {
position: relative;
width: 50%;
height: 100%;
background-color: #888;
}
.box {
position: absolute;
left: calc(50% - 25px);
top: calc(50% - 25px);
width: 50px;
height: 50px;
border-radius: 25px;
background-image: linear-gradient(45deg, #000, #aaa);
transform: translate3d(0, 0, 0);
transition: all 0.2s;
will-change: transform;
}
<div id="page-id" class="page">
<div id="area-id" class="hover-area">
<div id="box-id" class="box" />
</div>
</div>
Note that is runs smoother on Chrome than on Safari. I'm not sure divs and css is the best way to go here for performance.
If I misunderstood the final result, explain more and I'll try to help.
Related
I'm working on my Javascript project.
In this project I have to create some animations.
In this specific case, I have to make a ball bounce up and down.
The code below works great just from the top to the bottom, but not viceversa.
var div = document.getElementById('container-ball');
function createBall(event)
{
var x = event.clientX;
var y = event.clientY;
var newBall = document.createElement('div');
newBall.style.position = "absolute"
newBall.style.width = '15px';
newBall.style.height = '15px';
var bx = newBall.style.left = x + 'px';
var by = newBall.style.top = y + 'px';
newBall.style.borderRadius = '10px';
newBall.style.backgroundColor = 'white';
var incrementPos = 0;
var id = setInterval(bounce, 5);
function bounce()
{
incrementPos++;
by = newBall.style.top = incrementPos + y + "px";
if(by == 650 + "px")
{
clearInterval(id)
var id2 = setInterval(function bounceUp()
{
incrementPosYMax -= 650
by = newBall.style.bottom = by + "px" - incrementPosYMax
}, 5)
}`/*Function that make the ball bounce down and up(but when it came at 650 px it stopped )*/ì
} /*End of the set interval */`
div.appendChild(newBall);
}
div.addEventListener("click", createBall);
This down below is the HTML CODE
<html>
<head>
<link rel= "stylesheet" href="style/style.css">
</head>
<body>
<div id ="container-ball">
</div>
<script src="js/main.js"></script>
</body>
Working example (comments see below):
const areaHeight = 150; // it is 650px in the original question
var div = document.getElementById('container-ball');
function createBall(event) {
var x = event.clientX;
var y = event.clientY;
var newBall = document.createElement('div');
newBall.className = 'ball';
var bx = newBall.style.left = x + 'px';
var by = newBall.style.top = y + 'px';
var incrementPos = 0;
var id = setInterval(bounce, 5);
let direction = 1; // 1 = down, -1 = up
function bounce() {
incrementPos += direction;
by = newBall.style.top = incrementPos + y + "px";
if (by == areaHeight + "px" || by == y + 'px') {
direction = -direction;
}
}
div.appendChild(newBall);
}
#container-ball {
width: 300px;
height: 157px;
background: gray;
}
#container-ball .ball {
position: absolute;
width: 15px;
height: 15px;
border-radius: 10px;
background-color: white;
border: 1px solid #333;
box-sizing: border-box;
}
<div id="container-ball" onclick="createBall(event)"></div>
Click on the grey box
Now, the explanation.
I've moved ball's styles to CSS - this is easier to control in the future. So when I have a class for ball, I can write in my code: newBall.className = 'ball';
I removed incrementPosYMax because I do not really understand if you need it
I understand your 'bounce' as bounce, so my ball just fall to the floor and then return to the original position. I do not really understand if you mean that (please, comment if it is wrong).
Your program is quite small, so I do not see the need for another setInterval, so all the animation in my example is inside only one setInterval
I've added new variable direction to control the direction for the ball (1 = down, -1 = up)
I do not like the parts with by == areaHeight + "px", but I keep them for you, because you use it in your code.
This code have some bugs, that you (or me if you ask) can fix. I just need to understand that my approach is correct
How the direction works:
Take a look at this line by = newBall.style.top = incrementPos + y + "px"; here you set new "y" coordinate for the ball as sum of 'original' "y" coordinate (in y) and offset (the distance that ball moved over the time) in incrementPos. So, if you increase incrementPos, then the ball's position will be lower (because "zero" in browser is at the top left corner, bigger "y" means lower the element).
Before my change, in your code you changed the offset with this line: incrementPos++; (means you increase incrementPos by 1 on every bounce step).
To move to another direction, you need to subtract 1 on every bounce step.
To reflect the "direction" of that move, I've added direction variable (so 1 means move down, and -1 means move up)
Now the offset is changed by: incrementPos += direction; (so I add this direction, not always 1)
Sometimes we need to change the "direction" with this code: direction = -direction;
The "levels" where we need to change direction is checked by this code: if (by == areaHeight + "px" || by == y + 'px') - here we check bottom (areaHeight) and top (y - it is where user clicks the mouse)
I'm trying to create a zoomed visual box with the possibility to move the visual on the Y avis from top to bottom with the mouse following.
My problem is that I need to limit the navigation to the top and the bottom of the visual to avoid white spaces over and below the image while navigate with the mouse to the extremes.
Here is my code :
HTML
<div class="follower-container">
<img src="https://picsum.photos/400/600?image=1083" class="follower-image" alt="" />
</div>
CSS
.follower-container {
position: relative;
height: 100vh;
max-width: 600px;
overflow: hidden;
border: 1px solid blue;
}
.follower-image {
position: absolute;
top: 50%;
left: 0; right: 0;
width: 100%;
display: block;
width: 100%;
height: auto;
transform: translateY(-50%);
}
JS
var mouse = { x:0, y:0 };
var image_position = { x:0, y:0 };
// Get mouse position
function getMouse(e){
mouse.x = e.clientX || e.pageX;
mouse.y = e.clientY || e.pageY;
}
$('.follower-container').mousemove(function(e){
getMouse(e);
});
// Move visual regarding mouse position
function moveImage(){
var distY = mouse.y - image_position.y;
image_position.y += distY/5;
$('.follower-image').css({
'top': image_position.y + "px"
});
}
setInterval(moveImage, 20);
My jsfiddle : https://jsfiddle.net/balix/hc2atzun/22/
You can use a combination of Math.min() and Math.max() to restrict the position to a certain range.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/min
Math.max() will return the larger of the two passed numbers:
Math.max(0, 100); // 100
Math.max(12, 5); // 12
Math.min() will return the smaller of the two passed numbers:
Math.min(0, 100); // 0
Math.min(12, 5); // 5
By combining them, you can restrict a given number to be within a range. If it is outside of the range, it will max out (or min out) at the ends of the range:
function keepInRange(n, minNum, maxNum) {
return Math.min(Math.max(minNum, n), maxNum);
}
keepInRange(-12, 0, 100); // 0
keepInRange(30, 0, 100); // 30
keepInRange(777, 0, 100); // 100
Now, you just need to figure out the math behind what number range to keep your top CSS value inside of. I highly, highly recommend that you give a go at that yourself.
If you really can't figure out the solution on your own, here it is for this case: https://jsfiddle.net/oeynjcpL/
Animation gets even better with requestAnimationFrame ;)
var mouse = { x:0, y:0 };
var image_position = { x:0, y:0 };
// Get mouse position
function getMouse(e){
mouse.x = e.clientX || e.pageX;
mouse.y = e.clientY || e.pageY;
}
$('.follower-container').mousemove(function(e){
getMouse(e);
});
// Returns number n if is inside of minNum and maxNum range
// Otherwise returns minNum or maxNum
function keepInRange(n, minNum, maxNum) {
return Math.min(Math.max(minNum, n), maxNum);
}
// Move visual regarding mouse position
function moveImage(){
var distY = mouse.y - image_position.y;
image_position.y += distY/5;
// minHeight is the containing element's height minus half the height of the image
var minHeight = $('.follower-container').height() - $('.follower-image').height() / 2;
// maxHeight is half the height of the image
var maxHeight = $('.follower-image').height() / 2;
$('.follower-image').css({
'top': keepInRange(image_position.y, minHeight, maxHeight) + "px"
});
requestAnimationFrame(moveImage);
}
requestAnimationFrame(moveImage);
I could find similar questions involving jQuery UI lib, or only css with no handle to drag, but nothing with pure maths.
What I try to perform is to have a resizable and rotatable div. So far so easy and I could do it.
But it gets more complicate when rotated, the resize handle does calculation in opposite way: it decreases the size instead of increasing when dragging away from shape.
Apart from the calculation, I would like to be able to change the cursor of the resize handle according to the rotation to always make sense.
For that I was thinking to detect which quadrant is the resize handle in and apply a class to change cursor via css.
I don't want to reinvent the wheel, but I want to have a lightweight code and simple UI. So my requirement is jQuery but nothing else. no jQuery UI.
I could develop until achieving this but it's getting too mathematical for me now.. I am quite stuck that's why I need your help to detect when the rotation is enough to have the calculation reversed.
Eventually I am looking for UX improvement if anyone has an idea or better examples to show me!
Here is my code and a Codepen to try: http://codepen.io/anon/pen/rrAWJA
<html>
<head>
<style>
html, body {height: 100%;}
#square {
width: 100px;
height: 100px;
margin: 20% auto;
background: orange;
position: relative;
}
.handle * {
position: absolute;
width: 20px;
height: 20px;
background: turquoise;
border-radius: 20px;
}
.resize {
bottom: -10px;
right: -10px;
cursor: nwse-resize;
}
.rotate {
top: -10px;
right: -10px;
cursor: alias;
}
</style>
<script type="text/javascript" src="js/jquery.js"></script>
<script>
$(document).ready(function()
{
new resizeRotate('#square');
});
var resizeRotate = function(targetElement)
{
var self = this;
self.target = $(targetElement);
self.handles = $('<div class="handle"><div class="resize" data-position="bottom-right"></div><div class="rotate"></div></div>');
self.currentRotation = 0;
self.positions = ['bottom-right', 'bottom-left', 'top-left', 'top-right'];
self.bindEvents = function()
{
self.handles
//=============================== Resize ==============================//
.on('mousedown', '.resize', function(e)
{
// Attach mouse move event only when first clicked.
$(document).on('mousemove', function(e)
{
var topLeft = self.target.offset(),
bottomRight = {x: topLeft.left + self.target.width(), y: topLeft.top + self.target.height()},
delta = {x: e.pageX - bottomRight.x, y: e.pageY - bottomRight.y};
self.target.css({width: '+=' + delta.x, height: '+=' + delta.y});
})
.one('mouseup', function(e)
{
// When releasing handle, round up width and height values :)
self.target.css({width: parseInt(self.target.width()), height: parseInt(self.target.height())});
$(document).off('mousemove');
});
})
//============================== Rotate ===============================//
.on('mousedown', '.rotate', function(e)
{
// Attach mouse move event only when first clicked.
$(document).on('mousemove', function(e)
{
var topLeft = self.target.offset(),
center = {x: topLeft.left + self.target.width() / 2, y: topLeft.top + self.target.height() / 2},
rad = Math.atan2(e.pageX - center.x, e.pageY - center.y),
deg = (rad * (180 / Math.PI) * -1) + 135;
self.currentRotation = deg;
// console.log(rad, deg);
self.target.css({transform: 'rotate(' + (deg)+ 'deg)'});
})
.one('mouseup', function(e)
{
$(document).off('mousemove');
// console.log(self.positions[parseInt(self.currentRotation/90-45)]);
$('.handle.resize').attr('data-position', self.positions[parseInt(self.currentRotation/90-45)]);
});
});
};
self.init = function()
{
self.bindEvents();
self.target.append(self.handles.clone(true));
}();
}
</script>
</head>
<body>
<div id="all">
<div id="square"></div>
</div>
</body>
</html>
Thanks for the help!
Here is a modification of your code that achieves what you want:
$(document).ready(function() {
new resizeRotate('#square');
});
var resizeRotate = function(targetElement) {
var self = this;
self.target = $(targetElement);
self.handles = $('<div class="handle"><div class="resize" data-position="bottom-right"></div><div class="rotate"></div></div>');
self.currentRotation = 0;
self.w = parseInt(self.target.width());
self.h = parseInt(self.target.height());
self.positions = ['bottom-right', 'bottom-left', 'top-left', 'top-right'];
self.bindEvents = function() {
self.handles
//=============================== Resize ==============================//
.on('mousedown', '.resize', function(e) {
// Attach mouse move event only when first clicked.
$(document).on('mousemove', function(e) {
var topLeft = self.target.offset();
var centerX = topLeft.left + self.target.width() / 2;
var centerY = topLeft.top + self.target.height() / 2;
var mouseRelativeX = e.pageX - centerX;
var mouseRelativeY = e.pageY - centerY;
//reverse rotation
var rad = self.currentRotation * Math.PI / 180;
var s = Math.sin(rad);
var c = Math.cos(rad);
var mouseLocalX = c * mouseRelativeX + s * mouseRelativeY;
var mouseLocalY = -s * mouseRelativeX + c * mouseRelativeY;
self.w = 2 * mouseLocalX;
self.h = 2 * mouseLocalY;
self.target.css({
width: self.w,
height: self.h
});
})
.one('mouseup', function(e) {
$(document).off('mousemove');
});
})
//============================== Rotate ===============================//
.on('mousedown', '.rotate', function(e) {
// Attach mouse move event only when first clicked.
$(document).on('mousemove', function(e) {
var topLeft = self.target.offset(),
center = {
x: topLeft.left + self.target.width() / 2,
y: topLeft.top + self.target.height() / 2
},
rad = Math.atan2(e.pageX - center.x, center.y - e.pageY) - Math.atan(self.w / self.h),
deg = rad * 180 / Math.PI;
self.currentRotation = deg;
self.target.css({
transform: 'rotate(' + (deg) + 'deg)'
});
})
.one('mouseup', function(e) {
$(document).off('mousemove');
$('.handle.resize').attr('data-position', self.positions[parseInt(self.currentRotation / 90 - 45)]);
});
});
};
self.init = function() {
self.bindEvents();
self.target.append(self.handles.clone(true));
}();
}
The major changes are the following:
In the resize event, the mouse position is transformed to the local coordinate system based on the current rotation. The size is then determined by the position of the mouse in the local system.
The rotate event accounts for the aspect ratio of the box (the - Math.atan(self.w / self.h) part).
If you want to change the cursor based on the current rotation, check the angle of the handle (i.e. self.currentRotation + Math.atan(self.w / self.h) * 180 / Math.PI). E.g. if you have a cursor per quadrant, just check if this value is between 0..90, 90..180 and so on. You may want to check the documentation if and when negative numbers are returned by atan2.
Note: the occasional flickering is caused by the box not being vertically centered.
Nico's answer is mostly correct, but the final result calculation is incorrect -> which is causing the flickering BTW. What is happening is the increase or decrease in size is being doubled by the 2x multiplication. The original half-width should be added to the new half-width to calculate the correct new width and height.
Instead of this:
self.w = 2 * mouseLocalX;
self.h = 2 * mouseLocalY;
It should be this:
self.w = (self.target.width() / 2) + mouseLocalX;
self.h = (self.target.height() / 2) + mouseLocalY;
I'm trying to write a very very simple zoom plugin that should have just a button to zoom in, zoom out, and the pan function to move the image around.
For now I've writte the part to zoom in and zoom out.
My problem is that I can't find a way to center the image inside the "zoombox".
This is my code so far:
$.fn.zoom = function() {
var img = this;
img.attr("style", "-ms-transform: scale(1); -ms-transform-origin: 0% 0%; -webkit-transform: scale(1); -webkit-transform-origin: 0% 0%").wrap('<div style="width: 400px; height: 400px; overflow: hidden;" class="zoombox" data-scale="1"></div>');
$("body").on("click.zoom", ".zoomin, .zoomout", function() {
if( $(this).hasClass("zoomin") ) {
var zoomFactor = (Number(img.parent().attr("data-scale")) + 0.1).toFixed(1);
} else {
var zoomFactor = (Number(img.parent().attr("data-scale")) - 0.1).toFixed(1);
}
img.parent().attr("data-scale", zoomFactor);
console.log(zoomFactor);
img.css({"-webkit-transform": "scale(" + zoomFactor + ")", "-ms-transform":"scale(" + zoomFactor + ")"});
});
};
Fiddle:
http://jsfiddle.net/xM7r4/1/
I know the style is not the best but I'm just trying to make it works without think about the style of the code.
How can I center the image inside the box, thinking that I will have to apply a pan effect later that will change the transform-origin values?
PS: I care about compatibility only on Chrome and IE9 for now.
edit for comment
You are correct. Here I've updated to work with transform-origin. It takes the dimensions of the containing div and divides by two (to get the centerpoint of the containing div) and passes these into the image's css transform-origin property:
http://jsfiddle.net/xM7r4/23/
I've tested with different dimensioned images, and it works.
original
You'll need to move the image using margin-left and margin-top depending on if you are zooming in or out.
http://jsfiddle.net/xM7r4/21/
Since you are increasing your image by a scale of 1%, you need to move the margins accordingly, negative for zoom-in, position for zoom-out.
$("body").on("click.zoom", ".zoomin, .zoomout", function() {
var imgWidth = $(img).width();
var imgHeight = $(img).height();
var scaleWidth = Math.floor(imgWidth * 0.01);
var scaleHeight = Math.floor(imgHeight * 0.01);
if( $(this).hasClass("zoomin") ) {
var zoomFactor = (Number(img.parent().attr("data-scale")) + 0.1).toFixed(1);
moveLeft -= scaleWidth;
moveTop -= scaleHeight;
} else {
var zoomFactor = (Number(img.parent().attr("data-scale")) - 0.1).toFixed(1);
moveLeft += scaleWidth;
moveTop += scaleHeight;
}
console.log(moveTop);
console.log(moveLeft);
img.parent().attr("data-scale", zoomFactor);
console.log(zoomFactor);
img.css({"-webkit-transform": "scale(" + zoomFactor + ")", "-ms-transform":"scale(" + zoomFactor + ")", "marginLeft": moveLeft, "marginTop": moveTop});
});
How do I find out the absolute position of an element on the current visible screen (viewport) using jQuery?
I am having position:relative, so offset() will only give the offset within the parent.
I have hierarchical divs, so $("#me").parent().offset() + $("#me").offset() doesn't help either.
I need the position in the window, not the document, so when the document is scrolled, the value should change.
I know I could add up all the parent offsets, but I want a cleaner solution.
var top = $("#map").offset().top +
$("#map").parent().offset().top +
$("#map").parent().parent().offset().top +
$("#map").parent().parent().parent().offset().top;
Any ideas?
Update:
I need to get the exact gap in pixels between the top of my div and the top of the document, including padding/margins/offset?
My code:
HTML
<div id="map_frame" class="frame" hidden="hidden">
<div id="map_wrapper">
<div id="map"></div>
</div>
</div>
CSS
#map_frame{
border:1px solid #800008;
}
#map_wrapper {
position:relative;
left:2%;
top:1%;
width:95%;
max-height:80%;
display:block;
}
#map {
position:relative;
height:100%;
width:100%;
display:block;
border:3px solid #fff;
}
jQuery to resize the map to fill the screen*
var t = $("#map").offset().top +
$("#map").parent().offset().top +
$("#map").parent().parent().offset().top +
$("#map").parent().parent().parent().offset().top;
$("#map").height($(window).height() - t - ($(window).height() * 8 / 100));
Thanks...
See .offset() here in the jQuery doc. It gives the position relative to the document, not to the parent. You perhaps have .offset() and .position() confused. If you want the position in the window instead of the position in the document, you can subtract off the .scrollTop() and .scrollLeft() values to account for the scrolled position.
Here's an excerpt from the doc:
The .offset() method allows us to retrieve the current position of an
element relative to the document. Contrast this with .position(),
which retrieves the current position relative to the offset parent.
When positioning a new element on top of an existing one for global
manipulation (in particular, for implementing drag-and-drop),
.offset() is the more useful.
To combine these:
var offset = $("selector").offset();
var posY = offset.top - $(window).scrollTop();
var posX = offset.left - $(window).scrollLeft();
You can try it here (scroll to see the numbers change): http://jsfiddle.net/jfriend00/hxRPQ/
For the absolute coordinates of any jquery element I wrote this function, it probably doesnt work for all css position types but maybe its a good start for someone ..
function AbsoluteCoordinates($element) {
var sTop = $(window).scrollTop();
var sLeft = $(window).scrollLeft();
var w = $element.width();
var h = $element.height();
var offset = $element.offset();
var $p = $element;
while(typeof $p == 'object') {
var pOffset = $p.parent().offset();
if(typeof pOffset == 'undefined') break;
offset.left = offset.left + (pOffset.left);
offset.top = offset.top + (pOffset.top);
$p = $p.parent();
}
var pos = {
left: offset.left + sLeft,
right: offset.left + w + sLeft,
top: offset.top + sTop,
bottom: offset.top + h + sTop,
}
pos.tl = { x: pos.left, y: pos.top };
pos.tr = { x: pos.right, y: pos.top };
pos.bl = { x: pos.left, y: pos.bottom };
pos.br = { x: pos.right, y: pos.bottom };
//console.log( 'left: ' + pos.left + ' - right: ' + pos.right +' - top: ' + pos.top +' - bottom: ' + pos.bottom );
return pos;
}
BTW, if anyone want to get coordinates of element on screen without jQuery, please try this:
function getOffsetTop (el) {
if (el.offsetParent) return el.offsetTop + getOffsetTop(el.offsetParent)
return el.offsetTop || 0
}
function getOffsetLeft (el) {
if (el.offsetParent) return el.offsetLeft + getOffsetLeft(el.offsetParent)
return el.offsetleft || 0
}
function coordinates(el) {
var y1 = getOffsetTop(el) - window.scrollY;
var x1 = getOffsetLeft(el) - window.scrollX;
var y2 = y1 + el.offsetHeight;
var x2 = x1 + el.offsetWidth;
return {
x1: x1, x2: x2, y1: y1, y2: y2
}
}