Change multiple colors of a SVG object - fabricjs

I have a SVG logo rendered to the canvas using fabric.js, the original SVG is all black in color but I need the user to be able to change the color of each different parts of the logo, resulting in a object with multiple colors, e.g.:
wikimediauruguay.org/images/5/53/Wikimedia-logo.png
How can I achieve this? If I just use object.setFill() it changes the color of the entire object but I need to change the color of every part separately to whatever colors the user choose. Thanks.
EDIT: found the solution, just posted my answer below in case somebody else has the same question.

Perhaps someone who knows something about fabric.js would answer in a way that makes more sense for your case, but with plain old svg, an object is often a <g< element with things ( like <rect>, <path>, <ellipse>) inside. Each child of the group, can have its own event handler:
<g>
<path onclick='handle(evt)' attrs=stuff />
<rect onclick='handle(evt)' attrs=stuff />
<circle onclick='handle(evt)' attrs=stuff />
</g>
The function activated by the click can then interrogate evt.target to see which of the subelements received the click, sorta like this:
if (evt.target.nodeName=="path") {evt.target.setAttribute("fill","purple")}

Solved mi problem in a very simple way: I just needed to edit the SVG on Illustrator so that every different colored part of the logo will be on a different layer, then when I loaded the SVG via fabric.loadSVGFromURL() each layer will be treated as a different object by fabric.js, then I just could edit each object (layer) separately (setFill(), etc).

Related

How can I detect if an SVG contains a specific symbol?

I am using an svg file with symbols as follow:
<svg xmlns="http://www.w3.org/2000/svg">
<use xlink:href="aFile.svg#symbolName"/>
</svg>
Now I would like to know if the "aFile.svg" really contains the "symbolName" and simply raise an exception if not. For now, I did put an event onload on the <use> tag. When the <use> get loaded, I check the content's item size getBoundingClientRect(). If the size is greater than 0,0 it means we do have an item….
Everything was working fine until I tried on Safari. On Safari, I am getting a racing condition problem. The size of the <use> tag is not always ready when safari firesw onload. Which means that, sometimes I get the size, sometimes not (it is really random…). I temporarily fixed it using a window.timeout(...) but it is not the proper way to fix racing condition issues. So just to say, I am wondering if there is a cleaner way to achieve the same?
You probably cannot do this directly. If you first injected your file aFile.svg into the DOM you could simply use querySelector() to detect if the Symbol is available.
For injecting the SVG file this like may be useful: https://css-tricks.com/ajaxing-svg-sprite/

SVG-animation on hover - first steps, how to?

