I have the following SVG image inline on a webpage:
const svg = document.getElementById('svg');
const filter = document.createElementNS('http://www.w3.org/2000/svg', 'filter');
filter.setAttribute('id', 'image');
filter.setAttribute('x', '0%');
filter.setAttribute('y', '0%');
filter.setAttribute('width', '100%');
filter.setAttribute('height', '100%');
const feImage = document.createElementNS('http://www.w3.org/2000/svg', 'feImage');
feImage.setAttribute('xlink:href', 'http://lorempixel.com/100/100/');
filter.appendChild(feImage);
svg.querySelector('defs').appendChild(filter);
svg.querySelector('circle').setAttribute('filter', 'url(#image)');
<svg width="200" height="200" viewBox="0 0 200 200" id="svg">
<defs></defs>
<circle cx="100" cy="100" r="50" fill="none" stroke-width="2" stroke="gray"></circle>
</svg>
Now I would like to add an image filter to this svg dynamically and then apply the filter to the circle. But what happens is the circle becomes invisible the filter does not work.
When I add the filter to the svg hardcoded everything works just fine:
<svg width="200" height="200" viewBox="0 0 200 200" id="svg">
<defs>
<filter id="image" x="0%" y="0%" width="100%" height="100%">
<feImage xlink:href="http://lorempixel.com/100/100/"></feImage>
</filter>
</defs>
<circle cx="100" cy="100" r="50" fill="none" stroke-width="2" stroke="gray" filter="url(#image)"></circle>
</svg>
My question is why doesn't this work?
You need to use: document.createElementNS(…) for svg elements and in this case setAttributeNS(…) as well;
For instance change:
const filter = document.createElement('filter');
to
const filter = document.createElementNS('http://www.w3.org/2000/svg', 'filter');
Additionally change:
feImage.setAttribute('xlink:href', 'http://lorempixel.com/100/100/');
to
feImage.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', 'http://lorempixel.com/100/100/');
fiddle
Related
Is it possible to convert the following polygon svg to a rectangular shaped. The internal pattern should also be converted along too. Also since this is a cutout of a bigger pattern, I am using feMorphology filter to show where the clipPath and what shape the clipPath is in.
XML
<svg id="SvgjsSvg1008" width="550" height="650" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.dev/svgjs">
<defs id="SvgjsDefs1009">
<clipPath id="SvgjsClipPath1022">
<polygon id="SvgjsPolygon1015" points="131,55 131,218 90,280 5,235">
</polygon>
</clipPath>
<filter id="dilate_shape">
<feMorphology operator="dilate" in="SourceGraphic" radius="5" />
</filter>
</defs>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="550" height="650" viewBox="0 0 550 650" xml:space="preserve">
<g transform="matrix(1,0,0,1,0,0)" clip-path="url("#SvgjsClipPath1022")">
<g filter="url("#dilate_shape")">
<polygon points="131,55 131,218 90,280 5,235" fill="blue" fill-opacity="0.5" clip-path="url("#SvgjsClipPath1019")"></polygon>
</g>
<g transform="matrix(1,0,0,1,0,0)" clip-path="url("#SvgjsClipPath1022")">
<image xlink:href="https://www.dropbox.com/pri/get/3574%20%5BConverted%5D.svg?_subject_uid=360738345&raw=1&size=1280x960&size_mode=3&w=AADi5fkmMPJI2i6WvcO3faWWR_30_D8f5IdWsJhFarM2JA" x="-75" y="-75" width="150" height="150" transform="matrix(1,0,0,1,109,185)"></image>
</g>
</g>
</svg>
</svg>
I want to assign a svg-mask to a svg-image. I can make this work using an id on the mask like this:
<svg id="svg1" width="5cm" height="5cm" viewBox="0 0 200 200"
xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<mask id="mask">
<circle cx="100" cy="100" r="100" fill="white"></circle>
</mask>
</defs>
<rect x="0" y="0" width="200" height="200" fill="red" mask="url(#mask)"></rect>
</svg>
However I want to load this svg multiple times, with a different id in the svg-tag. Therefore I will generate duplicates of the '#mask'-id. Using multiple id's is invalid code. So I want to use a class to refer to the appropriate mask. That means I cannot use the mask=url()-technique.
<svg id="svg2" width="5cm" height="5cm" viewBox="0 0 200 200"
xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<mask class="mask">
<circle cx="100" cy="100" r="100" fill="white"></circle>
</mask>
</defs>
<rect x="0" y="0" width="200" height="200" fill="red" mask="url(can't use this)"></rect>
</svg>
Is there a way I can apply a mask to the rect element if the mask has a class instead of id? Maybe using javaScript or some other way I didn't think of.
The full story/context:
I am actually making an svg image slider-module for Joomla with php. This php generates a module containing javascript, css and an svg. I use the javascript to animate the mask.
I do actually have it working with unique id's. I was just wondering if there is a way to assign a mask to an element without referring to id's. I may want to do this because my code is getting a bit more confusing to read, because I have to use some php in my javascript/svg and css for each unique id.
No. You can only reference masks via an id. You cannot reference SVG masks any other way.
According to your description I understand you have a identical grafical entity you want to mask with different forms, multiple times. Write that down DRY:
<!-- start with an invisible svg that only contains mask definitions -->
<svg width="0" height="0"
xmlns="http://www.w3.org/2000/svg">
<defs>
<!-- first, you have a circular mask -->
<mask id="circle-mask">
<circle cx="100" cy="100" r="80" fill="white" />
</mask>
<!-- then, you have a different mask, lets say a diamond -->
<mask id="diamond-mask">
<polygon points="100,20 180,100 100,180 20,100" fill="white" />
</mask>
</defs>
</svg>
<!-- further into your document, you want to mask a rectangle -->
<svg id="svg1" width="5cm" height="5cm" viewBox="0 0 200 200"
xmlns="http://www.w3.org/2000/svg">
<!-- reference the circle mask -->
<rect x="0" y="0" width="200" height="200" fill="red" mask="url(#circle-mask)" />
</svg>
<!-- with the circle again, as often as you want, nothing changes -->
<svg id="svg2" width="5cm" height="5cm" viewBox="0 0 200 200"
xmlns="http://www.w3.org/2000/svg">
<!-- the mask is the same, so no difference to above -->
<rect x="0" y="0" width="200" height="200" fill="red" mask="url(#circle-mask)" />
</svg>
<!-- and now with the diamond; that one is different -->
<svg id="svg3" width="5cm" height="5cm" viewBox="0 0 200 200"
xmlns="http://www.w3.org/2000/svg">
<!-- if the mask changes, you need to change the reference -->
<rect x="0" y="0" width="200" height="200" fill="red" mask="url(#diamond-mask)" />
</svg>
You could also reference the masks in a stylesheet and give your referencing elements a class according to the mask shape:
.masked.circular rect {
mask: url(#circle-mask);
}
.masked.diamond rect {
mask: url(#diamond-mask);
}
<svg width="0" height="0"
xmlns="http://www.w3.org/2000/svg">
<defs>
<mask id="circle-mask">
<circle cx="100" cy="100" r="80" fill="white" />
</mask>
<mask id="diamond-mask">
<polygon points="100,20 180,100 100,180 20,100" fill="white" />
</mask>
</defs>
</svg>
<svg id="svg1" class="masked circular" width="5cm" height="5cm" viewBox="0 0 200 200"
xmlns="http://www.w3.org/2000/svg">
<rect x="0" y="0" width="200" height="200" fill="red" />
</svg>
<svg id="svg2" class="masked circular" width="5cm" height="5cm" viewBox="0 0 200 200"
xmlns="http://www.w3.org/2000/svg">
<rect x="0" y="0" width="200" height="200" fill="red" />
</svg>
<svg id="svg1" class="masked diamond" width="5cm" height="5cm" viewBox="0 0 200 200"
xmlns="http://www.w3.org/2000/svg">
<rect x="0" y="0" width="200" height="200" fill="red" />
</svg>
I am trying to manipulat an svg file with fabric.js. I am loading the svg string fabric.loadSVGFromString into the canvas correctly. My question is how can I manipulate some parts of the svg.
For example, I imported an electronic breadbord with lot of holes as shown in the pic below .
How could I detect if a hole has been clicked? How could I get the clicked part?
Thank you
I did a very short demo of what you could do.
The point is create a svg that can be imported as single shapes, lock the shapes on the canvas, then handle click events.
var canvas = new fabric.Canvas("bb");
svg = '<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile="tiny" x="0in" y="0in" width="6.50331in" height="2.1in" viewBox="0 0 468.238 151.2" ><g partID="57380"><g id="breadboardbreadboard"> <g id="background"> <rect fill="#D9D9D9" height="151.199" width="468.238" y="0"/> </g> <g id="stripes"><rect fill="#B3B0B0" height="0.4" width="468.238" y="20.932"/> <rect fill="#B3B0B0" height="0.4" width="468.238" y="129.475"/> </g> <g > <rect fill="#FF0000" height="0.4" width="468.238" y="19.2"/> <rect fill="#FF0000" height="0.4" width="468.238" y="148.799"/> <rect fill="#0000FF" height="0.4" width="468.238" y="2.4"/> <rect fill="#0000FF" height="0.4" width="468.238" y="132"/> </g> <g > <rect fill="#CCC9C9" height="7.2" width="468.238" y="71.2"/> </g> <g id="sockets"> <g id="pin1A"> <path fill="#BFBFBF" d="M8.526,115.2c0,-1.322,1.072,-2.394,2.394,-2.394s2.394,1.071,2.394,2.394"/> <path fill="#E6E6E6" d="M13.313,115.2c0,1.322,-1.072,2.394,-2.394,2.394s-2.394,-1.071,-2.394,-2.394"/> <circle r="1.197" fill="#383838" cx="10.92" cy="115.2"/> </g> <g id="pin1B"> <path fill="#BFBFBF" d="M8.526,108c0,-1.322,1.072,-2.395,2.394,-2.395s2.394,1.072,2.394,2.395"/> <path fill="#E6E6E6" d="M13.313,108c0,1.321,-1.072,2.393,-2.394,2.393S8.526,109.322,8.526,108"/> <circle r="1.197" fill="#383838" cx="10.92" cy="108"/> </g> <g id="pin1C"> <path fill="#BFBFBF" d="M8.526,100.799c0,-1.321,1.072,-2.393,2.394,-2.393s2.394,1.071,2.394,2.393"/> <path fill="#E6E6E6" d="M13.313,100.799c0,1.322,-1.072,2.395,-2.394,2.395s-2.394,-1.072,-2.394,-2.395l0,0"/> <circle r="1.197" fill="#383838" cx="10.92" cy="100.8"/> </g></g></g></svg>';
fabric.loadSVGFromString(svg, function(objs) {
var obj;
for(var i = 0; i < objs.length; i++) {
obj = objs[i];
obj.lockMovementX = true;
obj.lockMovementY = true;
obj.hasControls= false;
canvas.add(obj);
}});
canvas.on("object:selected", function(evt) {
var id = evt.target.id;
if (id.slice(0,3) == 'pin') {
alert(id.slice(3,5));
}
});
<script src="http://www.deltalink.it/andreab/fabric/fabric.js"></script>
<canvas width="600" height="400" id="bb" ></canvas>
One approach is this:
first you have to store an array of coords of each hole on your breadboard image
after you load your background image loop over each entry in that array and create a shape (say, fabric.Circle) over the corresponding hole
right after a shape is created attach a mousedown event handler to this shape
do your click processing in that event handler
Here are a couple of references that can help you with that:
http://fabricjs.com/per-pixel-drag-drop/
mouse:down vs. mousedown in fabric.js
I have an svg element as plain text
<svg height="100" width="100">
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
</svg>
How can I render this on a HTML page by inserting it with JavaScript/jquery without recreating every node with
document.createElementNS("http://www.w3.org/2000/svg", "svg")
(because otherwise it will not be rendered)
Use innerHTML to insert it.
document.documentElement.innerHTML = '<svg height="100" width="100">\n <circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />\n</svg>'
try the following code.
var svg = '<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="30px" height="30px" viewBox="0 0 30 30" enable-background="new 0 0 30 30" xml:space="preserve">'+
'<path fill="#156BB1" d="M22.906,10.438c0,4.367-6.281,14.312-7.906,17.031c-1.719-2.75-7.906-12.665-7.906-17.031S10.634,2.531,15,2.531S22.906,6.071,22.906,10.438z"/>'+
'<circle fill="#FFFFFF" cx="15" cy="10.677" r="3.291"/></svg>';
var mysvg = new Image();
mysvg.src = 'data:image/svg+xml,' + escape(svg);
and then use mysvgvariable to place it where ever you want within your html.
do this with JQuery:
var str = '<svg height="100" width="100"> <circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" /></svg>'
$("body").append(str)
working code here
So, I added some custom SVG icons to my web app using <img src="/svg.svg">.
I then decided that I wanted to add a toggleClass() to show a clicked state. This led me to a little jQuery snippet which converts an IMG to a full blown svg:
jQuery('img.svg').each(function(){
var $img = jQuery(this);
var imgID = $img.attr('id');
var imgClass = $img.attr('class');
var imgURL = $img.attr('src');
jQuery.get(imgURL, function(data) {
// Get the SVG tag, ignore the rest
var $svg = jQuery(data).find('svg');
// Add replaced image's ID to the new SVG
if(typeof imgID !== 'undefined') {
$svg = $svg.attr('id', imgID);
}
// Add replaced image's classes to the new SVG
if(typeof imgClass !== 'undefined') {
$svg = $svg.attr('class', imgClass+' replaced-svg');
}
// Remove any invalid XML tags as per http://validator.w3.org
$svg = $svg.removeAttr('xmlns:a');
// Replace image with new SVG
$img.replaceWith($svg);
}, 'xml');
});
The result is that I end up getting a rather complicated SVG object, with a structure like this:
Parent: SVG
G
Path/Polygon
What I want, is to the target the entire SVG with a click event and to change the fill.
Template.tabsOne.events({
'click .replaced-svg': function(){
$(this).attr('fill', 'blue);
}
})
Unfortunately, I've tried several variations of the click handler and my svg refuses to change its fill. Any suggestions?
EDIT: here's one example:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" width="209.217px" height="209.216px" viewBox="0 0 209.217 209.216" style="enable-background:new 0 0 209.217 209.216;" xml:space="preserve" class="svg custom-icon replaced-svg">
<g>
<polygon points="104.605,124.953 54.991,124.953 54.991,84.259 104.605,84.259 154.217,84.259 154.217,124.953 "></polygon>
<rect y="84.259" width="44.24" height="40.694"></rect>
<rect x="164.968" y="84.259" width="44.243" height="40.694"></rect>
<polygon points="209.217,73.509 159.593,73.509 109.98,73.509 109.98,22.174 209.217,22.174 "></polygon>
<polygon points="0,22.174 99.229,22.174 99.229,73.509 49.615,73.509 0,73.509 "></polygon>
<polygon points="0,135.704 49.615,135.704 99.229,135.704 99.229,187.042 0,187.042 "></polygon>
<polygon points="209.217,187.042 109.98,187.042 109.98,135.704 159.593,135.704 209.217,135.704 "></polygon>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>
Here's another one:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" width="512px" height="640px" viewBox="0 0 512 640" enable-background="new 0 0 512 640" xml:space="preserve" class="black svg custom-icon replaced-svg">
<path d="M165.438,474.562c49.922,49.922,131.031,49.922,180.969,0c49.906-49.922,49.906-131.047,0-180.961L165.438,474.562z M448,0.32L64,0C28.641,0,0,28.641,0,64v512c0,35.359,28.641,64,64,64h384c35.359,0,64-28.641,64-64V64 C512,28.641,483.359,0.32,448,0.32z M192,64c17.602,0,32,14.398,32,32s-14.398,32-32,32s-32-14.398-32-32S174.398,64,192,64z M96,64 c17.602,0,32,14.398,32,32s-14.398,32-32,32s-32-14.398-32-32S78.398,64,96,64z M256,576c-106.078,0-192-85.922-192-192 s85.922-192,192-192s192,85.922,192,192S362.078,576,256,576z" fill="#000000"></path>
</svg>
Something like this (you need to change the fill attribute on the <path> element):
$(document).ready(function(){
$('svg').click(function(e){
$(this).find('path').attr('fill', '#cc0000');
});
});
See this fiddle for a running example
So in your example, this change should do it:
Template.tabsOne.events({
'click .replaced-svg': function(){
$(this).find('path').attr('fill', 'blue');
}
})