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;
}