123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574 |
- /**
- * FRaphael
- * An extension for Raphael.js to make it easier to work with Filter Effects
- *
- * Copyright © 2013 Chris Scott <chris.scott@factmint.com>
- * Delivered with and licensed under the MIT licence
- *
- */
- // Create the global FRaphael object
- (function(scope) {
- var version = "0.0.1",
- license = "MIT";
-
- var ns = "http://www.w3.org/2000/svg",
- idCounter = 0;
-
- var FR = {
- // Object prototype for a filter
- Filter: function(id) {
- if (id == undefined) {
- id = "filter-" + idCounter++;
- while(FR.filters[id] != undefined) {
- id = "filter-" + idCounter++;
- }
- }
-
- if (FR.filters[id] != undefined) {
- throw "A filter with id " + id + " already exists";
- }
-
- this.element = document.createElementNS(ns, "filter");
- this.element.setAttribute("id", id);
- this.element.setAttribute("x", "-25%");
- this.element.setAttribute("y", "-25%");
- this.element.setAttribute("width", "150%");
- this.element.setAttribute("height", "150%");
-
- this.lastFEResult = null;
-
- FR.filters[id] = this;
- this.id = id;
- },
-
- // Object prototype for an effect
- FilterEffect: function(type, attributes) {
- this.element = document.createElementNS(ns, type);
- for (var key in attributes) {
- this.element.setAttribute(key, attributes[key]);
- }
- },
-
- // Return the filter applied to an element or a new filter if none are currently applied
- getFilter: function(element) {
- var filterId = element.data("filterId");
- var filter = null;
-
- if (filterId == undefined) {
- filterId = "element-filter-" + element.id;
- filter = element.paper.createFilter(filterId);
- element.filter(filterId);
- } else {
- filter = FR.filters[filterId];
- }
-
- return filter;
- },
-
- // maintain a list of filters by id
- filters: {}
- };
-
- FR.Filter.prototype = {
- addEffect: function(type, attributes, children) {
- var effect = new FR.FilterEffect(type, attributes);
-
- if (children) {
- if (children instanceof Array) {
- for (var x in children) {
- if (!children.hasOwnProperty(x)) continue;
- effect.element.appendChild(children[x].element);
- }
- } else {
- effect.element.appendChild(children.element);
- }
- }
-
- this.element.appendChild(effect.element);
-
- return this;
- },
-
- chainEffect: function(type, attributes, children) {
- if (attributes == undefined) {
- attributes = {};
- }
-
- var inId;
- var outId;
- if (attributes.in == undefined) {
- inId = this.getLastResult();
- } else {
- inId = attributes.in;
- }
- if (attributes.result == undefined) {
- outId = idCounter++;
- } else {
- outId = attributes.result;
- }
-
- this.lastFEResult = outId;
-
- attributes.in = inId;
- attributes.result = outId;
-
- this.addEffect(type, attributes, children);
-
- return this;
- },
-
- getLastResult: function() {
- return (this.lastFEResult == undefined) ? "SourceGraphic" : this.lastFEResult;
- },
-
- merge: function(in1, in2, attributes) {
- var mergeNode1 = new FR.FilterEffect("feMergeNode", {
- in: in1
- });
- var mergeNode2 = new FR.FilterEffect("feMergeNode", {
- in: in2
- });
-
- this.chainEffect("feMerge", attributes, [mergeNode1, mergeNode2]);
-
- return this;
- },
-
- compose: function(in1, in2, operator, attributes) {
- if (attributes == undefined) {
- attributes = {};
- }
-
- if (operator == undefined) {
- operator = "over";
- }
-
- attributes.in = in1;
- attributes.in2 = in2;
- attributes.operator = operator;
-
- this.chainEffect("feComposite", attributes);
-
- return this;
- },
-
- arithmeticCompose: function(in1, in2, k1, k2, k3, k4) {
- if (k1 == undefined) {
- k1 = 0;
- }
- if (k2 == undefined) {
- k2 = 0;
- }
- if (k3 == undefined) {
- k3 = 0;
- }
- if (k4 == undefined) {
- k4 = 0;
- }
-
- this.compose(in1, in2, "arithmetic", {
- k1: k1,
- k2: k2,
- k3: k3,
- k4: k4
- });
-
- return this;
- },
-
- addBlur: function(stdDeviation, attributes) {
- if (!stdDeviation) {
- throw "Standard deviation is required to perform a blur filter";
- }
-
- if (attributes == undefined) {
- attributes = {};
- }
- attributes.stdDeviation = stdDeviation;
-
- this.chainEffect("feGaussianBlur", attributes);
-
- return this;
- },
-
- addOffset: function(dx, dy, attributes) {
- if (dx == undefined | dy == undefined) {
- throw "dx and dy values are required to perform an offset FE";
- }
-
- if (attributes == undefined) {
- attributes = {};
- }
- attributes.dx = dx;
- attributes.dy = dy;
-
- this.chainEffect("feOffset", attributes);
-
- return this;
- },
-
- addLighting: function(x, y, z, color, type, attributes) {
- if (x == undefined | y == undefined | z == undefined) {
- throw "Three co-ordinates are required to create a light source";
- }
-
- var previousResult = this.getLastResult();
-
- var id = idCounter++;
-
- if (attributes == undefined) {
- attributes = {};
- }
-
- attributes.result = id;
- if (color != undefined) {
- attributes["lighting-color"] = color;
- }
-
- if (type == undefined || type == "diffuse") {
- type = "feDiffuseLighting";
- } else if (type == "specular") {
- type = "feSpecularLighting";
- }
-
- var lightSource = new FR.FilterEffect("fePointLight", {
- x: x,
- y: y,
- z: z
- });
-
- this.chainEffect(type, attributes, lightSource).arithmeticCompose(previousResult, id, 3, 0.2, 0, 0);
-
- return this;
- },
-
- addShiftToColor: function(color, moveBy, attributes) {
- if (color == undefined) {
- throw "A colour string is a required argument to create a colorMatrix";
- }
- if (moveBy == undefined) {
- moveBy = 0.5;
- }
-
- var remainingColor = 1 - moveBy, x = remainingColor;
-
- if (attributes == undefined) {
- attributes = {};
- }
-
- var colorObject = Raphael.color(color);
- var r = colorObject.r * moveBy / 255,
- g = colorObject.g * moveBy / 255,
- b = colorObject.b * moveBy / 255;
-
- /**
- * r' x 0 0 0 r r
- * g' 0 x 0 0 g g
- * b' = 0 0 x 0 b . b
- * a' 0 0 0 1 0 o
- * 1 1
- */
- attributes.values = x + " 0 0 0 " + r + " 0 " + x + " 0 0 " + g + " 0 0 " + x + " 0 " + b + " 0 0 0 1 0 ";
-
- this.chainEffect("feColorMatrix", attributes);
-
- return this;
- },
-
- addRecolor: function(color, opacity, attributes) {
- if (color == undefined) {
- throw "A colour string is a required argument to create a colorMatrix";
- }
- if (opacity == undefined) {
- opacity = 1;
- }
-
- if (attributes == undefined) {
- attributes = {};
- }
-
- var colorObject = Raphael.color(color);
- var r = colorObject.r / 255,
- g = colorObject.g / 255,
- b = colorObject.b / 255;
-
- /**
- * r' 0 0 0 0 r r
- * g' 0 0 0 0 g g
- * b' = 0 0 0 0 b . b
- * a' 0 0 0 a 0 a
- * 1 1
- */
- attributes.values = "0 0 0 0 " + r + " 0 0 0 0 " + g + " 0 0 0 0 " + b + " 0 0 0 " + opacity + " 0 ";
-
- this.chainEffect("feColorMatrix", attributes);
-
- return this;
- },
-
- addDesaturate: function(saturation, attributes) {
- if (saturation == undefined) {
- saturnation = 0;
- }
- if (attributes == undefined) {
- attributes = {};
- }
- attributes.values = saturation;
- attributes.type = "saturate";
- this.chainEffect("feColorMatrix", attributes);
- return this;
- },
-
- addConvolveMatrix: function(matrix, attributes) {
- if (matrix == undefined) {
- throw "A matrix (usually 9 numbers) must be provided to apply a convolve matrix transform";
- }
-
- if (attributes == undefined) {
- attributes = {};
- }
-
- attributes.kernelMatrix = matrix;
-
- this.chainEffect("feConvolveMatrix", attributes);
-
- return this;
- },
-
- createShadow: function(dx, dy, blur, opacity, color) {
- if (dx == undefined) {
- throw "dx is required for the shadow effect";
- }
- if (dy == undefined) {
- throw "dy is required for the shadow effect";
- }
- if (blur == undefined) {
- throw "blur (stdDeviation) is required for the shadow effect";
- }
-
- if (opacity == undefined) {
- opacity = 0.6;
- }
-
- var previousResult = this.getLastResult();
-
- if (color == undefined) {
- color = "#000000";
- }
-
- this.addOffset(dx, dy, {
- in: "SourceAlpha"
- });
-
- this.addRecolor(color, opacity);
-
- this.addBlur(blur);
-
- this.merge(this.getLastResult(), previousResult);
-
- return this;
- },
-
- createEmboss: function(height, x, y, z) {
- if (height == undefined) {
- height = 2;
- }
- if (x == undefined) {
- x = -1000;
- }
- if (y == undefined) {
- y = -5000;
- }
- if (z == undefined) {
- z = 300;
- }
-
- // Create the highlight
-
- this.addOffset(height * x / (x + y), height * y / (x + y), {
- in: "SourceAlpha"
- });
-
- this.addBlur(height * 0.5);
-
- var whiteLightSource = new FR.FilterEffect("fePointLight", {
- x: x,
- y: y,
- z: z
- });
-
- this.chainEffect("feSpecularLighting", {
- surfaceScale: height,
- specularConstant: 0.8,
- specularExponent: 15
- }, whiteLightSource);
-
- this.compose(this.getLastResult(), "SourceAlpha", "in");
- var whiteLight = this.getLastResult();
-
- // Create the lowlight
-
- this.addOffset(height * -1 * x / (x + y), height * -1 * y / (x + y), {
- in: "SourceAlpha"
- });
-
- this.addBlur(height * 0.5);
-
- var darkLightSource = new FR.FilterEffect("fePointLight", {
- x: -1 * x,
- y: -1 * y,
- z: z
- });
-
- this.chainEffect("feSpecularLighting", {
- surfaceScale: height,
- specularConstant: 1.8,
- specularExponent: 6
- }, darkLightSource);
-
- this.compose(this.getLastResult(), "SourceAlpha", "in");
- this.chainEffect("feColorMatrix", {
- values: "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0"
- });
- var darkLight = this.getLastResult();
-
- this.arithmeticCompose(whiteLight, darkLight, 0, 0.8, 0.5, 0);
-
- this.merge("SourceGraphic", this.getLastResult());
-
- return this;
- }
- };
-
- scope.FRaphael = FR;
- })(this);
- /**
- * add a filter to the paper by id
- */
- Raphael.fn.createFilter = function(id) {
- var paper = this;
- var filter = new FRaphael.Filter(id);
- paper.defs.appendChild(filter.element);
-
- return filter;
- };
- /**
- * Apply a filter to an element by id
- */
- Raphael.el.filter = function(filter) {
- var id = (filter instanceof FRaphael.Filter) ? filter.id : filter;
-
- this.node.setAttribute("filter", "url(#" + id + ")");
- this.data("filterId", id);
-
- return this;
- };
- /**
- * Get the current filter for an element or a new one if not
- */
- Raphael.el.getFilter = function() {
- return FRaphael.getFilter(this);
- };
- /**
- * A shorthand method for applying blur
- */
- Raphael.el.blur = function(stdDeviation) {
- if (stdDeviation == undefined) {
- stdDeviation = 3;
- }
-
- this.getFilter().addBlur(stdDeviation);
-
- return this;
- };
- /**
- * A shorthand method for applying a drop shadow
- */
- Raphael.el.shadow = function(dx, dy, blur, opacity, color) {
- if (dx == undefined) {
- dx = 3;
- }
- if (dy == undefined) {
- dy = 3;
- }
- if (blur == undefined) {
- blur = 3;
- }
-
- this.getFilter().createShadow(dx, dy, blur, opacity, color);
-
- return this;
- };
- /**
- * A shorthand method for applying lighting
- */
- Raphael.el.light = function(x, y, z, color, type) {
- if (x == undefined) {
- x = this.paper.width;
- }
- if (y == undefined) {
- y = 0;
- }
- if (z == undefined) {
- z = 20;
- }
-
- this.getFilter().addLighting(x, y, z, color, type);
-
- return this;
- };
- /**
- * A shorthand method for applying a colour shift
- */
- Raphael.el.colorShift = function(color, shift) {
- if (color == undefined) {
- color = "black";
- }
- if (shift == undefined) {
- shift = 0.5;
- }
-
- this.getFilter().addShiftToColor(color, shift);
-
- return this;
- };
- /**
- * A shorthand method for embossing
- */
- Raphael.el.emboss = function(height) {
- this.getFilter().createEmboss(height);
-
- return this;
- };
- /**
- * A shorthand method for desaturating
- */
- Raphael.el.desaturate = function(saturation) {
- this.getFilter().addDesaturate(saturation);
-
- return this;
- };
- /**
- * A shorthand method for complete desaturation
- */
- Raphael.el.greyScale = function() {
- this.getFilter().addDesaturate(0);
-
- return this;
- };
|