These palette cycle images are breathtaking: http://www.effectgames.com/demos/canvascycle/?sound=0
I'd like to make some (or all) of these into desktop backgrounds.
I could use an animated gif version, but I have no idea how to get that from the canvas "animation". Is there anything available yet that can do something along these lines (speficially for that link and generally speaking).
I have a solution but it is dependent on you being familiar with the Javascript Console in Firefox (install the Firebug plugin), Chrome or Safari.
In case you're not, google it, or try to just right click anywhere on the page, choose "Inspect Element" and look for "Console"...
What the code does:
It allows you to take 10 screen-grabs of the CANVAS element every 1/10th of a second. Both these values are easily modified since you can tweak to find the number of iterations you'd like to get. For the example you give, the canvas element ID is 'mycanvas'.
Once it has the screen-grabs it outputs the images into the page. At that point you can save the individual images.
Running the code
Paste in the following code into the Javascript Console:
var canvas = document.getElementById("mycanvas");
var shots = [];
var grabLimit = 10; // Number of screenshots to take
var grabRate = 100; // Miliseconds. 500 = half a second
var count = 0;
function showResults() {
//console.log(shots);
for (var i=0; i<shots.length; i++) {
document.write('<img src="' + shots[i] + '"/>\n');
}
}
var grabber = setInterval(function(){
count++;
if (count>grabLimit) {
clearInterval(grabber);
showResults();
}
var img = canvas.toDataURL("image/png");
shots.push(img);
}, grabRate);
and press CTRL-Enter to execute it.
It should take a few seconds to run so please be patient.
After that you should have all the necessary frames (any maybe more) to create an animated GIF via ImageMagick, this website MakeAGif.com, or other app.
Side Note
If for some reason you need to output as GIF of JPG instead of PNG just update, as needed, this:
var img = canvas.toDataURL("image/png");
to one of these:
var img = canvas.toDataURL("image/gif");
var img = canvas.toDataURL("image/jpg");
Support for output as gif or jpg may not be in all browsers (should be most).
(BIG) UPDATE #1
First, I'm keeping the code above intact rather than updating it because both approaches could be helpful to others.
Second, this new code DOES NOT SOLVE the problem. It kind-of does but one major drawback. It creates an animated GIF (Yipee!) but its in various shades of green (Boooo!). Not sure how/why, but maybe someone can take it from here and see what I've missed.
So here we go... same rules apply - copy and paste it into the Javascript Console of a browser (it lags in Firefox but Google Chrome its pretty fast... 10 seconds or so to run).
var jsf = ["/Demos/b64.js", "LZWEncoder.js", "NeuQuant.js", "GIFEncoder.js"];
var head = document.getElementsByTagName("head")[0];
for (var i=0;i<jsf.length;i++) {
var newJS = document.createElement('script');
newJS.type = 'text/javascript';
newJS.src = 'http://github.com/antimatter15/jsgif/raw/master/' + jsf[i];
head.appendChild(newJS);
}
// This post was very helpful!
// http://antimatter15.com/wp/2010/07/javascript-to-animated-gif/
var w = setTimeout(function() { // give external JS 1 second of time to load
console.log('Starting');
var canvas = document.getElementById("mycanvas");
var context = canvas.getContext('2d');
var shots = [];
var grabLimit = 10; // Number of screenshots to take
var grabRate = 100; // Miliseconds. 500 = half a second
var count = 0;
function showResults() {
console.log('Finishing');
encoder.finish();
var binary_gif = encoder.stream().getData();
var data_url = 'data:image/gif;base64,'+encode64(binary_gif);
document.write('<img src="' +data_url + '"/>\n');
}
var encoder = new GIFEncoder();
encoder.setRepeat(0); //0 -> loop forever, 1+ -> loop n times then stop
encoder.setDelay(500); //go to next frame every n milliseconds
encoder.start();
var grabber = setInterval(function(){
console.log('Grabbing '+count);
count++;
if (count>grabLimit) {
clearInterval(grabber);
showResults();
}
var imdata = context.getImageData(0,0,canvas.width,canvas.height);
encoder.addFrame(context);
}, grabRate);
}, 1000);
It uses some helpful code, pointers and JS files referenced in this blog post JavaScript to (Animated) GIF. I use some JS files directly but you should copy these locally if you're going to use it a lot.
The output for me was this GIF:
So its something, but not what you need...
EDIT: Finally put that code to use, here's the result:
Imagick generated a large image, so I went ahead and optimized with gimp.
The client-side code is a modified version of Michael's code:
var canvas = document.getElementById("mycanvas");
var shots = [];
var grabLimit = 30; // Number of screenshots to take
var grabRate = 100; // Miliseconds. 500 = half a second
var count = 0;
function postResults() {
console.log("START---------");
for (var i = 0; i < shots.length; i++) {
document.write(shots[i]+"<br />");
}
console.log("END-----------");
}
var grabber = setInterval(function(){
count++;
if (count>grabLimit) {
clearInterval(grabber);
postResults();
}
var img = canvas.toDataURL("image/png");
shots.push(img.replace("data:image/png;base64,",""));
}, grabRate);
It will write a bunch of base64 strings to the screen. Copy them and save them into a text file and then upload it to your web server. Then run the other script (see below) and it will write the image to your web server. The resulting image will be large and possibly choppy, so open up GIMP and optimize for difference and GIF. When saving, force it to use the same delay for all frames so the animation is smooth.
May not be too hard using PHP.
Grab the dataURL (base64 encoded string) for each frame with JS and send it to the server.
Decode the base64 strings and convert them to Imagick objects.
Add these Imagick objects as frames to a new Imagick object.
Save the Imagick object to the file system as a GIF.
Since michael already posted a nice JS solution, I'll add the server side code if you wish to automate it:
<?php
$imageData = array_map('rtrim', file('data.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES)); //base 64 strings separated by newlines
$delay = 100;
$filename = 'coolmoviebro.gif';
$gif = new Imagick();
for($i = 0; $i < count($imageData); $i++) {
$tempImg = new Imagick();
$tempImg->readimageblob(base64_decode($imageData[$i]));
$gif->addImage($tempImg);
}
$gif->setFormat('gif');
$gif->setImageDelay($delay);
$gif->writeImages($filename, true);
?>
I haven't written much PHP for a year or two so be sure to double check everything and so on.
I took a look at the code, and it seems like it should be possible with a little hacking.
By looking at the list of cycle objects in Palette.Cycles, you should be able to figure out the cycle length of each cycle with the cycle.high, cycle.low, and cycle.rate fields. That said, you'll need a different algorithm for each of the six possible values for cycle.reverse.
Once you know the length of each cycle in the list (in milliseconds, if I'm reading the code correctly), you can find the least common multiple of all of the cycle lengths, which would tell you the total length of the animation. In practice, though, you'd want to floor-divide them by your sample period first, (say, 100 milliseconds for ten frames a second) in order to get a lower common multiple.
Then rig the animate function in main.js to take a tickCount parameter and pass that into palette.cycle, instead of using anything based on real time. Increase the tick count by your sample period with each iteration.
From there, you should be able to modify the Bitmap class's render method, adding the necessary logic to rip the canvas to a file. There appear to be libraries that can manage this last bit for you. I would recommend saving the files using the tick count as the file name (with enough leading zeros to keep them in order.) Stitching all the images together into an animated GIF might be possible to execute as a batch job using the right software.
Of course, I haven't actually tried this. You might want to put in checks, for instance, to make sure that you don't stumble upon an animation with an epic cycle length and creation millions of images on your hard drive.
As an aside, you could also, with a little more work, figure out the exact time until the next update and take irregular samples, but you'd have to figure out how to store that delay information such that you could use it to assemble the completed GIF.
Sadly, according to the art creator, it is not quite possible to convert it to GIF animation due to different parts of the picture having different cycles.
Try PhantomJS
This script saves 100 frames.
var webPage = require('webpage');
var fs = require('fs');
var page = webPage.create();
var NB_FRAME = 100;
var current = 0;
page.open('http://www.effectgames.com/demos/canvascycle/?sound=0',
function(status) {
if (status === "success") {
var current = 0;
var grabber = setInterval(function () {
var frame = page.evaluate(function() {
return document.getElementById('mycanvas').toDataURL("image/png").split(",")[1];
});
fs.write("./frame-" + current + ".png",atob(frame), 'wb');
if (++current === NB_FRAME) {
window.clearInterval(grabber);
phantom.exit(0);
}
}, 1000);
}
});
Run it:
phantomjs SaveCanvasFrame.js
Then use ImageMagick
convert *.png animated.gif
Here we go :
Related
Before I start on this, I really want to avoid using Jquery for reasons I won't go into, so please don't advise me to, as that is not what I am looking for.
I have web page in development which sends multiple ajax requests off and each one returns a lump of html containing an outer div containing inner divs and images. The issue I have is the html returned is showing on the screen before the images within it are finished rendering, giving me a couple of seconds of broken images, which looks amateur.
Is there a way that anyone knows of (without JQuery), that I can programmatically inspect everything within the outer div (possibly using recursion as there are several embedded inner divs etc) and only show the div if all the contents have finished rendering?
var fakeResponse = "<div class=\"outer\"><div class=\"inner\"><img src=\"http://upload.wikimedia.org/wikipedia/commons/5/58/Sunset_2007-1.jpg\" /></div></div>",
fakeDiv = document.createElement("div"), // here we will store the response, so we can use the .getXXX functions
images,
imagesReady = 0,
i,
length,
callback = function() { // this will help us to determine if all images have been loaded and to show the response if the preloading has finished
imagesReady++;
if (imagesReady == length) {
document.body.appendChild(fakeDiv.firstChild);
}
};
fakeDiv.innerHTML = fakeResponse; // let the browser convert the response into dom nodes
images = fakeDiv.getElementsByTagName("img"); // get all the included images
for (i = 0, length = images.length; i < length; i++) { // then preload each picture
var img = new Image();
img.onerror = callback; // add event handlers to determine the state of processing
img.onload = callback;
img.src = images[i].src; // init the actual loading
}
I have a web page which contains an iframe, the iframe contains a table which contains up to 1,000 canvas elements. The canvas elements are filled by a function defined in the iframe, which is called by the parent page. The function iterates and fills each canvas with a base64 data URI contained in a hidden span in the parent page. I have read a few other Stackoverflow posts regarding similar issues but I have already tried the most commonly used suggestion (using onload) and that does not seem to work. I am fairly fresh to html/javascript so I doubt I am doing things the best way. I was hoping someone could guide me on how to assure that all the canvas elements are successfully filled with the base64 data on the first load so I don't need to refresh multiple times to get me thumbnails? Here is some code, it is VERY trimmed up code from my html page. If I were to paste the entire page, it might be a bit too much too handle sensibly. Here is what I believe to be the essential actors.
My onload call to my function which fills the canvases, this is on the main page which contains the iframe -
<body onload="document.getElementById('framePage').contentWindow.fillCans();">
Here is the fillCans() function which is contained within the framePage iframe (I probably should move this to the main html page, but so far this is how the script has... evolved haha). pgCnt element contains the "counter" numbers if you will. It will defines how many elements to iterate through. When var c is assigned, the "can"+i elements are the canvases. When var imgSrc is assigned, the "span"+i elements are the hidden span elements on the parent page which contain the base 64 data URIs -
function fillCans() {
var cnt = document.getElementById("pgCnt").innerHTML;
var cnt = cnt.split("-");
var cnt1 = cnt[0];
var cnt2 = cnt[1];
for (var i=cnt1; i <= cnt2; i++) {
var targ = "span"+i
var c = document.getElementById("can"+i);
var ctx = c.getContext("2d");
ctx.fillStyle="#FFFFFF";
ctx.fillRect(0,0,128,128);
var image = new Image();
var imgSrc = parent.document.getElementById("span"+i).innerHTML;
imgSrc = imgSrc.split("*");
image.src = imgSrc[0];
ctx.drawImage(image,0,0);
}
Like I said, this will load fine, firebug reports no errors, however all the canvases are white with no thumbnails. When I hit refresh a few times they all finally load. I've read that the image data is asynchronously loaded but I am not certain to as what that means, I assume it means it doesn't play by the rules that onload plays by? Anyways as usual, thanks for all the help stackoverflow community. It will be great to know how to get these thumbnails to load successfully on the first page load.
After #Sebastian's great suggestions I was able to get my JavaScript functioning as it should. I am pasting in the new function. As it works now, in case anyone ever comes across a similar issue. I had to implement an onload call to a function which drew the image, and then utilize in IIFE for loop to insure that each thumbnail was drawn with correctly iterated data.
function fillCans() {
var cnt = document.getElementById("pgCnt").innerHTML;
var cnt = cnt.split("-");
var cnt1 = cnt[0];
var cnt2 = cnt[1];
for (var i=cnt1; i <= cnt2; i++) {
(function(iterable){
var c = document.getElementById("can"+iterable);
var ctx = c.getContext("2d");
ctx.fillStyle="#FFFFFF";
ctx.fillRect(0,0,128,128);
var image = new Image();
var imgSrc = parent.document.getElementById("span"+iterable).innerHTML;
imgSrc = imgSrc.split("*");
image.onload = function() {
ctx.drawImage(image, 0, 0);
};
image.src = imgSrc[0];
})(i);
}
}
You are right, the problem is that the image has to be loaded first, before you can draw it.
In your case image is not loaded at the time you call
ctx.drawImage(image,0,0);
However later on the browser has cached the image URL and in between the calls to the assignment of the source property and the drawImage call the browser has already retrieved the image from the cache.
To make it work in any case, you need to wait for the image to load:
image.onload = function() {
ctx.drawImage(image, 0, 0);
};
image.src = imgSrc[0];
Be sure to first register the callback and then set the src.
See HTML Canvas DrawImage Tutorial
i remove the link for copyright reason !.. sorry !
When you are in Firefox, the images at the left (all the mockup) is loaded, after a few refresh, in chrome and safari, it NEVER show
I think it's a image not loaded in memory problem, but i cant know when image are all loaded, i event put the script at the end, but no luck
So the question, what should do to make sur the images are loaded.. of is there and error in the JavaScript code ?
n.b.
i have tough about encoding the images as base64 for canvas display... is it possible or intelligent to do that ?
Actually, you can determine when all the images have finished loading. In order to do this, you just need to specify a callback function for the onload property of the image object. So, you would end up with something like this (in addition to the code you already have in canvas.js):
var loaded_images = 0;
var image_objects = [];
// This is called once all the images have finished loading.
function drawOnCanvas() {
for (var i = 0; i < image_objects.length; ++i) {
ctx.drawImage(image_objects[i], 0, 0);
}
}
function handleLoadedImage() {
++loaded_images;
// Check to see if all the images have loaded.
if (loaded_images == 7) {
drawOnCanvas();
}
}
document.ready = function() {
for (var i=0;i<myimages.length;i++) {
var tempimage = new Image();
tempimage.src= myimages[i];
tempimage.onload = handleLoadedImage;
image_objects[i] = tempimage;
}
}
The key concept is that you are keeping track of the number of images that have finished loading. Once all of the images are done loading, you know you can draw on the canvas.
I'm by no means any kind of coder or programmer, but I've been trying to load and display some gifs so that they all animate from the beginning at the same time. In other words, I need them to be synchronised.
I've done a lot of Googling and what I've come up with seems to work with Chrome/Safari and Firefox, but as usual, Internet Explorer refuses to cooperate.
My current code is this:
var images = ["thephoto1", "thephoto2", "thephoto3", "thephoto4", "thephoto5"];
function initImages() {
for (var i = 0; i < images.length; i++) {
imageId = images[i];
image = document.getElementById(imageId);
image.style.visibility = "visible";
}
}
function preloadImages(urls) {
var img = new Array();
for (var i = 0; i < urls.length; i++) {
img[img.length] = new Image();
img[img.length - 1].src = urls[i];
}
}
window.onload = function() {
var img = new Array(
"http://desmond.imageshack.us/Himg391/scaled.php?server=391&filename=countdown.gif&res=medium",
"http://icanhascheezburger.files.wordpress.com/2010/11/funny-pictures-gif-cat-love.gif",
"http://i52.photobucket.com/albums/g9/cpchick/Random%20Gifs/therock.gif",
"http://www.mobileapples.com/Assets/Content/Screensavers/dc_1owy86mw.gif",
"http://www.pptbackgrounds.fsnet.co.uk/images/powerpoint-countdown-u1.GIF"
);
preloadImages(img);
initImages();
}
With some added CSS:
.preload
{
visibility: hidden;
}
It's basically this and this script combined.
You can view a live example here: http://jsfiddle.net/AN9uB/
Some possible methods or potentially helpful posts:
Restart an animated GIF from JavaScript without reloading the image.
Javascript to only display animated gif when loaded.
However from reading the comments on those links, I'm not entirely sure they're possible.
The test gifs I'm using are just random images I found and a small forewarning, some of the images are fairly large since I needed to test a variety gifs ranging in size and duration. All of the gifs loop except for the first countdown image which only plays once.
On some occasions the first countdown image doesn't play at all (it's stuck on 10) when viewing the page with Firefox 3.6.18, but otherwise the rest of the gifs all load and display at the same time.
The main problem is that I cannot think of a way to make this work with Internet Explorer. I thought perhaps to preload the images and then refresh the page via javascript. It's an ugly solution, but I think it would work.
Flash is the obvious tool to be doing this kind of thing in, but the friend who I'm making this for only uses gifs.
Is there a more elegant solution that works across all major browsers? Also ideally, to only use Javascript, not JQuery.
Thanks for any help.
Well, you're not really preloading the images, because you're only calling the preloadImages function after the pages has loaded, i.e. after the actual IMG tags in the html have been read, and the browser's probably started downloading them. The last part may depend on the visibility:hidden style, but I'm not sure - at least I don't expect all browsers to agree on what to do in that case.
Also, you have the URLs defined both in the JavaScript, and in the HTML. That's both redundant and also harder to change later.
I must admit I have no idea if this will work right everywhere, but you might try
only having the image URLs in JavaScript - you can then add then
into the HTML, when they're ready
using the onload event handler
on the JS Image objects to assert that an image has been loaded.
Once they've all loaded, add them to the document (i.e. the page).
Now, I don't know when a given browser starts the animation-clock on a gif. If it starts the moment the gif has been loaded, there's not much you can do, since the gif's won't load at the same time. If, however, they first start animating when they're placed in the document (which seems probable, but never trust a browser), then there's a chance.
// This function will add an array of images to div#images
function showImages(images) {
var container = document.getElementById("images"); // get the #images div
for( var i = 0, l = images.length ; i < l ; i++ ) {
var img = document.createElement('IMG'); // create the IMG tag
img.src = images[i].src; // set the src - the image should be preloaded when this happens
container.appendChild(img); // add the IMG tag to the #images div
}
}
// This one will create JS Image objects from an array of URLs
function loadImages(urls) {
var remaining = urls.length; // the images still waiting to be fetched
var images = []; // empty array to hold the created Image objects
// this function will be called for Image object that is loaded
var onloadHandler = function() {
remaining--; // decrement the number of remaining images
if( remaining < 1 ) {
showImages(images); // if all images have loaded, add them to the page
}
};
// create an Image object for each URL
for( var i = 0, l = urls.length ; i < l ; i++ ) {
var img = new Image();
img.onload = onloadHandler; // set the onload-handler before setting the src
img.src = urls[i];
images.push(img); // add the Image object to the images array
}
}
window.onload = function() {
var urls = [
"http://desmond.imageshack.us/Himg391/scaled.php?server=391&filename=countdown.gif&res=medium",
"http://icanhascheezburger.files.wordpress.com/2010/11/funny-pictures-gif-cat-love.gif",
"http://i52.photobucket.com/albums/g9/cpchick/Random%20Gifs/therock.gif",
"http://www.mobileapples.com/Assets/Content/Screensavers/dc_1owy86mw.gif",
"http://www.pptbackgrounds.fsnet.co.uk/images/powerpoint-countdown-u1.GIF"
];
loadImages(urls);
};
Here's a jsfiddle of it all: http://jsfiddle.net/Frqys/2/
Again, I have no idea if this'll actually work in every browser. It seems ok in Safari, Chrome and Firefox (all on Mac OS), but it's impossible to be sure. I'd advise you to copy a gif file a number of times, and give each file a different name, so it'll have a unique URL. That should prevent it from caching, and keep its animation clock separate. Then try loading all those GIFs instead of a bunch of different ones, and see if they stay in sync.
I'm trying to preload a number of images generated on the server to a small website. The preloading is done using setWindowTimeout and uses an Image object, sets the onload callback and then applies the new request uri.
For some requests, the server may have to signal that the image is 'unchanged' and I'm doing it by sending done a small 1x1 pixel gif (seems like I need to send an actual image, returning empty content will cause the Image object to not fire onload). In my onload handler I would like to determine the size of the fetched image and then determine if I should update the visual image with the given image.
Below is a snippet of my current solution (the output is a div to help debug):
refresh: function() {
var newSrc = '/screenshot.ashx?tick=' + new Date().getTime();
var imgObj = new Image();
var self = this;
// this is called when load is done
imgObj.onload = function() {
//if (imgObj.complete)
// return;
if (imgObj.width > 1 && imgObj.height > 1) {
output.innerHTML += '<br/>Updating image:' + newSrc;
img.src = newSrc; //fiddler shows no reload here, read from cache
}
else {
output.innerHTML += '<br/>Empty image:' + newSrc;
}
self.setupNewRefresh();
};
output.innerHTML += '<br/>Loading image:' + newSrc;
imgObj.src = newSrc;
},
I seem to have two problems:
a) the imgObj.complete is false when the function is first called but it is only called once (hence my commenting it out) and
b) I can't seem to rely on the width or height property of the image when loaded. From my tests of fetch the 1x1 pixel, it sometimes reads out 50 which seems to be default when creating a new Image() and it sometimes reads out the correct size of 1.
My ultimate goal is to have a small chunk of javascript logic that queries the server for a new image periodically, does nothing if nothing new has happened (1 pixel image) or loads the new image. I might be going about it the wrong way here or have overlooked important properties or calls, so I'm happy to receive feedbacks.
EDIT: upon suggestion from mplungjan, I preloaded into a hidden div instead, which helped me with problem b). Still no solution to problem a) though; reports complete = false once and is not called again
I'll go ahead and answer my own question.
Preloading the image into a hidden element in the DOM instead of a code-level element was the trick for actually getting correct sizes (thanks mplungjan). The a) issue, namely the complete event, remains unsolved.
As a side note I ended up using the XMLHttpRequest instead as it allowed by to look at the size of the payload returned.