• Das Erstellen neuer Accounts wurde ausgesetzt. Bei berechtigtem Interesse bitte Kontaktaufnahme über die üblichen Wege. Beste Grüße der Admin

[FRAGE] Canvas und drawImage mit einzelnen Dateien

terra75

Member
Hi,

kann mir mal ein Canvas Experte helfen?
Hab hier folgende Funktion:

PHP:
	MAP.prototype.createMap = function (context, info, zoom) {
		
		var mapWidth = this.mapInfo[zoom].mapWidth;
		var mapHeight = this.mapInfo[zoom].mapHeight;
		
		for (var x=0; x < mapWidth; x++){
			for (var y=0; y < mapHeight; y++){
				var imageName = "img/map/1/" + zoom + "/" + x + "-" + y + ".jpg";
				
				var img = new Image();
				img.src = imageName;
				
				img.onload = function() {
					context.drawImage(img, x * img.width, y * img.height, img.width, img.height);
				};

			}			
		}
				
		//canvas.restore();
	}

Wie man sieht, sollen einzelne Bilder zu einer großen Karte zusammengefügt werden.

Was mich grade irritiert ist, das immer nur das letzte Bild angezeigt wird.
Kann mir wer sagen, was ich falsch mache?

Und ja, alle Bilder mit der aufgerufenen Bezeichnung sind vorhanden. :icon6:

Gruß,
Terra
 
Was mich grade irritiert ist, das immer nur das letzte Bild angezeigt wird.
dein x ist am ende der schleife bei mapWidth, wenn dann die onloads kommen, werden alle bilder an die selbe x-pos abgebildet.
mit y das selbe.
zusätzlich ist dein img zwar mit var innerhalb des for-blocks, aber lokale variablen sind in js bis es5 immer funktions-lokal, also ist auch dein variable am ende der schleifen immer auf dem letzten bild. mit es6 könntest du let nutzen, sonst kannst du das über eine closure kapseln und so die variablen in den args merken
Code:
for (var x=0; x < mapWidth; x++){
            for (var y=0; y < mapHeight; y++){
                var imageName = "img/map/1/" + zoom + "/" + x + "-" + y + ".jpg";
                
                var img = new Image();
                img.src = imageName;
                
                img.onload = (function(x,y,img) {
                    return functionm()
                    {
                      context.drawImage(img, x * img.width, y * img.height, img.width, img.height);
                    }
                })(x,y,img);

            }            
        }
 
Cool, danke. Auch, wenn ich nur die hälfte von dem verstanden habe, was du geschrieben hast. :)

Nur noch zur Verständnis-Frage. Kann ich alle Bilder in einen Container stecken, so das ich diese dann komplett mit der Maus verschieben kann? Versuche so eine Art Leaflet zu erstellen aber im ganz kleinen und selbst angepassten Stiel.

Gruß,
Terra
 
Zuletzt bearbeitet:
wenn du das ganze in
Code:
<script type="application/javascript;version=1.7">
<script>
packst, funktioniert das ganze so wie du es hast, wenn du var durch let ersetzt im ff (und chrome? nicht ganz sicher)
Code:
MAP.prototype.createMap = function (context, info, zoom) {
        
        var mapWidth = this.mapInfo[zoom].mapWidth;
        var mapHeight = this.mapInfo[zoom].mapHeight;
        
        for (let x=0; x < mapWidth; x++){
            for (let y=0; y < mapHeight; y++){
                var imageName = "img/map/1/" + zoom + "/" + x + "-" + y + ".jpg";
                
                let img = new Image();
                img.src = imageName;
                
                img.onload = function() {
                    context.drawImage(img, x * img.width, y * img.height, img.width, img.height);
                };

            }            
        }
                
        //canvas.restore();
    }
vielleicht wird es dann klarer.
das paradigma von js ist, es gibt nur einen thread, in dem ein codeblock immer komplett ausgeführt wird.
in deinem fall wird also der obige codeblock vollständig ausgeführt. erst dann kommt der nächste codeblock an die reihe, das ist der onload-block des 1. bildes, das fertig geladen ist(das muss nicht das 1. sein).
zu diesem zeitpunkt ist aber (dadurch, dass der obige codeblock durchlaufen ist) x = mapWidth, y=mapHeight und img das letzte bild.
auf diese variablen greift aber das onload zu (closure, nur deswegen existieren die variablen ja überhaupt noch, da jedes onload eine referenz auf diese variablen hällt) und damit hat jedes onload eine referenz auf die variablen mit den werten x = mapWidth, y=mapHeight und img das letzte bild.
durch let sorgst du dafür, dass diese variablen nicht mehr funktionslokal, sondern blocklokal sind. jedes onload hat also eine eigene y und img-variable und alle onloads einer zeile haben eine referenz auf dieselbe x variable.

