Reputation: 2615
Is it possible to use CSS3 translate transforms with jQuery offset instead of top/left position? I currently have the setup below, but it feels a little buggy and slow. Any thoughts?
$('.artists-list-container ul li a').on('mouseenter', function() {
var image = $(this).parent('li').find('.image-container');
$(this).parent('li').addClass('active');
image.show();
$(document).mousemove(function(e) {
image.offset({
left: e.pageX,
top: e.pageY
});
});
});
I thought this might've worked but it doesn't follow the mouse.
$(document).mousemove(function(e) {
image.css({transform: 'translateX(' + e.pageX + 'px) translateY(' + e.pageY + 'px)'})
});
Upvotes: 3
Views: 2743
Reputation: 89750
As mentioned in comments, the current code translates the element by the absolute no. of pixels and if the element is already positioned at an offset from (0,0) of the document (say due to extra elements before it or margin/padding etc) then there will be that much space between the mouse pointer and the actual position of the image.
To avoid this, we must first pickup the original position of the element and then calculate the translate values with respect to that position. Below is a sample snippet.
$(document).ready(function() {
var image = $('li').find('.image-container');
var position = image.offset();
$(document).mousemove(function(e) {
image.css({
transform: 'translateX(' + (e.pageX - position.left) + 'px) translateY(' + (e.pageY - position.top) + 'px)'
})
});
})
ul {
list-style-type: none;
}
img {
vertical-align: top;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li>
<img class='image-container' src='http://lorempixel.com/200/200/nature/1' />
</li>
</ul>
The vertical-align:top;
on the img
tag plays a key part in the above code because the default is baseline
and since img
is a replaced element the offset().top
value seems to be the position of the baseline from the document (0,0) in pixels. This is what was causing the slight offset in the code I had provided in comments to the question. The problem can be seen in the below snippet.
$(document).ready(function() {
var image = $('li').find('.image-container');
var position = image.offset();
$(document).mousemove(function(e) {
image.css({
transform: 'translateX(' + (e.pageX - position.left) + 'px) translateY(' + (e.pageY - position.top) + 'px)'
})
});
})
ul {
list-style-type: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li>
<img class='image-container' src='http://lorempixel.com/200/200/nature/1' />
</li>
</ul>
Note: The above problem for some reason is seen only in Chrome.
The below snippet is a demo of the how the works properly even when extra elements are present in the DOM above it.
$(window).load(function() {
var image = $('li').find('.image-container');
var position = image.offset();
$(document).mousemove(function(e) {
image.css({
transform: 'translateX(' + (e.pageX - position.left) + 'px) translateY(' + (e.pageY - position.top) + 'px)'
})
});
})
ul {
list-style-type: none;
}
img {
vertical-align: top;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>Some content before the image</div>
<p>Some other content before the image</p>
<ul>
<li>
<img class='image-container' src='http://lorempixel.com/200/200/nature/1' />
</li>
</ul>
Another thing to note is that if there are multiple images prior to the current element in the DOM then it is best to calculate the offsets after those images are completely loaded. Else, the offset is just based on the line height of the element and not the actual height of the image. You can see the problem in the below snippet.
$(document).ready(function() {
var image = $('li').find('.image-container');
var position = image.offset();
$(document).mousemove(function(e) {
image.css({
transform: 'translateX(' + (e.pageX - position.left) + 'px) translateY(' + (e.pageY - position.top) + 'px)'
})
});
})
ul {
list-style-type: none;
}
img {
vertical-align: top;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>Some content before the image</div>
<p>Some other content before the image</p>
<img src='http://lorempixel.com/100/100/nature/1' />
<ul>
<li>
<img class='image-container' src='http://lorempixel.com/200/200/nature/1' />
</li>
</ul>
The below is the fixed version where the offsets are calculated on $(window).load()
.
$(window).load(function() {
var image = $('li').find('.image-container');
var position = image.position();
$(document).mousemove(function(e) {
image.css({
transform: 'translateX(' + (e.pageX - position.left) + 'px) translateY(' + (e.pageY - position.top) + 'px)'
})
});
})
ul {
list-style-type: none;
}
img {
vertical-align: top;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>Some content before the image</div>
<p>Some other content before the image</p>
<img src='http://lorempixel.com/100/100/nature/1' />
<ul>
<li>
<img class='image-container' src='http://lorempixel.com/200/200/nature/1' />
</li>
</ul>
Note: All snippets are verified in Chrome v50 dev-m, Opera v35, Firefox 44.0.2, IE11, Edge
Upvotes: 3