1. Code
  2. JavaScript

Erstellen eines Leinwand-Bildeditors mit Leinwand

Scroll to top
20 min read

German (Deutsch) translation by Władysław Łucyszyn (you can also view the original English article)

Wir werden nicht wild darauf sein, es ist einfach keine Zeit, aber wir werden sehen, wie einfach es ist, Dinge wie Drehen, Ändern der Größe, Übersetzen und sogar subtile Farbmanipulation zu tun. Machen Sie sich nichts vor, dass wir mit Photoshop gleichkommen werden, obwohl dies theoretisch möglich ist, aber wenn man bedenkt, dass wir innerhalb der Grenzen von nichts Komplexerem als einem Browser arbeiten, finde ich es persönlich immer noch ziemlich bemerkenswert.

Dieses Tutorial enthält einen Screencast für Tuts+ Premium-Mitglieder.


Was benötigen Sie für dieses Tutorial? 

Um eine funktionierende Version der Demo lokal zu erstellen, müssen Sie einen Webkit-basierten Browser wie Safari, Chrome oder Opera verwenden. Die Demo funktioniert in Firefox, muss jedoch über einen Webserver ausgeführt werden, damit die meisten Funktionen funktionieren. Denken Sie nicht einmal an die Verwendung von IE. Nur Version 9 unterstützt sogar das Leinwand-Element, und um ehrlich zu sein, würde ich nicht einmal darauf vertrauen, dass IE9 den Code und die Funktionalität korrekt wiedergibt.


Anfangen

Das zugrunde liegende HTML ist wirklich ziemlich trivial; Für die Struktur des Editors benötigen wir lediglich folgende Grundelemente:

1
<!DOCTYPE html> 
2
<html lang="en"> 
3
	<head> 
4
		<meta charset="utf-8"> 
5
		<title>Canvas Image Editor</title> 
6
		<link rel="stylesheet" href="image-editor.css"> 
7
		<link rel="stylesheet" href="ui-lightness/jquery-ui-1.8.7.custom.css"> 
8
	</head> 
9
	<body> 
10
		<div id="imageEditor"> 
11
            <section id="editorContainer"> 
12
                <canvas id="editor" width="480" height="480"></canvas> 
13
            </section> 
14
            <section id="toolbar"> 
15
                <a id="save" href="#" title="Save">Save</a> 
16
                <a id="rotateL" href="#" title="Rotate left">Rotate Left</a> 
17
                <a id="rotateR" href="#" title="Rotate right">Rotate Right</a> 
18
                <a id="resize" href="#" title="Resize">Resize</a> 
19
                <a id="greyscale" href="#" title="Convert to grayscale">B&W</a> 
20
                <a id="sepia" href="#" title="Convert to sepia tone">Sepia</a> 
21
            </section> 
22
        </div> 
23
        <script src="jquery-1.4.4.min.js"></script> 
24
        <script src="jquery-ui-1.8.7.custom.min.js"></script> 
25
        <script> 
26
        </script> 
27
	</body> 
28
</html>

Speichern Sie die Seite als image-editor.html. Abgesehen von den Standard-HTML-Elementen, aus denen das Grundgerüst der Seite besteht, haben wir ein benutzerdefiniertes Stylesheet, das wir gleich hinzufügen werden, und ein Stylesheet, das von der jQuery-Benutzeroberfläche bereitgestellt wird. Am Ende der Seite, kurz vor dem schließenden </body>-Tag, befindet sich ein Verweis auf jQuery (aktuelle Version zum Zeitpunkt des Schreibens ist 1.4.4), ein Verweis auf die jQuery-Benutzeroberfläche (aktuelle Version 1.8.7)) und ein leeres Skript-Tag, in das wir den Code einfügen, der dem Editor seine Funktionalität verleiht.

Die in diesem Beispiel verwendeten jQuery-UI-Komponenten sind in der Größe und im Dialogfeld skalierbar, und das Thema ist UI-Leichtigkeit.

Die sichtbaren Elemente auf der Seite sind recht einfach. Wir haben ein äußeres enthaltendes <div> -Element, in dem sich zwei <section> -Elemente befinden. Das erste enthält das <canvas> -Element, mit dem wir unser Bild bearbeiten. Die zweite enthält eine Symbolleiste mit Schaltflächen, mit denen die Manipulationen durchgeführt werden. Aus den id-Attributen, die jeder Schaltfläche zugewiesen wurden, sollte ziemlich offensichtlich sein, was jede Schaltfläche tut.


