Partage
  • Partager sur Facebook
  • Partager sur Twitter

Charger des images

HTML5 + canvas

Sujet résolu
    Staff 12 juillet 2012 à 22:43:29

    Bonsoir à tous, je développe un canvas et j'ai un problème : je me rends compte que je ne sais pas charger des images, ou plutôt, je ne sais pas optimiser les chargements d'images.

    var img= new Image();
        img.src = 'img.png';
              
    context.drawImage(img, 35, 35);
    


    Bon okay, ça c'est bien, mais ça prends un peu de temps, charger une image de cette façons n'est pas un problème à première vue, mais moi je cherche à faire des animations basées sur ces chargement d'images.
    Méthode classique, j'ai créé mon propre Timer, qui fais un tick à chaque interval que j'ai défini moi même.

    Et là, c'est des problèmes d'affichages partout : si je fais
    img.src = 'img.png';
    

    dans un Timer avec un nouveau chargement d'images à chaque tick, il a même pas le temps de charger l'image A que je lui demande de charger la B, et ainsi de suite, et donc il a jamais le temps de rien charger.


    J'en viens donc à ma question : existe-t-il un moyen de charger toutes mes images à l'avance, de façons à pouvoir simplement les "rappeler" dès que j'en ai besoin ?



    L'utilisation de
    img.onload = function() {
            context.drawImage(img, 35, 35); 
    }
    

    est clairement pas logique au sein d'une boucle, je ne pense même pas que ça ait de rapport avec mon problème (il me semble que ça sert à éfficher une image que quand elle a fini de charger afin d'éviter un affichage saccadé), après, peut-être que ça peut servir quand même dans mon cas, je n'en sais trop rien


    Si quelqu'un à une solution à m'apporter, je suis preneur, bonne soirée à tous ^^
    • Partager sur Facebook
    • Partager sur Twitter
      12 juillet 2012 à 23:00:04

      On sait pas ce que tu veux faire exactement.

      Pour précharger tes images, tu crées autant d'instances d'Image que tu as d'images et tu renseignes leur attribut src. C'est tout.

      Après, si tu as besoin de les dessiner, en même temps, dans une boucle, tu ne peux lancer ta boucle que quand elles sont toutes chargées. Tu as donc besoin d'écouter tous les événements onload pour déterminer quand elles sont toutes chargées.
      • Partager sur Facebook
      • Partager sur Twitter
        Staff 12 juillet 2012 à 23:10:11

        Une image, ça met un certain temps à charger, or, dans aucun jeu vidéo on ne voit de lag, ça veut dire qu'il doit bien exister des astuces pour optimiser, et j'imagine que les concepteurs des jeux vidéos que nous connaissons chargent tout à l'avance, et c'est ce que je cherche à faire.

        J'ai déjà pensé à ta technique, et j'avais écrit ça :
        image.src = 'image1.png'; image.onload = function() { context.drawImage(image, 50, 50); }
        image.src = 'image2.png'; image.onload = function() { context.drawImage(image, 50, 50); }
        image.src = 'image3.png'; image.onload = function() { context.drawImage(image, 50, 50); }
        // ........
        


        Au début de mon script, mais ça n'a pas l'air de changer grand chose à mon problème :/
        • Partager sur Facebook
        • Partager sur Twitter
          12 juillet 2012 à 23:19:39

          Tu peux choisir de mettre ou pas une barre de progression. Si tu n'en mets pas, quelque chose comme ceci suffira :

          function loadImage(img) {
            var image = new Image();
            image.src = img;
          }
          function preload() {
            var args = preload.arguments;
            var i, l;
            for(i = 0, l = args.length; i < l; ++i) {
              loadImage(args[i]);
            }
          };
          preload("/images/wall/vaisseau.jpg", "http://media.fallengalaxy.com/style/imports/images/controls.png",
           "http://media.fallengalaxy.com/style/imports/images/ships.png",
           "http://media.fallengalaxy.com/style/imports/images/abilities.png", "pix-transparent.png",
           "http://wiki.fallengalaxy.com/images/0/0f/Cr%C3%A9dits.png",
           "http://wiki.fallengalaxy.com/images/b/b9/R0.png"/*, ...*/);
          


          Tu mets ça dans le <head> du fichier HTML, et il ne te reste plus qu'à attendre l'événement « load » sur <body> (puisque « load » se déclenche quand toutes les images sont chargées).
          • Partager sur Facebook
          • Partager sur Twitter
            12 juillet 2012 à 23:34:55

            Si tu relis mon poste tu verras que je ne te suggère pas d'afficher les images à l'événement onload mais de lancer ta boucle une fois toutes les images chargées.

            Il n'y a rien à optimiser. Dans les jeux vidéos il y a un écran de chargement ;)

            lumiru, je ne pense pas que l'événement load de body soit affecté par ton script. Et je déconseille de lancer les chargements dans uen boucle comme tu le fais. ça a tendance à déclencher un erreur Stack Overflow sur IE quand le nombre d'images est trop important.
            • Partager sur Facebook
            • Partager sur Twitter
              Staff 12 juillet 2012 à 23:39:54

              Ta technique marche sûrement lumiru, mais ne résout pas plus mon problème, donc soit nos méthodes ne sont pas efficaces, soit mon problème ne vient pas d'où je crois.

              Pour détailler mon problème, c'est un personnage qui bouge quand il marche, mais le personnage disparaît par moments au début, puis après tout baigne (Chrome), mais sur Firefox, les changements d'images n'ont même pas le temps de s'effectuer, le personnage reste sur la même image tout le temps au lieu de marcher.

              Aussi, dès qu'il change de zone, le décor du fond change. Lors du passage du décor A au décor B, le fond devient blanc pendant une seconde, le temps du chargement, mais une fois que c'est fait, on peut repasser de la zone A à la zone B tant qu'on veut, le changement de décor sera instantané.

              Je viens de vous décrire les comportement anormaux de mon canvas, en espérant que ça vous dise quelque chose ... ^^"


              Citation : Geoffrey-Zéro

              je ne te suggère pas d'afficher les images à l'événement onload mais de lancer ta boucle une fois toutes les images chargées..



              Je ne dois pas bien comprendre, mais quelle est la différence entre ce que tu viens de dire, et ce que j'ai fait ? ^^" (mis à part que moi j'ai lancé leur affichage, mais je demande aussi leur chargement, et ce avant la boucle ?)
              • Partager sur Facebook
              • Partager sur Twitter
                12 juillet 2012 à 23:50:19

                Je sais pas à quoi sert ta boucle donc c'est pas évident.

                Ce que je suggère c'est de précharger les images avant de lancer ton jeu ou ta zone.

                En admettant que le premier niveau de ton jeu demande 10 images, tu charges les 10 images, et une fois qu'elles sont toutes chargées, tu lances le jeu. En suite, pour le changement de niveau il y a deux politiques : soit tu arrêtes le jeu au changement de niveau, le temps de précharger toutes les images du niveau 2, et tu le fais repartir quand tout est chargé, soit tu précharges les images du niveau 2 en même temps que celles du niveau 1 pour qu'il n'y ait pas du tout de latence au changement de niveau.
                • Partager sur Facebook
                • Partager sur Twitter
                  12 juillet 2012 à 23:56:27

                  Citation : Geoffrey-Zéro

                  lumiru, je ne pense pas que l'événement load de body soit affecté par ton script.


                  Ça fait plusieurs années que je me sers de ça pour charger mes images (ici, 18), je pense que je m'en serais rendu compte s'il ne fonctionnait pas :p . onload ne se déclenche que si toutes les images sont chargées (même celles seulement déclarées dans JS).

                  Citation : Geoffrey-Zéro

                  Et je déconseille de lancer les chargements dans uen boucle comme tu le fais. ça a tendance à déclencher un erreur Stack Overflow sur IE quand le nombre d'images est trop important.


                  Sur les quelques test que j'ai fait à l'époque sur IE, je n'ai jamais rencontré ce problème (alors que j'en ai un avec un chargement de 44 images). Après, connaissant le caractère totalement aléatoire d'IE… Cela étant dit, aujourd'hui, je ne ferais pas de la même manière (44 images, il y a des trucs à fusionner…).

                  Citation : J-Edward

                  les changements d'images n'ont même pas le temps de s'effectuer, le personnage reste sur la même image tout le temps au lieu de marcher.


                  Mets tous les états de ton personnage sur la même image, c'est moins lourd pour le serveur et le client et ça peut accessoirement résoudre le problème.
                  • Partager sur Facebook
                  • Partager sur Twitter
                    Staff 12 juillet 2012 à 23:59:56

                    Je suis d'accord avec ce principe, mais ce n'est pas ça que je fais justement ?

                    Ma boucle, c'est la boucle infinie du jeu, boucle où on affiche toutes les images en temps réel. Je fais le context.drawImage(mon_image, 10, 10) dans cette boucle à chaque tour de boucle, puis pendant que le personnage marche, je change le mon_image.src à un certain intervalle.

                    Pour en revenir à la question, en quoi ce que je fais est différent de ce que tu suggères ?

                    Edit :

                    Citation : lumiru

                    Mets tous les états de ton personnage sur la même image



                    Tu veux dire avoir une grosse image avec tous les états, et prendre que des morceaux de cette image ?
                    Ca tombe bien qu'on en parle, parce que je n'ai jamais réussi à le faire, si tu as une idée elle sera la bienvenue ^^

                    drawImage(image, sx, sy, sLargeur, sHauteur, dx, dy, dLargeur, dHauteur)
                    


                    Cette fonction n'a jamais marché chez moi si j'ajoute les 4 derniers paramètres.

                    Et sinon, tu sais comment faire pour recadrer l'image, mais pas seulement pendant un draw ?
                    Genre au lieu de faire image.src"aaa.png", on ferait image.setSubRect(x, y, largeur, hauteur);
                    En gros, y a-t-il un équivalent du setSubRect (qu'on peut voir dans de nombreuses librairies graphiques, comme celles du C++) en Javascript ? Ca pourrait en effet résoudre mon problème
                    • Partager sur Facebook
                    • Partager sur Twitter
                      13 juillet 2012 à 0:40:23

                      Déjà, pour un personnage qui marche, utilise un spritesheet. D'ailleurs, tu devrais rassembler le plus d'images possible en une seule image. On perd beaucoup de temps à faire des requêtes HTTP.

                      Ce que je suggère c'est d'attendre le chargement de *toutes* les images, pas d'attendre le chargement de *chaque* image.

                      Pour le onload de body je suis surpris mais il semblerait effectivement que ça fonctionne. Par contre, ça ne suffirait pas ici car il faut pouvoir précharger les images des différents niveaux. Et puis, généralement, on ne précharge pas les images d'un jeu tant que l'utilisateur n'a pas cliqué sur un bouton de lancement du jeu.

                      L'erreur Stack Overflow est une des plus connues.
                      • Partager sur Facebook
                      • Partager sur Twitter
                        Staff 13 juillet 2012 à 0:49:33

                        Citation : Geoffrey-Zéro

                        utilise un spritesheet.



                        Euh, qu'est-ce ? ^^" (Je n'ai pas trouvé sur Google :euh: )

                        Citation : Geoffrey-Zéro

                        tu devrais rassembler le plus d'images possible en une seule image.



                        Je vais me citer moi-même, car tu n'as pas dû voir mon édit sur mon ancien message ^^ (je l'ai mis en édit parce qu'il n'y avais pas encore eu de réponse ^^ )

                        Citation : J-Edward

                        Et sinon, tu sais comment faire pour recadrer l'image, mais pas seulement pendant un draw ?
                        Genre au lieu de faire image.src"aaa.png", on ferait image.setSubRect(x, y, largeur, hauteur);
                        En gros, y a-t-il un équivalent du setSubRect (qu'on peut voir dans de nombreuses librairies graphiques, comme celles du C++) en Javascript ? Ca pourrait en effet résoudre mon problème



                        :)
                        • Partager sur Facebook
                        • Partager sur Twitter
                          13 juillet 2012 à 10:28:09

                          Citation : Geoffrey-Zéro

                          L'erreur Stack Overflow est une des plus connues.


                          Soit, le fait que je ne l'ai jamais rencontré s'explique sûrement par le fait que je n'utilise jamais IE (surtout parce que je suis sur Ubuntu) et que la plupart des gens qui utilisaient mes scripts sont un public averti :p .

                          Citation : J-Edward

                          Citation : Geoffrey-Zéro

                          utilise un spritesheet.



                          Euh, qu'est-ce ? ^^" (Je n'ai pas trouvé sur Google :euh: )


                          Sauf erreur de ma part, un sprite est un ensemble d'images ayant une fonction proche rassemblées dans la même image qui seront découpées ensuite par le soin du développeur pour réduire considérablement le nombre de requêtes HTTP (en gros, ce que tu demandes de faire :p ).

                          Citation : J-Edward

                          Et sinon, tu sais comment faire pour recadrer l'image, mais pas seulement pendant un draw ?
                          Genre au lieu de faire image.src"aaa.png", on ferait image.setSubRect(x, y, largeur, hauteur);
                          En gros, y a-t-il un équivalent du setSubRect (qu'on peut voir dans de nombreuses librairies graphiques, comme celles du C++) en Javascript ? Ca pourrait en effet résoudre mon problème


                          https://developer.mozilla.org/fr/Tutor [...] .C3.A9coupage
                          Et rien ne t'empêche d'utiliser un autre canvas comme image source.
                          • Partager sur Facebook
                          • Partager sur Twitter
                            13 juillet 2012 à 12:24:16

                            Tu peux faire autant de canvas que tu veux. Je te dis ça parce que tu peux très bien faire un drawImage découpé dans ces canvas pour séparer ton spritesheet en plusieurs images et es réutiliser par la suite. En plus il parait que c'est plus rapide de passer un canvas dans un autre canvas que de passer une image dans un canvas.

                            Mais je ne vois pas trop l'intérêt de cette pratique. Que tu fasses une fonction qui change l'origine de l'image ou que tu fasses une fonction qui change le recadrage de l'image, ça revient un peu au même non ?
                            • Partager sur Facebook
                            • Partager sur Twitter
                              13 juillet 2012 à 15:21:09

                              bon alors:
                              - afficher un canvas dans un canvas est effectivement plus rapide ( sinon le navigateur doit decoder l'image avant de l'afficher )

                              - stocké une image au format image pose un problem: chrome vide les image de la memoire vive quand elle ne sont pas afficher, ca ne plante pas mais ca peut causer des gros ralenti quand tu dessine une image par intermitence: donc canvas necessaire pour stocker tes image efficacement

                              - drawImage dans un onload est une tres mauvaise idée ( que va t'il se passer quand ton image sera charger apres que la frame soit passé ? )

                              - preloader toute tes image en meme temps ou passer au prechargement de l'image suivante dans un onload est une mauvaise methode ( ca marche quand il y a peut de contenu mais des qu'on a une grosse quantite d'image a charger le navigateur risque d'interpreter une boucle infini )

                              - un sprite sur un canvas ce gere comme sur une image drawImage(tonimage ou ton canvas,x,y,Xdebutderecadrage,Ydebutderecadrage,largeur du recadrage, hauteur du recadrage);



                              voila une version (tres) simplifié du preloadeur que j'utilise pour mes jeux video, ill sert a la fois de preloadeur et de bank d'image

                              function ImgBank(){
                              	this.buffer = new Array();
                              	this.pic = new Object();
                              	this.error = 0;
                              	this.unload = 0;
                              	this.loaded = 0;
                              	this.nextLoad = 0;
                              }
                              ImgBank.prototype = {
                              
                              	/** met la bank d'image a 0 pour traiter une nouvelle liste, sans vider les image deja charger
                              	 * lance une boucle prechargeant une a une les image et appelant le callback une fois terminé
                              	 * @methode preload
                              	 * @param {Array} imglist
                              	 * @param {function} callback
                              	*/
                              		preload: function(imglist,callback){
                              			this.unload = imglist.length;
                              			this.error = 0;
                              			this.loaded = 0;
                              			this.nextLoad = 0;
                              			this.buffer = imglist;
                              						
                              			var self = this;
                              			var timer = function(){
                              				if(self.loaded == self.unload){	
                              					callback();
                              				}else if(self.loaded > self.nextLoad){	
                              					self.nextLoad++;
                              					self.loadimg();
                              					setTimeout(timer,10);
                              				}else{
                              					setTimeout(timer,10);
                              				}
                              			}
                              			this.loadimg();
                              			setTimeout(timer,10);
                              		
                              		},
                              
                              	/** charge une image
                              	 * une fois l'image charger creer un canvas pour l'y afficher puis valide le next load ( le timer passera a l'image suivante)
                              	 * @methode loadimg
                              	*/
                              		loadimg: function(){	
                              			var ref = this.buffer[this.nextLoad];
                              			var img = new Image();
                              				img.src = ref;
                              			var self = this;
                              			
                              			img.onload = function(){
                              				var canvas = document.createElement("canvas");
                              					canvas.height = img.height;
                              					canvas.width = img.width;
                              				var ctx = canvas.getContext("2d");
                              					ctx.drawImage(img,0,0);
                              					
                              				self.pic[ref] = canvas;
                              				self.loaded++;
                              			};
                              			
                              			img.onerror = function(){
                              				self.loaded++;
                              				self.error++;			
                              			};
                              			
                              		}
                              	
                              }
                              


                              et pour le tester un ptit script:

                              var list = ["monimage1.jpg","monimage.png","monimage.jpg"];
                              var bank = new ImgBank();
                              
                              // donc par exemple tu as    bank.pic["monimage1.jpg"]     pour avoir ton image pret a l'emploi
                              	
                              function draw(){
                              	for(var k in bank.pic){
                              		document.body.appendChild(bank.pic[k]);
                                              //tu peut faire ctx.drawImage(bank.pic[k],x,y); si tu as un context ou l'afficher
                              	}
                              }
                              	
                              bank.preload(list,draw);
                              
                              • Partager sur Facebook
                              • Partager sur Twitter
                                Staff 13 juillet 2012 à 16:32:13

                                Merci, je ne te réponds pas encore parce que ça fait une plombe que j'éssaie de l'adapter à mon code, je te donne des nouvelles ^^

                                Edit :

                                Génial, ton astuce me donne exactement ce que je cherchais, merci beaucoup diesel !
                                Merci beaucoup également à lumiru et Geoffrey ;)

                                Je marque donc je sujet en résolu, mais j'aimerais poser quelques questions complémentaires :

                                Tout d'abord, pendant le chargement des ressources au début, est-il possible de mettre une barre de chargement ? Ou au pire une petite animation "chargement en cours" ?


                                aussi, j'aimerais avoir plus d'infos sur le recadrage des images, je n'y suis jamais arrivé, je n'ai jamais réussi à faire marcher la fonction drawImage() si on ajoute les 4 derniers paramètres de recadrage.

                                Ce code marche :
                                context.drawImage(image, 10, 10, 10, 10);
                                


                                Mais ce code ne marche pas :
                                context.drawImage(image, 10, 10, 10, 10, 10, 10, 10, 10);
                                


                                Dans le 2ème cas, l'image n'apparaît jamais, sans aucune erreur Javascript...

                                N'y a-t-il pas un autre moyen de redimentionner l'image en dehors de la fonction draw ?
                                • Partager sur Facebook
                                • Partager sur Twitter

                                Charger des images

                                × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                • Editeur
                                • Markdown