Javascript memory leak - Canvas HTML5 jQuery - javascript

Im building a website that uses canvas primarily, the only cannvas involved however is a line thats drawn horizontally, the line is about 13000px long.
When the user scrolls my window then scrolls horizontally along m canvas path, Example.
Ive notived that on firefox (version 6.0.2) my document fails to scroll. In my console I receive something along the lines of (NS_ERROR_OUT_OF_MEMORY).
After googling this I've found that it could be a potential memory leak? How does this work, is it because of the way I've written my code? or is this a browser/hardware issue?
Im re-initisalising my function on window resize etc and I'm curious as to whether this may have any imapct?
// Initate the plugin
$(window).resize(function() {
if(this.resizeTO) clearTimeout(this.resizeTO);
this.resizeTO = setTimeout(function() {
$(this).trigger('resizeEnd');
}, 500);
});
$(window).bind('resizeEnd', function() {
$("#path").scrollPath({drawPath: true, wrapAround: false});
});
$("#path").scrollPath({drawPath: true, wrapAround: false});
$(document).ready(init);
$('.wrapper').css({'top' : '0px','left' : '0px'});
$('.wrapper > div').css({'height' : + $(window).height() +'px'});
function init() {
// Set window height and width variables
var windowheight = $(window).height();
var windowwidth = $(window).width();
// Check monitor size and workot if incentives needs extra space etc
var bff = 4020 + (1993 - windowwidth);
// Move divs into position
$('.culture').css('top', + - windowheight + 'px');
$('.careerpath').css('top', + - windowheight + 'px');
$('.training').css('top', + - windowheight + 'px');
$('.apply').css('top' , + - windowheight + 'px');
/* ========== DRAWING THE PATH AND INITIATING THE PLUGIN ============= */
$.fn.scrollPath("getPath")
// Move to 'start' element
.moveTo(0, 0, {name: "div"})
.lineTo(2400, 0, {name: "div1"})
.lineTo((bff-550), 0, {name: "div2"})
.lineTo(bff, 0, {name: "div3"})
.lineTo(bff, -windowheight, {name: "div4"})
.lineTo((bff + 1993), -windowheight, {name: "div5"})
.lineTo((bff + 1993 + 1837), -windowheight, {name: "div6"})
.lineTo((bff + ((1993 + 1837 + 1795) - 325)), -windowheight, {name: "div7"})
// We're done with the path, let's initate the plugin on our wrapper element
// Window resize function
$(window).resize(function() {
if(this.resizeTO) clearTimeout(this.resizeTO);
this.resizeTO = setTimeout(function() {
$(this).trigger('resizeEnd');
}, 500);
});
$(window).bind('resizeEnd', function() {
$("#path").scrollPath({drawPath: true, wrapAround: false});
});
$("#path").scrollPath({drawPath: true, wrapAround: false});
}

Ok, now that I googled for the plugin you used I know what is going on.
http://joelb.me/scrollpath/
The "line" is in fact a shape and the scrollPath is generating a nice big canvas for that. The problem is inside the scrollPath stuff. It creates too many canvas instances or leaks something.
You should trace/document the bug a bit better and report it to the author.
The suggestion to create the path from a single DOM element is invalid now that we know you didn't mean a single straight line. I have no idea what is your trget exactly, but you might be able to achieve it with impress.js

You're doing it wrong. This approach will lead to nothing but pain.
I don't think you have a leak, you simply have a memory hog of a program. And other than memory you will also have huge performance issues with this. 2D canvas is heavily affected by fillrate (number of pixels drawn). Drawing this many pixels will be incredibly slow, even on fast computers.
So do NOT make a gigantic canvas and then scroll the window/viewport over it. Instead, make a small canvas, which renders only the visible portion of a bigger thing.

Related

Infinite scrolling div glitching with images

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();

Why are my animations delayed in Firefox? How can I improve this scroll script?

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.

Do not execute jQuery script if CSS is of particular value