Hinzufügen der Stile

Wie das HTML ist auch das verwendete CSS äußerst einfach und besteht aus folgenden Elementen:

1
#imageEditor { width:482px; margin:auto; padding:20px; border:1px solid # 4b4b4b; -moz-border-radius:8px; -webkit-border-radius:8px; border-radius:8px; background-color:#ababab; } 
2
#editorContainer { display:block; width:480px; height:480px; } 
3
#editor { display:block; margin:0 20px 20px 0; border:1px solid # 4b4b4b; } 
4
#toolbar { display:block; margin:20px 0 0; } 
5
#toolbar a { margin-right:10px; outline:none; color:#4b4b4b; } 
6
#resizer { border:2px dashed #000; } 
7
#tip { padding:5px; margin:0; border:1px solid #000; -moz-border-radius:3px; -webkit-border-radius:3px; border-radius:3px; position:absolute; background-color:#fff; background-color:rgba(255,255,255,.3); -moz-box-shadow:1px 1px 1px rgba(0,0,0,0.5); -webkit-box-shadow:1px 1px 1px rgba(0,0,0,0.5); box-shadow:1px 1px 1px rgba(0,0,0,0.5); }

Speichern Sie diese als image-editor.css im selben Verzeichnis wie die HTML-Seite. Hier gibt es nichts wirklich Bemerkenswertes, hauptsächlich das Layout des Editors und seiner Bestandteile in der im folgenden Screenshot dargestellten Weise:


Vollbild


Der lustige Teil

Sie müssen nur noch den Code hinzufügen, mit dem der Editor funktioniert. Fügen Sie zunächst den folgenden Code zum leeren <script> -Element unten auf der Seite hinzu:

1
(function($){		 
2
    //get canvas and context 
3
    var editor = document.getElementById("editor"), 
4
        context = editor.getContext("2d"),		 
5
        //create/load image 
6
        image = $("<img/>", { 
7
            src: "img/graffiti.png", 
8
            load: function() { 
9
                context.drawImage(this, 0, 0);	 
10
            } 
11
    }), 
12
    //more code to follow here... 
13
})(jQuery);

Zunächst platzieren wir unseren gesamten Code in einem Closure und aliasen das jQuery-Objekt als $-Zeichen. Mir ist klar, dass sich jQuery für uns automatisch auf das $-Zeichen aliasiert. Es gibt jedoch Situationen, in denen dies zu Konflikten mit anderem Code führen kann, der möglicherweise verwendet wird, wenn unser Code von anderen implementiert wird.

Dies ist auch die empfohlene Methode zum Erstellen von Plugins. Wenn Sie dies also jedes Mal tun, wenn jQuery verwendet wird, können Sie viel schneller und einfacher zurückkehren und Code bei Bedarf in ein Plugin umwandeln. Ich finde es einfach bequemer, meinen gesamten jQuery-Code in einem solchen Abschluss zu schreiben. Es hat auch andere Vorteile, wie das Entfernen einer Abhängigkeit von globalen Variablen - alle von uns erstellten Variablen werden in den meisten Situationen außerhalb des Abschlusses sicher vor anderem Code verborgen.

Um mit der Leinwand arbeiten zu können, benötigen wir einen Verweis auf ihren Kontext. Dies ist ein Objekt, das die Oberfläche der Leinwand darstellt, auf der die meisten Methoden zum Arbeiten mit der Leinwand aufgerufen werden. Einige Methoden können direkt für das Leinwand-Objekt aufgerufen werden, die meisten werden jedoch für das Kontextobjekt aufgerufen.

Das Abrufen des Kontexts erfolgt in zwei Schritten. Wir erhalten zuerst einen Verweis auf das <canvas> -Element selbst mit der Standardmethode JavaScript document.getElementById(). Anschließend rufen wir die Methode getContext() auf der Leinwand auf (dies ist eine solche Methode, die direkt auf der Leinwand aufgerufen werden kann. und aus gutem Grund!), wobei 2d als Kontext angegeben wird. Gegenwärtig ist 2d der einzige weit verbreitete Kontext, obwohl wir uns in Zukunft möglicherweise darauf freuen können, mit 3D-Kontexten zu arbeiten.

Sowohl das Leinwand- als auch das Kontextobjekt werden in Variablen der obersten Ebene gespeichert (das Äquivalent zu globalen Variablen, wenn wir nicht innerhalb eines Abschlusses arbeiten), damit alle von uns definierten Funktionen sie verwenden können.