I'd like to do an animation to an element when hovering it.
As I do use svg-elements for both situations (standard and hover-state) I guess I must somehow manipulate the first svg-element when hovering it by editing the svg-code inline.
I basically'd need a starting point there:
How would I "redraw" in an animated manner the hover-image and not just swap it?
Do I need a 3rd party library (which)?
If I had multiple of these situations, how would I keep my code clean by not having 10 svg-codes inline within my html-source?
Thanks for your answer(s)!
The code for the svg-image(s) is here
<svg id="Ebene_1" data-name="Ebene 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 800">
<defs><style>.cls-1,.cls-2{fill:none;stroke:#000;}.cls-1{stroke-miterlimit:10;stroke-width:7px;}.cls-2{stroke-linejoin:bevel;stroke-width:5px;}</style></defs>
<title>arrows_demoZeichenfläche 1</title>
<line class="cls-1" x1="325.5" y1="333" x2="325.5" y2="539"/>
<polyline class="cls-2" points="242 455.67 325.75 539.42 409.42 455.75"/><path class="cls-1" d="M670.5,135.79c0,11.62-8,11.73-8,23.35s8,11.68,8,23.3-8,11.65-8,23.28,8,11.64,8,23.26-8,11.63-8,23.25,8,11.63,8,23.25-8,11.63-8,23.25,8,11.63,8,23.25-8,11.62-8,23.25,8,11.62,8,23.25-8,11.63-8,23.25,8,11.63,8,23.25-8,11.62-8,23.25,8,11.62,8,23.25-8,11.63-8,23.25,8,11.63,8,23.25v31"/>
<polyline class="cls-2" points="587 455.67 670.75 539.42 754.42 455.75"/></svg>
You could use JavaScript to manipulate the value of the points attribute, but such changes would be sudden, so the change would look like a stop motion film.
Like this Codepen, what you could do is give a path element a stroke-dasharray that is equal to the getTotalLength of the path in order to "erase" the straight line (of the arrow) off the page, then quickly switch the value of the d attribute, and then "redraw" the line back onto the page?
However, I don't believe that's what you're looking for. I believe HTML5 Canvas, with my limited knowledge about it, would be the more feasible option for what you're trying to accomplish.
Actually, I guess it might be possible using a CSS3 3D transform, like so. The problem, however, is that the line doesn't have any depth, so when you initially set rotateX(90deg) on the path in CSS the line becomes invisible instead of appearing as a straight line...

change svg:g id to inkscape:label

I made a vector graphic in Inkscape, including layers and sub-layers for further use in Processing. I named all the layers in the UI, and realized that the final SVG only creates an inkscape:label attribute with that name, but id remains generic:
<svg:g id="layer1" inkscape:label="My custom label">
I know I can manually edit the labels in the XML editor, but is there a setting somewhere to automatically use the layer name as id?
I recently came across this question, as I was looking for the same topic. As it turned out, Inkscape (v0.92) has functions for that purpose now.
You can set IDs, and Labels in the Inkscape GUI in Object Properties menu, and they will be applied to the XML code then.
Example
Inkscape GUI
Draw a yellow rectangle and select it
Click on Object -> Object Properties...
In the menu set ID to yellow_rect and Label to #yellow_rect
Apply changes by a click on Set
To complete this example, repeat the steps above to create red_rect, set Label and ID
Eventually, group both rectanbles and set identifiers for the group as well.
XML Code
When I open the SVG file, Inkscape put my identifiers to the appropriate XML tags.
<g
id="rect_group">
<rect
rx="0.11797347"
y="250.69791"
x="5.0270834"
height="18.785416"
width="30.427082"
id="yellow_rect1"
style="fill:#f4ff00;fill-opacity:1;stroke:#000000;stroke-width:0.52916667;stroke-linejoin:round;stroke-miterlimit:3.79999995;stroke-dasharray:none;stroke-opacity:1" />
<rect
rx="0.11797347"
y="258.89999"
x="24.606249"
height="16.933332"
width="33.602081"
id="red_rect1"
style="fill:#f40000;fill-opacity:1;stroke:#000000;stroke-width:0.52916664;stroke-linejoin:round;stroke-miterlimit:3.79999995;stroke-dasharray:none;stroke-opacity:1" />
</g>
I have the same requirement when I am creating a svg for Fritzing, because fritzing doesn't refer to the inkscape:label. In such circumstance, I can make sure that label holds the legit value for id. So I make a script to do saving myself out of the dirty and heavy job.
Please note that the script ONLY READ 'Plain SVG' format.
https://gist.github.com/TerrenceSun/972ef4eea97f331af1e6abfcafb7c6e5
I don't know about a setting to automatically use the layer name as id. But why not do it the other way round: if you remove the inkscape:label attribute, then the layer name automatically becomes the id of the svg:g in the inkscape UI.
The attribute inkscape:groupmode=layer is enough to make the svg:g a layer element.

Targeting a g id in an SVG for rollovers

I'm working with an SVG file that has been output from Adobe Illustrator, so there is probably quite a bit of unnecessary code. After searching and searching I was able to come up with this.
<?xml-stylesheet type="text/css" href="SVG_css.css"?>
path:hover{
fill:#005289;
}
which gets the rollovers to work from the external stylesheet, but it of course targets every path as a rollover.
For instance, I need to target paths in a group so three elements highlight when rolled over. here is the code structure from Illustrator.
<g id="WIRE_ROOM">
<path fill="#BCBEC0" d="M357.3,24.4c0,0.6-0.6,1-1.4,1h-8.1c-0.8,0-1.4-0.5-1.4-1v-8.9c0-0.6,0.6-1,1.4-1h8.1c0.8,0,1.4,0.5,1.4,1
V24.4z"/>
<path fill="#BCBEC0" d="M357.3,51.4c0,0.6-0.6,1-1.4,1h-8.1c-0.8,0-1.4-0.5-1.4-1v-8.9c0-0.6,0.6-1,1.4-1h8.1c0.8,0,1.4,0.5,1.4,1
V51.4z"/>
<path fill="#BCBEC0" d="M376.7,24.4c0,0.6-0.6,1-1.4,1h-8.1c-0.8,0-1.4-0.5-1.4-1v-8.9c0-0.6,0.6-1,1.4-1h8.1c0.8,0,1.4,0.5,1.4,1
V24.4z"/>
</g>
I've tried associating the ID to the stylesheet, and didn't have any luck...I also tried associating a class directly into the SVG.
If I add class="locations" to the path it of course only rolls over that one element and not the group of elements. When I added the class like this nothing happened. g id="WIRE_ROOM" class="locations"
I would appreciate if someone could assist me with this, as I've searched and tried everything I know to try.
So in the case of "WIRE_ROOM" those are different pieces of equipment, and I need the hover to highlight all 3 of those areas to signify one common area. Thank you!
For future reference, the selector you're looking for is g#WIRE_ROOM:hover path or g.locations:hover path (I'd recommend using the class instead of an ID).
The hover state on the group is triggered when any of the child elements are moused-over, and then the selector applies the hover style to all the child paths.
You have to specifically mention the paths in the selector -- you can't rely on inheritance -- because your file directly sets fill colors on the paths, which takes precedence over any inherited style.

