Vor kurzem hatte ich die Gelegenheit, einen Service für einen Online-Shop zu schreiben, mit dem ich eine Bestellung zum Drucken meiner Fotos aufgeben kann.
Der Dienst ging von einem „einfachen“ Bildeditor aus, dessen Erstellung ich teilen möchte. Und das alles, weil ich unter der Fülle aller Arten von Plugins keine geeignete Funktionalität gefunden habe. Außerdem wurden die Nuancen von CSS-Transformationen für mich plötzlich zu einer nicht ganz trivialen Aufgabe.

Die Hauptaufgaben:
- Möglichkeit zum Hochladen von Bildern von Ihrem Gerät, Google Drive und Instagram.
- Bildbearbeitung: Bewegen, Drehen, horizontale und vertikale Reflexion, Zoomen, automatische Bildausrichtung zum Ausfüllen des Zuschneidebereichs.
Wenn sich das Thema als interessant herausstellt, werde ich in der nächsten Veröffentlichung die Integration mit Google Drive und Instagram im Backend-Teil der Anwendung, in dem das beliebte NodeJS + Express-Bundle verwendet wurde, ausführlich beschreiben.
Für die Organisation des Frontends habe ich das wunderbare Vue-Framework gewählt. Nur weil es mich nach harter Winkel- und nerviger Reaktion inspiriert. Ich denke, es macht keinen Sinn, die Architektur, Routen und andere Komponenten zu beschreiben. Gehen wir direkt zum Editor.
Übrigens kann die Demo des Editors hier gestochert
werden .
Wir werden zwei Komponenten benötigen:
Bearbeiten - enthält die Hauptlogik und Steuerelemente
Vorschau - ist für die Anzeige des Bildes verantwortlich
Bearbeiten von Komponentenvorlagen:<Edit> <Preview v-if="image" ref="preview" :matrix="matrix" :image="image" :transform="transform" @resized="areaResized" @loaded="imageLoaded" @moved="imageMoved" /> <input type="range" :min="minZoom" :max="maxZoom" step="any" @change="onZoomEnd" v-model.number="transform.zoom" :disabled="!imageReady" /> <button @click="rotateMinus" :disabled="!imageReady">Rotate left</button> <button @click="rotatePlus" :disabled="!imageReady">Rotate right</button> <button @click="flipY" :disabled="!imageReady">Flip horizontal</button> <button @click="flipX" :disabled="!imageReady">Flip vertical</button> </Edit>
Die Vorschau-Komponente kann 3 Ereignisse auslösen:
geladen - Bildladeereignis
resized - Fenstergröße
ändern Ereignis
Bewegtbild - Bewegtereignis
Parameter:
Bild -
Bild Link
Matrix - Transformationsmatrix für die Transformation der CSS-Eigenschaft
transform - ein Objekt, das die Transformation beschreibt
Um die Position des Bildes besser steuern zu können, hat img eine absolute Positionierung, und die Eigenschaft
transform-origin , der
Transformationsreferenzpunkt , wird auf den Anfangswert „0 0“ gesetzt, der dem Ursprung in der oberen linken Ecke des Originalbilds (vor der Transformation!) Entspricht.
Das Hauptproblem, auf das ich gestoßen bin, besteht darin, dass Sie sicherstellen müssen, dass sich der Transformationsursprungspunkt immer in der Mitte des Bearbeitungsbereichs befindet. Andernfalls verschiebt sich während der Transformationen der ausgewählte Teil des Bildes. Die Verwendung dieser
Transformationsmatrix hilft, dieses Problem zu lösen.
Komponentenbearbeitung
Eigenschaften der Edit-Komponente: export default { components: { Preview }, data () { return { image: null, imageReady: false, imageRect: {},
Die Werte von imageRect und areaRect werden von der Preview-Komponente an uns übergeben. Dabei werden die Methoden imageLoaded und areaResized aufgerufen. Objekte haben die folgende Struktur:
{ size: { width: 100, height: 100 }, center: { x: 50, y: 50 } }
Die Mittelwerte könnten jedes Mal berechnet werden, es ist jedoch einfacher, sie einmal aufzuschreiben.
Die berechnete Matrixeigenschaft entspricht den Koeffizienten der Transformationsmatrix.
Die erste Aufgabe, die gelöst werden muss, besteht darin, das Bild mit einem beliebigen Seitenverhältnis im Zuschneidebereich zu zentrieren, während das Bild vollständig passen sollte. Ungefüllte Bereiche (nur) oben und unten oder (nur) links und rechts sind akzeptabel. Bei allen Transformationen muss dieser Zustand beibehalten werden.
Zunächst begrenzen wir die Werte für das Zoomen. Dazu überprüfen wir das Seitenverhältnis unter Berücksichtigung der Ausrichtung des Bildes.
Komponentenmethoden: _setMinZoom(){ let rotate = this.matrix.c !== 0; let horizontal = this.imageRect.size.height < this.imageRect.size.width; let areaSize = (horizontal && !rotate || !horizontal && rotate) ? this.areaRect.size.width : this.areaRect.size.height; let imageSize = horizontal ? this.imageRect.size.width : this.imageRect.size.height; this.minZoom = areaSize/imageSize; if(this.transform.zoom < this.minZoom) this.transform.zoom = this.minZoom; }, _setMaxZoom(){ this.maxZoom = this.areaRect.size.width/config.image.minResolution; if(this.transform.zoom > this.maxZoom) this.transform.zoom = this.maxZoom; },
Kommen wir nun zu den Transformationen. Zunächst beschreiben wir die Reflexionen, da sie den sichtbaren Bereich des Bildes nicht verschieben.
Komponentenmethoden: flipX(){ this.matrix.b == 0 && this.matrix.c == 0 ? this.transform.flip = !this.transform.flip : this.transform.flop = !this.transform.flop; }, flipY(){ this.matrix.b == 0 && this.matrix.c == 0 ? this.transform.flop = !this.transform.flop : this.transform.flip = !this.transform.flip; },
Transformationen von Zoomen, Drehen und Verschieben erfordern bereits Anpassungen des Transformationsursprungs.
Komponentenmethoden: onZoomEnd(){ this._translate(); }, rotatePlus(){ this.transform.rotate += 90; this._setMinZoom(); this._translate(); }, rotateMinus(){ this.transform.rotate -= 90; this._setMinZoom(); this._translate(); }, imageMoved(translate){ this._translate(); },
Es ist die
_translate- Methode
, die für alle Feinheiten von Transformationen verantwortlich ist. Es müssen zwei Referenzsysteme eingeführt werden. Das erste, nennen wir es Null, beginnt in der oberen linken Ecke des Bildes. Wenn wir die Koordinaten mit der Transformationsmatrix multiplizieren, gehen wir zu einem anderen Koordinatensystem und nennen es lokal. In diesem Fall können wir den Übergang von lokal zu Null umkehren, indem wir die
inverse Transformationsmatrix finden.
Es stellt sich heraus, dass wir zwei Funktionen benötigen.
Die erste besteht darin, von Null auf das lokale System zu wechseln. Der Browser führt dieselben Konvertierungen durch, wenn wir die CSS-Transformationseigenschaft angeben.
img { transform: matrix(a, b, c, d, tx, ty); }
Die zweite - um die ursprünglichen Koordinaten des Bildes zu finden, nachdem bereits Koordinaten transformiert wurden.
Es ist am bequemsten, diese Funktionen mit Methoden einer separaten Klasse zu schreiben.
Klasse transformieren: class Transform { constructor(center, matrix){ this.init(center, matrix); } init(center, matrix){ if(center) this.center = Object.assign({},center); if(matrix) this.matrix = Object.assign({},matrix); } getOrigins(current){
_Übersetzungsmethode mit detaillierten Kommentaren: _translate(checkAlign = true){ const tr = new Transform(this.transform.center, this.matrix);
Durch die Ausrichtung wird das Bild an den Rändern des Zuschnitts „aufgeklebt“, wodurch leere Felder vermieden werden.
Vorschau-Komponente
Die Hauptaufgabe dieser Komponente besteht darin, das Bild anzuzeigen, Transformationen anzuwenden und auf die Bewegung der Maustaste über dem Bild zu reagieren. Bei der Berechnung des Offsets aktualisieren wir die Parameter
transform.x und
transform.y . Am Ende der Bewegung lösen wir das
verschobene Ereignis aus und teilen der Edit-Komponente mit, dass wir die Position des Transformationszentrums neu berechnen und transform.x und transform.y anpassen müssen.
Vorschau der Komponentenvorlage:<div ref = "area" class = "edit-zone"
@ mousedown = "onMoveStart"
@ touchstart = "onMoveStart"
mouseup = "onMoveEnd"
@ touchend = "onMoveEnd"
@ mousemove = "onMove"
@ touchmove = "onMove">
<img
v-if = "Bild"
ref = "Bild"
load = "imageLoaded"
: src = "Bild"
: style = "{'transform': transformStyle, 'transform-origin': transformOrigin}">
Die Funktionalität des Editors ist sauber vom Hauptprojekt getrennt und liegt
hier .
Ich hoffe, dieses Material wird für Sie nützlich sein. Vielen Dank!