Make GIF in Javascript from multiple images without any library - javascript

Is there any way to make GIFs in Javascript without any library? I've searched half the internet and found nothing.

Note to potential downvoters
I know that this is a low-quality question. However, I believe that the usual reasons for not answering have been invalidated. Seeing as it has been almost a month since it was asked, I doubt I'll be doing their homework as it would probably be past due for weeks at this point. And as for them not taking the time to research this myself, I'm fixing the problem at its source by teaching them how to research it.
Answer
Note that I'm answering this assuming that you know some Javascript, but not all of it, and that you want to learn how to write code that you might not yet know how to do. If you're just looking for the solution, then feel free to copy-paste it or whatever. But reading through this and understanding the process I used to tackle this problem, like how to think through it and look things up, will help you in the future. If you learn how to learn, you'll be a lot better off.
Now, first of all, we need a way to display images. I know from experience that there are two main methods to do this. One involves using the canvas element to draw the image; this is the more complicated method. Method 2 uses the image tag, <img>. I assume this is the method you want. If you didn't know how to draw an image using HTML and Javascript, a simple google search (here's another) can clear that up. (Actually, the second one mostly returns the canvas method-- but you probably knew about the image tag already anyways.)
Next we need a way to change what image is displayed. Well, that should be as simple as changing the thing that tells the image what to display in the first place. The top result from the first example search is a nice documentation page-- perfect. Reading through, I get to this part:
HTML Images Syntax
In HTML, images are defined with the tag.
The tag is empty, it contains attributes only, and does not have a closing tag.
The src attribute specifies the URL (web address) of the image:
<img src="url">
Well that looks like it's what controls what the image is. So we simply have to make a Javascript script that changes this property regularly. To do this right, we need a JS method that will wait for a specified amount of time. Searching "javascript wait" in Google, I see the second result is from the same source webpage that told me about images, so I'll go with that one. In the docs, I see this:
The setInterval() Method
The setInterval() method repeats a given function at every given time-interval.
window.setInterval(function, milliseconds);
The window.setInterval() method can be written without the window prefix.
The first parameter is the function to be executed.
The second parameter indicates the length of the time-interval between each execution.
(It even gives an example!)
This seems to be what we want. Using this method, I can make a simple code to change between two images every second (explanatory comments to the right, scroll to see them better):
function changePicture(){ //Make the function that will change the pictures
var picture = document.getElementById('picture1'); //Get the picture element from the HTML
if(picture.src == "https://vignette.wikia.nocookie.net/starpolar/images/9/96/Paul_blart.jpg/revision/latest?cb=20150419171412"){ //If it's the Paul Blart picture then:
picture.src = "https://i.ytimg.com/vi/A5KWYdrOXDk/movieposter.jpg"; //Set it to the Bee Movie
} else { //If it's not Paul Blart (and is therefore probably the Bee Movie image):
picture.src = "https://vignette.wikia.nocookie.net/starpolar/images/9/96/Paul_blart.jpg/revision/latest?cb=20150419171412"; //Set it to Paul Blart
}
}
setInterval(changePicture, 1000); //Use the new method we learned about to schedule our picture-changing function to happen once every second
<!--This is the image that we will change.-->
<img id="picture1" src="https://vignette.wikia.nocookie.net/starpolar/images/9/96/Paul_blart.jpg/revision/latest?cb=20150419171412"/>
Now, that's a good start-- we can change between 2 different totally definitely random images.
But we need more. We don't want to code in every new image manually; what we need is an array. It's hard to know that through a search, but if you've taken a primer course or learned some JS on your own you should be pretty familiar with arrays and would probably realize that they are a good option here. So we set up the array with all the images we want it to change between (scroll to the right to see it all):
var gifImages = ["https://vignette.wikia.nocookie.net/starpolar/images/9/96/Paul_blart.jpg/revision/latest?cb=20150419171412","https://i.ytimg.com/vi/A5KWYdrOXDk/movieposter.jpg","http://content.tv3.ie/content/images/0647/1_165781.jpg","https://upload.wikimedia.org/wikipedia/en/thumb/4/47/Spongebob-squarepants.svg/1200px-Spongebob-squarepants.svg.png","http://i0.kym-cdn.com/entries/icons/mobile/000/016/958/Dankkkk.jpg"];
Then we need to change our original function. It should cycle through this array of images; to do this, we'll need to make a variable to hold the position in the array.
var currentFrame = 0; //Call it a frame because we're making a gif and every image (so every array index) will be a frame
function changePicture(){
currentFrame++;
}
This should work for now, let's test it:
//Set up the array of frames
var gifImages = ["https://vignette.wikia.nocookie.net/starpolar/images/9/96/Paul_blart.jpg/revision/latest?cb=20150419171412","https://i.ytimg.com/vi/A5KWYdrOXDk/movieposter.jpg","http://content.tv3.ie/content/images/0647/1_165781.jpg","https://upload.wikimedia.org/wikipedia/en/thumb/4/47/Spongebob-squarepants.svg/1200px-Spongebob-squarepants.svg.png","http://i0.kym-cdn.com/entries/icons/mobile/000/016/958/Dankkkk.jpg"];
var currentFrame = 0; //Call it a frame because we're making a gif and every image (so every array index) will be a frame
function changePicture(){
document.getElementById("picture1").src = gifImages[currentFrame]; //Get the gif element and set its source to the current frame
currentFrame++; //Increase the current frame by 1
}
setInterval(changePicture,1000);
<!--This is the image that we will change.-->
<img id="picture1" src="https://vignette.wikia.nocookie.net/starpolar/images/9/96/Paul_blart.jpg/revision/latest?cb=20150419171412"/>
You should notice 2 things. The first is that the images have wildly different sizes and length/width ratios, resulting is the image changing size every time. The second thing you should notice is that when we run out of images, it doesn't loop back. Oops! (Side note: I actually made this mistake. Don't be discouraged by mistakes, we all make them!)
To remedy the first problem, we can look back to the HTML images documentation and see that there are two properties that can help us: width and height. These will lock the image into the specified dimensions. Frame sizes shouldn't be much of a problem with an actual gif, since the frames would probably all be the same size, but it doesn't hurt to be safe:
<img id="picture1" width="100px" height="100px" src="https://vignette.wikia.nocookie.net/starpolar/images/9/96/Paul_blart.jpg/revision/latest?cb=20150419171412"/>
The second problem is a bit harder. But looking through the code, we should be able to spot the bug fairly quickly. (Note: An experienced developer such as myself might be able to see a problem like this in a small, simple program almost instantly. Don't be discouraged if you can't; we all started somewhere and becoming a 1337 hax0r comes with time.)
If we look at how our program operates, we could say it works like this:
Loop forever:
Set the image to the current frame
Increase the frame
It becomes apparent that while we have a limited number of frames, we keep increasing the current one without going back down! Our progress through the gif would look like this (skipping to every 3rd frame and assuming 10 frames total):
|---------
---|------
------|---
---------|
---------- | <-- ???? oh no
This can be solved with a simple if statement that resets the current frame to 0 when we go past the end of our gif (line above is also shown for clarity):
currentFrame++; //Increase the current frame by 1
if(currentFrame >= gifImages.length){ //If we've gone past the end of our array of frames, then:
currentFrame = 0; //Reset back to frame 0
}
With those changes, our gif looks like this:
//Set up the array of frames
var gifImages = ["https://vignette.wikia.nocookie.net/starpolar/images/9/96/Paul_blart.jpg/revision/latest?cb=20150419171412","https://i.ytimg.com/vi/A5KWYdrOXDk/movieposter.jpg","http://content.tv3.ie/content/images/0647/1_165781.jpg","https://upload.wikimedia.org/wikipedia/en/thumb/4/47/Spongebob-squarepants.svg/1200px-Spongebob-squarepants.svg.png","http://i0.kym-cdn.com/entries/icons/mobile/000/016/958/Dankkkk.jpg"];
var currentFrame = 0; //Call it a frame because we're making a gif and every image (so every array index) will be a frame
function changePicture(){
document.getElementById("picture1").src = gifImages[currentFrame]; //Get the gif element and set its source to the current frame
currentFrame++; //Increase the current frame by 1
if(currentFrame >= gifImages.length){ //If we've gone past the end of our array of frames, then:
currentFrame = 0; //Reset back to frame 0
}
}
setInterval(changePicture,1000);
<!--This is the image that we will change.-->
<img id="picture1" width="100px" height="100px" src="https://vignette.wikia.nocookie.net/starpolar/images/9/96/Paul_blart.jpg/revision/latest?cb=20150419171412"/>
Now all we need to do is get the right frames into the array and speed it up! For a real gif, assuming this is a website you're making, the files will probably be named something like gif-frame-1, gif-frame-2, gif-frame-3, etc. Because of this, we can automate making the array, putting it into a for loop:
var gifImages = [];
for(var i=0;i<200;i++){ //Change 200 to the number of frames your gif has
gifImages[i] = "pictures/gif-frame-"+i+".png";
}
That code probably won't work with your website right away-- you have to change around the names of the images and number of frames and things-- but it should help you. If you specify where these pictures are, how they're named, how many there are, etc. then I could help you more, but I don't have enough information to write that part. Also note that this can be sped up about 50 times faster than it's going at the moment, but it looks weird without real frames to be used. For now, we can consider our final code to be:
//Set up the array of frames
var gifImages = ["https://vignette.wikia.nocookie.net/starpolar/images/9/96/Paul_blart.jpg/revision/latest?cb=20150419171412","https://i.ytimg.com/vi/A5KWYdrOXDk/movieposter.jpg","http://content.tv3.ie/content/images/0647/1_165781.jpg","https://upload.wikimedia.org/wikipedia/en/thumb/4/47/Spongebob-squarepants.svg/1200px-Spongebob-squarepants.svg.png","http://i0.kym-cdn.com/entries/icons/mobile/000/016/958/Dankkkk.jpg"];
var currentFrame = 0; //Call it a frame because we're making a gif and every image (so every array index) will be a frame
function changePicture(){
document.getElementById("picture1").src = gifImages[currentFrame]; //Get the gif element and set its source to the current frame
currentFrame++; //Increase the current frame by 1
if(currentFrame >= gifImages.length){ //If we've gone past the end of our array of frames, then:
currentFrame = 0; //Reset back to frame 0
}
}
setInterval(changePicture,100);
<!--This is the image that we will change.-->
<img id="picture1" width="100px" height="100px" src="https://vignette.wikia.nocookie.net/starpolar/images/9/96/Paul_blart.jpg/revision/latest?cb=20150419171412"/>
It's possible to touch it up (for example, you could move the function inside the setInterval itself) but this should work fine.
I hope this answer has helped you to learn how to figure these things out!

