I was animating radial gradients with jQuery when I suddenly noticed something strange (check out this JSFiddle). When the mouse pointer is moved over the left side of the element the position animation is smooth, but when far to the right it isn't smooth at all (notice the jumps in position if you move your mouse slowly enough).
This feels like some kind of rounding error, but I'm not sure as to why it happens. Any ideas? I have only tested it on Google Chrome for the time being and it only happens in the horizontal direction.
CSS
html { background: #fff; }
html, body {
width: 100%;
height: 100%;
}
body { background: #000; }
JavaScript
$('body').on('mousemove', function(event) {
var x = event.pageX;
var y = event.pageY;
$(this).css('background', '-webkit-radial-gradient(' + x + 'px ' + y + 'px, transparent 10%, #000 5%)');
});
Can you replicate this or does it only happen to me?
EDIT: Works fine in Safari.
I could replicate it: like it's is said in this answer, it's not smooth because it's relying on the mousemove event only. Try to use a ticker, that's relying on time intervals. I modified your fiddle to use the ticker found in the already linked thread, here it is: http://jsfiddle.net/rh4Ljro4/
Here is the relevant javascript:
var container = $('body');
var contWidth = container.width();
var intervalId;
var mouseX, mouseY;
//this function is called 60 times per second.
function ticker(){
$(container).css('background', '-webkit-radial-gradient(' + mouseX + 'px ' + mouseY + 'px, transparent 10%, #000 5%)');
}
//this interval calls the ticker function every 16 milliseconds
intervalId = setInterval(ticker, 16); //33 millisecond is about 30 fps while 16 would be roughly 60fps
container.mousemove(function(e){
mouseX = e.offsetX; //store the current mouse position so we can reference it during the interval
mouseY = e.offsetY;
});
Related
I'm trying to position the center of a div element to the center of the mouse cursor, that will follow along its movements.
Already I came up with the code below, but the problem with this one is, that the following div is not positioned at the center of my cursor, but with some offset off the cursor.
WORKFLOW
The basic idea behind my code is, when the mouse enters the .post-entry div element, the .pointer within the current item should be displayed and follow the cursor of the mouse. When the mouse leaves the div it should be hidden.
CODE
HTML post item:
<article class="col-md-4 col-sm-6 post-entry">
<a href="#" title="">
<figure class="post-thumb">
<img src="http://placehold.it/300x300" alt="">
<div class="pointer" style="background: red;"></div>
</figure><!-- End figure.post-thumb -->
</a>
</article><!-- End article.col-md-4 post-entry -->
JS:
$('.entry .post-entry').each(function() {
$(this).on("mouseenter", mouseEnter);
$(this).on("mousemove", mouseMove);
$(this).on("mouseleave", mouseLeave);
});
function mouseEnter(event) {
console.log('enter');
var target = $(this);
var dot = target.find('.pointer');
var mX = (event.clientX);
var mY = (event.clientY);
set(
dot, {
x: mX,
y: mY,
force3D: !0
}
);
};
function mouseMove(event) {
console.log('move');
var target = $(this);
var dot = target.find('.pointer');
// var offset = target.offset();
// var width = target.width();
// var height = target.height();
// var top = offset.top;
// var left = offset.left;
var mX = (event.clientX);
var mY = (event.clientY);
$(dot).css('-webkit-transform', 'translate3d(' + mX + 'px, ' + mY + 'px, 0)');
};
function mouseLeave(event) {
console.log('leave');
var target = $(this);
var dot = target.find('.pointer');
$(dot).css('-webkit-transform', 'translate3d(0, 0, 0) scale(0, 0)');
};
function onClick(event) {
event.preventDefault();
console.log('click');
};
function set(el, obj) {
var dot = $(el).css('-webkit-transform', 'translate3d(' + obj.x + 'px, ' + obj.y + 'px, 0px)');
return dot;
};
PROBLEM / DEMO
As mentioned before, the span is following the mouse cursor, only the span is not positioned to the center of the cursor. It will be offset the mouse. See live demo here
I tried already something like this for the mX and mY variables, but with no succes:
var mX = (event.clientX - $(this).offset().left) / $(this).width() * $(this).width() - .125 * $(this).width();
var mY = (event.clientY - $(this).offsetTop) / $(this).height() * $(this).height() - .125 * $(this).width();
Also the answer from #hiEven doesn't work and will let me with the same issue:
transform: calc(mX - 50%, mY - 50%)
I know I should do something with dividing the .pointer by half, but how I should implement that in the code is a big question mark for me.
UPDATE
I created two new Codepen projects:
Use without images: http://codepen.io/anon/pen/GqGOLv. When you hover over the first item you will see that the brown pointer is correctly following your mouse cursor - what I am looking for. But when hovering over the second one, you will see the red pointer, only when you are at the very left side of the item.
When I use images: http://codepen.io/anon/pen/QExOkx. The problem by this example is that when you at the very top of the first column, you will see the brown pointer. When hover at the top left corner of the second item you will see a little piece of the red pointer, the same as the example without images.
Both pointer should follow the mouse cursor correctly. And I am searching for a solution that works with the use of an image.
Beside these two examples, when I add to the first one, an extra margin-left to the first item, the brown pointer will not be in the center of the mouse cursor, only when it's set to margin-left zero.
So I don't know what's missing and why it only works with the first example (without images) and only for the first item?
Try the code below
<html>
<head>
<style>
#mouse_div{
position: absolute;
background-color: black;
}
</style>
<script>
var div_width = 100;
var div_height = 100;
var div_x, div_y;
function mouse_position(event){
var mouse_x = event.clientX;
var mouse_y = event.clientY;
document.getElementById("mouse_div").style.width = div_width + "px";
document.getElementById("mouse_div").style.height = div_height + "px";
div_x = mouse_x - (div_width / 2);
div_y = mouse_y - (div_height / 2);
document.getElementById("mouse_div").style.left = div_x + "px";
document.getElementById("mouse_div").style.top = div_y + "px";
}
</script>
</head>
<body onmousemove="mouse_position(event)" onload="mouse_position(event)">
<div id="mouse_div"></div>
</body>
</html>
This program gets the position of your mouse, the width, and the height of the div. Then, it takes the x and subtracts the div's width divided by two from it (this centres the div's x position on your mouse). The program then does the same thing for the mouse y. Once all of the variables are defined, I use JavaScript to access the CSS of the div to place the div where it needs to be.
Note: you must make sure that the position of the div is set to absolute or the program will not work.
I assume you want the circle being center of your mouse, right?
try do this
transform: `translate(calc(${mx}px - 50%), calc(${my}px - 50%))
here is the demo
Based on my latest update, I did not conform to the correct formula that is needed to center the element .pointer to the mouse.
In order to use the following calculation within mouseMove:
var mX = (event.clientX);
var mY = (event.clientY);
Should be changed to this:
var height = dot.height();
var width = dot.width();
var offset = target.offset();
var w = target.width();
var h = target.height();
var top = offset.top;
var left = offset.left;
var mX = (event.clientX - left) - width / 2 - 15; // 15 = padding
var mY = (event.clientY - top) - height / 2;
So this formule is considering that the following DOM element .pointer will follow the mouse movements of the user. I don't know exactly why this working, but the offset from the previous item will be decreased from the current clientX coordinates, so the position of the second item is reset to zero, so the pointer will start at the left side of each item.
Here is a working demo of above code: http://codepen.io/anon/pen/AXdxZO?editors=0110
I'm trying to rotate a container with javascript and css property transform and transform-origin, the idea is to rotate it around certain coordinates (For example a pinch gesture center between the two fingers), I'm using this simple code (snippet attached) right now to rotate the container and using the onclick event to capture the anchor point. It is working properly as long as you keep clicking without moving the cursor to a different position on the container. There's an issue when you change the click position once the container has been rotated, the expected behavior is to keep track of the transformation and start rotating for that new point, however right now the container is doing an odd jump. I think that some x,y translation need to be added to the container, but i can figure out what's the correct factor to add to the container.
I'm not sure if I've illustrated well the expected behavior, to make sure here's and example: Imagine you pin a note to a surface at certain position, then, you start rotating the note, having the pin as anchor point. Now, after rotating the note a little, you put out the pin (Keeping the note at the same place), then you place the pin on a different position on the note and rotate again with that new anchor point. That's the expected behavior, hope i have explained myself well.
Here's a snippet to show it better, also available on codepen, cheers.
http://codepen.io/vasilycrespo/pen/GZeYpB
var angle = 15,
scale = 1,
origin = { x: 0, y: 0};
var transform = function (e) {
var map = document.getElementById("map");
angle += 15;
map.style.transformOrigin = e.pageX + "px " + e.pageY + "px";
map.style.transform = "rotate("+angle+"deg) scale("+ scale +")";
};
.content{
position: fixed;
top: 0px;
left: 0px;
margin-top:0;
margin-left:0;
background-color: #ccc;
width: 100%;
height: 100%;
}
.square{
position: absolute;
width: 400px;
height: 400px;
background-image: url(http://www.pnas.org/site/misc/images/15-02545.500.jpg);
background-size: cover;
}
<div class="content" onclick="transform(event)">
<div class="square" id="map"></div>
</div>
The problem is that every time you click, the div changes position based on where you click. After the first click, you should save e.pageX and e.pageY, and in the next clicks you should use the saved values. You can change your transform function to this:
var transform = (function () {
var pageX, pageY;
return function(e) {
if (typeof pageX === "undefined") {
pageX = e.pageX
pageY = e.pageY
}
var map = document.getElementById("map"), xr;
angle += 15;
map.style.transformOrigin = pageX + "px " + pageY + "px";
map.style.transform = "rotate("+angle+"deg) scale("+ scale +")";
}
}())
See updated Code Pen.
So I have been trying endlessly to try and do something similar too what this site is doing (http://whois.domaintools.com/). I'm trying to get a webpage, so wherever the mouse moves over the webpage, that kind of effect follows it (I'm sorry I don't know what I would call the effect).
I've read how to ask questions on here, but I don't know what too look for so it's difficult for me to attempt this. So far this link (http://p5js.org/learn/demos/Hello_P5_Drawing.php) I've used the code from this and played around with it but i'm just puzzled as too how I would go about doing this.
Thanks for any help, I've been banging my head against a brick wall for a good couple of days now.
This seems to be some kind of particle system. I would start the following way: First create a class for a particle, it should have a random x and y coordinate, and it should change it's postion periodically to a random new postion. Then create a lot of instances of the particle and distribute them over the page.
http://jsfiddle.net/aggoh0s1/3/
/* each particle will move in a 100px100px square */
var gutterWidth = 100;
/* class definition */
var Particle = function(x, y) {
var t = this;
t.x = x;
t.y = y;
t.elem = $('<div class="particle" />');
t.elem.css({ left: x+"px", top: y+"px"});
$('body').append(t.elem);
/* create a new position every 500-1000 milliseconds */
var milliSecs = 500 + Math.random() * 500;
t.ptinterval = setInterval(function() {
var dx = Math.round(Math.random() * gutterWidth);
var dy = Math.round(Math.random() * gutterWidth);
t.elem.animate({left: (t.x + dx)+"px", top: (t.y + dy) + "px"}, 600);
}, milliSecs);
};
/* create a 1000px1000px area where particles are placed each 100px */
var particles = [];
var newParticle;
for(var x = 0; x < 1000; x = x + gutterWidth) {
for(var y = 0; y < 1000; y = y + gutterWidth) {
newParticle = new Particle(x,y);
particles.push(newParticle);
}
}
CSS:
.particle {
width: 2px;
height: 2px;
background-color: black;
position: absolute;
}
Using this logic, you could also use a canvas to display the particles instead of a html div like it is done on whois.domaintools.com. The next step should be to connect the particles with lines to each other, and after that some code should hide all particles that are some distance away from the mouse position.
I've developed the following solution for the effect which you are referring. This is done using jQuery using the event mousemove(). Bind this event to your body where the content is.
Method :
Create an element with the following css on your body. You can create the element onthefly using jQuery as well.
<div class='hover'></div>
CSS
.hover{
position:absolute;
width:100px;
height:100px;
background-color:#fff;
}
The add the following code to your page.
$('body').mousemove(function(event){
$('.hover').css({
'top' : event.pageY,
'left': event.pageX
})
});
The above code will bind an event to your mouse move and I change the element position according to the mouse coordinates.
This fiddle shows a running example
I've given you the basic idea of the solution! You will have to medle with the css and jquery to add the looks and feels of the effect which you refer to.
See the simple example
<img id="imgMove" src="Images/img1.jpg" height="100" width="100" style="position: absolute;" />
JQuery
$(document).ready(function () {
$(document).mousemove(function (e) {
$("#imgMove").css({ "top": e.pageY - 50, "left": e.pageX - 50 }); // e.pageX - Half of Image height, width
})
})
I am trying to create and effect where you have a vertical list, and when you hover it with your mouse, a separate "cursor" div should travel up and down vertically along this list, horizontally aligned with your pointer.
I am using this code:
$(document).mousemove( function(e) {
mouseY = e.pageY;
mouseX = e.pageX;
translateY = 'translateY(' + mouseY + 'px)';
translateX = 'translateX(' + mouseX + 'px)';
});
Then with jQuery:
$(".sidebarnav").mouseover(function(){
$('.sidebarnav .cursor').css({'transform': translateY});
});
All this kind of work, but the cursor div does not perfectly align with my mouse pointer. It does if you move real slow and with precision, but it doesn't if you move a bit faster. Is there any technical reason to this lack of precision, or is my code just wrong?
Here is a jsfiddle
http://jsfiddle.net/txks3wtj/
A fiddle would definitely help. But if I understand your code correctly I believe you can't just update the .cursor's position on mouseover of the .sidebarnav - instead you need to update its position on mousemove ie all the time.
Since you don't want the cursor to move when not hovering the sidebar you'd need to keep track of whether or not it is hovered. Something like:
var isOver = false;
$('.sidebarnav').mouseover(function () {
isOver = true;
}).mouseout(function () {
isOver = false;
});
$(document).mousemove( function(e) {
mouseY = e.pageY;
mouseX = e.pageX;
translateY = 'translateY(' + mouseY + 'px)';
translateX = 'translateX(' + mouseX + 'px)';
if (isOver) {
$('.sidebarnav .cursor').css({'transform': translateY});
}
});
Untested.
Edit: It would increase performance if you cached your queries as well;
var sidebar = $('.sidebarnav');
var cursor = sidebar.find('.cursor');
Edit2: You may have better results with mouseenter and mouseleave too I think. I think over/out triggers as soon as you hover a child of the element as well.
I've set up a jsfiddle illustrating my situation: http://jsfiddle.net/j5o0w5qc/1/
Basically, I've got three nested HTML elements: a viewport div on the outside, a stage div in the middle, and a canvas on the inside. The stage div provides a perspective setting for 3d transformations applied to the canvas. The viewport has overflow: hidden; so we don't see anything outside of the viewport. It also has a listener attached, listening for mousedown.
In my actual app that I'm building, the canvas might be transformed to any arbitrary 3d transformation, involving translation and rotation in 3d space. What I would like to happen is for the viewport div to intercept a click, and draw a spot on the canvas in the place you clicked. I'm intercepting the event with the viewport div, and I'm using offsetX and offsetY in Chrome. This works great for Chrome, but I know I can't rely on offsetX and offsetY in other browsers, so I'd like to use pageX and pageY, normalized via jQuery, but I'm not sure quite how to do that.
What I've currently got in the jsfiddle works great in Chrome, except when you click in the viewport NOT on the canvas. When you click on the canvas, it draws a dot there, regardless of the canvas's transformation. Chrome is doing all the hard work and giving me exactly what I want with offsetX and offsetY. However, when you click in the viewport NOT on the canvas, I guess it's giving me offsetX and offsetY values relative to the viewport, rather than the canvas, and then interpreting that and drawing a dot on the canvas. For example, if I transform the canvas and then click in the upper right corner of the viewport, a dot appears in the upper right corner of the canvas, regardless of where that corner actually appears on the page.
In Firefox, however, it works great as long as there is no transformation applied to the canvas, but as soon as the canvas is transformed, all of a sudden, the dot being drawn is displaced, and I can't figure out how to take my pageX and pageY values and figure out exactly where in the canvas I am clicking.
Does anyone have any brilliant solutions? I've been bashing my head against this problem for far too long. I'm pretty sure I need to manually calculate some 3d transformation matrices or something, and I've spent hours writing methods to return the inverse of a matrix, and to multiply a matrix by a vector, and all sorts of stuff, but none of it has actually solved the problem for me, and I'm not sure what I'm missing.
Stackoverflow says code is required with jsfiddle links, so here's all my code:
HTML:
<div id="viewport">
<div id="stage">
<canvas id="myCanvas" width="300" height="300"></canvas>
</div>
</div>
<div id="stuff">
<button onclick="transformMe()">Transform</button>
<input id="blah" type="text" size="45"></input>
</div>
CSS:
#viewport, #stage, #myCanvas {
width: 300px;
height: 300px;
position: absolute;
top: 0;
left: 0;
}
#viewport {
border: 1px solid #000;
overflow: hidden;
}
#stage {
perspective: 1000px;
transform-style: preserve-3d;
}
#myCanvas {
background-color: green;
transform-style: preserve-3d;
}
#stuff {
position: absolute;
top: 350px;
}
Javascript:
var counter = 0;
$('#viewport').mousedown(function _drawOnCanvas (e)
{
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var xpos, ypos;
if (typeof e.offsetX=='undefined')
{
xpos = e.pageX - $('#myCanvas').offset().left;
ypos = e.pageY - $('#myCanvas').offset().top;
}
else
{
xpos = e.offsetX;
ypos = e.offsetY;
}
ctx.fillRect(xpos-5, ypos-5, 10, 10);
});
function transformMe()
{
counter++;
var angle = (counter * 30) % 360;
$('#myCanvas').css('transform','perspective(1000px) rotate3d(5,6,7,' + angle + 'deg)');
$('input').val('counter: ' + counter + ', angle: ' + angle);
};
For Firefox, you can use event.layerX and event.layerY. Think of them as Firefox's versions of offsetX & offsetY.
DEMO: http://jsfiddle.net/dirtyd77/j5o0w5qc/3/
JAVASCRIPT:
var counter = 0;
$('#viewport').mousedown(function _drawOnCanvas (e)
{
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var xpos, ypos;
if (typeof e.offsetX=='undefined')
{
xpos = e.originalEvent.layerX;
ypos = e.originalEvent.layerY;
}
else
{
xpos = e.offsetX;
ypos = e.offsetY;
}
ctx.fillRect(xpos-5, ypos-5, 10, 10);
});
function transformMe()
{
counter++;
var angle = (counter * 30) % 360;
$('#myCanvas').css('transform','perspective(1000px) rotate3d(5,6,7,' + angle + 'deg)');
$('input').val('counter: ' + counter + ', angle: ' + angle);
};
If you change viewport to myCanvas in line 3 of the either Kyle S or Dom's jsfiddles:
$('#myCanvas').mousedown(function _drawOnCanvas (e)
it no longer places a dot when you "click in the viewport NOT on the canvas."
It seems there's a new issue with Firefox - if there's a transformation it only lets you paint on half ( the bottom left of diagonal - but depends on transformation ).