- - - Aktualisiert - - -

Kann ich alle Bilder in einen Container stecken, so das ich diese dann komplett mit der Maus verschieben kann? Versuche so eine Art Leaflet zu erstellen aber im ganz kleinen und selbst angepassten Stiel.
ich kann dir nicht folgen, was genau willst du?
 
Jetzt habe ich es verstanden. Glaub ich zumindest. Werde es wahrscheinlich noch 3-4 mal durchlesen um es verinnerlicht zu haben. Nicht desto trotz danke für die gute Erklärung. (gab ein Like) ;)

ich kann dir nicht folgen, was genau willst du?
Ich verstehe Canvas noch nicht so 100%. Von daher kenne ich die genaue Funktionsweise nicht.

Jetzt habe ich ja die Bilder in den Canvas Container geschmissen. Die liegen meines Verständnisses einzeln da rum. Jetzt möchte ich das gesamte Paket aber wie eine Map mit der Maus verschieben, da ja die Map größer ist, als das Canvas selbst. Dafür muss ich ja irgendein return aus der Funktion zurück geben, damit ich es z.B. mit onPress / onMouseMove oder mit dem Eventmanager verarbeiten kann.

Irgendwie schein ich grade komplett eine Gehirnblokade zu haben. ^^

Gruß,
Terra
 
Jetzt habe ich ja die Bilder in den Canvas Container geschmissen. Die liegen meines Verständnisses einzeln da rum.
nee, ein canvas ist sowas wie ein cdc in der mfc falls dir das was sagt, ein devicecontext in den du ein gesammtbild zeichnen kannst, wenn du mehrere einzelne bilder reinzeichnetst, ist dein Canvas am ende aber das gesammtbild

Jetzt möchte ich das gesamte Paket aber wie eine Map mit der Maus verschieben,
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/translate

da ja die Map größer ist, als das Canvas selbst.
hier mal nur das stichwort viewport

Dafür muss ich ja irgendein return aus der Funktion zurück geben, damit ich es z.B. mit onPress / onMouseMove oder mit dem Eventmanager verarbeiten kann.
hab ich jetzt nicht verstanden, eher nein

- - - Aktualisiert - - -

das könntest du dir mal ansehen
 
Es ist wohl einfach zu spät um noch irgendwas zu verstehen.

Um noch eine Frage in den Raum zu schmeißen.
Ist dann canvas.getContext("2d"); die Zeichenebene, die ich verwende, wo alle Bilder, die ich geladen habe, reingeschmissen wurden?

Sprich, ich könnte wie bei Photoshop die Ebenen wie folgt anlegen und dort jewals was anderes zeichnen?:
PHP:
var Ebene_3 = canvas.getContext("2d");
var Ebene_2 = canvas.getContext("2d");
var Ebene_1 = canvas.getContext("2d");

Sorry, wenn ich jetzt anfange dumme Fragen zu stellen. Bin müde. :hurt:

Nacht erstmal,
Terra
 
Ist dann canvas.getContext("2d"); die Zeichenebene, die ich verwende, wo alle Bilder, die ich geladen habe, reingeschmissen wurden?
ja, aber

Sprich, ich könnte wie bei Photoshop die Ebenen wie folgt anlegen und dort jewals was anderes zeichnen?:
PHP:
var Ebene_3 = canvas.getContext("2d");
var Ebene_2 = canvas.getContext("2d");
var Ebene_1 = canvas.getContext("2d");
nein, du hast nur eine zeichenebene, "dein zeichenblatt"

vielleicht wäre svg das was du suchst, das hat nur den nachteil, es ist nicht hardwarebeschleunigt.
svg - vektorgrafik(aber bilder kannst du auch einfügen, es ist sozusagen ein html-bild)
canvas - pixelgrafik
 
OK Danke.

Aber nein, ich möchte schon die Hardwarebeschleunigung nutzen. Wenn es einmal so läuft, wie ich es mir vorstelle, soll ja noch mehr kommen. da wird dann die Beschleunigung sehr nützlich sein.

Ich werde mir mal wohl paar Frameworks Quellcodes anschauen, wie es dort gemacht wurde. Vielleicht bringt mich das zum Ziel.

Bis hier hin hast du mir aber schon sehr weitergeholfen. Vielen Dank.

Gruß,
Terra

- - - Aktualisiert - - -