I ended up just writing a script to replace the image in a div every second. It's not technically a gif, but it works very similarly.
eval('var frame' + word + ' = 0;');
var bigFrame = 0;
setInterval(function(){ //everysecond
var noSpaces = input.replace(/\s/g, '');
var cLetter = noSpaces[bigFrame];
var thisSource = 'media/signs/alphabet/' + cLetter + '.jpeg';
$('#video').attr("src", thisSource);
$('#video').attr("alt", thisSource);
$('#video').attr("height", "350");
if(bigFrame + 1 >= noSpaces.length){
bigFrame = 0;
} else{
bigFrame = bigFrame +1;
}
for(word = 0; word < wordList.length; word ++){ //for every word
var currentFrame = eval('frame' + word);
currentWord = wordList[word];
currentWord = currentWord.toLowerCase();
console.log('current word is ' + currentWord);
var letterList = currentWord.split('');
var currentLetter = letterList[currentFrame];
var currentSource = 'media/signs/alphabet/' + currentLetter + '.jpeg';
var currentImage = "#" + eval('"image' + word + '"');
$(currentImage).attr("src", currentSource);
$(currentImage).attr("alt", currentSource);
$(currentImage).attr("height", "200");
if(currentFrame + 1 >= letterList.length){
eval('frame' + word + ' = 0');
} else{
eval('frame' + word + ' = currentFrame + 1;');
}
}
}, 1000);
I don't know if this is helpful, or even understandable, but it might help someone. Also, I haven't bothered to edit the code, so there are definitely bits that won't make sense and weird variable names.

