在没有任何库的情况下从多个图像用 Javascript 制作 GIF

Posted

技术标签:

【中文标题】在没有任何库的情况下从多个图像用 Javascript 制作 GIF【英文标题】:Make GIF in Javascript from multiple images without any library 【发布时间】:2018-06-24 12:37:05 【问题描述】:

有没有什么方法可以在没有任何库的情况下用 javascript 制作 GIF?我搜索了一半的互联网,一无所获。

【问题讨论】:

只需将图像存储在数组中并循环遍历数组。 在另一半:emergent.unpythonic.net/software/01126462511-glif 【参考方案1】:

潜在反对者的注意事项

我知道这是一个低质量的问题。但是,我认为通常不回答的原因已经无效。自从被问到已经快一个月了,我怀疑我会做他们的功课,因为此时可能会过期几周。至于他们没有花时间自己研究这个问题,我正在通过教他们如何研究来从源头上解决问题。

回答

请注意,我回答这个问题的前提是您知道一些 Javascript,但不是全部,并且您想学习如何编写您可能还不知道如何编写的代码。如果您只是在寻找解决方案,请随意复制粘贴或其他任何内容。但是通读这篇文章并理解我用来解决这个问题的过程,比如如何思考和查找问题,将在未来帮助你。如果你学会了如何学习,你会变得更好。

现在,首先,我们需要一种显示图像的方法。我从经验中知道有两种主要方法可以做到这一点。一种是使用canvas element 来绘制图像;这是更复杂的方法。方法 2 使用image tag、<img>。我认为这是您想要的方法。如果您不知道如何使用 html 和 Javascript 绘制图像,一个简单的google search (here's another) 可以解决这个问题。 (实际上,第二个主要返回 canvas 方法——但你可能已经知道图像标签了。)

接下来我们需要一种方法来更改显示的图像。嗯,这应该就像改变告诉图像首先显示什么的东西一样简单。第一个示例搜索的最高结果是 nice documentation page-- 完美。通读,我到了这一部分:

HTML 图片语法

在 HTML 中,图像是用标签定义的。

标签为空,只包含属性,没有结束标签。

src属性指定图片的URL(网址):

<img src="url">

嗯,这看起来像是控制图像的东西。因此,我们只需制作一个定期更改此属性的 Javascript 脚本。为了做到这一点,我们需要一个 JS 方法来等待指定的时间。在 Google 中搜索 "javascript wait",我看到 second result 来自告诉我有关图像的同一来源网页,所以我会选择那个。在文档中,我看到了这个:

setInterval() 方法

setInterval() 方法在每个给定时间间隔重复给定函数。

window.setInterval(function, milliseconds);

window.setInterval()方法可以不用window前缀写。

第一个参数是要执行的函数。

第二个参数表示每次执行之间的时间间隔长度。

(它甚至给出了一个例子!)

这似乎是我们想要的。使用这种方法,我可以编写一个简单的代码来每秒在两个图像之间切换(右侧的解释性 cmets,滚动以更好地查看它们):

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"/>

现在,这是一个好的开始——我们可以在 2 张完全随机的不同图像之间进行切换。

但我们需要更多。我们不想手动在每个新图像中编码;我们需要的是array。通过搜索很难知道这一点,但是如果您参加过入门课程或自学了一些 JS,那么您应该对数组非常熟悉,并且可能会意识到它们在这里是一个不错的选择。所以我们用我们希望它改变的所有图像设置数组(向右滚动查看全部):

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

现在应该可以了,让我们测试一下:

//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"/>

您应该注意两件事。首先是图像的大小和长宽比差异很大,导致图像每次都在改变大小。您应该注意的第二件事是,当我们用完图像时,它不会循环回来。哎呀! (旁注:我实际上犯了这个错误。不要因错误而气馁,我们都会犯错误!)

为了解决第一个问题,我们可以回顾一下HTML images documentation,发现有两个属性可以帮助我们:widthheight。这些会将图像锁定到指定的尺寸。对于实际的 gif,帧大小应该不是什么大问题,因为帧可能都是相同的大小,但为了安全也没有什么坏处:

<img id="picture1"   src="https://vignette.wikia.nocookie.net/starpolar/images/9/96/Paul_blart.jpg/revision/latest?cb=20150419171412"/>

第二个问题有点难。但是通过查看代码,我们应该能够相当快地发现错误。 (注意:像我这样有经验的开发人员可能几乎可以立即在一个小而简单的程序中看到这样的问题。如果你不能,不要气馁;我们都是从某个地方开始的,随着时间的推移成为 1337 hax0r .)

如果我们看看我们的程序是如何运行的,我们可以说它是这样工作的:

永远循环: 将图像设置为当前帧 增加框架

很明显,虽然我们的帧数有限,但我们会不断增加当前帧数而不会退缩!我们通过 gif 的进度如下所示(跳到每 3 帧并假设总共 10 帧):

|---------

----|------

--------|---

---------|

--------- |

这可以通过一个简单的if 语句来解决,当我们超过 gif 的末尾时将当前帧重置为 0(为了清楚起见,上面的行也显示了):

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

通过这些更改,我们的 gif 看起来像这样:

//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"   src="https://vignette.wikia.nocookie.net/starpolar/images/9/96/Paul_blart.jpg/revision/latest?cb=20150419171412"/>

现在我们需要做的就是将正确的帧放入数组并加快速度!对于一个真正的 gif,假设这是一个你正在制作的网站,文件可能会被命名为 gif-frame-1gif-frame-2gif-frame-3 等。因此,我们可以自动制作数组,把它进入for 循环:

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";

该代码可能无法立即在您的网站上运行 - 您必须更改图像名称和帧数以及其他内容 - 但它应该对您有所帮助。如果你指定这些图片在哪里,它们是如何命名的,有多少等等,那么我可以帮助你更多,但我没有足够的信息来写那部分。另请注意,这可以比现在快大约 50 倍,但是如果没有使用真实的帧,它看起来很奇怪。现在,我们可以认为我们的最终代码是:

//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"   src="https://vignette.wikia.nocookie.net/starpolar/images/9/96/Paul_blart.jpg/revision/latest?cb=20150419171412"/>

可以对其进行修改(例如,您可以将函数移动到 setInterval 本身内部)但这应该可以正常工作。

我希望这个答案可以帮助您了解如何解决这些问题!

【讨论】:

你能告诉我我们如何使用每秒帧率的特性来制作这个gif吗?我猜setInterval(fn, 1000/fps) 会工作【参考方案2】:

我最终只是编写了一个脚本来每秒替换 div 中的图像。从技术上讲,它不是一个 gif,但它的工作原理非常相似。

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

我不知道这是否有帮助,甚至可以理解,但它可能对某人有所帮助。另外,我没有费心去编辑代码,所以肯定有一些没有意义的位和奇怪的变量名。

【讨论】:

以上是关于在没有任何库的情况下从多个图像用 Javascript 制作 GIF的主要内容,如果未能解决你的问题,请参考以下文章

在没有第三方库的情况下从 JSON 获取动态对象的最佳方法是啥?

如何在没有任何循环php的情况下从数组中随机获取项目

如何在不将单独的帧图像写入磁盘的情况下从 C++ 程序中生成的多个图像编码视频?

我可以在没有任何锁的情况下从不同的线程读取内存缓冲区吗?

在没有 django ModelForm 的情况下从用户上传图像的问题

如何在不删除任何文件的情况下从跟踪中分离文件夹?