How do I redefine a fill attribute in SVG using the set tag? I thought I knew, but

I am working with the standard blank map 6 svg from wikimedia commons. I added a list of languages and I am trying to make it so that when I hover over a language the corresponding countries light up, change colour, or in some way indicate that they are 'selected'. Here is the code I am using.
<set
attributeName="fill"
from="#E0E0E0"
to="#FF8888"
begin="tEnglish.mouseover"
end="tEnglish.mouseout"
/>
where tEnglish is the text object that says 'English'. No luck.
Whether with or or any other method, I can not get the svg to change an attribute that has been defined. Meaning that if I delete the fill="#E0E0E0" attribute from one of the s, I can get it to work (altho then the default is black instead of grey, which does not fit the style of the website I'm working on), but if the already contains a fill= attribute, it will not change as directed by the tag.
I have also tried deleting the fill="#E0E0E0" and defining it in a stylesheet - same problem.
So, is it completely impossible to redefine an attribute of a path once it is declared? Does only work if the attribute has not been defined elsewhere? Is my idea even possible? I appreciate any help that can be proffered.
I would also welcome any suggestions as to other methods (jquery, javascript) that you might have; although I am not so experienced with any of them I can do some research, any lead is appreciated.
It works if you put the set tag inside the element:
<rect
width="248.57143"
height="225.71428"
x="137.14285"
y="395.21933"
id="myrect"
style="fill:#800000;">
<set
attributeName="fill"
to="#008000"
begin="mouseover"
end="mouseout"
/>
</rect>
You can also achieve it using simple CSS:
#myrect {
fill: #800000;
}
#myrect:hover {
fill: #008000;
}
Example with css: http://fiddle.jshell.net/7dcnZ/2/
Of course, JavaScript is also an option:
$('#myrect').hover(function() {
$(this).css('fill', '#008000');
},
function() {
$(this).css('fill', '#800000');
});
Example with JavaScript: http://fiddle.jshell.net/7dcnZ/3/

Resources