Related

Random image with text when refresh

I am a java script beginner and I am trying to design a web page that shows two random numbers when refresh. According to these numbers a specific text should appear.
The numbers are images between 0 - 5.
as far, I wrote this code and it worked to change the images when refresh
var images1= new Array ("images/0.png","images/1.png","images/2.png","images/3.png",
"images/4.png","images/5.png");
var images2= new Array ("images/1.png","images/5.png","images/0.png","images/3.png",
"images/4.png","images/2.png");
function RandomImg() {
var x = Math.floor(Math.random() * images1.length);
var img1= document.getElementById('image1');
img1.src = images1[x];
var img2= document.getElementById('image2');
img2.src = images2[x];
}
RandomImg();
after that, according to the images appear, if the first number in the image is bigger than the second, a text should appear and and vice versa.
So my questions are "Is it possible to link each image to a number then execute if statement? or am i going in the wrong direction regarding this ? and how is possible to do that ?"
I did a lot of research but didn't reach anything clear since a week :(
Thanks :)
Yes, it is possible. You can use map in JavaScript to make a key value pair.
Just study about map and implement it.
https://developer.mozilla.org

Simple Collision Detection in Javascript / Jquery?

I am working on a portion of a project that I am trying to detect when certain divs hit each other. In the code that I made, that doesn't work, I basically say take the first div's left amount, compare it to the other div's left amount, if they are within a certain amount it triggers an alert. If I get that much to work I am going to implant a way to say that if the distance between the two divs is 0 then it will run a certain function. I am afraid the scope of this project is too big for me, even though I am basically at the last part, because I have spent hours researching a simple way to add collision detection, but everything I find looks like rocket science to me, that is why I tried to create my own way below. So in summary, what I want to know is why my collision detection code doesn't work, how I can make it work if possible, and if not possible what is the next best option that I should use.
//Collision
function collision(){
var tri = $('#triangle');
var enemyPos = $('.object1').css('left');
var minHit = enemyPos - 32.5;
var maxHit = enemyPos + 32.5;
var triLoc = tri.css('left');
if(triLoc > minHit && triLoc < maxHit){
alert('hit');
}
}
collision();
}
}
full code: https://jsfiddle.net/kc59vzpy/
If the code you have above is definitely where the problem is, then you need to look at the enemyPos variable. Getting the left position also adds px, so enemyPos is 100px or something like that. When you add 32.5, you get 100px32.5 and when you subtract you get NaN, neither of which you want.
Before you add or subtract, use enemyPos = parseInt($('.object1').css('left')); to turn it into an actual number.

