Hover not working on animated div - javascript

I've made a codepen with a Tweenlite animated box. The box has a :hover in the css.
If you place your mouse in the path of the animated box so it 'hits' the mouse you see the hover effect doesn't happen. It only happens when the mouse moves on it.
How do I fix that?
http://codepen.io/anon/pen/EfAGn
.box {
width:50px;
height:50px;
position:relative;
margin-bottom:2px;
}
.red {
background-color:red;
}
.red:hover{
background-color: white;
}

Here's a continuation of Jcubed's answer:
Essentially it's calculating the mouses position and checking it against the objects position, and then seeing if the distance between the two is less than 25px every 100ms.
If the object is less than 25px (half of the object width) then it is inside of it and will add the hover class. If it is greater it will remove the hover class.
CodePen
(function() {
$("#restart").on("click", function() {
tl.restart();
})
var mX, mY, distance, mousePosition,
$distance = $('#distance span'),
$element = $('#redBox');
// Movement
var tl = new TimelineLite()
tl.to($element, 15, {x:550});
setInterval(function() {
function calculateDistance(elem, mouseX, mouseY) {
return Math.floor(Math.sqrt(Math.pow(mouseX - (elem.offset().left+(elem.width()/2)), 2) + Math.pow(mouseY - (elem.offset().top+(elem.height()/2)), 2)));
}
$(document).mousemove(function(e) {
mX = e.pageX;
mY = e.pageY;
mousePosition = (mX, mY);
$distance.text(distance);
});
distance = calculateDistance($element, mX, mY);
if(distance < 25){
console.log("Mouse Has Entered");
//adding hovered class
$($element).addClass('hovered');
}
else{
// removing hovered class
$($element).removeClass('hovered');
}
// Setting Timeout
}, 100);
})();
Here's some additional information from Chris Coyier

This is probably a browser bug. It seems that the browser is only reevaluating the hover state when the mouse moves, not when the animation changes.
What you'll need to do is probably to manually check where the box is and where the user's mouse is every frame, and update the css through JavaScript instead of relying on :hover.
Or, simply wait for browsers to fix this bug. As #Pondwater pointed out, it works in firefox 30.

Related

Position mouse pointer on line/path in Raphael

