I made a little function that allows to click on a text element which then flys (animated top/left offset with absolute position) to a specific location and disappears.
Here is a fiddle of the problem.
Here is my code from the click handler (in coffescript):
var hoveringSelection = $ "<div class='flying cm-variable'>#{selection}</div>"
var dropdownToggle = $ '#watchlist-dropdown'
hoveringSelection.css({
position: 'absolute'
top: window.mouse.y
left: window.mouse.x
display: 'block'
opacity: 1
})
.appendTo('body')
.animate({
top: dropdownToggle.offset().top
left: dropdownToggle.offset().left
opacity: 0.0
},
{
duration: 1500
easing: 'easeOutCubic'
complete: () ->
hoveringSelection.remove()
updateQueueSize()
}
as you can see it should be at opacity 0 and then removed. The problem is that it shows for a split second (with a ~50% chance) before it gets removed.
I tested it with alerts before the .remove() is called so that the javascript execution halts, but it still did it before the alert was executed. Therefore the issue has to appear right before the completion callback of animate() is called.
I could not observe such behaviour in Firefox.
How can I avoid it?
I have seen that this is a bug (http://www.brycecorkins.com/blog/jquery-fadein-opacity-bug-in-chrome-and-ie-8/). The problem is the opacity. I made a few changes to your script to get your goal. At the end of animation I set opacity to 0.01 and then on complete I execute function that remove the element. I hope that this help you.
http://jsfiddle.net/XjesX/1/
$(function () {
$.extend($.easing, {
easeOutCubic: function (x, t, b, c, d) {
return c * ((t = t / d - 1) * t * t + 1) + b;
}
});
var mouseListener = function (event) {
if (!window.mouse) window.mouse = {
x: 0,
y: 0
};
window.mouse.x = event.clientX || event.pageX;
window.mouse.y = event.clientY || event.pageY;
};
document.addEventListener('mousemove', mouseListener, false);
var fly = function() {
var hoveringSelection = $("<div class='flying'>A word</div>");
var dropdownToggle = $('#flytome');
hoveringSelection.css({
position: 'absolute',
top: window.mouse.y,
left: window.mouse.x,
display: 'block',
opacity: 1.0
})
.appendTo('body')
.animate({
top: dropdownToggle.offset().top,
left: dropdownToggle.offset().left,
opacity: 0.01
}, 1500, 'easeOutCubic' ,function(){
alert($('.flying').length);
$('.flying').remove();
alert($('.flying').length);
});
};
$('#flyBtn').click(fly);
});
I have added alert($('.flying').length); before and after remove to show that that element is removed from the DOM. If you remove that 2 lines you'll see in a better way that there is no flickering effect.
Related
I want to create the effect similar to the old mouse trails where the div is delayed but follows the cursor.
I have come reasonably close by using set interval to trigger an animation to the coordinates of the cursor.
$("body").mousemove(function (e) {
if (enableHandler) {
handleMouseMove(e);
enableHandler = false;
}
});
timer = window.setInterval(function(){
enableHandler = true;
}, 250);
function handleMouseMove(e) {
var x = e.pageX,
y = e.pageY;
$("#cube").animate({
left: x,
top: y
}, 200);
}
JSFiddle
There are two problems that remain now:
The 'chasing' div is very jumpy (because of the required use of set interval)
If the mouse move stops before the animation is triggered, the div is left in place, away from the cursor.
I did it slightly differently. Instead of using setInterval (or even setTimeout) - I just made the animation take x amount of milliseconds to complete. The longer the animation, the less responsive the following div will seem to be.
The only problem I notice is that it gets backed up if the mouse is moved a lot.
$(document).ready(function () {
$("body").mousemove(function (e) {
handleMouseMove(e);
});
function handleMouseMove(event) {
var x = event.pageX;
var y = event.pageY;
$("#cube").animate({
left: x,
top: y
}, 1);
}
});
https://jsfiddle.net/jvmravoz/1/
Remove SetInterval and add a $("#cube").stop(); to stop the old animation based on old (x,y) so you can start a new "faster" one.
$(document).ready(function() {
$("body").mousemove(function (e) {
$("#cube").stop();
handleMouseMove(e);
});
function handleMouseMove(event) {
var x = event.pageX,
y = event.pageY;
$("#cube").animate({
left: x,
top: y
}, 50);
}
});
Working example
https://jsfiddle.net/jabnxgp7/
Super late to the game here but I didn't really like any of the options for adding a delay here since they follow the mouse's previous position instead of moving towards the mouse. So I heavily modified the code from Mike Willis to get this -
$(document).ready(function () {
$("body").mousemove(function (e) {
mouseMoveHandler(e);
});
var currentMousePos = { x: -1, y: -1 };
function mouseMoveHandler(event) {
currentMousePos.x = event.pageX;
currentMousePos.y = event.pageY;
}
mouseMover = setInterval(positionUpdate, 15);
function positionUpdate() {
var x_cursor = currentMousePos.x;
var y_cursor = currentMousePos.y;
var position = $("#cube").offset();
var x_box = position.left;
var y_box = position.top;
$("#cube").animate({
left: x_box+0.1*(x_cursor-x_box),
top: y_box+0.1*(y_cursor-y_box)
}, 1, "linear");
}
});
-----------------------------------------------------------------------
body { overflow:hidden; position:absolute; height:100%; width:100%; background:#efefef; }
#cube {
height:18px;
width:18px;
margin-top:-9px;
margin-left:-9px;
background:red;
position:absolute;
top:50%;
left:50%;
}
.circleBase {
border-radius: 50%;
}
.roundCursor {
width: 20px;
height: 20px;
background: red;
border: 0px solid #000;
}
https://jsfiddle.net/rbd1p2s7/3/
It saves the cursor position every time it moves and at a fixed interval, it updates the div position by a fraction of the difference between it and the latest cursor position. I also changed it to a circle since the circle looked nicer.
One concern here is that it triggers very often and could slow down a weak machine, reducing the update frequency makes the cursor jump more than I'd like, but maybe there's some middle ground between update frequency and jumpiness to be found, or using animation methods I'm not familiar with to automate the movement.
Here is a solution that might mimic the mouse-trail a bit more because it is only remembering the last 100 positions and discarding older ones which kind of sets the length of the mouse trail.
https://jsfiddle.net/acmvhgzm/6/
$(document).ready(function() {
var pos = new Array();
$("body").mousemove(function (e) {
handleMouseMove(e);
});
timer = window.setInterval(function() {
if (pos.length > 0) {
$('#cube').animate(pos.shift(),15);
}
}, 20);
function handleMouseMove(event) {
var x = event.pageX,
y = event.pageY;
if (pos.length = 100) {
pos.shift();
}
pos.push({'left':x, 'top':y});
}
});
Old mouse-trail feature used a list of several windows shaped like cursors which updated their positions with every frame. Basically, it had a list of "cursors" and every frame next "cursor" in list was being moved to current cursor position, achieving effect of having every fake cursor update its own position with a delay of fake cursors - 1 frames.
Smooth, on-demand delayed movement for a single object can be simulated using requestAnimationFrame, performance.now and Event.timeStamp. Idea is to hold mouse events in internal list and use them only after specific time passed after their creation.
function DelayLine(delay, action){
capacity = Math.round(delay / 1000 * 200);
this.ring = new Array(capacity);
this.delay = delay;
this.action = action;
this._s = 0;
this._e = 0;
this._raf = null;
this._af = this._animationFrame.bind(this);
this._capacity = capacity;
}
DelayLine.prototype.put = function(value){
this.ring[this._e++] = value;
if (this._e >= this._capacity) this._e = 0;
if (this._e == this._s) this._get();
cancelAnimationFrame(this._raf);
this._raf = requestAnimationFrame(this._af);
}
DelayLine.prototype._get = function(){
var value = this.ring[this._s++];
if (this._s == this._capacity) this._s = 0;
return value;
}
DelayLine.prototype._peek = function(){
return this.ring[this._s];
}
DelayLine.prototype._animationFrame = function(){
if (this._length > 0){
if (performance.now() - this._peek().timeStamp > this.delay)
this.action(this._get());
this._raf = requestAnimationFrame(this._af);
}
}
Object.defineProperty(DelayLine.prototype, "_length", {
get: function() {
var size = this._e - this._s;
return size >= 0 ? size : size + this._capacity;
}
});
var delayLine = new DelayLine(100, function(e){
pointer.style.left = e.x - pointer.offsetWidth/2 + "px";
pointer.style.top = e.y - pointer.offsetHeight/2 + "px";
});
document.addEventListener("mousemove", function(e){
delayLine.put(e);
}, false);
https://jsfiddle.net/os8r7c20/2/
Try removing setInterval , using .css() , css transition
$(document).ready(function () {
var cube = $("#cube");
$("body").mousemove(function (e) {
handleMouseMove(e);
});
function handleMouseMove(event) {
var x = event.pageX,
y = event.pageY;
cube.css({
left: x + cube.width() / 2 + "px",
top: y + cube.height() / 2 + "px"
}).parents("body").mousemove()
}
});
body {
overflow:hidden;
position:absolute;
height:100%;
width:100%;
background:#efefef;
}
#cube {
height:50px;
width:50px;
margin-top:-25px;
margin-left:-25px;
background:red;
position:absolute;
top:50%;
left:50%;
transition:all 1.5s ease-in-out;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="container">
<div id="cube"></div>
</div>
I'm trying to make a sub navigation menu animate a fixed position change after a user has scrolled down 200 pixels from the top. It works but it's very buggy, like when the user scrolls back to the top it doesn't always return to the original position, etc. I'm not strong with javascript / jquery, but I thought this would be simple to do. What am I missing?
Here's my fidde:
http://jsfiddle.net/visevo/bx67Z/
and a code snippet:
(function() {
console.log( "hello" );
var target = $('#side-nav');
var scrollDis = 200;
var reset = 20;
var speed = 500;
$(window).scroll(function() {
console.log( $(window).scrollTop() );
if( $(window).scrollTop() > scrollDis ) {
$(target).animate({
top: reset + 'px'
}, speed);
} else {
$(target).animate({
top: scrollDis + 'px'
}, speed);
}
});
})();
How about a little bit of css and jquery both ??
What I did is added transition to side-nav to animate it and rectified your js to just change it's css. You can set how fast it moves by changing the time in transition.
FIDDLE
#side-nav {
position: fixed;
top: 100px;
left: 10px;
width: 100px;
background: #ccc;
-webkit-transition:all 0.5s ease-in-out;
}
(function () {
var target = $('#side-nav');
var scrollDis = 100;
var reset = 20;
var speed = 500;
$(window).scroll(function () {
if ($(this).scrollTop() >= scrollDis) {
target.css("top", reset);
} else {
target.css("top", scrollDis);
}
});
})();
NOTE: When you cache a jQuery object like this
var target = $("#side-nav");
You don't need to use $ again around the variable.
Since I am commenting all over the place I should probably actually contribute an answer.
The issue is that you are adding scroll events every time a scroll occurs, which is causing more scrolling to occur, which causes more scroll events, hence infinite loop. While cancelling previous events will fix the problem, it's cleaner to only fire the event when you pass the threshold, IE:
(function () {
console.log("hello");
var target = $('#side-nav');
var scrollDis = 200;
var reset = 20;
var speed = 500;
var passedPosition = false;
var bolMoving = false;
$(window).scroll(function () {
if (bolMoving) return; // Cancel double calls.
console.log($(window).scrollTop());
if (($(window).scrollTop() > scrollDis) && !passedPosition) {
bolMoving = true; //
$(target).animate({
top: reset + 'px'
}, speed, function() { bolMoving = false; passedPosition = true; });
} else if (passedPosition && $(window).scrollTop() <= scrollDis) {
bolMoving = true;
$(target).animate({
top: scrollDis + 'px'
}, speed, function() { bolMoving = false; passedPosition = false; });
}
});
})();
http://jsfiddle.net/bx67Z/12/
http://jsfiddle.net/bx67Z/3/
I just added .stop() in front of the .animate() , and it works a lot better already.
$(target).stop().animate({
top: reset + 'px'
}, speed);
} else {
$(target).stop().animate({
top: scrollDis + 'px'
}, speed);
You can also use .stop(true)
http://jsfiddle.net/bx67Z/5/
$(target).stop(true).animate({
top: reset + 'px'
}, speed);
} else {
$(target).stop(true).animate({
top: scrollDis + 'px'
}, speed);
You can also use .stop(true, true)
http://jsfiddle.net/bx67Z/4/
$(target).stop(true, true).animate({
top: reset + 'px'
}, speed);
} else {
$(target).stop(true, true).animate({
top: scrollDis + 'px'
}, speed);
So the reason .stop(true) works so well, is that it clears the animation queue. The reason yours was being "buggy" is because on every scroll the animation queue was "bubbling up" , thus it took a long time for it to reach the point where it would scroll back to the original position.
For information about .stop() , see here http://api.jquery.com/stop
I have got a menu on my homepage and on hover I would like them to enlarge. This is exactly what I have achieved, except there is one flaw:
When I move off before the animation ends, the option stops the animation and subtracts 30 from the width that left off from the previous animation. So it always intersects with the other animation and causes false results.
Example:
I move quickly to menu option 1, it only expands little - let's say by 10px - while I am on it, and as I move off the width decreases by 30px, which is more than the previously moved 10px, which results in a smaller button overall.
I would like to somehow capture how much it has moved during the mouseover animation and only decrease the width in the leaving function by that amount. Or, of course some other easy solution, if there is one...
Here's the code:
$('.menu_option').hover(
function() {
var w = $(this).width()+30+"";
$(this).stop().animate({ width:w}, 150, 'easeOutQuad');
}, function() {
var w = $(this).width()-30+"";
$(this).stop().animate({ width:w}, 150, 'easeOutQuad');
});
What you can do is make another variable which is the origin width then when you put it back go back to the origin:
js:
var o = $('.menu_option').width();
$('.menu_option').hover(function () {
var w = $(this).width() + 30 + "";
$(this).stop().animate({
width: w
}, 150, 'easeOutQuad');
}, function () {
$(this).stop().animate({
width: o
}, 150, 'easeOutQuad');
});
http://jsfiddle.net/Hive7/qBLPa/6/
You need to complete the previous animation before the width is calculated
$('.menu_option').hover(function () {
var $this = $(this).stop(true, true);
var w = $this.width() + 30;
$this.animate({
width: w
}, 150, 'easeOutQuad');
}, function () {
var $this = $(this).stop(true, true);
var w = $this.width() - 30 + "";
$this.animate({
width: w
}, 150, 'easeOutQuad');
});
Demo: Fiddle
Is there anyway to make a div box shake on page load? Like maybe just once or twice?
Update: At this URL I have it still not working on page load, what am I doing wrong?
http://tinyurl.com/79azbav
I think I'm stuck at the onpage load; that failure can be seen here:
Get onpage to work correctly
I've also tried initiating the animation with my already implemented body onLoad:
<body onLoad="document.emvForm.EMAIL_FIELD.focus(); document.ready.entertext.shake();" >
But still failing like a champ.
Try something like this:
EDIT:
Changed Shake() to shake() for consistency with jQuery conventions.
jQuery.fn.shake = function() {
this.each(function(i) {
$(this).css({ "position": "relative" });
for (var x = 1; x <= 3; x++) {
$(this).animate({ left: -25 }, 10).animate({ left: 0 }, 50).animate({ left: 25 }, 10).animate({ left: 0 }, 50);
}
});
return this;
}
EDIT:
In my example the left position is set to 25, but you can reduce this for a more subtle effect or increase it for a more pronounced effect.
Using the shake function:
$("#div").shake();
Here's a jsFiddle that demonstrates it: http://jsfiddle.net/JppPG/3/
Slight variation on #James-Johnson's excellent answer for ~shaking~ elements that are absolute positioned. This function grabs the current left position of the element and shakes it relative to this point. I've gone for a less violent shake, gas mark 10.
jQuery.fn.shake = function () {
this.each(function (i) {
var currentLeft = parseInt($(this).css("left"));
for (var x = 1; x <= 8; x++) {
$(this).animate({ left: (currentLeft - 10) }, 10).animate({ left: currentLeft }, 50).animate({ left: (currentLeft + 10) }, 10).animate({ left: currentLeft }, 50);
}
});
return this;
}
I make a jquery tooltip but have problem with it, when mouse enter on linke "ToolTip" box tooltip don't show in next to link "ToolTip" it show in above linke "ToolTip" , how can set it?
Demo: http://jsfiddle.net/uUwuD/1/
function setOffset(ele, e) {
$(ele).prev().css({
right: ($(window).width() - e.pageX) + 10,
top: ($(window).height() - e.pageY),
opacity: 1
}).show();
}
function tool_tip() {
$('.tool_tip .tooltip_hover').mouseenter(function (e) {
setOffset(this, e);
}).mousemove(function (e) {
setOffset(this, e);
}).mouseout(function () {
$(this).prev().fadeOut();
});
}
tool_tip();
Something like this works, you've still got a bug where the tooltip sometimes fades away on the hover of a new anchor. I'll leave you to fix that, or for another question.
function setOffset(ele, e) {
var tooltip = $(ele).prev();
var element = $(ele);
tooltip.css({
left: element.offset().left - element.width() - tooltip.width(),
top: element.offset().top - tooltip.height(),
opacity: 1
}).show();
}
And here's the jsFiddle for it: http://jsfiddle.net/uUwuD/4/
you need to calculate the window width and minus it with the width of your tooltip and offset
if(winwidth - (offset *2) >= tooltipwidth + e.pageX){
leftpos = e.pageX+offset;
} else{
leftpos = winwidth-tooltipwidth-offset;
}
if you want more detail please refer :)