UNPKG

@svgdotjs/svg.filter.js

Version:

A plugin for svg.js adding filter functionality

653 lines (490 loc) 16.2 kB
# svg.filter.js A plugin for [svg.js](https://svgdotjs.github.io) adding filter functionality. svg.filter.js is licensed under the terms of the MIT License. - [Examples](#examples) - [Furthermore](#furthermore) - [unfilter](#unfilter) - [referencing the filter node](#referencing-the-filter-node) - [Animating filter values](#animating-filter-values) - [Chaining Effects](#chaining-effects) - [Effect Classes](#effect-classes) ## Usage ### Npm ```sh npm i @svgdotjs/svg.filter.js ``` ### Yarn ```sh yarn add @svgdotjs/svg.filter.js ``` Include this plugin after including the svg.js library in your html document. Here is how each filter effect on the example page is achieved. ## Examples - [gaussian blur](#gaussian-blur) - [horizontal blur](#horizontal-blur) - [desaturate](#desaturate) - [contrast](#contrast) - [sepiatone](#sepiatone) - [hue rotate 180](#hue-rotate-180) - [luminance to alpha](#luminance-to-alpha) - [colorize](#colorize) - [posterize](#posterize) - [darken](#darken) - [lighten](#lighten) - [invert](#invert) - [gamma correct 1](#gamma-correct-1) - [gamma correct 2](#gamma-correct-2) - [drop shadow](#drop-shadow) - [extrude](#extrude) ### original ```javascript var image = draw.image('path/to/image.jpg').size(300, 300) ``` ### gaussian blur ```javascript image.filterWith(function(add) { add.gaussianBlur(30) }) ``` ### horizontal blur ```javascript image.filterWith(function(add) { add.gaussianBlur(30, 0) }) ``` ### desaturate ```javascript image.filterWith(function(add) { add.colorMatrix('saturate', 0) }) ``` ### contrast ```javascript image.filterWith(function(add) { var amount = 1.5 add.componentTransfer({ type: 'linear', slope: amount, intercept: -(0.3 * amount) + 0.3 }) }) ``` ### sepiatone ```javascript image.filterWith(function(add) { add.colorMatrix('matrix', [ .343, .669, .119, 0, 0 , .249, .626, .130, 0, 0 , .172, .334, .111, 0, 0 , .000, .000, .000, 1, 0 ]) }) ``` ### hue rotate 180 ```javascript image.filterWith(function(add) { add.colorMatrix('hueRotate', 180) }) ``` ### luminance to alpha ```javascript image.filterWith(function(add) { add.colorMatrix('luminanceToAlpha') }) ``` ### colorize ```javascript image.filterWith(function(add) { add.colorMatrix('matrix', [ 1.0, 0, 0, 0, 0 , 0, 0.2, 0, 0, 0 , 0, 0, 0.2, 0, 0 , 0, 0, 0, 1.0, 0 ]) }) ``` ### posterize ```javascript image.filterWith(function(add) { add.componentTransfer({ type: 'discrete', tableValues: [0, 0.2, 0.4, 0.6, 0.8, 1] }) }) ``` ### darken ```javascript image.filterWith(function(add) { add.componentTransfer({ type: 'linear', slope: 0.2 }) }) ``` ### lighten ```javascript image.filterWith(function(add) { add.componentTransfer({ type: 'linear', slope: 1.5, intercept: 0.2 }) }) ``` ### invert ```javascript image.filterWith(function(add) { add.componentTransfer({ type: 'table' tableValues: [1, 0] }) }) ``` ### gamma correct 1 ```javascript image.filterWith(function(add) { add.componentTransfer({ g: { type: 'gamma', amplitude: 1, exponent: 0.5 } }) }) ``` ### gamma correct 2 ```javascript image.filterWith(function(add) { add.componentTransfer({ g: { type: 'gamma', amplitude: 1, exponent: 0.5, offset: -0.1 } }) }) ``` ### drop shadow You will notice that all the effect descriptions have a drop shadow. Here is how this drop shadow can be achieved: ```javascript var text = draw.text('SVG text with drop shadow').fill('#fff') text.filterWith(function(add) { var blur = add.offset(0, 1).in(add.$sourceAlpha).gaussianBlur(1) add.blend(add.$source, blur) }) ``` This technique can be achieved on any other shape of course: ```javascript var rect = draw.rect(100,100).fill('#f09').stroke({ width: 3, color: '#0f9' }).move(10,10) rect.filterWith(function(add) { var blur = add.offset(20, 20).in(add.$sourceAlpha).gaussianBlur(5) add.blend(add.$source, blur) this.size('200%','200%').move('-50%', '-50%') }) ``` If the drop shadow should get the colour of the shape so it appears like coloured glass: ```javascript var rect = draw.rect(100,100).fill('#f09').stroke({ width: 3, color: '#0f9' }).move(10,10) rect.filterWith(function(add) { var blur = add.offset(20, 20).gaussianBlur(5) add.blend(add.$source, blur) this.size('200%','200%').move('-50%', '-50%') }) ``` ### extrude ```javascript image.filterWith(function(add){ var matrix = add.convolveMatrix([ 1,0,0,0,0,0, 0,1,0,0,0,0, 0,0,1,0,0,0, 0,0,0,1,0,0, 0,0,0,0,1,0, 0,0,0,0,0,1 ]).attr({ devisor: '2', preserveAlpha: 'false' }).in(add.$sourceAlpha) //recolor it var color = add.composite(add.flood('#ff2222'),matrix,'in'); //merge all of them toggether add.merge(color,add.$source); }) ``` ## Furthermore Some more features you should know about. ### unfilter The `unfilter` method removes the filter attribute from the node: ```javascript image.unfilter() ``` ### creating a reusable filter its also posible to create a filter by using the `new` keyword *NOTE: when creating a filter this way, it can take an optional attr object* ```javascript var filter = new SVG.Filter(); // create the filters effects here filter.offset(20, 20).gaussianBlur(5); filter.blend(filter.$source, blur); filter.size('200%','200%').move('-50%', '-50%') ``` then once you have created the filter you can use it one multiple elements ```javascript var image = new SVG.Image(); var shape = new SVG.Rect(10, 10); image.filterWith(filter); shape.filterWith(filter); ``` ### referencing the filter node An internal reference to the filter node is made in the element: ```javascript image.filterer() ``` This can also be very useful to reuse an existing filter on various elements: ```javascript otherimage.filterWith(image.filterer()) ``` ### Animating filter values Every filter value can be animated as well: ```javascript var hueRotate image.filterWith(function(add) { hueRotate = add.colorMatrix('hueRotate', 0) }) hueRotate.animate(3000).attr('values', 360) ``` ### Chaining Effects [Method chaining](https://en.wikipedia.org/wiki/Method_chaining) is a programing style where each function returns the object it belongs to, for an example look at JQuery.<br> it's possible to chain the effects on a filter when you are creating them, for example: ```javascript image.filterWith(function(add){ add.flood('black',0.5).composite(add.$sourceAlpha,'in').offset(10).merge(add.$source) }) ``` this would create a basic shadow filter where the first input on the `composite` effect would be the `flood` effect, and the input on the offset effect would be the `composite` effect.<br> same with the `merge` effect, its first input would be the `offset` effect, and its second input would be `add.$source` some effects like [Merge](#merge), [Blend](blend), [Composite](#composite), [DisplacementMap](displacementmap) have thier arguments changed when they are chained, for example ```javascript image.filterWith(function(add){ add.flood('black',0.5).composite(add.$sourceAlpha,'in') }) ``` the `composite` effects first input is set to the `flood` effect and its second input becomes the first argument, this is the same for the merge, blend, composite, and displacmentMap effect. <br> for more details check out each effects doc below ## Effect Classes - [Base Effect Class](base-effect-class) - [Blend](#blend) - [ColorMatrix](#colormatrix) - [ComponentTransfer](#componenttransfer) - [Composite](#composite) - [ConvolveMatrix](#convolvematrix) - [DiffuseLighting](#diffuselighting) - [DisplacementMap](#displacementmap) - [Flood](#flood) - [GaussianBlur](#gaussianblur) - [Image](#image) - [Merge](#merge) - [Morphology](#morphology) - [Offset](#offset) - [SpecularLighting](#specularlighting) - [Tile](#tile) - [Turbulence](#turbulence) ### Base Effect Class #### in(effect) gets or sets the `in` attribute of the effect - **effect:** this can be another effect or a string <br> if **effect** is not provided it will look for another effect on the same filter whose `result` is equal to this effects `in` attribute, else it will return the value of the `in` attribute ```javascript image.filterWith(function(add){ var offset = add.offset(10) //create the blur effect and then set its input var blur = add.gaussianBlur(3) //set the input to an effect blur.in(offset) //this will return the offset effect var input = blur.in() //set the input to a string blur.in('another-result-as-a-string') //this will return a string since there is no other effect which has a matching result attribute var input2 = blur.in() }) ``` #### in2(effect) gets or sets the `in2` attribute of the effect <br> this function works the same as the [in](#ineffect) method. <br> it's only on effects ([Blend](#blend), [Composite](#composite), and [DisplacementMap](#displacementmap)) #### result(string) gets or sets the `result` attribute of the effect - **string:** if a string is provided it will set the value of the `result` attribute. <br> if no arguments are provided it will act as a getter and return the value of the `result` attribute ### Blend [W3 doc](https://www.w3.org/TR/SVG/filters.html#feBlendElement) ```javascript filter.blend(in1, in2, mode) //or new SVG.BlendEffect({in1, in2, mode}) ``` - **in1**: an effect or the result of effect - **in2**: same as **in1** - **mode**: "normal | multiply | screen | darken | lighten" defaults to "normal" **chaining** when this effect is called right after another effect, for example: ```javascript filter.offset(10).blend(filter.$source) ``` the first input is set to the `offset` effect and the second input is set to `filter.$source` or what ever was passed as the first argument, and the second input becomes the **mode** ### ColorMatrix [W3 doc](https://www.w3.org/TR/SVG/filters.html#feColorMatrixElement) ```javascript filter.colorMatrix(type, values); //or new SVG.ColorMatrixEffect({type, values}); ``` - **type**: "matrix | saturate | hueRotate | luminanceToAlpha" - **values** - **type="matrix"**: values would be a matrix the size of 4x5 - **type="saturate"**: number (0 to 1) - **type="hueRotate"**: number (0 to 360) deg - **type="luminanceToAlpha"**: value not needed ### ComponentTransfer [W3 doc](https://www.w3.org/TR/SVG/filters.html#feComponentTransferElement) ```javascript filter.componentTransfer(components); // or filter.componentTransfer(function (add) { add.funcA({ type... }) }); //or new SVG.ComponentTransferEffect(); ``` - **components**: an object which is set for all chanels or `r`, `g`, `b`, `a` properties for each chanel ```javascript type: "identity | table | discrete | linear | gamma", //type="table" tableValues: "0 0.5 2 1", //number separated by spaces //type="linear" slope: 1, //number intercept: 3,//number //type="gamma" amplitude: 0, //number exponent: 0, //number offset: 0 //number } ``` ### Composite [W3 doc](https://www.w3.org/TR/SVG/filters.html#feCompositeElement) ```javascript filter.composite(in1, in2, operator); //or new SVG.CompositeEffect({in1, in2, operator}); ``` - **in1**: an effect or the result of an effect - **in2**: same as **in1** - **operator**: "over | in | out | atop | xor | arithmetic" defaults to "over" **chaining** when this effect is called right after another effect, for example: ```javascript filter.flood('black',0.5).composite(filter.$sourceAlpha,'in') ``` the first input is set to the `flood` effect and the second input is set to `filter.$sourceAlpha` or what ever was passed as the first argument.<br> also the second argument becomes the **operator** ### ConvolveMatrix [W3 doc](https://www.w3.org/TR/SVG/filters.html#feConvolveMatrixElement) ```javascript filter.convolveMatrix(matrix); //or new SVG.ConvolveMatrixEffect({matrix}); ``` - **matrix**: a square matrix of numbers that will be applied to the image - exmaple: ```javascript [ 1,0,0, 0,1,0, 0,0,1 ] ``` ### DiffuseLighting [W3 doc](https://www.w3.org/TR/SVG/filters.html#feDiffuseLightingElement) ```javascript filter.diffuseLighting(surfaceScale, lightingColor, diffuseConstant, kernelUnitLength); //or new SVG.DiffuseLightingEffect({surfaceScale, lightingColor, diffuseConstant, kernelUnitLength}); ``` ***very complicated, just check out the W3 doc*** ### DisplacementMap [W3 doc](https://www.w3.org/TR/SVG/filters.html#feDisplacementMapElement) ```javascript filter.displacementMap(in1, in2, scale, xChannelSelector, yChannelSelector); //or new SVG.DisplacementMapEffect({in1, in2, scale, xChannelSelector, yChannelSelector}); ``` ***very complicated, just check out the W3 doc*** **chaining** when this effect is called right after another effect, for example: ```javascript filter.offset(20,50).displacementMap(filter.$source,2) ``` the first input is set to the `offset` effect and the second input is set to `filter.$source` or what ever was passed as the first argument.<br> also the second argument becomes the **scale**, and the third argument is the **xChannelSelector** and so on ### Flood [W3 doc](https://www.w3.org/TR/SVG/filters.html#feFloodElement) ```javascript filter.flood(color,opacity); //or new SVG.FloodEffect(color,opacity); ``` - **color**: a named or hex color in string format - **opacity**: number form 0 to 1 ### GaussianBlur [W3 doc](https://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement) ```javascript filter.gaussianBlur(x, y); //or new SVG.GaussianBlurEffect({x, y}); ``` - **x**: blur on the X - **y**: blur on the y, will default to the **x** if not provided ### Image [W3 doc](https://www.w3.org/TR/SVG/filters.html#feImageElement) ```javascript filter.image(src); //or new SVG.ImageEffect({src}); ``` ### Merge [W3 doc](https://www.w3.org/TR/SVG/filters.html#feMergeElement) ```javascript filter.merge(); //or new SVG.MergeEffect(); ``` - **Array**: an Array of effects or effect results `filter.merge([effectOne,"result-two",another_effect])` - **chaining** you can also chain the merge effect `filter.offset(10).merge(anotherEffect)` which will result in a merge effect with its first input set to the `offset` effect and its second input set to `anotherEffect` ### Morphology [W3 doc](https://www.w3.org/TR/SVG/filters.html#feMorphologyElement) ```javascript filter.morphology(operator, radius); //or new SVG.MorphologyEffect({operator, radius}); ``` - **operator**: "erode | dilate" - **radius**: a single number or a string of two numbers separated by a space - the first number is the X - the second number is the Y, if no second number was provided it will default to the first number ### Offset [W3 doc](https://www.w3.org/TR/SVG/filters.html#feOffsetElement) ```javascript filter.offset(x, y); //or new SVG.OffsetEffect({x, y}); ``` - **x**: move on the X - **y**: move on the y, will default to the **x** if not provided ### SpecularLighting [W3 doc](https://www.w3.org/TR/SVG/filters.html#feSpecularLightingElement) ```javascript filter.specularLighting(surfaceScale, lightingColor, diffuseConstant, specularExponent, kernelUnitLength); //or new SVG.SpecularLightingEffect(surfaceScale, lightingColor, diffuseConstant, specularExponent, kernelUnitLength); ``` ***very complicated, just check out the W3 doc*** ### Tile [W3 doc](https://www.w3.org/TR/SVG/filters.html#feTileElement) ```javascript filter.tile(); //or new SVG.TileEffect(); ``` ***no arguments, but if you want to find out what it does check out the W3 doc*** ### Turbulence [W3 doc](https://www.w3.org/TR/SVG/filters.html#feTurbulenceElement) ```javascript filter.turbulence(baseFrequency, numOctaves, seed, stitchTiles, type); //or new SVG.TurbulenceEffect({baseFrequency, numOctaves, seed, stitchTiles, type}); ``` ***very complicated, just check out the W3 doc***