How to use svg filters with raphael js? - svg

i would like to know, which techniques should i use to apply svg filters to raphael paths?
I know that raphael tries to be as much cross browser with IE it can, but i was wondering if there was a way to add the filters using javascript.

I built a library to do this. You can do something like:
var paper = Raphael("test");
var circle = paper.circle(100, 100, 50, 50).attr({fill: "red", stroke: "black"});
circle.emboss();
Have a look at a fiddle: http://jsfiddle.net/chrismichaelscott/5vYwJ/
or the project page: http://chrismichaelscott.github.io/fraphael

It's quite possible to extend Raphaël to add svg filters, for blur look at raphael.blur.js. That can serve as a starting point for adding other filter effects. For a bit more info on filters (along with examples) see the SVG Primer.

One hacky way to use SVG filters with Raphael objects is the following technique. It creates Raphael rectangle and adds filter definition to the same SVG document. This of course doesn't work with older browsers which lack support for inline SVG. But this is not a big problem, because older browsers have also no SVG filter support.
Although this is not jQuery tagged question, for simplicity the code uses jQuery for DOM manipulations. The namespace problem is solutioned using dummy SVG element, which has the advantage that SVG elements can be created using text strings (instead of DOM methods). Let the browser do what browser can!
The working example is in http://jsbin.com/ilinan/1.
<html>
<head>
<script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.0.0/raphael-min.js"></script>
<script src="http://code.jquery.com/jquery-1.8.2.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var p = Raphael("cont", 300, 200);
$(p.canvas).attr("id", "p");
var rect = p.rect(10, 10, 100, 100);
$(rect.node).attr("id", "rect");
$("#rect").attr("filter", "url(#innerbewel)");
$("#rect").attr("fill", "red");
var f = "<filter id='innerbewel' x0='-50%' y0='-50%' width='200%' height='200%'>\
<feGaussianBlur in='SourceAlpha' stdDeviation='2' result='blur'/>\
<feOffset dy='3' dx='3'/>\
<feComposite in2='SourceAlpha' operator='arithmetic'\
k2='-1' k3='1' result='hlDiff'/>\
<feFlood flood-color='white' flood-opacity='0.8'/>\
<feComposite in2='hlDiff' operator='in'/>\
<feComposite in2='SourceGraphic' operator='over' result='withGlow'/>\
\
<feOffset in='blur' dy='-3' dx='-3'/>\
<feComposite in2='SourceAlpha' operator='arithmetic'\
k2='-1' k3='1' result='shadowDiff'/>\
<feFlood flood-color='black' flood-opacity='0.8'/>\
<feComposite in2='shadowDiff' operator='in'/>\
<feComposite in2='withGlow' operator='over'/>\
</filter>";
// Create dummy svg with filter definition
$("body").append('<svg id="dummy" style="display:none"><defs>' + f + '</defs></svg>');
// Append filter definition to Raphael created svg
$("#p defs").append($("#dummy filter"));
// Remove dummy
$("#dummy").remove();
$("#rect").attr("fill", "orange");
});
</script>
</head>
<body>
<div id="cont"></div>
</body>

Related

Snap.svg failing to .transform() for <use> tag element

I have an use element in my svg like
<use x="47.46" y="44.64" cx="48" cy="45" id="9zh7" href="#msi_plane" class="markerpoint"></use>
I have a hover function which transform .markerpoint class element on hover:-
$('.markerpoint').hover(
function() {
var mySnapElement = Snap(this);
mySnapElement.transform('s1.5');
}
function() {
mySnapElement.transform('s1');
}
)
Transform is working fine for other element of svg like <circle ....> or <rect ....> but throwing error for <use ...> tag
Error response data:-
snap.svg.js:2786 Uncaught TypeError: Cannot read properties of null (reading 'substring')
at Element.elproto.getBBox (snap.svg.js:2786)
at extractTransform (snap.svg.js:2834)
at Element.elproto.transform (snap.svg.js:2896)
at SVGUseElement.<anonymous> (main.js:1023)
at SVGUseElement.handle (jquery.min.js:2)
at SVGUseElement.dispatch (jquery.min.js:2)
at SVGUseElement.v.handle (jquery.min.js:2)
Seems like it is limitation of Snap.svg in handling <use>
Looking for hacks or suggestion for way forward
It seems from a quick perusal of the source code that Snap.svg does not support bare href but requires you to use xlink:href
The use of href is new in SVG 2, SVG 1.1 was xlink:href only.

How can I use an SVG image as a map marker in OpenLayers-3?