Nach diesen beiden Variablen erstellen wir ein weiteres, genanntes image. Wir verwenden hier jQuery, um schnell und mühelos ein neues Bildelement zu erstellen. Wir setzen den src code eines Bildes (ein Beispiel für ein lizenzfreies Bild ist im Code-Download enthalten), damit wir mit etwas arbeiten können, und fügen einen onload-Ereignishandler hinzu, der das Bild einfach auf die Leinwand malt, die das Bild geladen hat . Bei der Arbeit mit der Leinwand ist es wichtig sicherzustellen, dass alle verwendeten Bilder vollständig geladen sind, bevor sie zur Leinwand hinzugefügt werden.

Das Bild wird mit der drawImage() -Methode auf die Leinwand gezeichnet, die für das context objekt aufgerufen wird. Diese Methode benötigt drei Argumente (optional mehr, wie wir später im Beispiel sehen werden). Diese Argumente sind das zu verwendende Bild (mit this Schlüsselwort bezeichnet), die x-Position auf der Leinwand, um mit dem Zeichnen des Bildes zu beginnen, und die y-Position auf der Leinwand, um mit dem Zeichnen des Bildes zu beginnen. Das Beispielbild hat in diesem Beispiel die exakte Größe der Leinwand und ist der Einfachheit halber vollständig quadratisch.

Wir sind jetzt bereit, in dieser Phase weiteren Code hinzuzufügen. Nach dem von uns erstellten Bild wird ein nachfolgendes Komma eingefügt, sodass das nächste hinzugefügte Codebit auch eine Variable ist.


Hinzufügen von Symbolleistenfunktionen

Als nächstes müssen wir den Code hinzufügen, um die Symbolleistenschaltflächen zu handhaben. Wir könnten einfach eine Reihe von Klick-Handlern hinzufügen, einen für jede Schaltfläche, die wir hinzufügen möchten. Obwohl es einfach zu machen ist, wäre es nicht sehr effizient und würde nicht unglaublich gut skalieren.

Idealerweise möchten wir eine einzelne Master-Funktion, die einen Klick auf eine beliebige Schaltfläche ausführt und die richtige Funktion aufruft. Zum Glück ist das auch sehr einfach. Fügen Sie den folgenden Code direkt nach dem gerade erstellten Bild hinzu:

1
//toolbar functions 
2
tools = { 
3
}; 
4
$("#toolbar").children().click(function(e) { 
5
    e.preventDefault();			 
6
    //call the relevant function 
7
    tools[this.id].call(this); 
8
});

So einfach ist das. Lassen Sie uns erläutern, was dieser Code bewirkt. Die letzte von uns erstellte Variable der obersten Ebene heißt tools und enthält ein leeres (zu diesem Zeitpunkt) Objekt. Die Funktionen für jede einzelne Schaltfläche werden alle in dieses Objekt eingefügt, wie wir in Kürze sehen werden.

Anschließend fügen wir einen einzelnen click-Handler hinzu, der mit der ID toolbar an alle untergeordneten Elemente des Elements angehängt wird (eines der <section> -Elemente im zugrunde liegenden HTML-Code).

Innerhalb dieser Klickverarbeitungsfunktion stoppen wir zuerst das Standardverhalten des Browsers, das darin besteht, dem Link zu folgen (dies verhindert das unerwünschte "Sprung nach oben" -Verhalten, das manchmal auftritt, wenn Standardelemente <a> als Schaltflächen verwendet werden).

Zuletzt wenden wir die JavaScript-Methode call() auf die Funktion an, die in der Eigenschaft des tools-Objekts enthalten ist und mit dem id-Attribut des Links übereinstimmt, auf den geklickt wurde. Da der Link, auf den geklickt wurde, nur das id-Attribut benötigt, können wir das Standard-Schlüsselwort this verwenden, ohne es in ein jQuery-Objekt zu verpacken. Die call() -Methode erfordert, dass das Schlüsselwort this auch als Argument an das Schlüsselwort übergeben wird.

Wenn wir nun dem Tools-Objekt unter der Eigenschaft save eine Funktion hinzufügen, wird die Funktion aufgerufen, wenn wir auf die Symbolleistenschaltfläche mit der id save klicken. Jetzt müssen wir nur noch eine Funktion für jede unserer Symbolleistenschaltflächen hinzufügen.


Speichern des bearbeiteten Bildes