So, bin an einem Punkt, wo ich keine Lust mehr habe. Jetzt hab ich mir einige Frameworks angeschaut und verstehe weniger als vorher. Nicht nur, das es immer nur stellenweise funktioniert, auch das manche Sachen funktionieren und andere wieder nicht, obwohl diese funktionieren müssten.

Ich glaube, ich gebe es auf. Mich regt die ganze scheiße nur noch auf.
Canvas ist nicht mein Ding. Ist ja auch nirgends das ganze schön auf Deutsch beschrieben. Naja, egal.

Gruß,
Terra
 
terra, lass dich nicht entmutigen. Stell einfach weiter Fragen. Irgendjemand wird dir bestimmt helfen.
 
Was bringt es mir, wenn ich jedes mal nachfrage, wie was funktioniert - mir jemand ein fertiges Script hier reinstellt aber ich nicht verstehe, wieso das Script dann funktioniert.

Ich hab ja schon Schwierigkeiten, zu verstehen, wieso die Änderung von tsseh funktioniert.
PHP:
img.onload = (function(x,y,img) {
return functionm(){
context.drawImage(img, x * img.width, y * img.height, img.width, img.height);
}
})(x,y,img);
Wäre von allein niemals darauf gekommen. Und ganz ehrlich - ich raffe es irgendwie immer noch nicht, was genau in der Funktion passiert.

Jetzt habe ich mir als Referenz das Framework Easel.js ausgesucht. Das hab ich mal benutzt um eine Tilemap in Canvas zu zeichnen und so eine Art GUI über die Map zu legen. Das hat ja auch alles funktioniert.
Aus dem Framework hab ich jetzt ein wenig für mein eigenes Script abgeschaut. Nur scheitert es schon an den einfachsten Anweisungen, ganz zu Schweigen, das ich es irgendwie hinbekommen würde, nur mit einem Canvas mehrere Layers zu erstellen.

Wie auch immer. Ich raffe es nicht. Und ich möchte auch nicht alle 2 Min. hier eine Frage stellen. Zwar würde das dann dein Forum gut füllen aber ich würde mir irgendwann sehr dämlich dabei vorkommen. Von daher lasse ich es dann lieber ganz sein.

Terra
 
es ist absolut in ordnung, hier fragen zu stellen. auch ruhig alle 2 minuten. dafür ist das forum da. aber ich kann das nur anbieten und keinen zwingen. manchmal hilft auch drüber schlafen.
 
Nun, ich muss eh mal das ganze Ding überdenken. Mir ist auch aufgefallen, das beim einladen von über 16.000 Bildchen Fehler auftreten. Da die Map aus mehreren Zoomstufen besteht, ist die größte Stufe halt eine Map mit unheimlich vielen Einzelbildern. Wird wahrscheinlich mit dem RAM zu tun haben oder so. Weis ich noch nicht. Fehler: net::ERR_INSUFFICIENT_RESOURCES

Die ganze Sache ist aber zum Glück auch nicht so Wichtig, das ich es unbedingt machen muss. War nur eine Idee, die mir im Kopf rumschwirrte.

Terra
 
Wenn jedes Bildchen auch nur 100KB hat würdest du damit ja auch mehr als 1GB füllen.
Ich würde da an deiner Stelle für die verschiedenen Zoomstufen verschiedene Bilder verwenden... und immer auch nur die Bilder laden, die du wirklich brauchst. Sonst kommst du j auch ziemlich schnell an die Belastungsgrenzen deines Servers und deiner Datenleitung.
 
Was du sagst, war auch mein Anfangsgedanke zu dem ganzen Kram. Nur die ganze Sache überfordert mein Programmier-Technisches können bei weitem.

Wie bereits geschrieben, wollte ich eine Art LeafLet erstellen.
Dazu bietet das Spiel GuildWars2 eine API an, wo man sich die MapBilder holen kann.
https://wiki.guildwars2.com/wiki/API:Tile_service

Ein Beispiel zeigt wie man mit LeafLet eine Karte erstellen kann. Edit fiddle - JSFiddle

Sicherlich ist LeafLet dafür keine schlechte Lösung aber auch nicht die optimalste, wie ich finde. Es ist vergleichbar mit MS Windows und Apple OS, wobei LeafLet Windows ist. Es muss ein breites Spektrum abdecken und ist somit sehr langsam - im Gegensatz zu Apple, wo ein vorgegebenes System schnell und genau entworfen werden kann.

Und mit dem, was ich bis jetzt erstellt habe, sehe ich mich meinen Gedanken bestätigt. Die größtmöglich darstellbare Zoomstufe, die bei mir geht lädt 10x schneller als Leaflet.

