Blur mit dem HTML5-Element <canvas>
JavaScript, HTML5
Das HTML5-Element <canvas>
enthält keine Methode zum Blurren. Soll die
Funktionalität hinzugefügt werden, muss hierfür eine eigene Routine implementiert werden.
Voraussetzung für das Funktionieren der blur-Funktion ist das Vorhandensein der Methoden
getImageData
und
putImageData
.
Das nachstehende Script enthält eine solche Implementierung.
Beispiel
Initialisierung
Der Funktion kann ein Objekt zum Initialisieren übergeben werden, das folgende Optionen enthalten kann.
- id - (string) ID des canvas-Elements
- w - (integer) gewünschte Breite des canvas-Elements
- h - (integer) gewünschte Höhe des canvas-Elements
- init - (boolean) true = es werden die Optionen zum Initialisieren des canvas-Elements verwendet (w, h),
false = es werden externe Eigenschaften verwendet (CSS, Attribute width/height usw.)
API
Das Objekt bietet eine API mit folgenden Methoden:
- blur() - zum Blurren des canvas-Inhalts
- insertImg(strSrc, x, y) - Einfügen eines Bildes in den Kontext, dessen Quelle in strSrc übergeben wird. Ausserdem werden die Startkoordinaten in x und y übergeben.
- getPixel(x, y) - Ermitteln eines Pixels mit den Koordinaten (x,y).
Rückgabe ist ein Array mit [0]:rot, [1]:grün, [2]:blau, [3]:alpha - restoreCtx() - Macht schrittweise die Blur-Schritte rückgängig
Beispiel: Initialisieren des Objekts
Der nachstehende Quellcode zeigt, wie das Objekt im onload
-Event initialisiert wird.
JavaScript:
window.onload = function(){ objAPI = qpCanvas({ id: 'canvas_id', w: 100, h: 100 }).insertImg("distel.jpg", 0, 0); }
Über die zurückgegebene Referenz objAPI
kann später auf die API zugegriffen werden, zum Beispiel:
JavaScript:
objAPI.blur();
Quellcode des Scripts
Kurze Anmerkung zum Blurren:
Es werden zu jedem Pixel für jeden Farbkanal (rot, grün, blau) die Farbwerte
der umliegenden Pixel addiert und der arithmetische Mittelwert gebildet (Division durch Acht). Der berechnet
Wert wird dem jeweils zugehörigen Farbkanal im Ausgangspixel zugewiesen.
Der folgende Quellcode zeigt die oben vorgestellte Routine.
JavaScript:
/* ****************************************************************************************** * * Das Script kann frei verwendet werden, dieser Kommentar sowie die Nennung des Nicks * müssen jedoch erhalten bleiben. * * Quaese (www.quaese.de), 2010 * ****************************************************************************************** */ function qpCanvas(objOpts){ var opts = { cvs: null, ctx: null, w: 400, h: 300, id: null, init: true, stack: [] }; for(strKey in objOpts){ opts[strKey] = objOpts[strKey]; } var _this = this; var init = function(){ if((typeof opts.id == "undefined") || (opts.id==null) || (document.getElementById(opts.id)==null)){ alert('keine gültige ID'); return; } opts.cvs = document.getElementById(opts.id); if(opts.cvs.getContext){ opts.ctx = opts.cvs.getContext("2d"); // Falls das canvas-Element über die Optionen initialisiert werden soll if(opts.init){ opts.cvs.style.width = opts.w + "px"; opts.cvs.style.height = opts.h + "px"; }else{ if(window.getComputedStyle){ opts.w = parseInt(window.getComputedStyle(opts.cvs, null)['width']); opts.h = parseInt(window.getComputedStyle(opts.cvs, null)['height']); }else if(opts.cvs.currentStyle){ opts.w = parseInt(opts.cvs.currentStyle['width']); opts.h = parseInt(opts.cvs.currentStyle['height']); } } opts.cvs.width = opts.w; opts.cvs.height = opts.h; // Context bequemer zugänglich machen ctx = opts.ctx; // Hilfskomponente für Contextinhalt initialisieren opts.tmp = null; } } // Methode zum Ermitteln eines Pixels (weniger performant als nachfolgende Variante) // x - (integer) Spaltenwert // y - (integer) Zeilenwert // Rückgabe: (array) Pixel, mit [0]:rot, [1]:grün, [2]:blau, [3]:alpha opts.getPixel__ = function(x, y){ // Falls die benötigte Methode nicht verfügbar ist if(typeof ctx.getImageData != "function") return opts; var objImageData = ctx.getImageData(x, y, 1, 1); return objImageData.data; } // Methode zum Ermitteln eines Pixels (performanter als obige Variante) // x - Spaltenwert // y - Zeilenwert // Rückgabe: (array) Pixel, mit [0]:rot, [1]:grün, [2]:blau, [3]:alpha opts.getPixel = function(x, y){ var blnNull = false; // Falls kein ImageData-Objekt existiert if(opts.tmp == null){ // Falls die benötigte Methode nicht verfügbar ist if(typeof ctx.getImageData != "function") return opts; // Temp. ImageData-Objekt erstellen opts.tmp = ctx.getImageData(0, 0, opts.cvs.width, opts.cvs.height); // Markieren, dass ein temp. ImageData-Objekt erstellt wurde blnNull = true; } //var z = y*(opts.w-1)*4; var z = y*opts.w*4; var s = 4*x; var oPxl = [opts.tmp.data[z + s], opts.tmp.data[z + s +1], opts.tmp.data[z + s + 2], opts.tmp.data[z + s + 3]]; // Falls ein temp. ImageData-Objekt erstellt wurde if(blnNull){ // Temp. ImageData-Objekt wieder zerstören und neu initialiseren delete opts.tmp; opts.tmp = null; } return oPxl; } // Methode zum Berechnen des Blur-Wertes eines Pixels // x - (integer) Spaltenwert // y - (integer) Zeilenwert // Rückgabe: (object) Basisobjekt, um Kette nicht zu unterbrechen opts.blurPixel = function(x, y){ // Umliegende Pixel ermitteln (unter Beachtung der Ränder) var oPxl = { xminus_yminus: opts.getPixel(((x==0)? x+1 : x-1), ((y==0)? y+1 : y-1)), xminus_y: opts.getPixel(((x==0)? x+1 : x-1), y), xminus_yplus: opts.getPixel(((x==0)? x+1 : x-1), ((y==opts.h-1)? y-1 : y+1)), x_yminus: opts.getPixel(x, ((y==0)? y+1 : y-1)), x_yplus: opts.getPixel(x, ((y==opts.h-1)? y-1 : y+1)), xplus_yminus: opts.getPixel(((x==opts.w-1)? x-1 : x+1), ((y==0)? y+1 : y-1)), xplus_y: opts.getPixel(((x==opts.w-1)? x-1 : x+1), y), xplus_yplus: opts.getPixel(((x==opts.w-1)? x-1 : x+1), ((y==opts.h-1)? y-1 : y+1)) }; // Umliegende Pixel summieren var intRed = oPxl.xminus_yminus[0]+oPxl.xminus_y[0]+oPxl.xminus_yplus[0]+oPxl.x_yminus[0]+oPxl.x_yplus[0]+oPxl.xplus_yminus[0]+oPxl.xplus_y[0]+oPxl.xplus_yplus[0]; var intGreen = oPxl.xminus_yminus[1]+oPxl.xminus_y[1]+oPxl.xminus_yplus[1]+oPxl.x_yminus[1]+oPxl.x_yplus[1]+oPxl.xplus_yminus[1]+oPxl.xplus_y[1]+oPxl.xplus_yplus[1]; var intBlue = oPxl.xminus_yminus[2]+oPxl.xminus_y[2]+oPxl.xminus_yplus[2]+oPxl.x_yminus[2]+oPxl.x_yplus[2]+oPxl.xplus_yminus[2]+oPxl.xplus_y[2]+oPxl.xplus_yplus[2]; // Errechnen der mittleren Farben pBlurRed = intRed/8; pBlurGreen = intGreen/8; pBlurBlue = intBlue/8; // Zeilen- und Spaltenwert zum Ermitteln des Offsets des aktuellen Pixels im ImageData-Objekt berechnen var z = y*opts.w*4; var s = 4*x; // Pixel setzen opts.tmp.data[z + s] = pBlurRed; opts.tmp.data[z + s + 1] = pBlurGreen; opts.tmp.data[z + s + 2] = pBlurBlue; return opts; }; // Methode zum Blurren des Context-Inhaltes // Rückgabe: (object) Basisobjekt, um Kette nicht zu unterbrechen opts.blur = function(){ // Falls die benötigte Methode nicht verfügbar ist if(typeof ctx.getImageData != "function"){ alert("Ihr Browser unterstützt die angeforderte Funktion nicht."); return opts; } // Temp. ImageData-Objekt erstellen opts.tmp = ctx.getImageData(0, 0, opts.w, opts.h); // Zeilen for(var m=0; m<opts.h; m++){ // Spalten for(var n=0; n<opts.w; n++){ // Pixel blurren opts.blurPixel(n, m); } } // Aktuellen Kontext speichern opts.stack[opts.stack.length] = ctx.getImageData(0, 0, opts.w, opts.h); // Kontext leeren ctx.clearRect(0, 0, opts.w, opts.h); // Temp. ImageData-Objekt in Context rendern ctx.putImageData(opts.tmp, 0, 0); // Temp. ImageData-Objekt zerstören und neu initialisieren delete opts.tmp; opts.tmp = null; // Kette nicht unterbrechen return opts; }; // Undo-Methode // Rückgabe: (object) Basisobjekt, um Kette nicht zu unterbrechen opts.restoreCtx = function(){ if(opts.stack.length > 0){ // Kontext leeren ctx.clearRect(0, 0, opts.w, opts.h); // Gesichertes ImageData-Objekt in Context rendern ctx.putImageData(opts.stack[opts.stack.length-1], 0, 0); opts.stack.pop(); } // Kette nicht unterbrechen return opts; } // Methode zum Einfügen eines Bildes // strSrc - (string) Bildquelle // x - (integer) Horizontale Startkoordinate // y - (integer) Vertikale Startkoordinate // Rückgabe: (object) Basisobjekt, um Kette nicht zu unterbrechen opts.insertImg = function(strSrc, x, y){ var objImg = new Image(); objImg.src = strSrc; objImg.onload = function(){ ctx.drawImage(this, x, y); }; // Kette nicht unterbrechen return opts; } // Initialisierungsfunktion init(); // Kette nicht unterbrechen return opts; }