Wir beginnen mit der Speicherfunktion, da sie ziemlich klein und einfach ist. Fügen Sie dem Tools-Objekt den folgenden Code hinzu:

1
//output to <img>  
2
save: function() { 
3
    var saveDialog = $("<div>").appendTo("body");						 
4
    $("<img/>", { 
5
        src: editor.toDataURL() 
6
    }).appendTo(saveDialog);							 
7
    saveDialog.dialog({ 
8
        resizable: false, 
9
        modal: true, 
10
        title: "Right-click and choose 'Save Image As'", 
11
        width: editor.width + 35 
12
    }); 
13
},

Als erstes erstellt unsere Funktion ein neues <div> -Element und hängt es an den <body> der Seite an. Dieses Element wird in Verbindung mit der Dialogkomponente der jQuery-Benutzeroberfläche verwendet, um ein modales Dialogfeld zu erstellen.

Als nächstes erstellen wir ein neues Bildelement; Dieses Mal setzen wir src auf eine Base-64-codierte Darstellung von allem, was in der Zeichenfläche enthalten ist. Diese Aktion wird mit der toDataURL() -Methode ausgeführt, die übrigens eine andere Methode ist, die direkt auf dem Leinwand-Element aufgerufen wird. Nach der Erstellung wird das Bild an das im vorherigen Schritt erstellte <div> -Element angehängt.

Schließlich initialisieren wir die Dialogkomponente; Dies geschieht mit der dialog() -Methode, einer Methode, die von der jQuery-Benutzeroberfläche hinzugefügt wurde. Ein Objektliteral wird an die dialog() -Methode übergeben, mit der wir die verschiedenen Konfigurationsoptionen festlegen können. In diesem Fall konfigurieren wir das Dialogfeld so, dass die Größe nicht geändert werden kann und dass es modal ist (eine Überlagerung wird auf den Rest der Seite angewendet, während das Dialogfeld geöffnet ist). Wir setzen den Titel des Dialogfelds auch auf eine Zeichenfolge, die Anweisungen zum Speichern des Objekts enthält, und machen das Dialogfeld groß genug, um das Bild aufzunehmen, indem wir seine Breite auf die Breite des Canvas-Elements plus 35 Pixel einstellen.


Drehung

Ein gemeinsames Merkmal von Bildeditoren ist die Möglichkeit, ein Element zu drehen. Mithilfe der integrierten Leinwand-Funktionalität ist es ziemlich einfach, dies in unserem Editor zu implementieren. Fügen Sie nach der Speicherfunktion die folgenden Funktionen hinzu:

1
rotate: function(conf) { 
2
    //save current image before rotating 
3
    $("<img/>", { 
4
        src: editor.toDataURL(), 
5
        load: function() { 
6
            //rotate canvas 
7
            context.clearRect(0, 0, editor.width, editor.height); 
8
            context.translate(conf.x, conf.y); 
9
            context.rotate(conf.r); 
10
            //redraw saved image 
11
            context.drawImage(this, 0, 0);	 
12
        } 
13
    }); 
14
}, 
15
rotateL: function() { 
16
    var conf = { 
17
        x: 0, 
18
        y: editor.height, 
19
        r: -90 * Math.PI / 180 
20
    }; 
21
    tools.rotate(conf); 
22
}, 
23
rotateR: function() { 
24
    var conf = { 
25
        x: editor.width, 
26
        y: 0, 
27
        r: 90 * Math.PI / 180 
28
    }; 
29
    tools.rotate(conf); 
30
},

Wir haben also drei neue Funktionen hinzugefügt. eine Master-Rotationsfunktion und dann jeweils eine Funktion für die Symbolleistenschaltflächen nach links und rechts drehen. Die Funktionen rotateL() und rotateR() erstellen jeweils nur ein benutzerdefiniertes Konfigurationsobjekt und rufen dann die Master-Funktion rotate() auf, die das Konfigurationsobjekt übergibt. Es ist die Master-Funktion rotate(), die die Drehung tatsächlich ausführt. Ein wichtiger Punkt ist, dass es die Leinwand selbst ist, die wir drehen, nicht das Bild auf der Leinwand.

Das Konfigurationsobjekt, das wir in den Funktionen rotateL() und rotateR() erstellen, ist sehr einfach. Es enthält drei Eigenschaften - x, y und r. Die Eigenschaften x und y beziehen sich auf den erforderlichen Translationsbetrag und r auf den Rotationsbetrag. Jeder Klick auf eine Drehtaste dreht das Bild um plus oder minus 90 Grad.