Ja, es gibt auch andere Engines - ich weis. Aber möchte mal was komplett eigenes entwickeln.

Terra
 
Dazu bietet das Spiel GuildWars2 eine API an, wo man sich die MapBilder holen kann.
https://wiki.guildwars2.com/wiki/API:Tile_service
ahh, ja in der hinsicht bieten sie bei gw2 ne menge unterstützung, die event-api war geil. dann haben sie das aber durch ihre megaserver aber alles wieder zerstört.

Und mit dem, was ich bis jetzt erstellt habe, sehe ich mich meinen Gedanken bestätigt. Die größtmöglich darstellbare Zoomstufe, die bei mir geht lädt 10x schneller als Leaflet.
das was dort lange dauert ist eigentlich das laden der bilder, sind die einmal gecachet, sollte das ganz flüssig laufen.
 
terra75 schrieb:
Ja, es gibt auch andere Engines
Für die Layer Problematik habe ich früher mal mit kineticJs gearbeitet. Das Projekt ist zwar eingestapft worden, aber das heißt ja nicht das es nicht mehr geht.
https://github.com/ericdrowell/KineticJS/
Die docs gibst auch noch.
KineticJS documentation: Class: Layer



terra75 schrieb:
Ich hab ja schon Schwierigkeiten, zu verstehen, wieso die Änderung von tsseh funktioniert.

PHP:
img.onload = (function(x,y,img) { 
return functionm(){ 
context.drawImage(img, x * img.width, y * img.height, img.width, img.height); 
} 
})(x,y,img);

Das ist im Prinzip "ganz einfach". Wie tsseh schon schreib, kommen die onload events erst wenn die schleife schon durch ist. Um das Problem zu lösen musst du für jedes x, y einen eigenen scope erstellen, das macht man über eine Selbstaufrufende Funktion , die wiederum eine Funktion returnt. Die Parameter die an die Selbstaufrufende Funktion übergeben werden stehen ganz unten. })(x,y,img);

Vielleicht kann das tsseh besser erläutern.

Kann man das zoomen nicht direkt das canvas erledigen lassen?
Zooming via HTML5 Canvas Context

Praktisch wie bei GTA 5. Wenn man schneller fährt zoomt sich die minimap raus und wenn man steht wider rein.
Nun gut GuildWars ist ja auch kein GTA.

PS.: habe mir SOMA geholt... nach 45min spielen hatte ich Erscheinungen von diarrhö
https://www.youtube.com/watch?v=BZTfi1jv-EE
Die Storry macht mir mehr angst als die Viecher die da rum rennen.
 
Naja, ich muss nur das hier })(x,y,img); lernen und verstehen. Aber ich komme dem Verständnis schon näher. Komme halt aus der Generation, wo alles noch nacheinander abgerufen wird. Sich selbst aufrufende Funktionen habe ich noch nie verwendet. Hab ich nie durchgeblickt, wann und warum man das verwenden kann.

Kann man das zoomen nicht direkt das canvas erledigen lassen?
Könnte man, aber wie man im Beispiel sieht, wird das Bild unscharf und genau das verhindern die Zoomstufen, die in der API angeboten werden.

Mein Problem bis jetzt war aber immer, das ich ein ganz falsches Bild von Canvas hatte. Hab es mit Flash verglichen, wo ich mehrere Ebenen für verschiedene Bilder habe und alles ganz einfach Händeln könnte. Das man aber mehrere Canvas-Schichten übereinander legen muss um "Ebenen" zu erreichen, wird mit keinem Wort erwähnt.


Die Storry macht mir mehr angst als die Viecher die da rum rennen.
Dann scheint ja das Spiel echt gut umgesetzt worden zu sein. :grin:

Terra
 
Zuletzt bearbeitet:
dann schreib es mal so auf (ich hab mal context noch mit als parameter reingenommen, je nachdem wo die funktion steht kannst du das auch weglassen)
Code:
function getCallback(context, x, y, img)
{ 
  return functionm()
  { 
    context.drawImage(img, x * img.width, y * img.height, img.width, img.height); 
  };
}
und rufe die funktion dann so auf
Code:
...
img.onload = getCallback(context, x, y, img);
...
das eigentliche ziel ist es die variablen x, y und img für das onload zu sichern. dafür gibt es viele möglichkeiten.
 
Interessant. Dein Beispiel jetzt kann ich voll und ganz nachvollziehen.
Vielleicht bin ich einfach nur zu alt um neues einfach zu verstehen. :grin:

Danke,
Terra
 
Zurück
Oben