Why does my image preloading method work?

My Question
The goal is to make sure all images are fully loaded before a new game can begin. My second solution (Fiddle B) achieves this goal more consistently and accurately than my first solution (Fiddle A). Why?
JSFIDDLE A
JSFIDDLE B
Methods
Here is what both of my fiddle solutions do to preload images:
There is an array of absolute image URLS for all of the assets required by the game
The init() function has a for loop which generates a new canvas Image() per URL in the array
Each newly created image is pushed into another array
So, we now have the first array containing URL strings, and a second array containing 'HTMLImageElement' objects
Fiddle B differs from Fiddle A, in that it utilises the '.onload' event, plus a counter. The two fiddles use different ways of checking to see if all the image assets have loaded:
Fiddle A: compares the length of the two arrays. If they match, start the game
for (var i = 0; i < allGameImageUrls.length; i++) {
var img = new Image();
img.src = allGameImageUrls[i];
allGameImages.push(img);
console.log(allGameImages.length);
if (allGameImages.length >= allGameImageUrls.length) {
setUpGame();
} else {
// images haven't loaded yet
console.log('STILL LOADING');
STAGE.fillText("Loading ...", 20, 400);
}
}
Fiddle B: compares the second array length with the counter variable. This counter goes up by 1 every time an image's '.onload' event completes.
for (var i = 0; i < allGameImageUrls.length; i++) {
var img = new Image();
img.onload = function () {
assetCount++;
console.log('assetCount = ' + assetCount);
setUpGame();
};
img.src = allGameImageUrls[i];
allGameImages.push(img);
}
My Question Expanded
Fiddle A frequently (but not always) triggers the start of a new game before the full list of image assets has been properly loaded, causing game errors. Fiddle B consistently loads all of the image assets before allowing a new game to start. This can be seen in both fiddles from the 'Fully loaded' messages written to the canvas.
Although I can see that Fiddle A works better than Fiddle B, I don't understand why it is superior. Not all tutorials relating to loading HTMLImageElements use '.onload', and the tutorials that don't use it seem perfectly adequate.
Additionally, I don't understand why comparing the lengths of two arrays is not as accurate as comparing the second array length to a counter.
I understand how the two solutions differ, but I want to know why solution B works better than solution A.
Previous Research
Here are just a few examples of previous research I have done in order to try to answer my own question.
An article on pre-loading images which doesn't have any reference to an .onload event
The accepted solution to this question does not use .onload event, but it still works
The accepted solution to this other question is very similar to my Fiddle B (although I discovered it much later). However, the explanation of the solution hasn't helped me to answer my own question.
You are comparing two very different approaches: sequential with for (which is incorrect) and event-based.
Image downloading is asynchronous process so when the image src property is set the browser starts downloading it. It can be, however, very fast especially if the image was already had been downloaded by the browser and cached internally (in fact, it is blazingly fast). So when the next iteration starts it is already available (or, at least, almost all of them are available at the end of the loop). But if you clear the cache or use incognito mode and download them from the remote location (not your local server) then boom! - the loop ends with no image downloaded at all.
Another approach slightly better but the game is set up for every image downloaded, which is probably do not what is required.
Consider the following approach:
var length = allGameImageUrls.length,
count = 0;
var i, img;
for (i = 0; i < length; i++) {
img = new Image();
img.onload = function () {
count++;
// count is increased on every callback
// so if number of executed callbacks equals
// the number of images then all the images
// are downloaded
if (count === length) {
setUpGame();
}
};
img.src = allGameImageUrls[i];
allGameImages.push(img);
}
The only drawback is if one of the images does not exist, the game never starts so you need to workaround it with timeout.