On my website, I have a sidebar DIV on the left and a text DIV on the right. I wanted to make the sidebar follow the reader as he or she scrolls down so I DuckDuckGo'ed a bit and found this then modified it slightly to my needs:
<script type='text/javascript'>//<![CDATA[
$(window).load(function(){
$(function(){
var $sidebar = $('#sidebar'),
sidebarOffset = $sidebar.offset(),
$window = $(window),
gap = $('#header').css('marginBottom').replace(/[^-\d\.]/g, ''),
distance = ($window.scrollTop()) - (sidebarOffset.top - gap),
footerHeight = $('#footer').outerHeight();
$window.scroll(function(){
distance = ($window.scrollTop()) - (sidebarOffset.top - gap);
if ( distance > 0 ) {
$sidebar.css({'top': gap + 'px', 'position' : 'fixed'});
} else {
$sidebar.css({'top': '0', 'position': 'relative'});
}
})
});
});//]]>
</script>
And it works just like I want it to. However, my website uses Skeleton framework to handle responsive design. I've designed it so that when it goes down to mobile devices (horizontal then vertical), sidebar moves from being to the left of the text to being above it so that text DIV can take 100% width. As you can probably imagine, this script causes the sidebar to cover parts of text as you scroll down.
I am completely new to jQuery and I am doing my best through trial-and-error but I've given up. What I need help with is to make this script not execute if a certain DIV has a certain CSS value (i.e. #header-logo is display: none).
Ideally, the script should check for this when user resizes the browser, not on website load, in case user resizes the browser window from normal size to mobile size.
I imagine it should be enough to wrap it in some IF-ELSE statement but I am starting to pull the hair out of my head by now. And since I don't have too much hair anyway, I need help!
Thanks a lot in advance!
This function will execute on window resize and will check if #header-logo is visible.
$(window).resize(function() {
if ($('#header-logo').is(':visible')) {
// Your code
}
});
I think you need to check this on load to, because you don't know if the user will start with mobile view or not. You could do something like this:
$(window).resize(function() {
if ($('#header-logo').is(':visible')) {
// Your code
}
}).resize();
This will get executed on load and on resize.
EDIT: You will probably need to turn off the scroll function if #header-logo is not visible. So, instead of create the function inside the scroll event, you need to create it outside:
$(window).resize(function() {
if ($('#header-logo').is(':visible')) {
var $sidebar = $('#sidebar'),
sidebarOffset = $sidebar.offset(),
$window = $(window),
gap = $('#header').css('marginBottom').replace(/[^-\d\.]/g, ''),
distance = ($window.scrollTop()) - (sidebarOffset.top - gap),
footerHeight = $('#footer').outerHeight();
function myScroll() {
distance = ($window.scrollTop()) - (sidebarOffset.top - gap);
if ( distance > 0 ) {
$sidebar.css({'top': gap + 'px', 'position' : 'fixed'});
} else {
$sidebar.css({'top': '0', 'position': 'relative'});
}
}
$window.on('scroll', myScroll);
} else {
$(window).off('scroll', myScroll);
}
});
Didn't test it, but you get the idea.
$("#headerLogo").css("display") will get you the value.
http://api.jquery.com/css/
I also see you only want this to happen on resize, so wrap it in jquery's resize() function:
https://api.jquery.com/resize/

Window vertically re-sizing issue

Im trying to resize the window. it works horizentally but not vertically. What am I doing wrong here:
$(window).resize(resizeWebSite());
function resizeWebSite(){
$("#panel").height($(window).height() - 10);
$("#map").height($(window).height() - 10);
console.log($("#map").height() / 2);
var loaderTop = ($("#map").height() - $("#loadingIndicator").height()) / 100 + $("#map").position().top;
var loaderLeft = (($("#map").width() - $("#loadingIndicator").width()) / 100) + $("#map").position().left;
$("#loadingIndicator").css({'position' : 'absolute' , 'left' : loaderLeft + 'px', 'top' : loaderTop + 'px'});
}
resizeWebSite();
When you set up the event handler, you need to pass only the name of the function:
$(window).resize(resizeWebSite);
Now a further problem with this will be that on a desktop/laptop machine (traditional computer, as opposed to a tablet), where the user can resize the browser window interactively, the browser will fire "resize" events very rapidly. All that work inside the handler will make the response very sluggish and jumpy.
To counteract that, you can use a timer. The idea is to respond to a "resize" event by first cancelling any pending timer, and then setting a new timer for some time in the future (a hundred milliseconds or so) to run the actual code that deals with the new window size. That way, while the user is moving the mouse quickly to change the window size, the only work you're doing is clearing and resetting a timer, which is pretty fast. Only when the user stops moving the mouse for a while do you actually do any real work.
That would look something like this:
var resizeTimer = null;
$(window).resize(function() {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(resizeWebSite, 100);
});
Maybe this is what you're after?
function resizeWebSite(){
$("#panel").height($(window).height() - 10);
$("#map").height($(window).height() - 10);
console.log($("#map").height() / 2);
var loaderTop = ($("#map").height() - $("#loadingIndicator").height()) / 100 + $("#map").position().top;
var loaderLeft = (($("#map").width() - $("#loadingIndicator").width()) / 100) + $("#map").position().left;
$("#loadingIndicator").css({'position' : 'absolute' , 'left' : loaderLeft + 'px', 'top' : loaderTop + 'px'});
}
$(document).ready(resizeWebSite);
$(window).resize(resizeWebSite);
By the way, if you're using margin on the elements you're reading the height on, and you want to include them in the calculations, use outerWidth(true) instead of width()

Slow scrolling background in Javascript or CSS?

I'm trying to figure out how to make a background image scroll slower than the page contents. I haven't got a clue how it's done. The perfect example of what I'm trying to do is here
Is this done in CSS or jQuery/Javascript?
This is made by javascript (jQuery):
(function () {
var a = document.body,
e = document.documentElement;
$(window).unbind("scroll").scroll(function () {
a.style.backgroundPosition = "0px " + -(Math.max(e.scrollTop, a.scrollTop) / 8) + "px";
});
})();
The effect on the link you posted is done in Javascript using jQuery.
If you examine the code of a script of the site here, you can find:
.style.backgroundPosition="0px "+-(Math.max(e.scrollTop,a.scrollTop)/8)+"px"
Practically, the background-position CSS property is modified on page scrolling calculating Y-axis depending on page scroll position. If you have some knowledge of Javascript, jQuery or Mootools, you can reproduce the effect very easily.
I think it's impossible to do it using only CSS.
This one works for high bg images.
(function () {
var body = document.body,
e = document.documentElement,
scrollPercent;
$(window).unbind("scroll").scroll(function () {
scrollPercent = 100 * $(window).scrollTop() / ($(document).height() - $(window).height());
body.style.backgroundPosition = "0px " + scrollPercent + "%";
});
})();

Categories

Resources