In der Funktion rotateL() müssen wir die Leinwand um den gleichen Abstand wie die Höhe der Leinwand auf der vertikalen Achse verschieben, um sicherzustellen, dass das Bild nach dem Drehen der Leinwand sichtbar bleibt. Eine Drehung nach links wird als negative Drehung gegen den Uhrzeigersinn eingestuft. Bei der Berechnung des Drehwinkels können wir keine Grad verwenden. Wir müssen 90 Grad (oder in diesem Fall -90 Grad) in Bogenmaß umrechnen. Dies ist einfach und erfordert die folgende Formel:

Die Anzahl der Grad multipliziert mit Pi geteilt durch 180

Die Funktion rotateR() ist genauso einfach. Dieses Mal verschieben wir die Leinwand auf der horizontalen Achse anstelle der vertikalen Achse und verwenden eine positive Zahl für die Drehung im Uhrzeigersinn.

In der Master-Funktion rotate() müssen wir erneut eine Kopie des aktuellen Bilds auf der Zeichenfläche abrufen. Dazu erstellen wir mit jQuery ein neues Bildelement und setzen dessen src erneut auf das Ergebnis der toDataURL() -Methode. Dieses Mal möchten wir das Bild nach dem Drehen der Leinwand wieder auf die Zeichenfläche zeichnen, sodass wir dem Bild einen onload-Ereignishandler hinzufügen.

Sobald das Bild geladen wurde, löschen wir zuerst die Zeichenfläche mit der Methode clearRect(). Diese Methode akzeptiert vier Argumente. Die ersten beiden Argumente sind die x- und y-Koordinaten, mit denen das Löschen beginnen soll. Das dritte und vierte sind die Größe des zu räumenden Bereichs. Wir möchten die gesamte Leinwand löschen, also beginnen wir bei 0,0 (der oberen linken Ecke der Leinwand) und löschen die gesamte Breite und Höhe der Leinwand.

Sobald wir die Zeichenfläche gelöscht haben, übersetzen wir sie (verschieben sie im Wesentlichen) mit der Transformationsmethode translate(). Wir werden entweder die volle Höhe oder die volle Breite der Leinwand übersetzen, je nachdem, ob die Funktion rotateL() oder rotateR() die Drehung initiiert hat. Das Übersetzen der Leinwand ist erforderlich, da die Drehung standardmäßig unten rechts auf der Leinwand erfolgt und nicht wie erwartet in der Mitte.

Wir können dann die Leinwand drehen und das Bild dann wieder auf die Leinwand zeichnen. Obwohl wir dasselbe Bild zurück auf die Leinwand zeichnen, wurde die Leinwand gedreht, sodass das Bild automatisch ebenfalls gedreht wird. Auch hier können wir auf das Bild verweisen, um es an die drawImage() -Methode zu übergeben, da wir uns im Load-Handler für das Bild befinden und jQuery sicherstellt, dass this auf das Bild verweist. Der nächste Screenshot zeigt das Bild, das einmal nach links gedreht wurde:


Ändern der Bildgröße

Das Ändern der Bildgröße ist unsere komplexeste Interaktion, und die dafür erforderliche Funktion ist ziemlich groß, obwohl das Ändern der Größe der Leinwand selbst ziemlich trivial ist. Fügen Sie dem tools-Objekt nach den gerade betrachteten Rotationsfunktionen die folgende Funktion hinzu:

