I'm trying to play a gif animation on my website and I want it to freeze on the last frame. I'm using this code found elsewhere to make it work, but it will either jump back to the first frame or disappear completely. Any help would be greatly appreciated.
<img src="http://s12.postimg.org/wq43xqknx/logoani.gif" id="img1" style="float: left"/>
setTimeout(function () {
setInterval(function () {
$('#img1').attr('src', $('#img1').attr('src'))
}, 1)
}, 1000)
and here's the jsfiddle: http://jsfiddle.net/m6e6n/6/
At the moment JavaScript does not provide an interface to deal with the frames inside an animated GIF, you'll have to find a hacky way to do this but ...
I absolutely discourage you to use that script
Look what you're telling the CPU to execute every millisecond, that is, 1000 times per second:
Traverse the HTML document to find the element "img1"
Change its attribute "src" to ....
... traverse again the HTML document for the same node
read its attribute src
assign it to the element found in 1
Then, the browser will go to the cache and find the image
Depending on the browser, the image may be decoded and rendered. It should start at frame 1, but who knows
So you can thank god if the browser still has some time for additional tasks.
Besides, assuming you don't care about overloading your users' computers, you'll have to finetune the timing very precisely to match the GIF framerate. And bad news is, if the framerate is quick, that's gonna be impossible to achieve across different browsers, or even different computers, let alone mobile devices.
So what can you do? I'd recommend you to play the GIF, then substitute it with a still image of the last frame. Just calculate how much time does one iteration take, then issue a timeout to change the gif with the still image.
Related
So I have this page where on a button click, an image is added embellishments after performing various calculations on its meta-data which is stored as data-attributes.
Since the calculations can take a few seconds, I want to show an overlay on the image. So I do:
$(selectedImageId).addClass('loading');
//perform series of calculations here...
$(selectedImageId).removeClass('loading').addClass('calculated-embellishments');
I would imagine the script to first show the loading overlay on the image, then perform the lengthy calculations, then replace the overlay with the selected embellishment class. However it seems like the DOM is updated only at the end such that I never see the loading class, it just directly jumps from the plain image to the embellishment class after a few seconds.
If I add an alert('test') just before the last line which adds the embellishment then I can see the loading overlay on the image, but not otherwise.
How can I make this work the way I want it to, as I explained above?
Any pointers are very welcome!
What probably happens is that your "lengthy calculations" make the browser "hang" for processing, not having a chance to re-paint your image to reflect the newly added loading class.
Then once the calculations are done, your last instruction replaces the loading class by calculated-embellishments class, and now the browser has time to re-paint. But the loading class is already gone.
You have at least 3 workarounds to let the browser actually display your loading class before your calculations keep it busy:
Use a setTimeout as proposed by #csum, which is a bet on how much time the browser would need before doing the re-paint that will show your image with loading class. Hence the need to "test" different timeout values, but still without any guarantee as the result will depend on each client current performance (CPU, memory, current CPU load!).
Start your calculations in a callback of the "load" event of your overlay (if it is an image). But I am not sure if "load" event is a guarantee of the image (overlay) being painted (shown on screen).
Use 2 nested requestAnimationFrame to make sure at least 1 re-paint has occured, before you start your calculations.
You probably want to execute your calculations using setTimeout.
var WAIT = 10; // experiment with small values here
$(selectedImageId).addClass('loading');
setTimeout(function () {
//perform series of calculations here...
$(selectedImageId).removeClass('loading').addClass('calculated-embellishments');
}, WAIT);
If you want to play around with the timeout length, here is a jsfiddle with a demo using a similar situation: https://jsfiddle.net/v2n19w4d/1/ I find that a timeout length close to zero was not working.
This is really an issue with getting the DOM to update before it is blocked while doing your calculations. See jQuery append in loop - DOM does not update until the end, for example.
I've been trying to find a good approach to solve a very commmon problem in the retina era.
Lets say the following is given:
Create a website with responsive images
No CSS background images
Websites basic functionality must be working without JS
The websites images must be optimized for retina displays.
An easy way to solve this could be something like this:
<img src="img.jpg" data-highres="img#2x.jpg" />
and write some kind of js to swap out img.jpg with img#2x.jpg if retina device is detected. This would work, but if I enter the website on a retina device both img.jpg and img#2x.jpg would be loaded. Not very bandwidth friendly :(
Is it possible somehow to intercept and change the src of the image before the original src is loaded?
Or do any of you have another approach in solving the problem?
In the future, you might be able to use the picture element. In the meantime, the only approach I've seen that might work is to:
Put a div or span where you want the image. Style it to have the dimensions and layout of the image. Add some kind of identifying mark to it so you can find it (e.g. class="retina-image")
Store information about the different sizes of images you have on or in the element (e.g. using data-something attributes)
Put a <noscript><img src="..." alt="..."></script> inside the div
On DOM ready:
use JS to find all the elements with the identifier from step 1
detect the type of image you want
find the attribute that tells you what URL to use for that image
add the image to the DOM inside the container from step 1
This is the approach used by the picturefill library.
This will probably be flame-bait, but here's my two cents:
The concern over bandwidth is largely a mobile platforming issue. Considering that most modern mobile platforms now adopt high pixel density displays, the demand for high res images from these devices is going to be pretty high. Why serve 3 times the image resolution 90% of the time (a low res and high res image) when you can server 2 times the resolution 100% of the time?
Hence, in some scenarios, it may be easier to just serve up the higher resolution images (halving their width/height styles) and leave it at that - thereby saving (expensive) time and energy elsewhere. In an industry where everything seems to be a compromise, this sometimes makes the most sense as an approach.
I think Quentin essentially answered your question.
What I can add is that while having an img element without a src it technically not per spec, omitting it won't break your code and you'll avoid pre-loading. Then you can just have <img data-src="" data-src2=""> and swap images in/out using JavaScript. The <noscript> element is probably the "correct" way of doing this but it makes code really verbose and I've heard some browsers will pre-load <noscript> images as well.
One service I've come across which makes images responsive (including Retina displays) is Pixtulate. They also provide the JavaScript to correctly size and replace your img.src url before the page has finished loading. Should be pretty simple to drop in.
If you do something with window.devicePixelRatio and loop through images that have data-highres attribute.
This is quite rough, but you get my drift.
$(document).ready(function() {
if (window.devicePixelRatio > 1) {
// retina
} else {
// default
}
});
Use a blank/clear 1px gif
<img src="clear.gif" data-basesrc="img1" />
Then use basesrc atttribute and append the appropriate filename in your JS.
I'm working on a HTML 5 game, it is already online, but it's currently small and everything is okay.
Thing is, as it grows, it's going to be loading many, many images, music, sound effects and more. After 15 minutes of playing the game, at least 100 different resources might have been loaded already. Since it's an HTML5 App, it never refreshes the page during the game, so they all stack in the background.
I've noticed that every resource I load - on WebKit at least, using the Web Inspector - remains there once I remove the <img>, the <link> to the CSS and else. I'm guessing it's still in memory, just not being used, right?
This would end up consuming a lot of RAM eventually, and lead to a downgrade in performance specially on iOS and Android mobiles (which I slightly notice already on the current version), whose resources are more limited than desktop computers.
My question is: Is it possible to fully unload a Resource, freeing space in the RAM, through JavaScript? Without having to refresh the whole page to "clean it".
Worst scenario: Would using frames help, by deleting a frame, to free those frames' resources?.
Thank you!
Your description implies you have fully removed all references to the resources. The behavior you are seeing, then, is simply the garbage collector not having been invoked to clean the space, which is common in javascript implementations until "necessary". Setting to null or calling delete will usually do no better.
As a common case, you can typically call CollectGarbage() during scene loads/unloads to force the collection process. This is typically the best solution when the data will be loaded for game "stages", as that is a time that is not time critical. You usually do not want the collector to invoke during gameplay unless it is not a very real-time game.
Frames are usually a difficult solution if you want to keep certain resources around for common game controls. You need to consider whether you are refreshing entire resources or just certain resources.
All you can do is rely on JavaScript's built in garbage collection mechanism.
This kicks in whenever there is no reference to your image.
So assuming you have a reference pointer for each image, if you use:
img.destroy()
or
img.parentNode.removeChild(img)
Worth checking out: http://www.ibm.com/developerworks/web/library/wa-memleak/
Also: Need help using this function to destroy an item on canvas using javascript
EDIT
Here is some code that allows you to load an image into a var.
<script language = "JavaScript">
var heavyImage = new Image();
heavyImage.src = "heavyimagefile.jpg";
......
heavyImage = null; // removes reference and frees up memory
</script>
This is better that using JQuery .load() becuase it gives you more control over image references, and they will be removed from memory if the reference is gone (null)
Taken from: http://www.techrepublic.com/article/preloading-and-the-javascript-image-object/5214317
Hope it helps!
There are 2 better ways to load images besides a normal <img> tag, which Google brilliantly discusses here:
http://www.youtube.com/watch?v=7pCh62wr6m0&list=UU_x5XG1OV2P6uZZ5FSM9Ttw&index=74
Loading the images in through an HTML5 <canvas> which is way way faster. I would really watch that video and implement these methods for more speed. I would imagine garbage collection with canvas would function better because it's breaking away from the DOM.
Embedded data urls, where the src attribute of an image tag is the actual binary data of the image (yeah it's a giant string). It starts like this: src="data:image/jpeg;base64,/9j/MASSIVE-STRING ... " After using this, you would of course want to use a method to remove this node as discussed in the other answers. (I don't know how to generate this base64 string, try Google or the video)
You said Worst scenario: Would using frames help, by deleting a frame, to free those frames' resources
It is good to use frame. Yes, it can free up resource by deleting the frames.
All right, so I've made my tests by loading 3 different HTML into an < article > tag. Each HTML had many, huge images. Somewhat about 15 huge images per "page".
So I used jQuery.load() function to insert each in the tag. Also had an extra HTML that only had an < h1 >, to see what happened when a page with no images was replacing the previous page.
Well, turns out the RAM goes bigger while you start scrolling, and shoots up when going through a particularly big image (big as in dimensions and size, not just size). But once you leave that behind and lighter images come to screen, the RAM consumption actually goes down. And whenever I replaced using JS the content of the page, the RAM consumption went really down when it was occupying to much. Virtual Memory remained always high and rarely went down.
So I guess the browser is quite smart about handling Resources. It does not seem to unload them if you leave it there for a long while, but as soon you start loading other pages or scrolling, it starts loading / freeing up.
I guess I don't have anything to worry about after all...
Thanks everyone! =)
I'm playing some notes at regular intervals. Each one is delayed by a random number of milliseconds, creating a jarring irregular effect. How do I fix it?
Note: I'm OK with some latency, just as long as it's consistent.
Answers of the type "implement your own small SoundManager2 replacement, optimized for timing-sensitive playback" are OK, if you know how to do that :) but I'm trying to avoid rewriting my whole app in Flash for now.
For an example of app with zero audible latency see the flash-based ToneMatrix.
Testcase
(see it here live or get it in an zip):
<head>
<title></title>
<script type="text/javascript"
src="http://www.schillmania.com/projects/soundmanager2/script/soundmanager2.js">
</script>
<script type="text/javascript">
soundManager.url = '.'
soundManager.flashVersion = 9
soundManager.useHighPerformance = true
soundManager.useFastPolling = true
soundManager.autoLoad = true
function recur(func, delay) {
window.setTimeout(function() { recur(func, delay); func(); }, delay)
}
soundManager.onload = function() {
var sound = soundManager.createSound("test", "test.mp3")
recur(function() { sound.play() }, 300)
}
</script>
</head>
<body>
</body>
</html>
I know this isn't the answer you want to hear, but there is no way to stop this, regardless of whether you wrote your own flash library to play sound or not.
For everyone who said "it works fine for me!" try resizing or moving your browser window as the poster's demo plays out. You'll hear more than just a subtle amount of delay. This is most noticeable in Firefox and IE, but even Chrome will experience it.
What's worse, if you click and hold the mouse down on the close box for the browser window, the sound completely stops until you release your mouse (you can release it outside of the close box and not actually close the window, FYI).
What is going on here?
It turns out that when you start resizing or moving around the browser window, the browser tries to multi-task the act of changing the window properties with the act of keeping up with the javascript going on in the window. It short-changes the window when it needs to.
When you hold down the mouse over the close box in the browser window, time stops completely. This is what is happening in smaller increments when you are re-sizing or moving the window: time is standing still in the javascript world in small, sporadic chunks (or large chunks, depending on how slow your machine is).
Now, you might say "sure, resizing the browser or holding down the close button makes the browser pause, but normally this wouldn't happen". Unfortunately you would be wrong.
It happens all the time, actually. I've run tests and it turns out that even by leaving the browser window completely still, not touching the mouse, and not touching the keyboard, backgrounds processes on the computer can still cause "hiccups", which means that for brief periods (perhaps as small as a few milliseconds) time is "standing still" in the browser, at completely random intervals outside of your control.
What do I mean by "standing still"? Let's say you have a setInterval() call (this applies to setTimeout also) running every 33 milliseconds (about 30 frames per second). Now, you would expect that after every 33 "real world" milliseconds your function would get called. And most of the time, this is true.
But when "hiccups" start happening, your setInterval call might happen in 43 milliseconds. What happened during the 10 ms? Nothing. Time stood still. Nothing on the browser was being updated. If you had sound playing, it will continue playing, but no NEW sound calls would start playing, because no javascript is being executed at all. If you had 5 setInterval() functions running, they would have all been paused for 10ms at some point.
The only way to tell that "time stood still" is to poll real-world time in your setInterval function callbacks. You'll be able to see that the browser tries to keep up most of the time, but that when you start resizing the window or doing something stressfull, the intervals will be longer than usual, but that all of your code will remain synched up (I'm making games using this technique, so you will see that all your game updates happen in synch, but just get slightly stuttered).
Usually, I should point out, these stutters are completely unnoticeable, and unless you write a function to log real-world time during setInterval times (as I have done in my own testing) you wouldn't even know about it. But it becomes a problem if you try to create some type of repetitive sound (like the beeping in the background of Asteriods) using repetitive play() calls.
My suggestion? If you have a sound that you know will loop, give it a long duration, maybe 10 seconds, and you'll be less likely to notice the hiccups (now, the graphics on the screen could still hiccup, but you're screwed there).
If you are writing a game and having the main character fire off a machine gun, don't do 10 rapid-succession calls to playSound('singleShot'), do one call to playSound('machineGunFire10Rounds'), or something along those lines.
You'll have to do some trickery to get around it, but in most cases you'll be alright.
It seems that Flash applets run in a process that is somehow less affected this whole "time freezing" thing going on in the regular browser/javascript environment, but I can still get it to happen, even on your link to the ToneMatrix example, by resizing or moving the browser window.
But Flash still seems much better than javascript. When I leave the browser alone I'd be willing to bet that Flash is not freezing for any amount of time and that intervals are always running on time.
tl;dr:
you're screwed in what you're hoping to achieve
try to deal with it using some workarounds
re-write your project in pure flash (no javascript)
wait for browsers to get better (Firefox 4 is getting a new javascript engine called JaegerMonkey which will be interesting to watch)
how do I know all this? I've done a lot of testing & logging with javascript, setInterval, and soundManager/html5 audio calls
In my comment to your question I mentioned that I don't hear the irregularity when I play your sample. That means I'm either "rhythm deaf", or that there may be something in your setup that interferes with good realtime performance. You don't mention any details of your environment, but you may have other processes running on your computer that are sucking up CPU cycles, or an older version of Flash that may not do a good job of handling sound latencies. I myself am using a recent version of Flash (10.something), whereas your parameters call for Flash 9. But maybe I should assume that if you're smart enough to be using SoundManager2 and StackOverflow that you would have eliminated these problems.
So here are some troubleshooting possibilities and comments that come to mind:
1) the SoundManager site has a number of demos, including JS-DOM "painting" + Sound, V2. Are you hearing irregular latencies and delays there? If not, maybe you can compare what they're doing there against what you're doing. If you are, then maybe look at your machine environment. When I run that demo, it is very responsive. (EDIT: Looking at it more closely, however, you can watch how the size of the brush stamps varies during a stroke. Since it varies with the time interval between mouse events (assuming you are keeping a constant mouse speed), you can visually see any irregularities in the pattern of mouse events. I can see occasional variation in stamp sizes, which does indicate that mouse events are not coming in at regular times. Which brings us to Javascript events.)
2) Javascript setTimeout() and setInterval() are not very reliable when it comes to timing. Mostly they will come back in some ballpark of the interval you have requested, but there can be large variations, usually delays, that make them unreliable. I've found that the same is true when using ActionScript inside Flash. You might want to print out the times that your sound.play() call is being made to see whether the irregularities are due to the irregularities in setTimeout/setInterval(). If that's the case, you could try shortening the interval, and then polling the system time to get much closer to the 300ms interval that you want. You can poll system time using new Date().getTime(), and this seems to have ms accuracy. Polling is of course a hideous hack that sucks up cycles when they could be used for something else, and I don't recommend it in general, but you may try it to see whether it helps. EDIT: Here's a writeup by John Resig on the handling of input and timer events in js.
3) When flash plays sounds, there is usually a latency involved, just so that the player can "build up a head of steam" and make sure there's enough stuff in the buffer to be played before the next buffer request is filled. There's a trade off between this latency and the reliability of uninterrupted playback. This might be a limitation you can't do anything about, short of "implement[ing] your own small SoundManager2 replacement", which I know you don't want to do.
Hope this helps. I recently wrote an AS3 sound experiment which exposed me to some of the basics, and will be watching this space to see what other suggestions people come up with.
You are using the javascript interval, which can not be guaranteed to fire at an exact time. I am sure that the internal Flash timing is far more reliable.
But this might help, fire recur AFTER you have triggered playing the sound.
window.setTimeout(function() { func(); recur(func, delay); }, delay);
As explained in another answer, there's no way you can avoid this. But...
I've done some experiments to mitigate this issues, and in the end I resorted to using:
lowLag: responsive html5 audio, which uses SoundManager2 for some cases where it's the fastest option available.
GSAP JS – Professional-Grade JavaScript Animation, in order to do the animation of properties and syncing of the audio (you probably don't care about this :P)
Take a peek at the source on the prototype of the demo, and (if possible) give lowLag a shot. It worked nicely for me.
Good luck!
I'd like to create a web based stop motion video player. Basically a slideshow that shows 2-4 images per second. Each image might be a maximum of 20KB. I don't want to preload all images in the slideshow as there might be thousands, however I need to preload more than just the next image in the show as this will not playback fast enough (because of the playback speed the browser needs to be loading more than one image at a time).
I've been looking at using the jQuery Cycle Plugin (http://malsup.com/jquery/cycle/) with a addSlide type function but don't know how to make it work.
Would something like this might work?
-Slideshow starts
-image is played back
-preloader will attempt to load up to the next 60 images
-playback will wait for the next image in line to completely load, but will not wait for all 59 others.
The playback / preloading order is important for this application.
You can create a function that pre-loads N images , when N images are loaded it calls itself again, How much is N ? can be 5 or 10 or you can come up with some formula to calculate N based on expected Images dimensions/Size and time duration of displaying
If all images size are almost the same , first image requested to load should finish loading first, So it wouldn't wait for all 59 others.
plus a variable 'loadedN' that holds index of last loaded image
FireBug is certainly needed to debug this App.
Playback function needs to check if the requested image index is loaded or not
I don't have the exact code in a usable way, but I can explain the framework. I have done something very similar in jQuery-- although I would say it's not so much jQuery as Javascript. Here's the basics of what I did...
Create a function that "preloads" an image. The way to do this in Javascript is simply to create an Image element, eg:
`function preloadAnImage(src) {
var i = new Image(1020, 492);
i.src = src;
}`
Create a list of images, eg. imagesToPreloadIndex = ['image1.jpg','image2.jpg'... ];
Create a function that works off this queue, eg:
function preloadNextImage() {
preloadAnImage(imagesToPreload.pop());
if (imagesToPreloadIndex.length > 0) {
setTimeout(preloadNextImage, 300);
}
}
That gives you the framework.
You'll want this synced up with the actual displaying of the images. This works fine as long as everything goes right. Then you need to decide what to do if you are getting behind in your loading... do you drop a frame or slow down your slideshow? I'm not familiar with a plugin that implements this strategy, but would be fun to implement.
I could imagine that this has to be multiprocessing. What I am thinking of is split the playback logic from the image preload logic. It works like this...
You start an initializer. Once N pictures are preloaded (you can show the dreaded "buffering" screen at this point), you call 2 functions.
Function 1 - shows an image, then starts a timer after which it is called again to show the next image. With 4 images per second, it would call itself every 250ms.
Function 2 - starts a timer. Here you can work with "expected values". You can expect that, for instance, 60 images are done processing after 15 seconds. So, after 7 seconds, you start loading the next 30 or 60 images and add those to the image array, all while the other function is still showing of the already present images.
Of course, you need a limiter on function 1, which stops playback at the Nth-1 element in order to avoid stack overflows or I/O Errors.
Don't know if this was useful, but it is how I understood the question.
Sounds problematic as you'll never be able to control how fast the client can attain the images. To use video would probably be an easier and more satisfactory result.
To make this work in js you'd have to build a buffering object which can pause and resume the playback whenever the count of preloaded images falls under/over a minimal threshold; like flash movies.