Sprite Animation using a for loop

I am trying to create an animation using a sprite sheet and a for loop to manipulate the background position until it has reached the total number or rows in the sheet. Ideally a reset back to the initial position would be practical, but I cannot even get the animation itself to trigger...
With the current function, no errors occur and the background position in my CSS does not change. I even recorded using Chrome DevTools Timeline and there was nothing either then everything related to my page loading. I have also tried using "background-position-y" as well as a simpler value rather then the math I currently have in place.
This is my function:
$(document).load(function() {
var $height= 324;
var $rows= 34;
for(var i=0; i<$rows; i++){
setTimeout(function() {
$('#selector').css("background-position", "0px ", "0" - ($height*i) + "px");
}, 10);
}
});
I hate to ask a question that is similar to previous issues, but I cannot seem to find another individual attempting sprite sheet animation with a for loop, so I suppose it is it's own problem.
p.s. I didn't include a snippet of my HTML and CSS because it is pretty standard and I don't see how that could be the problem. That being said, I am all ears to any potential thoughts!
I am completely revamping my answer
This issue is that the for() loop is not affected by the setTimeout so the function needs to be written on our own terms, not with a loop
Working Fiddle
Here it is..
var $height= 5;
var $rows= 25;
var i = 1; // Starting Point
(function animateMe(i){
if(i<=$rows){ // Test if var i is less than or equal to number of rows
var newHeight = 0-($height*i)+"px"; // Creat New Height Position
console.log(i); //Testing Purposes - You can Delete
$('#selector').css({"background-position": "0px "+ newHeight}); // Set New Position
i++; // Increment by 1 (For Loop Replacement)
setTimeout(function(){animateMe(i)}, 1000); // Wait 1 Second then Trigger Function
};
})(0);
Here is your solution
First Change
$(document).load() To $(document).ready()
And Change .css Syntex as
$('#selector').css("background-position",'0px '+(0 - ($height*i))+'px');
Here is fiddle Check it ihad implemented it on my recent project http://jsfiddle.net/krunalp1993/7HSFH/
Hope it helps you :)

