Rotate element on mousemove that's larger than viewport - javascript

I'm having an issue trying to rotate an element on mousemove. I believe the issue is related to the fact that my element is much larger than the screen (set in CSS with vh/vw units) but essentially the element sound rotate, from the centre on mousemove.
I have set up a jsFiddle here:
https://jsfiddle.net/cLx290p1/
function themeLanding() {
var graphic = $('.landing-2016');
if (graphic.length > 0) {
var offset = graphic.offset();
function mouse(evt) {
var centerX = (offset.left) + (graphic.width()/2),
centerY = (offset.top) + (graphic.height()/2),
mouseX = evt.pageX,
mouseY = evt.pageY,
radians = Math.atan2(mouseX - centerX, mouseY - centerY);
degree = (radians * (180 / Math.PI) * -1) + 90;
graphic.css('-moz-transform', 'rotate(' + degree + 'deg) translate(-50%,-50%)');
graphic.css('-webkit-transform', 'rotate(' + degree + 'deg) translate(-50%,-50%)');
graphic.css('-o-transform', 'rotate(' + degree + 'deg) translate(-50%,-50%)');
graphic.css('-ms-transform', 'rotate(' + degree + 'deg) translate(-50%,-50%)');
graphic.css('transform', 'rotate(' + degree + 'deg) translate(-50%,-50%)');
}
$(document).mousemove(mouse);
}
}
themeLanding();
I have also applied the same idea, without mousemove as a debugging check, to rotate the same element using CSS3.
https://jsfiddle.net/psywr840/1/
This seems to work fine so I am unsure where I am going wrong.
Any ideas? Thanks.

When using css transform functions, the functions are processed in order from left to right.
In your case, what you want is to first translate the element and THEN rotate it. For example this answer on SO explains something similar, only instead of rotate they use scale, but the principle is the same.
So what you want, is to change the order of transform functions:
graphic.css('-moz-transform', 'translate(-50%,-50%) rotate(' + degree + 'deg)');
graphic.css('-webkit-transform', 'translate(-50%,-50%) rotate(' + degree + 'deg)');
graphic.css('-o-transform', 'translate(-50%,-50%) rotate(' + degree + 'deg)');
graphic.css('-ms-transform', 'translate(-50%,-50%) rotate(' + degree + 'deg)');
graphic.css('transform', 'translate(-50%,-50%) rotate(' + degree + 'deg)');

Related

Using translate and rotate in CSS

I'm trying to make a div rotate and move along the Y axis. I got it to work individually but not in the same time. This is the code i use to rotate the div:
function rotate_horizon_bg(deg){
var rotate = "rotate(" + (deg) + "deg);";
var tr = new Array(
"transform:" + rotate,
"-moz-transform:" + rotate,
"-webkit-transform:" + rotate,
"-ms-transform:" + rotate,
"-o-transform:" + rotate
);
var horizon_bg = document.getElementById("horizon_bg");
horizon_bg.setAttribute("style", tr.join(";"));
}
This works perfectly to rotate the div. If I make a new function to transelateY. Only transelateY will work.
Any ideas on how I can get this to work
simultaneous?
Just combine the effects. Here's a literal example:
transform: rotate(-45deg) translate(-120px,-30px)
function rotate_horizon_bg(rotateDeg, x, y){
var horizon_bg = document.getElementById("horizon_bg");
horizon_bg.style.transform = "rotate(" + rotateDeg + "deg) translate(" + x + "px, " + y + "px)";
}
rotate_horizon_bg(-30, 3, 3);
<div id="horizon_bg">Translate Me!</div>

javascript find same position in rotated element