1
resize: function() { 
2
	//create resizable over canvas 
3
	var coords = $(editor).offset(), 
4
		resizer = $("<div>", { 
5
			id: "resizer" 
6
		}).css({ 
7
			position: "absolute", 
8
			left: coords.left, 
9
			top: coords.top, 
10
			width: editor.width - 1, 
11
			height: editor.height - 1 
12
		}).appendTo("body"); 
13
 
14
		var resizeWidth = null, 
15
			resizeHeight = null, 
16
            xpos = editor.offsetLeft + 5, 
17
			ypos = editor.offsetTop + 5; 
18
             
19
		resizer.resizable({ 
20
			aspectRatio: true, 
21
			maxWidth: editor.width - 1, 
22
			maxHeight: editor.height - 1, 
23
             
24
			resize: function(e, ui) { 
25
				resizeWidth = Math.round(ui.size.width); 
26
				resizeHeight = Math.round(ui.size.height); 
27
             
28
    	        //tooltip to show new size 
29
				var string = "New width: " + resizeWidth + "px,<br />new height: " + resizeHeight + "px"; 
30
                 
31
				if ($("#tip").length) { 
32
					$("#tip").html(string); 
33
				} else { 
34
					var tip = $("<p></p>", { 
35
						id: "tip", 
36
						html: string 
37
					}).css({ 
38
						left: xpos, 
39
						top: ypos 
40
					}).appendTo("body"); 
41
				} 
42
			}, 
43
			stop: function(e, ui) { 
44
             
45
				//confirm resize, then do it 
46
				var confirmDialog = $("<div></div>", { 
47
					html: "Image will be resized to " + resizeWidth + "px wide, and " + resizeHeight + "px high.<br />Proceed?" 
48
				});				 
49
                				 
50
				//init confirm dialog 
51
				confirmDialog.dialog({ 
52
					resizable: false, 
53
					modal: true, 
54
					title: "Confirm resize?", 
55
					buttons: { 
56
						Cancel: function() { 
57
                         
58
							//tidy up 
59
							$(this).dialog("close"); 
60
                            resizer.remove(); 
61
                            $("#tip").remove(); 
62
						}, 
63
					Yes: function() { 
64
                     
65
                    	//tidy up 
66
                        $(this).dialog("close"); 
67
                        resizer.remove(); 
68
                        $("#tip").remove();							 
69
                         
70
						$("<img/>", { 
71
							src: editor.toDataURL(), 
72
							load: function() {							 
73
 
74
								//remove old image 
75
								context.clearRect(0, 0, editor.width, editor.height); 
76
                                 
77
								//resize canvas 
78
								editor.width = resizeWidth; 
79
								editor.height = resizeHeight; 
80
                                 
81
								//redraw saved image 
82
								context.drawImage(this, 0, 0, resizeWidth, resizeHeight);	 
83
							} 
84
						}); 
85
					} 
86
				} 
87
			}); 
88
		} 
89
	}); 
90
},

Sehen wir uns also die Schritte an, die für unsere Größenänderungsfunktion erforderlich sind. Zuerst müssen wir dem Benutzer eine Möglichkeit geben, die Größe des Bildes zu ändern, und der Benutzer muss die neue Größe festlegen. Wir möchten dann bestätigen, ob der Benutzer die neue Größe anwenden möchte, und dann, wenn ja, die neue Größe anwenden. Die neue Funktion erledigt alle Dinge, mal sehen wie.

Zuerst erhalten wir die Koordinaten auf der Seite des Leinwand-Elements mithilfe der jQuery-offset() -Methode, damit wir wissen, wo das veränderbare Element positioniert werden muss. Anschließend erstellen wir ein neues <div> -Element, dessen Größe geändert werden kann, und geben ihm eine ID für die Größenänderung, damit wir leicht darauf verweisen können. Wir haben auch einige Stileigenschaften des neuen Elements festgelegt, um es über der Leinwand zu positionieren. Sobald diese Stile festgelegt sind, hängen wir sie an den <body> der Seite an. Die Größenänderung wird wie unten gezeigt als gepunkteter Rand um die Innenseite der Leinwand angezeigt:

Als nächstes initialisieren wir zwei weitere Variablen, setzen ihre Werte jedoch vorerst auf null. Diese werden verwendet, um die Breite und Höhe zu speichern, in die die Größe geändert wird, wenn die Größe geändert wird. Diese Variablen werden später in der Funktion ausgefüllt und verwendet. Die Variablen xpos und ypos werden verwendet, um einen Tooltipp zu positionieren, den wir gleich erstellen werden. Sie positionieren den Tooltipp 5 Pixel vom linken und oberen Rand der Leinwand entfernt.

Als Nächstes initialisieren wir die Größenänderung mit der resizable() -Methode der jQuery-Benutzeroberfläche. Wir konfigurieren die Größe so, dass das Seitenverhältnis gesperrt ist. Unser Bild ist quadratisch, daher möchten wir, dass es quadratisch bleibt, unabhängig von der Größe, auf die es angepasst wird. Wir stellen außerdem sicher, dass das Bild nicht vergrößert werden kann, damit die Bildqualität erhalten bleibt. Wenn das Bild vergrößert anstatt verkleinert würde, würde es blockartig werden. Die Konfigurationsoptionen maxWidth und maxHeight stellen sicher, dass das Bild nur verkleinert werden kann. Beide werden auf die aktuelle Breite und Höhe der Leinwand eingestellt.