Improving Efficiency in jQuery function

The while statement in this function runs too slow (prevents page load for 4-5 seconds) in IE/firefox, but fast in safari...
It's measuring pixel width of text on a page and truncating until text reaches ideal width:
function constrain(text, ideal_width){
$('.temp_item').html(text);
var item_width = $('span.temp_item').width();
var ideal = parseInt(ideal_width);
var smaller_text = text;
var original = text.length;
while (item_width > ideal) {
smaller_text = smaller_text.substr(0, (smaller_text.length-1));
$('.temp_item').html(smaller_text);
item_width = $('span.temp_item').width();
}
var final_length = smaller_text.length;
if (final_length != original) {
return (smaller_text + '…');
} else {
return text;
}
}
Any way to improve performance? How would I convert this to a bubble-sort function?
Thanks!
move the calls to $() outside of the loop, and store its result in a temporary variable. Running that function is going to be the slowest thing in your code, aside from the call to .html().
They work very very hard on making the selector engines in libraries fast, but it's still dog slow compared to normal javascript operations (like looking up a variable in the local scope) because it has to interact with the dom. Especially if you're using a class selector like that, jquery has to loop through basically every element in the document looking at each class attribute and running a regex on it. Every go round the loop! Get as much of that stuff out of your tight loops as you can. Webkit runs it fast because it has .getElementsByClassName while the other browsers don't. (yet).
Instead of removing one character at time until you find the ideal width, you could use a binary search.
I see that the problem is that you are constantly modifying the DOM in the loop, by setting the html of the temp_item, and then re reading the width.
I don't know the context of your problem, but trying to adjust the layout by measuring the rendered elements is not a good practice from my point of view.
Maybe you could approach the problem from a different angle. Truncating to a fixed width is common.
Other possibility (hack?) if dont have choices, could be to use the overflow css property of the container element and put the … in other element next to the text. Though i recommend you to rethink the need of solving the problem the way you are intending.
Hugo
Other than the suggestion by Breton, another possibility to speed up your algorithm would be to use a binary search on the text length. Currently you are decrementing the length by one character at a time - this is O(N) in the length of the string. Instead, use a search which will be O(log(N)).
Roughly speaking, something like this:
function constrain(text, ideal_width){
...
var temp_item = $('.temp_item');
var span_temp_item = $('span.temp_item');
var text_len_lower = 0;
var text_len_higher = smaller_text.length;
while (true) {
if (item_width > ideal)
{
// make smaller to the mean of "lower" and this
text_len_higher = smaller_text.length;
smaller_text = text.substr(0,
((smaller_text.length + text_len_lower)/2));
}
else
{
if (smaller_text.length>=text_len_higher) break;
// make larger to the mean of "higher" and this
text_len_lower = smaller_text.length;
smaller_text = text.substr(0,
((smaller_text.length + text_len_higher)/2));
}
temp_item.html(smaller_text);
item_width = span_temp_item.width();
}
...
}
One thing to note is that each time you add something to the DOM, or change the html in a node, the page has to redraw itself, which is an expensive operation. Moving any HTML updates outside of a loop might help speed things up quite a bit.
As other have mentioned, you could move the calls to $() to outside the loop. You can create a reference to the element, then just call the methods on it within the loop as 1800 INFORMATION mentioned.
If you use Firefox with the Firebug plugin, there's a great way of profiling the code to see what's taking the longest time. Just click profile under the first tab, do your action, then click profile again. It'll show a table with the time it took for each part of your code. Chances are you'll see a lot of things in the list that are in your js framework library; but you can isolate that as well with a little trial and error.

Categories