I'm creating a jquery script to vertically pan an image inside its container according to the cursor position.
This is what I've done :
$(window).load(function(){
var lastcalc = 0;
$(".tn3a-image").mousemove(function(e){
//var y = e.pageY-$(".tn3a-image").offset().top;
//var Cnt_h = $(".tn3a-image").height();
//var Cy = ($(".pan-view").height()-Cnt_h)/Cnt_h;
var calc = -(($(".pan-view").height()/$(".tn3a-image").height())-1)*(e.pageY-$(".tn3a-image").offset().top)
if (Math.abs(lastcalc-calc)<=1) return;
lastcalc = calc;
$(".tn3a-full-image").stop(true,true)
.animate({ top : calc }, 500, 'linear');
});
});
It works as expected but the performances are really bad. It looks really sluggish, although I have a good GPU/CPU. I came up with two solutions - get the calculations made in a single variable (named 'calc'), and use '.stop(true,true)'. Yet I'm still not really satisfied with it. It looks really bad compared to Flash-based solutions.
Any ideas on how to optimize this ?
Thanks for your help.
Here is a simple way to throttle your handler:
var IsPanning = false;
$(window).load(function(){
var lastcalc = 0;
$(".tn3a-image").mousemove(function(e){
if(IsPanning == false){
IsPanning= true;
var calc = -(($(".pan-view").height()/$(".tn3a-image").height())-1)*(e.pageY-$(".tn3a-image").offset().top)
if (Math.abs(lastcalc-calc)<=1) return;
lastcalc = calc;
$(".tn3a-full-image").stop(true,true)
.animate({ top : calc }, 500, 'linear');
});
IsPanning = false;
}
});
Sorry, I can't check to see if there are any syntax errors at the moment, hopefully it gives you the idea.
Related
I looked into different solutions, such as this one that I improved with a button : http://jsfiddle.net/3mhJJ/.
$(".button").click(function(){
$(".bar").each(function(i) {fluctuate($(this));});
});
I would like to be able to pause the recording, and also to stop the animation when the sound is over ( I didn't include it, but just imagine the sound lasts 10 seconds)
I have never worked with jQuery, but I am familiar with AngularJS, do you think there is any way for me to do this only with JQuery or by mixing JQuery and AngularJS together?
Otherwise, I'd like to recreate something like Soundcloud but I am not sure how hard it is.
Thanks for advices.
Not entirely sure that I understood what you're after but here's how to start/stop the animation:
http://jsfiddle.net/kyysdo4n/2/ (updated to include an auto-stop feature)
var stopAnimation = false;
var closeTimeoutHandle = null;
function fluctuate(bar) {
if(stopAnimation){
return;
}
var amplitude = Math.random() * 10;
console.log(amplitude);
var height = amplitude * 4;
//Animate the equalizer bar repeatedly
bar.animate({
height: height
}, function() {
fluctuate($(this));
});
}
$("#btnStart").click(function(i) {
stopAnimation = false;
fluctuate($('.bar'));
closeTimeoutHandle = setTimeout(function(){
stopAnimation = true;
}, 10 * 1000); //10 seconds
});
$("#btnStop").click(function(i) {
stopAnimation = true;
fluctuate($('.bar'));
// clear the timeout so that if the user starts the animation again
// it doesn't get stopped when the initial timeout gets called
if(closeTimeoutHandle){
clearTimeout(closeTimeoutHandle);
}
});
I'm not sure if this helps .. if it doesn't then please add more details, maybe more code ?
I'm currently using the following javascript as shown below.
It's working well when I place just text within the div .image_scroll_3 but as soon as I insert images the scroll glitches and won't move past the top of the image.
Any advice would be much appreciated
JS
<script>
(function($, undefined) {
$.fn.loopScroll = function(p_options) {
var options = $.extend({
direction: "upwards",
speed: 60
}, p_options);
return this.each(function() {
var obj = $(this).find(".image_scroll_2");
var text_height = obj.find(".image_scroll_3").height();
var start_y, end_y;
if (options.direction == "downwards") {
start_y = -text_height;
end_y = 0;
} else if (options.direction == "upwards") {
start_y = 0;
end_y = -text_height;
}
var animate = function() {
// setup animation of specified block "obj"
// calculate distance of animation
var distance = Math.abs(end_y - parseInt(obj.css("top")));
//alert("animate " + obj.css("top") + "-> " + end_y + " " + distance);
//duration will be distance / speed
obj.animate(
{ top: end_y }, //scroll upwards
1500 * distance / options.speed,
"linear",
function() {
// scroll to start position
obj.css("top", start_y);
animate();
}
);
};
obj.find(".image_scroll_3").clone().appendTo(obj);
$(this).on("mouseout", function() {
obj.stop();
}).on("mouseout", function() {
animate(); // resume animation
});
obj.css("top", start_y);
animate(); // start animation
});
};
}(jQuery));
$("#example4").loopScroll({ speed: 700 });
</script>
I think the problem is that your text_height is calculated before the images are actually loaded inside your .image_scroll_3 elements. So you'll need to wait for the images to load.
Put your loopScroll call inside a $(window).load like so:
$(window).load(function(){
$('#example4').loopScroll({speed:700});
});
That massive glitch should now be gone as the fix above should have helped mitigate it.
However, there is still some unwanted jank / stutter (don't want to use the word glitch again, lets keep it reserved for the initial problem) in movement of all images if you notice and I am guessing that is probably because we are animating the whole thing too fast. Passing in speed like 100 or 200 resolves that but this is not really a solution because, ideally, you should be able to put in any speed value and it should just produce smooth animations out of it.
I am working on exactly the same thing but before that, I want to know if the above fix for the glitch helps you and we are finally done with it? Let me know.
Update:
Here is my version that I spoke of earlier, for your perusal.
Because all you are trying to do is loop images in a very linear fashion, I, for one, do not see the need to rely on animate() function of jQuery. There is requestAnimationFrame API that I have leveraged instead. In fact, in my demonstration below I have completely abandoned jQuery in favour of vanilla JavaScript only because I kept finding alternatives to pretty much everything we needed to do in this demo. But of course, this is also a very subjective matter; a taste thing; so if you want to go with jQuery, then by all means.
Another fundamental change I brought is rather than updating top values, I have resorted to updating translateY values.
Take a look at this jsFiddle and let me know if it fits your needs.
JavaScript code of which is as belows:
// [http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/]
window.requestAnimFrame=(function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||function(callback){window.setTimeout(callback,1000/60);};})();
var main=null;
var imageScroll2=null;
var imageScroll3=null;
var totalHeight=null;
var initY=null;
var destY=null;
var currY=null;
var increment=null;
var direction=null;
var UP=null;
var DOWN=null;
var isPlaying=null;
function init(){
main=document.getElementById('example4');
imageScroll2=main.getElementsByClassName('image_scroll_2')[0];
imageScroll3=main.getElementsByClassName('image_scroll_3')[0];
totalHeight=imageScroll3.clientHeight;
UP='upwards';
DOWN='downwards';
isPlaying=true;
direction=UP;
increment=10;
if(direction===DOWN){
initY= -totalHeight;
destY=0;
}else{
initY=0;
destY= -totalHeight;
}
currY=initY;
imageScroll2.appendChild(imageScroll3.cloneNode(true));
if(imageScroll2.addEventListener){
imageScroll2.addEventListener('mouseover',function(){isPlaying=false;},false);
imageScroll2.addEventListener('mouseout',function(){isPlaying=true;},false);
}else{
imageScroll2.attachEvent('onmouseover',function(){isPlaying=false;});
imageScroll2.attachEvent('onmouseout',function(){isPlaying=true;});
}
requestAnimFrame(render);
}
function render(){
if(isPlaying){
imageScroll2.style.transform='translate(0px,'+currY+'px)';
if(direction===DOWN){
currY+=increment;
if(currY>=destY){currY=initY;}
}else{
currY-=increment;
if(currY<=destY){currY=initY;}
}
}
requestAnimFrame(render);
}
//
init();
I'm having troubles getting this code to execute in a timely manner in Firefox. It seems to work just fine in Chrome.
JSFiddle here: http://jsfiddle.net/EXDhb/
Real live example page I'm working with here: http://mindevo.com/tests/tacos.html
I'm not sure if I'm leaving something out. I kind of hacked this together from reading a bunch of page-scroll scripts other people have put together. Not sure if this is even the best way for me to do what I'm trying to accomplish (which is to darken the next area until it's somewhat revealed. (I used halfway for this).
Here's my javascript:
$(document).ready(function(){
$(window).scroll(function(){
$('.dark').each(function(i){
var half_object = $(this).position().top + ($(this).outerHeight()/2);
var bottom_window = $(window).scrollTop() + $(window).height();
var bottom_object = $(this).position().top + $(this).outerHeight();
if(bottom_window > half_object){
$(this).animate({'opacity':'1'},200);
}
else if(bottom_object > $(window).scrollTop()) {
$(this).animate({'opacity':'.5'},200);
}
});
});
});
Is there a better way to do this? I tried adding/removing css classes but it invoked some crazy Chrome bug I was not pleased about.
Why does it work so slowly in Firefox?
Start by not having 6 separate jQuery $(this) operations and multiple $(window)! Use temp variables whenever you can to avoid requerying.
JSFIddle: http://jsfiddle.net/TrueBlueAussie/EXDhb/9/
$(document).ready(function () {
// window never changes
var $window = $(window);
$window.scroll(function () {
// Window height may have changed between scrolls
var windowHeight = $window.height();
var scrollTop = $window.scrollTop();
$('.dark').each(function (i) {
var $this = $(this);
var top = $this.position().top;
var outerH = $this.outerHeight();
var half_object = top + (outerH / 2);
var bottom_window = scrollTop + windowHeight;
var bottom_object = top + outerH;
console.log(half_object);
if (bottom_window > half_object) {
$this.stop().animate({
'opacity': '1'
}, 200);
} else if (bottom_object > scrollTop) {
$this.stop().animate({
'opacity': '.5'
}, 200);
}
});
});
});
And so on until you do not do anything twice that has an overhead that you do not need to have.
Update: Stop previous animations
The pause was not caused by the speed of the code above, but by not stopping multiple animations. The problem is that scroll fires frequently, so without .stop() animations get queued up and fire one after the other. This made it look much slower that it actually was.
Further optimizations might involve only processing elements that are actually onscreen, but that is pretty pointless given the apparent speed now.
You can cache your variables, which should help slightly:
$(document).ready(function(){
var $window = $(window);
$window.scroll( function(){
$('.dark').each(function(i){
var $this = $(this);
var outerHeight = $this.outerHeight();
var positionTop = $this.position().top;
var half_object = positionTop + (outerHeight/2);
var bottom_window = window.scrollTop() + window.height();
var bottom_object = positionTop + outerHeight;
if(bottom_window > half_object){
$this.animate({'opacity':'1'}, 200);
} else if(bottom_object > window.scrollTop()) {
$this.animate({'opacity':'.5'}, 200);
}
});
});
});
I realize there is already an accepted answer, but many times it is useful to do something only after the user has stopped scrolling, and not each time the "scroll" event fires. This event can can fire upwards of 50 times per second, leaving you with ~20ms to do what you need to do. This other StackOverflow question shows you how to do something only after scrolling has stopped. As #TrueBlueAussie mentioned in his answer, you would still want to stop any animations that were currently running.
I'm really new to this stuff and I could really do with someones help.
I have a js fiddle set up here: http://jsfiddle.net/hAZR7/
The issue I have is more a logic / math one I think as it seems to animate fine and start and move to the desired position. I'ts the return that has me stumped and I am not sure what equation or code I need to fix it. (Even though it is probably staring me in the face).
Would I be right in thinking this has something to do with it? when removing the class 'on' but what to set the margin as to calculate where it should return to.
$elm.removeClass('on');
var $left_margin = $extra_margin;
$elm.stop().animate({marginLeft : $left_margin}, 1500, function()
{$slider_started = false;});
Update - Thanks for your answers both examples work fine one adjusting slight dimensions in CSS but I don't think it is detrimental to the functionality. Is there any way of implementing this into a setInterval / automation for the slides or should I raise another question?
Seee this : http://jsfiddle.net/bt9Je/
Changes:
var $total_slider = $j('.top-slider-main > div').length;
and
var $left_margin = $extra_margin-((($total_slider-i))*$label_width);
You can change
var $left_margin = $extra_margin;
to
if( $elm.next().length ) {
var $left_margin = $elm.next().offset().left - $label_width;
} else {
var $left_margin = $j('.top-slider-main').first().width() - $label_width;
}
Edit To fix the bug described in the comment change the fiddle http://jsfiddle.net/hAZR7
var $total_slider = $j('.top-slider-main div').length;
to
var $total_slider = $j('.top-slider-main > div').length;
and line
var $left_margin = $extra_margin;
to
var $left_margin = $j('.top-slider-main').first().width() - ( ( $total_slider - i + 1 ) * $label_width );
I've forked the fiddle http://jsfiddle.net/gU2Tq/
I'm coding this content slider using JavaScript/Jquery.
Please have a look at my code on JSFiddle: http://jsfiddle.net/46JfZ/7/
cn = {
hero:function(r,lx,rx,fx,fs,ss,a){
rc=$(r).children(), rcw=rc.width(), rca=rc.size(), rw=rcw*rca, p='px'; $(r).css({'width':rw})
$(lx).click(function(){
n=$(r).position().left-rcw;
switch(fx){
case 'slide':$(r).stop(true,true).animate({left:(n<-rw+rcw?0:n)+p},ss,'easeOutExpo'); break;
case 'fade':$(r).stop(true,true).fadeOut(fs).animate({left:(n<-rw+rcw?0:n)+p},1).fadeIn(fs); break;
}
})
$(rx).click(function(){
n=$(r).position().left+rcw;
switch(fx){
case 'slide':$(r).stop(true,true).animate({left:(n>0?-rw+rcw:n)+p},ss,'easeOutExpo'); break;
case 'fade':$(r).stop(true,true).fadeOut(fs).animate({left:(n>0?-rw+rcw:n)+p},1).fadeIn(fs); break;
}
})
if(a.match('yes')){
$('#hero').append('<div id="indicate"></div>');
$.each(rc, function(){$('#indicate').append('<span></span>');})
function indicate(){
$('#indicate span').removeClass('active');
for(i=1;i<=rca; i++){
o=i-1,
newReelpos = - rcw*o,
q=$(r).position().left;
if(q == newReelpos ){
$('#indicate span:nth-child('+ i +')').addClass('active');
}
}
$(lx).click();
}
setInterval(indicate,3000);
$(lx).parent().hide();
}
}
}
$(function(){
cn.hero('#reel', '#next', '#prev', 'slide', 300, 600, 'yes');
});
I've missed something because the status dots(blue & green dots on the right) aren't working properly?
Any help would be appreciated greatly, Thank you
There are some things you're doing that aren't great or are wrong. For instance:
function(r,lx,rx,fx,fs,ss,a)
This is not a good way to name your arguments (or variables). What does all that mean? You might be asking yourself this in a few months as you try to fix something that has broken. Use descriptive argument, variable and function names.
You (inadvertently?) use global variables. For instance:
rc=$(r).children(), rcw=rc.width(), rca=rc.size(), // etc.
These are all essentially global. Since you have a closure, you can prepend a var on each of those and they will stay in scope for your handlers and interval calls.
Your code formatting is problematic, almost (in my opinion) guaranteeing you'll have code that's hard to visually inspect for problems and inefficiencies (especially combined with the above and the below other observations). Space your code out. For instance:
rc=$(r).children(), rcw=rc.width(), rca=rc.size(), rw=rcw*rca, p='px'; $(r).css({'width':rw})
Could be:
var $r = $(r),
rc = $r.children(),
rcw = rc.width(),
rca = rc.size(),
rw = rcw * rca,
p = 'px';
Now I ask you, which do you think is easier to read?
Note, you also have several spots like this where a statement has no ending ;. Do not get in the habit of doing this, as it's poor practice and can lead to problems later on that can be hard to spot. Yes, I know Javascript allows you to do this, but don't take the bug bait.
You also should be caching jQuery calls. So all of your $(r), which occur within the same closure, could (I argue in the case of animation, should) be:
var $r = $(r); // First, cache
...
$r.stop(true,true).animate() // Then use
This will speed up your code, since jQuery is doing less DOM traversals to find the element(s) you're working on.
Taking all that into consideration, your actual problem is that you're not calculating the dot selection very effectively (which appears to be, find the current left value and then iterate over the width of each cell, testing if the width matches the left value). Note, math is not my strong suit, so I imagine there's a more efficient/direct way of handling the underlying math, but doing a loop to find the "active" dot is not necessary.
My approach:
cn = {
hero: function(r,lx,rx,fx,fs,ss,a){
var $r = $(r),
rc = $r.children(),
rcw = rc.width(),
rca = rc.size(),
rw = rcw * rca,
p = 'px';
$r.css({width: rw});
$(lx).click(function(){
var n = $r.position().left - rcw;
switch(fx){
case 'slide':
$r.stop(true,true)
.animate({
left: (n<-rw+rcw?0:n)+p
}, ss);
break;
case 'fade':
$r.stop(true,true)
.fadeOut(fs)
.animate({
left: (n<-rw+rcw?0:n)+p
}, 1)
.fadeIn(fs);
break;
}
});
$(rx).click(function(){
var n = $r.position().left + rcw;
switch(fx){
case 'slide':
$(r).stop(true,true)
.animate({
left: (n>0?-rw+rcw:n)+p
}, ss);
break;
case 'fade':
$r.stop(true,true)
.fadeOut(fs)
.animate({
left: (n>0?-rw+rcw:n)+p
}, 1)
.fadeIn(fs);
break;
}
});
// I have no idea why you're using match; wouldn't a boolean true work?
if (a.match('yes')) {
$('#hero').append('<div id="indicate"></div>');
$.each(rc, function(){
$('#indicate').append('<span></span>');
});
function indicate(fire){
var $indicates = $('#indicate span'),
left = parseInt($r.position().left),
count = $indicates.length;
var i = (left / -rcw) + 2;
if (i > count || fire !== true) {
i = 1;
}
$indicates
.removeClass('active')
.parent()
.find('span:nth-child('+ i +')')
.addClass('active');
if (fire) {
$(lx).click();
}
setTimeout(function(){
indicate(true);
}, 3000);
}
indicate(false);
$(lx).parent().hide();
}
}
}
$(function(){
cn.hero('#reel', '#next', '#prev', 'slide', 300, 600, 'yes');
});
http://jsfiddle.net/kEG2p/