Die anpassbare Komponente verfügt über einige benutzerdefinierte Ereignishandler, denen wir Funktionen zuweisen können, die ausgeführt werden, wenn diese benutzerdefinierten Ereignisse ausgelöst werden. In diesem Beispiel werden zwei dieser Ereignishandler verwendet. resize, die jedes Mal ausgelöst wird, wenn die Größe der Größe geändert wird, und stop, der ausgelöst wird, sobald die Größenänderung abgeschlossen ist.

Die Größenänderungsfunktion kann zwei Argumente empfangen. Das erste ist ein Ereignisobjekt, das wir in diesem Beispiel nicht verwenden müssen, das aber deklariert werden muss, um das zweite Argument zu verwenden, das wir benötigen. Das zweite Argument ist ein Objekt, das nützliche Informationen zur Größenänderung enthält, einschließlich der Größe, in die es geändert wurde. Das erste, was wir in dieser Funktion tun, ist, unseren Variablen resizeWidth und resizeHeight die neue Größe der Größenänderung zuzuweisen, indem wir die Eigenschaften des ui-Objekts verwenden, das die Funktion empfängt.

Als letztes erstellt diese Funktion den Tooltipp, der dem Benutzer mitteilt, wie groß die Größe derzeit ist. Wenn dieser Tooltipp bereits vorhanden ist, müssen wir ihn nicht neu erstellen und können seinen inneren Text einfach auf eine Zeichenfolge setzen, die die aktuelle Größe der Größenänderung anzeigt. Wenn der Tooltip noch nicht vorhanden ist, z. B. wenn die Größe der Größe zum ersten Mal geändert wird, erstellen wir ihn von Grund auf neu. Der Tooltipp wird mit den zuvor erstellten xpos und ypos positioniert. Die Zeichenfolge wird jedes Mal von Grund auf neu erstellt, wenn sich die Größe der Größe ändert. Der Tooltipp sieht folgendermaßen aus:

Die Stoppfunktion, die einmal ausgeführt wird, wenn die Größenänderungsinteraktion endet, erstellt zunächst ein neues Dialogelement, das überprüft, ob der Besucher die neue Größe auf die Zeichenfläche anwenden möchte. Wieder einmal ist der Dialog so konfiguriert, dass er selbst nicht in der Größe geändert werden kann und modal ist. Wir fügen diesem Dialogfeld auch einige Schaltflächen hinzu. Die erste ist eine Schaltfläche zum Abbrechen, mit der der Besucher den Größenänderungsvorgang beenden kann, ohne die neue Größe anzuwenden. Alles, was wir hier tun, ist ein wenig Haushalt, das Entfernen des Dialogfelds, der Größenänderung und des Tooltipps.

Wir erstellen auch eine Ja-Schaltfläche, die die neue Größe anwendet. Wir erledigen hier auch die gleichen Reinigungsaufgaben, aber um die neue Größe anzuwenden, erstellen wir erneut ein neues Bild auf die gleiche Weise, wie wir es bereits mehrmals getan haben. Das Einstellen der neuen Größe der Leinwand ist äußerst einfach. Wir geben nur die Werte an, auf die die Größe geändert wurde, in die Eigenschaften width und height des Leinwand-Elements. Hier ist der Dialog, der genauso aussieht wie der Speicherdialog von früher, nur mit unterschiedlichem Inhalt:


Ändern der RGB-Werte einzelner Pixel

Ich habe bereits gesagt, dass wir den Inhalt des Leinwand-Elements vollständig auf Pixelebene steuern können, sodass unsere letzten beiden Funktionen der Symbolleistenschaltflächen genau das tun. Die erste Funktion wird verwendet, um das Bild in Graustufen umzuwandeln, die zweite Funktion ändert das Bild in Sepia. Wir werden uns zuerst die Graustufenfunktion ansehen:

1
greyscale: function() { 
2
	//get image data 
3
	var imgData = context.getImageData(0, 0, editor.width, editor.height), 
4
		pxData = imgData.data, 
5
		length = pxData.length; 
6
	for(var x = 0; x < length; x+=4) { 
7
		//convert to grayscale 
8
		var r = pxData[x], 
9
			g = pxData[x + 1], 
10
			b = pxData[x + 2], 
11
			grey = r * .3 + g * .59 + b * .11; 
12
		pxData[x] = grey; 
13
		pxData[x + 1] = grey; 
14
		pxData[x + 2] = grey;								 
15
	}						 
16
	//paint grayscale image back 
17
	context.putImageData(imgData, 0, 0);	 
18
},