I have a "base" reference rect (red)
Inside a rotated div (#map), I need a clone rect (yellow), it has to be same size and position of "base" rect, independent of its parent (#map) rotation.
This is where I am so far, any help would be welcoming.
http://codepen.io/christianpugliese/pen/oXKOda
var controls = { degrees: 0, rectX:125, rectY:55 };
var wBounds = document.getElementById("wrapper").getBoundingClientRect(),
mapBounds = document.getElementById("map").getBoundingClientRect(),
rectBounds = document.getElementById("rect").getBoundingClientRect();
var _x = ((mapBounds.width - wBounds.width) / 2) + $('#rect').position().left,
_y = ((mapBounds.height - wBounds.height) / 2) + $('#rect').position().top;
$('#rect').css({top: controls.rectY+'px', left:controls.rectX+'px'});
$('#mapRect').width(rectBounds.width);
$('#mapRect').height(rectBounds.height);
$('#mapRect').css({top: _y+'px',
left:_x+'px',
'transform': 'rotate('+ Math.round(-controls.degrees) +'deg)'});
$('#map').css('transform', 'rotate('+ Math.round(controls.degrees) +'deg)');
Since you're rotating the #mapRect an equal amount in the opposite direction you're getting rotation/orientation right but not the origin. The transform-origin would be the center of the #mapBounds, but relative to the #rect;
Fork of your pen: http://codepen.io/MisterCurtis/pen/vNBYZJ?editors=101
Since there is some rounding/subpixel positioning happening the yellow rect doesn't align pixel perfect.
function updateUI(){
var _x = ((mapBounds.width - wBounds.width) / 2) + $('#rect').position().left,
_y = ((mapBounds.height - wBounds.height) / 2) + $('#rect').position().top,
_ox = mapBounds.width/2 - _x, // origin x
_oy = mapBounds.height/2 - _y; // origin y
...
$('#mapRect').css({
'transform-origin': _ox + 'px ' + _oy + 'px', // now it rotates by the bounds
top: _y + 'px',
left: _x + 'px',
'transform': 'rotate(' + Math.round(-controls.degrees) + 'deg)'
});
}
Edit: Updated the pen. You'll have to ditch using Rectangle and instead use Polygon. This way you can use a plugin like https://github.com/ahmadnassri/google-maps-polygon-rotate to perform the rotation along the map center.

Javascript to transform on both X and Y axis (CSS 3D)

I am using tween to rotate on both X and Y axis. But I cant seem to find the correct javascript to edit the css.
Target is a div, and with the following code, it only rotates the axis while ignoring the Y axis.
It uses Tween JS
function update() {
target.style.left = position.x + 'px';
target.style.top = position.y + 'px';
target.style.webkitTransform = 'rotateY(' + Math.floor(position.rotation) + 'deg)';
target.style.MozTransform = 'rotateY(' + Math.floor(position.rotation) + 'deg)';
target.style.webkitTransform = 'rotateX(' + Math.floor(position.rotation) + 'deg)';
target.style.MozTransform = 'rotateY(' + Math.floor(position.rotation) + 'deg)';
}
How do I tell javascript to rotate on both and not just one axis?
You separate arguments with spaces. For example:
target.style.webkitTransform = 'rotateX(' + Math.floor(rotationX) + 'deg) rotateY(' + Math.floor(rotationY) + 'deg)';
(If you change the same property twice, you'll only overwrite previous value.)

How to zoom to mouse pointer while using my own mousewheel smoothscroll?

Part of my app contains functionality similar to google maps in that the user should be able to zoom in and out on an image within a container.
In the same way that google maps does I want the user to be able to scroll with the mousewheel and the pixel on the image to remain directly under the mousepointer at all times. So essentially the user will be zooming to wherever their mouse pointer is.
For the zooming/translating I am using css transforms like so:
visible
$('#image').css({
'-moz-transform': 'scale(' + ui.value + ') translate(' + self.zoomtrans.xNew + 'px, ' + self.zoomtrans.yNew + 'px)',
'-moz-transform-origin' : self.zoomtrans.xImage + 'px ' + self.zoomtrans.yImage + 'px',
'-webkit-transform': 'scale(' + ui.value + ') translate(' + self.zoomtrans.xNew + 'px, ' + self.zoomtrans.yNew + 'px)',
'-webkit-transform-origin' : self.zoomtrans.xImage + 'px ' + self.zoomtrans.yImage + 'px',
'-o-transform': 'scale(' + ui.value + ') translate(' + self.zoomtrans.xNew + 'px, ' + self.zoomtrans.yNew + 'px)',
'-o-transform-origin' : self.zoomtrans.xImage + 'px ' + self.zoomtrans.yImage + 'px',
'-ms-transform': 'scale(' + ui.value + ') translate(' + self.zoomtrans.xNew + 'px, ' + self.zoomtrans.yNew + 'px)',
'-ms-transform-origin' : self.zoomtrans.xImage + 'px ' + self.zoomtrans.yImage + 'px',
'transform': 'scale(' + ui.value + ') translate(' + self.zoomtrans.xNew + 'px, ' + self.zoomtrans.yNew + 'px)',
'transform-origin' : self.zoomtrans.xImage + 'px ' + self.zoomtrans.yImage + 'px'
});
I have managed to find various implementations of how to go about doing this however I am using a self rolled smooth scroll technique to interpolate the mouse events and provide momentum.
Trying to get the two to work correctly together is proving troublesome.
Rather than paste a whole load of code here I have created a jsFiddle that includes the mousewheel smoothscroll technique along with the zoom function that I have so far.
http://jsfiddle.net/gordyr/qGGwx/2/
This is essentially a fully functioning demo of this part of my app.
If you scroll the mousewheel you will see that the jqueryui slider interpolates and provides the momentum/deceleration correctly. However the zoom does not react correctly.
If you scroll your mousewheel only one point the zoom works perfectly but any further scrolls do not work. I assume this is because the scale of the image has now changed causing the calculations to be incorrect. I have attempted to compensate for this but have not had any luck so far.
So to summarise, I would like to modify the code in my jsFiddle so that the image zooms directly to the mousepointer at all times.
Huge thanks in advance to anyone willing to help.
You can do it more easily with css3 transitions.
Exemple: http://jsfiddle.net/BaliBalo/xn75a/
The main center-mouse algorithm is here:
//Center the image if it's smaller than the container
if(scale <= 1)
{
currentLocation.x = im.width() / 2;
currentLocation.y = im.height() / 2;
}
else
{
currentLocation.x += moveSmooth * (mouseLocation.x - currentLocation.x) / currentScale;
currentLocation.y += moveSmooth * (mouseLocation.y - currentLocation.y) / currentScale;
}
If you want to keep your already-existing code, I think you can do it approximatively the same: the trick to get the mouse position on the zoomed image when the image itself is zoomed by a css scale transform and using transform-origin is to substract the transform origin to your point then multiply by the factor and finally re-add the transform-origin.
You can also use translate as in this updated example: http://jsfiddle.net/BaliBalo/xn75a/15/
As you can see it even simplifies the formulas but don't forget to sub the center of the element to mouse point as a translation of {0, 0} will zoom on the middle of the image.
EDIT:
I stumbled on this answer of mine today and figured it wasn't really the behaviour you wanted. I didn't understand correctly the first time that you didn't want to center the point under the mouse but to keep it in the same spot. As I did it recently for a personnal project I tried to correct myself. So here is a more accurate answer :
http://jsfiddle.net/BaliBalo/7ozrc1cq/
The main part is now (if currentLocation is the top-left point of the image in the container) :
var factor = 1 - newScale / currentScale;
currentLocation.x += (mouseLocation.x - currentLocation.x) * factor;
currentLocation.y += (mouseLocation.y - currentLocation.y) * factor;
I also removed the CSS transition. I think the best way to achieve a smooth scrolling would be to use requestAnimationFrame to create an animation loop and instead of changing values directly in the zoom function, store values and animate towards them in the loop.

Safari: Absolutely positioned DIVs not moving when updated via DOM

I'm trying to animate some absolutely positioned DIVs on the page using JS to update the top and left CSS properties. Not a problem in Chrome, FF and even IE8 but try it in Safari 5 they just sit there... Weirdly if I resize the Safari window they will update their position...
You can see a demo of it here:
http://www.bikelanebrakes.com/tests/flyingThing1.html
The code that updates the DIVs is really simple, just a bit of jQuery (also updates the rotation, that works just fine in Safari):
$(this.elem).css({
'-webkit-transform': 'rotate(' + (( this.angle * (180 / Math.PI) ) * -1) +'deg)',
'-moz-transform': 'rotate(' + (( this.angle * (180 / Math.PI) ) * -1) +'deg)',
'transform': 'rotate(' + (( this.angle * (180 / Math.PI) ) * -1) +'deg)',
'left': Math.round(this.xPos) + 'px',
'top': Math.round(this.yPos) + 'px'
});
I've added position:relative to the body... I added the 'px' and rounded down the numbers incase Safari didn't like the long floating point values... Nothing, they just sit there until the window is resized...
Any thoughts or help, much-o appreciated!
Thanks,
Chris.
Replace the top and left properties with the translate sub-property of the transform CSS property. For me, this solved the problem in Safari 5.
Also, do not use string arguments for setInterval.
Demo: http://jsbin.com/okovov/2/
Bird.prototype.draw = function() {
var transform = 'rotate(' + (( this.angle * (180 / Math.PI) ) * -1) +'deg)' +
' translate('+ this.xPos + 'px,' + this.yPos + 'px)';
$(this.elem).css({
'-webkit-transform': transform,
'-moz-transform': transform,
'transform': transform
});
};
// ...
var timer1 = setInterval(function(){bird1.animate();}, 50);
var timer2 = setInterval(function(){bird2.animate();}, 50);
var timer3 = setInterval(function(){bird3.animate();}, 50);
50 milliseconds is a very small delay. Consider optimizing your functions, to make the animation perform smoother, for example, by replacing jQuery methods with vanilla JavaScript:
Bird.prototype.draw = function() {
this.elem.style.cssText = ['-webkit-', '-moz-', '', ''].join(
'transform:rotate(' + (( this.angle * (180 / Math.PI) ) * -1) +'deg)' +
' translate(' + this.xPos + 'px,' + this.yPos + 'px);'
); // Result: -webkit-transform: ...;-moz-transform:...;transform:...;
};
I know this might not be exactly what you're looking for, but you could try using jQuery's .offset() to change their position, as opposed to manually changing their CSS attributes. Your code would look something like this:
$(this.elem).css({
'-webkit-transform': 'rotate(' + (( this.angle * (180 / Math.PI) ) * -1) +'deg)',
'-moz-transform': 'rotate(' + (( this.angle * (180 / Math.PI) ) * -1) +'deg)',
'transform': 'rotate(' + (( this.angle * (180 / Math.PI) ) * -1) +'deg)'
})
.offset({
top: Math.round(this.yPos),
left: Math.round(this.xPos)
});
I hope that helps!
PS: if you're looking to set the relative position, use jQuery's .position().

Categories