I am trying to create map "pin-drops" (ie. map markers) in OpenLayers-3 (OL3) using SVG images.
Currently, I am using PNG images as the pindrops that reference the ol.style.Icon source (“src”) property attribute just fine. However, this fails using an SVG image. Is there some other way to use an SVG in the same manner? Maybe by using a reference besides ol.style.Icon even? There is already a lot of built-in SVG in Open Layers so this should be possible, but I haven't found a way to get this working in OL3. Is there some other way to do this in OL3 that I should consider?
Please note: we already tried using an ol.Vector layer, however when the user zooms in/out, the size of the SVG image grows/shrinks which is an inadequate workaround.
OL3 (fails):
var createMapMarkerImage = function() {
return function(feature, resolution) {
var iconStyle = new ol.style.Style({
image: new ol.style.Icon( ({
src: 'img/map_pindrop.svg' // OL3 doesn’t like this, but accepts a .PNG just fine
}))
});
return [iconStyle];
};
};
Very similar functionality, is the below example I found online, is almost perfect if it weren’t for the fact that the example uses OpenLayers-2 (OL2) functionality which calls openlayers.js library (instead of OL3’s ol.js library). Sadly, swapping these javascript files out fails.
OL2 (works -but is the old OL library):
http://dev.openlayers.org/sandbox/camptocamp/tipi/examples/vector-symbols.html
Searching online for a solution to this seems to produce only other confused people searching for a solution.
Please help,
FreeBeer
Based on #ahocevar answer, you can use data URIs for SVG:
new ol.style.Style({
image: new ol.style.Icon({
anchor: [0, 0],
src: 'data:image/svg+xml;utf8,<svg>/* SVG DATA */</svg>'
})
});
Convert the SVG to Base 64 . This (Link) helped me.
copied the base 64 and used it as a string in javascript .
Eg : var svg = "convertedBase64";
Then
var icon = new ol.style.Icon({
src:'data:image/svg+xml;base64,'+svg ,
other props
});
And you are done, may be a few Kbs more than SVG but this did work perfect for me .
SVG icons work fine as long as the content-type of your SVG image file is image/svg+xml. Also note that no external references are supported inside the SVG. OpenLayers 3 simply uses the drawImage function of the 2d context. You can find more details on the requirements of SVG content here: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Drawing_DOM_objects_into_a_canvas.
I had the same issue, but not even serving the image with the proper mime type helped.
It boiled down to the SVG not defining width and height properly.
I added the width and height attributes to the <svg> tag, like:
<svg width="100px" height="100px" version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0.75 0 30 45" xml:space="preserve">
After that I was able to use my svg just like any other image.
I also had issues to show the icon image, ahocevar answer helped me to
solve my problem but I had also to search for the php header, for SVG
In case you are or others who see this answer are using php to generate the SVG you have to use header function to identify the content-type
header('Content-type: image/svg+xml'); /* this line will do the magic */
echo '<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/>
</svg>';
Building upon SoonDead's answer, I had to come up with a way to add the width and height to the svg without touching the source. Using angular, this is sort of what I did:
$http.get('path/to/image.svg').then(function (response) {
// create element
var svgEl = angular.element(response.data);
// set width and height
svgEl.attr('width', '50px');
svgEl.attr('height', '50px');
// base64 encode
var base64Svg = btoa(unescape(encodeURIComponent(svgEl[0].outerHTML)));
// create the style
var style = new ol.style.Style({
image: new ol.style.Icon({
src: 'data:image/svg+xml;base64,'+base64Svg,
imgSize: [50, 50],
size: [50, 50],
})
});
// apply the style
feature.setStyle(style);
});
It's a little verbose, but it seems to do the job.

Scaling a circle in SVG