In this jsFiddle I have a Raphael canvas with a line/path where the user can drag it.
Problem is that the exact top pixel of the mouse pointer has to be on the line to be able to drag it. I need Raphael to be more "forgiving" and let the user drag the line if it touches the top, say, 3 pixels of the pointer.
I added mouseover/mouseout events to actually tell when the mouse is over the line, otherwise is very difficult to know.
So the question is: is there a way to drag the line without having to accurately position the mouse pointer?
var paper = Raphael("canvas", 600, 600);
var line = this.paper.path('M10 10 L50 50');
var start = function () {
this.odx = 0;
this.ody = 0;
},
move = function (dx, dy) {
this.translate(dx - this.odx, dy - this.ody);
this.odx = dx;
this.ody = dy;
},
up = function () {};
line.drag(move, start, up);
line.mouseover(function(e) {
line.attr('stroke', 'red');
});
line.mouseout(function(e) {
line.attr('stroke', 'black');
});
The Raphael library creates an SVG element (not a CANVAS element) for drawing purposes, and drawing lines creates PATH elements within the SVG element.
I had some success by increasing the line width on hover. You do have to trigger the width change by at least moving the mouse over the line, but it is a lot easier to select afterwards. While the visual effect produced may not be perfect (some width flicking can occur) continuing the drag operation proceeds without problem.
You could try increasing the line width by either modifying the stroke-width attribute in the mouseover and mouseout handlers:
line.mouseover(function(e) {
line.attr('stroke', 'red');
line.attr('stroke-width', '9')
});
line.mouseout(function(e) {
line.attr('stroke', 'black');
line.attr('stroke-width', '1')
});
or by modifying the stroke-width styling when hovering over all path elements in CSS using
path:hover {
stroke-width: 9;
}
According to this question Raphael does not provide an abstraction of all possible CSS styling so it could use VML in XML documents. If you can live without IE 9 support and expect users to have a modern browser you may be interested in the following snippet which
increases the width of a line on mouseover by setting class name draggable, which sets a move cursor,
when a move is started replaces draggable with dragging ( which removes the width increase) and sets a move cursor over the svg container,
removes class draggable, but not dragging, on mouseout
cleans up classes and the cursor in the up function.
"use strict";
var container = document.getElementById("canvas");
var paper = Raphael("canvas", 600, 600);
var line = this.paper.path('M10 10 L50 50');
var start = function () {
this.odx = 0;
this.ody = 0;
},
move = function (dx, dy) {
this.translate(dx - this.odx, dy - this.ody);
this.odx = dx;
this.ody = dy;
if( this.node.classList.contains("draggable")) {
this.node.classList.replace("draggable", "dragging");
container.classList.add("moveCursor");
}
},
up = function (e) {
line.node.classList.remove("draggable", "dragging");
container.classList.remove("moveCursor");
};
line.drag(move, start, up);
line.mouseover(function(e) {
if(!e.target.classList.contains("dragging")) {
e.target.setAttribute("class", "draggable");
}
});
.draggable:hover {
stroke-width: 9;
stroke:red;
cursor: move;
}
path.dragging {
stroke: red;
}
.moveCursor{
cursor:move;
}
svg {
border: thin solid blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
<!-- body-html -->
<div id="canvas"></div>
The code is basically for demonstration - if you drag a line outside the SVG element you can't see it or grab it to drag back.
Some CSS rules (i.e. path.dragging) needed additional specificity to take priority over the defaults supplied by Raphael.

Click through element but detect hover

I have an element that sits in the middle of my page as a sensor. That element is larger than the area underneath it, which contains links. I need to be able to both register when the mouse is over / moves over the sensor, as well as click the links below.
I've looked on SO, but I can't find a solution that works for my issue given that I need this circular sensor to float in the middle of the page.
#css
.sensor {
pointer-events: none; # does not register jquery's mouseenter, but allows the links to be clicked
}
#javascript
$('.sensor').mouseenter(doStuff) #only works if pointer events are enabled
Here's a fiddle of a basic mockup:
http://jsfiddle.net/3rym41ra/
Thanks in advance.
I placed a circular sensor on the page which changes the background when hovered.
Since the sensor now is a parent of the links, all events will bubble up. You can click on the elements while still using certain areas of the sensor as you like
$('body').mousemove(function(e) {
// We want it circular on page so we take 50% left and 50% top as the middle
// Cirle has radius = 100px
var middle = {
x: $(window).width() / 2,
y: $(window).height() / 2
}; // Our middle-point
var normalize = {
x: middle.x - e.pageX,
y: middle.y - e.pageY
}; // mouse-position relative to the middle
var radius = 100; // radius
// some Math
if (normalize.x * normalize.x + normalize.y * normalize.y < radius * radius) {
// inside circle
$('body').css('background', 'red');
} else {
// outside
$('body').css('background', 'white');
}
})
body,
html {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
One
Two
Three
Four

Stuttering orbit animation when updating css through javascript over small interval

I'm trying to make some DOM element rotate smoothly around a fixed point. I'm writing this from scratch using jQuery and no matter what update speed I choose for the setInterval or how small I go with the amount of degrees the orbit advances on each loop, I get this janky staircase animation effect. I've tried using jquery's .animate instead of the .css hoping it would smooth things out but I cant seem to get it to work. Any help is appreciated.
In other words, it's not as smooth as rotating an image in HTML5 canvas. I want to make it smoother.
Here is a jsFiddle demonstrating the issue.
Notice how the animation is not quite smooth?
For reference, here is the code:
HTML
<div id="div"></div>
<div class="dot"></div>
<button class="stop">STOP</button>
<button class="start">START</button>
CSS
#div{
position:absolute;
height: 20px;
width: 20px;
background-color: #000;
}
.dot{
position:absolute;
width: 5px;
height: 5px;
background-color: #000;
}
button{
position:absolute;
}
.stop{
top:200px;
}
.start{
top:225px;
}
THE ALL IMPORTANT JAVASCRIPT
$(document).ready(function(){
$('#div').data('angle', 90);
var interval;
$('.stop').on('click', function(){
if(interval){
clearInterval(interval);
interval = undefined;
}
});
$('.start').on('click', function(){
if(!interval){
interval = setBoxInterval();
}
});
interval = setBoxInterval();
});
function drawOrbitingBox(degrees){
var centerX = 100,
centerY = 100,
div = $('#div'),
orbitRadius = 50;
//dot might not be perfectly centered
$('.dot').css({left:centerX, top:centerY});
//given degrees (in degrees, not radians), return the next x and y coords
function coords(degrees){
return {left:centerX + (orbitRadius * Math.cos((degrees*Math.PI)/180)),
top :centerY - (orbitRadius * Math.sin((degrees*Math.PI)/180))};
}
//increment the angle of the object and return new coords through coords()
function addDegrees(jqObj, degreeIncrement){
var newAngle = jqObj.data('angle') + degreeIncrement;
jqObj.data('angle', newAngle);
return coords(newAngle);
}
//change the left and top css property to simulate movement
// I've tried changing this to .animate() and using the difference
// between current and last position to no avail
div.css(addDegrees(div, degrees), 1);
}
function setBoxInterval(){
var interval = window.setInterval(function(){
drawOrbitingBox(-0.2); //This is the degree increment
}, 10); //This is the amount of time it takes to increment position by the degree increment
return interval;
}
I'd rather not resort to external libraries/plugins but I will if that's the accepted way of doing this kind of stuff. Thank you for your time.
That's because the value you set for top and left properties is rounded up. You should try using CSS Transforms.
Combining CSS Animations/Transitions and CSS Transforms you should also be able to get the animation without JavaScript.
Oh, I run into that myself!
There is actually nothing you can do, the stuttering you see is the pixel size. The pixel is the minimal step for css based animations, you can't do "half pixels" or "0.2 pixels". You will see that the same keeps happening with css3 animations.
The only solution is to speed up your animation, i'm afraid.
Also, cosndsider using rquestAnimationFrame instead of interval: https://developer.mozilla.org/en-US/docs/Web/API/window.requestAnimationFrame

mouseMove very laggy

I am trying to obtain the image effect that 99designs is obtaining when hovering a mouse over a design.. [99designs.ca] Logo design contest: Runningbug Needs Logo 220626
I am currently obtaining the position of the mouse on mousemove, then using that to move my popover <img>, and everything works fine, but it is very laggy.. and presumably its from so many calls being made.
To get the position of the mouse:
jQuery(document).ready(function(){
//$("#special").click(function(e){
$(".imgWrap").mousemove(function(e){
//$('#status2').html(e.pageX +', '+ e.pageY);
//alert(e.pageX + ', ' + e.pageY);
mouseX = e.pageX;
mouseY = e.pageY;
});
})
I'm not sure of another way I can do this.. any ideas?
On the full course of events is the following:
User mouses over an img tag.
I get the position of the mouse as per above.
The <img> tag also calls a js function which changes the position of an img tag to the position of the mouse.
Actually, you can check it here: pokemonsite
update: I see there is a bounty placed (thanks !). I'm a little busy at the moment and can't check all the other answers, but I'll make sure to check them asap
There are several ways to improve performance when using mousemove events.
Use backface-visibility: hidden on popover element to force hardware acceleration. Same thing can be achived with transform: translate3d(0,0,0) but that makes difficult to use CSS transform function (see point #2).
Use CSS transform function for absolute positioning to avoid repaints but keep popover element absolute or fixed positioned.
When setting inline CSS via JS use requestAnimationFrame to avoid unnecesary layout trashing.
(maybe, optionally) hide cursor when hovering and use popover element as position indicator.
Move everything you can from JS to CSS ie. :hover state can be used to toggle display of popover element.
I made demo example combining all things listed. There is still some latency between cursor position and popover image and none of example links in original question work so I can't compare against it but I hope someone finds this useful.
DEMO
<div id="imgHolder" class="imgHolder">
<img src="//placehold.it/200x200" alt="" />
</div>
<div id="bigImgHolder" class="imgHover">
<img src="//placehold.it/500x500" alt="" />
</div>
.imgHover {
display: none;
backface-visibility: hidden;
position: fixed;
top: 0; left: 0;
pointer-events: none;
}
.imgHolder:hover ~ .imgHover { display: block; }
// uncomment if it makes sense
//.imgHolder:hover { cursor: none; }
var posX, posY;
$('#imgHolder').mousemove(HoverImg);
function HoverImg(e) {
posX = e.pageX;
posY = e.pageY;
window.requestAnimationFrame(showBigImg);
}
function showBigImg() {
$('#bigImgHolder').css({'-webkit-transform': 'translateX(' + posX + 'px) translateY(' + posY + 'px)', 'transform': 'translateX(' + posX + 'px) translateY(' + posY + 'px)' });
}
references:
http://davidwalsh.name/translate3d
http://www.paulirish.com/2012/why-moving-elements-with-translate-is-better-than-posabs-topleft/
https://css-tricks.com/using-requestanimationframe/
Try this:
jQuery(document).ready(function(){
$(".imgWrap").mousemove(function(e){
e.stopPropagation();
mouseX = e.pageX;
mouseY = e.pageY;
});
})
Use e.offsetX and e.offsetY or (recommended) e.clientX and e.clientY instead of pageX and pageY. Maybe this will be a better solution. Note: offsetx and offsety do not work in Firefox as far as I know.
If the absolute (x, y) position is not so important (meaning: some pixel-values can be omitted without destroying your logic), you could try to skip some frames of your mousemove-event.
var globalSkipCounter = 0;
var globalSkipRate = 5;
$(".imgWrap").mousemove(function(e){
if(globalSkipCounter >= globalSkipRate){
var mouseX = e.pageX;
var mouseY = e.pageY;
do_stuff(mouseX, mouseY);
globalSkipCounter = 0;
}
else{
globalSkipCounter+=1;
}
});
This way, you omit redrawing your image on every mousemove-event, instead your draw-routines (do_stuff) are only invoked, once every 5 events.
Cache the position and wrap the update in an if(oldpos !== newpos) type check (remembering to update oldpos inside it).
Use requestAnimationFrame to handle the update - if you have a normal function and pass that as the callback then it will only get called once per frame (ie, don't use an anonymous function).
Finally make use of transform:translate(x,y) to set the position and make better use of the GPU etc. Related to this, there's no harm in making use of the css will-change keyword if you want to use top/left instead.
try a maximum event per second based approach:
jQuery(document).ready(function(){
var now, then, delta, interval = 1000/60; //maximum 60 eps, you can change
//$("#special").click(function(e){
$(".imgWrap").mousemove(function(e){
now = Date.now();
delta = now - then;
if (delta > interval) {
then = now - delta % interval; //subtract extra waited time
//$('#status2').html(e.pageX +', '+ e.pageY);
//alert(e.pageX + ', ' + e.pageY);
mouseX = e.pageX;
mouseY = e.pageY;
// do your thing
}
});
})
EDIT: didn't know then is a reserved word in javascript, you can rename it.
I have a sortable listview in angular, but dragging items with the mouse in chrome was lagging a lot. I tried disabling all chrome extensions and now the lag is totally gone.
It turned out that TamperMonkey was causing it.
Instead of using "left" or "right" property of CSS rather try to use
"transform" I was trying to do the same thing as asked in question, and using "transform" worked for me like a charm.
for example:
transform: translate(30px);
this will move the element, 30px to the right.
by using transform we can move elements to left, right, top and bottom according to the need.

How to drag a DIV so its width and height are negative values?

I'm a bit stumped here. I am developing a feedback utility that will allow the user to "draw" boxes on a web page to highlight problem areas. Right now I have an overlay DIV that fills the screen and jQuery allows you to draw red outlined DIVs by clicking and dragging.
Here is the JS:
{
var $feedbackOverlay = jQuery('#feedbackOverlay');
var $original = { top: 0, left:0 };
$feedbackOverlay.bind('mousedown', function (e)
{
jQuery('<div id="currentHighlight"></div>')
.css('width', '1px')
.css('height', '1px')
.css('border', 'solid 3px #ff0000')
.css('border-radius', '5px')
.css('position', 'absolute')
.css('left', e.pageX)
.css('top', e.pageY)
.css('z-index', '8000001')
.appendTo('body');
$original = { top: e.pageY, left: e.pageX };
});
$feedbackOverlay.bind('mousemove', function (e)
{
var $currentHighlight = jQuery('#currentHighlight');
if ($currentHighlight.length > 0)
{
var $pos = { top: e.pageY, left: e.pageX };
if($pos.top < $original.top) $currentHighlight.css('top', $pos.top);
if ($pos.left < $original.left) $currentHighlight.css('left', $pos.left);
$currentHighlight.height(Math.abs($pos.top - $original.top));
$currentHighlight.width(Math.abs($pos.left - $original.left));
}
});
$feedbackOverlay.bind('mouseup', function (e)
{
var $currentHighlight = jQuery('#currentHighlight');
$currentHighlight.removeAttr('id');
});
var $feedbackInstructions = jQuery('#feedbackInstructions');
$feedbackInstructions.fadeIn(1000, function ()
{
setTimeout(function ()
{
$feedbackInstructions.fadeOut(1000);
}, 3000);
});
$feedbackOverlay.height(jQuery(document).height());
});
Here is a jsFiddle for the above:
http://jsfiddle.net/Chevex/RSYTq/
The problem is that I can't drag the boxes up or left. The first click puts the top left corner where the mouse clicked. After that subsequent dragging will change the width of the box. Letting go of the mouse completes the box and you may then start drawing another one. If you try to drag the DIV left or up while drawing it's width will remain at 0 but won't go negative.
Here you can find working solution: http://jsfiddle.net/RSYTq/34/
Something like this will get you closer to what you want: http://jsfiddle.net/RSYTq/18/
Doesn't quite handle move up and to the left and then switching to moving down and to the right quite right yet but it gives you the idea.
There's no such thing a a negative width - these are not coorindinates. You need to reposition and recalculate the corner positions relative to the corner that's not being moved.
Sounds like you need to check if the click origin (x,y) is > than the current mouse position, and then swap which one you use for the CSS top-left.
You would need to track the original start point somewhere (variables, data attributes on #currentHighlight, wherever you want), and check for width or height < 0. When so, set the #currentHighlight left/top CSS to be offset by original + (e.pageX - $currentHighlight.position().left) (for example). Then set the #currentHighlight width/height to the same difference (but positive: (e.pageX - $currentHighlight.position().left) * -1).

Categories