Das erste, was diese Funktion tun muss, ist, die aktuellen Pixeldaten der Leinwand abzurufen. Dazu verwenden wir die Methode getImageData(). Diese Methode akzeptiert vier Argumente, die mit der zuvor betrachteten clearRect() -Methode identisch sind. Die ersten beiden Argumente sind die x- und y-Positionen, an denen Daten erfasst werden sollen, und die dritten und vierten sind die Breite und Höhe des Bereichs bekommen. Wir wollen die gesamte Leinwand, also beginnen wir bei 0,0 (oben links) und fahren mit der Breite und Höhe der Leinwand fort. Das von der Methode zurückgegebene Objekt wird in der Variablen imgData gespeichert.

Das in dieser Variablen gespeicherte Objekt hat eine Eigenschaft namens data. Innerhalb dieser Eigenschaft befindet sich ein Array, das die r g b- und Alpha-Werte für jedes einzelne Pixel in der Zeichenfläche enthält. Das erste Element im Array enthält also den r-Wert des ersten Pixels, das zweite Element den g-Wert des ersten Pixels, das Das dritte Element enthält den b-Wert des ersten Pixels, und das vierte Element enthält den Alpha-Wert des ersten Pixels. Dieses Array ist unglaublich groß; Ein Bild mit 480 x 480 Pixeln enthält 230400 Pixel, und wir haben vier Elemente für jedes einzelne Pixel. Dies macht das Array insgesamt 921600 Elemente lang! Diese Länge wird auch zur Verwendung in der for-Schleife gespeichert, die wir als nächstes definieren.

Die for-Schleife unterscheidet sich ein wenig von der üblichen for-Schleife. Denken Sie daran, dass das Array in diskreten Blöcken mit jeweils 4 Elementen organisiert werden kann, wobei sich jedes Element in einem einzelnen Block auf die einzelnen rgba-Komponenten bezieht, sodass wir das Element vier Elemente gleichzeitig durchlaufen. Bei jeder Iteration erhalten wir jede Pixelkomponente und verwenden dann die Formel r * .3 + g * .59 + b * .11, um jede in Graustufen umzuwandeln. Anschließend speichern wir die konvertierte Pixelkomponente wieder in ihrem ursprünglichen Array-Element.

Sobald wir das gesamte Array durchlaufen haben, können wir den Inhalt des Arrays zurück auf die Leinwand schreiben und jedes ursprüngliche Pixel durch sein neues Graustufen-Gegenstück ersetzen. Verwenden Sie dazu die Methode putImageData(), die einfach das Gegenteil von getImageData() bewirkt.

Die Sepia-Tonfunktion ist identisch mit der Graustufenfunktion, außer dass wir eine andere Formel verwenden, um jede r g b -Komponente in einen Sepia-Ton umzuwandeln:

1
sepia: function() { 
2
	//get image data 
3
	var imgData = context.getImageData(0, 0, editor.width, editor.height), 
4
		pxData = imgData.data, 
5
		length = pxData.length; 
6
		for(var x = 0; x < length; x+=4) { 
7
			//convert to grayscale 
8
			var r = pxData[x], 
9
				g = pxData[x + 1], 
10
				b = pxData[x + 2], 
11
			sepiaR = r * .393 + g * .769 + b * .189, 
12
			sepiaG = r * .349 + g * .686 + b * .168, 
13
			sepiaB = r * .272 + g * .534 + b * .131; 
14
			pxData[x] = sepiaR; 
15
			pxData[x + 1] = sepiaG; 
16
			pxData[x + 2] = sepiaB;								 
17
		} 
18
					 
19
		//paint sepia image back 
20
		context.putImageData(imgData, 0, 0);						 
21
	}

Hier ist eine Aufnahme des Bildes, nachdem es in einen Sepia-Ton umgewandelt wurde:


Abschluss

In diesem Tutorial haben wir gesehen, wie die Leinwand in einen leistungsstarken Bildeditor umgewandelt werden kann, der uns einige der Funktionen der grundlegenden, anwendungsbasierten Bildeditoren bietet, mit denen wir vertraut sind. Dieses Beispiel kann leicht erweitert werden, um weitere Funktionen wie Zuschneiden und Zeichnen mit einem Buntstift-Werkzeug hinzuzufügen, obwohl ich Ihnen diese Funktion überlassen werde. Danke fürs Lesen.