I have the following SVG code. To scale the circle only, I have to scale the container. I can't scale the circle inside the SVG.
I can access the circle element, but why can't I like other html elements? Is the code scaling the circle or the container?
<html>
<head>
<script>
function scale() {
document.getElementById('container').setAttribute('currentScale', 1.5);
}
</script>
</head>
<body>
<svg height="150" width="150" id="container">
<circle cx="25" r="20" cy="20" fill="blue" id="circle"></circle>
</svg>
<button id="zoom" onclick="scale()">scale</button>
</body>
</html>
I've never tried scaling the whole SVG (using the pure SVG transform attribute). But look like you can just scale each element inside SVG. In this case you have to target (select) the circle first before calling setAttribute on it to modify the transform attribute to scale(1.5,1.5) like this:
function scale() {
document.querySelector("#container > circle")
.setAttribute('transform', 'scale(1.5,1.5)');
}
Here is the Demo. Note that you have to select the option No Wrap - in <head> (on the right hand in the Frameworks & Extensions section). Or better you should attach click handler right in JS code editor (not inline as an attribute in HTML code).
Here is one way to scale just the circle using JS:
Demo: http://jsfiddle.net/hb4nnau0/1/
For convenience, give your circle a transform attribute with no change in scale added. (Alternatively you can add this programmatically on demand.)
`
In your event handler, modify the scale of this transform element:
var circle = document.querySelector('circle'); // Or however, e.g. by id
var scale = circle.transform.animVal.getItem(0); // The first transform
scale.setScale(4,3); // Modify the transform

draggable rect in svg drags only diagnolly on the screen

I tried making a small program where in I wrote code to drag a rectangle in svg. The program is quite simple. My problem is that the rectangle drags only diagnolly on the screen and not on the entire web page.
Here is my code..
<?xml version="1.0"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="100%" height="100%"
onload="Init( evt )" >
<script type="text/ecmascript">
//<![CDATA[
var svgDocument;
var svgRoot;
var newP;
var bmousedown=0;
var myCirc;
function Init(evt){
svgRoot= document.getElementsByTagName("svg")[0];
newP = svgRoot.createSVGPoint();
myCirc = document.getElementById("mycirc");
}
function getMouse(evt){
var position = svgRoot.createSVGPoint();
position.x = evt.clientX;
position.y = evt.clientY;
return position;
}
function onMouseDown(evt){
bmousedown=1;
newP=getMouse(evt);
doUpdate();
}
function onMouseMove(evt){
if(bmousedown){
newP=getMouse(evt);
doUpdate();
}
}
function onMouseUp(evt){
bmousedown=0;
}
function doUpdate(){
myCirc.setAttributeNS(null, "x", newP.x );
myCirc.setAttributeNS(null, "y", newP.y );
}
// ]]></script>
<rect id="mycirc" fill: #bbeeff" x="0" y="0" width="80" height="80"
pointer-events="visible"
onmousedown="onMouseDown(evt)"
onmousemove="onMouseMove(evt)"
onmouseup="onMouseUp(evt)"/>
</svg>
Please help me as I am unable to understand why does it not move on the entire screen.
i see the problem recreated on firefox, but it's not a single problem, your code is all over the place. i suggest going back to the drawing board before posting specific questions.
i'd also recommend a good reference on SVG or using a JS vector graphics library, as it would simplify things a little and will ease up the development a lot, if you're not interested in getting down to the nitty-gritty.
Here is the correct solution: http://jsfiddle.net/mihaifm/5GHJs/
The mistakes I think you made:
onload="Init( evt )" makes the variable evt global, a bad and useless thing to have. All the functions are also global, but this should be ok for this example.
the function calls for onmousedown etc. were using this global evt. (wrong). In order to get the correct event for each call you need to register some handlers.

One SVG file, many SVG gradients inside

I’m making a set of buttons which use dynamic gradients. I’ve taken care of Firefox 3.6+ and WebKit by using their proprietary CSS extensions and all I need to do is support Opera, iOS and IE9 by using background-image: url("gradient.svg").
This is relatively easy, I made an SVG file, linked it and got it working. However, I’m making a set so I need at least 6 gradients. When I normally do it in images, I create a sprite for fast HTTP access. I’m not sure how to achieve this in SVG – can I use one file and access different parts of its XML by using #identifiers, like XBL does?
My current SVG:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="select-gradient" x1="0" x2="0" y1="0" y2="1">
<stop offset="0%" stop-color="rgb(231,244,248)"/>
<stop offset="100%" stop-color="rgb(207,233,241)"/>
</linearGradient>
<style type="text/css">
rect {
fill: url(#select-gradient);
}
</style>
</defs>
<rect x="0" y="0" rx="6" ry="6" height="100%" width="100%"/>
</svg>
And then I have CSS:
.button-1 {
background-image: url("gradient-1.svg");
}
.button-2 {
background-image: url("gradient-2.svg");
}
I want to do something like this:
.button-1 {
background-image: url("gradient.svg#gradient1");
}
.button-2 {
background-image: url("gradient.svg#gradient2");
}
Is it even possible? Can you help me out? I really don’t wanna push 6 XML files when I can do it with one.
If you just want gradients for button backgrounds, most of this can be acheived in css. For the remaining browsers, ie6 + can user ms filters:
http://msdn.microsoft.com/en-us/library/ms532847.aspx
iOS uses webkit to render, so you can use -webkit vendor prefix. Unfortunately you will still need svg for opera, but this may make it easier (or just use a normal image sprite for opera's 1% of users)
in theory - according to SVG documentation #Params it is possible. You could use 2 params for setting up both colors, you could create multiple rects with different gradients, height set to 0 and then make only one 100% (like ?gradient2=100%)
What you could do is load your SVG file that contains all of the definitions first, and then load your other SVG files.
Using Firefox, jQuery SVG , and a minor shot of framework...
in your XHTML:
<div id="common_svg_defs"><!--ieb--></div>
<div id="first_thing"><!--ieb--></div>
<div id="second_thing"><!--ieb--></div>
in your JavaScript:
var do_stuff = function()
{
// load your common svg file with this goo.
$('#common_svg_defs').svg({
loadURL: 'path/filename.svg',
onLoad: function(svg, error) { run_test(svg, error);} });
}
var run_test = function(svg, error)
{
if (typeof(error) !== "undefined")
{
if (typeof(console.log) !== "undefined")
{
console.log(error);
}
}
else
{
// load your other svg files here, or just
// set a flag letting you know it's ready.
$('#first_thing').svg({
loadURL: 'path/anotherfilename.svg',
onLoad: function(svg, error) { somecallback(svg, error);} });
$('#second_thing').svg({
loadURL: 'path/anotherfilename.svg',
onLoad: function(svg, error) { somecallback(svg, error);} });
}
}
Because the id can be found in the documents scope, the SVG are capable of finding the IRI reference.
This allows you to define things once (that would not otherwise be defined in a css) and avoid id collisions.
Cheers,
Christopher Smithson

Resources