jQuery: select one specific element from clicked SVG group - javascript

So, I have a simple SVG group:
<g id="interceptor">
<ellipse cx="0" cy="15" rx="6" ry="14" fill="url(#gradient)"/>
<polygon points="0,-20 7,10 0,0 -7,10" fill="blue"/>
<polygon points="0,-20 7,10 0,0" fill="darkblue"/>
</g>
Which I then use like this:
<g class="ship" id="ship3" transform="rotate(-180,480,24) translate(480,24)">
<use xlink:href="#interceptor"/>
</g>
Now, what I would like to do is to add a stroke around the first polygon from the group when the whole group is clicked, but I don't know how to access contents of the interceptor group via jQuery. I was thinking about something like this, but contents() returns nothing:
$(".ship").click(function() {
$(this).find("use").contents().find("polygon").first().attr({"stroke-width":1, stroke:"white"});
});
I can, of course, add a stroke around the entire ".ship" and it works, but it's not what I want because this creates strokes between polygons in the group. Additionally, there can be a number of separate objects that use the #interceptor group, and only the one that is actually clicked should get the stroke. Is that doable?

Related

How to create a svg line with a filter

I would like to create lines with the following effect :
The above was created with the following :
<svg id="svg_wrp" width="500" height="500">
<defs>
<filter id="crayon">
<feTurbulence
type="fractalNoise"
baseFrequency="1.001"
numOctaves="10"
result="noise">
</feTurbulence>
<feDisplacementMap
xChannelSelector="R"
yChannelSelector="G"
scale="50"
in="SourceGraphic"
result="newSource">
</feDisplacementMap>
<feGaussianBlur stdDeviation="1.1"/>
</filter>
</defs>
<polyline
points="200,100 100,200"
stroke="#000"
stroke-width="10"
fill="none"
filter=url(#crayon) ></polyline>
</svg>
Above in Fiddle
This method does not work for much of what I want to do.
The effect changes depending on the length and direction of the line.
The only way I can think of doing this is by replacing all the lines with polygons. Then it would be a straight forward use of filters. But then I would have to create a complex polygon with all the calculations for each corner and curve.
So before I do that I would like to know if there is a way to achieve the above with svg polylines or some other method.
Edit: Michael Mullany's answer solved the vertical and horizontal only line issue, but I still have problems with the effect differing on some lines, see below for an example. Notice effect is reduced in top left. Is there a way to have a consistent effect on all lines?
Default filter dimensions don't work on horizontal and vertical lines because they have a zero area bounding box. You need to change your filter units to:
<filter id="crayon" filterUnits="userSpaceOnUse" x="0" y="0" width="500" height="500">
Otherwise this filter is fine.
Update: there are some values for baseFrequency that cause anomalies/moire-like artifacts - apparently 1.001 is one of them for this case. If you tweak that baseFrequency down to 0.98, the problem disappears.

Why transforms shift the whole cordinate system instead just moving the element in SVG?

I have seen transforming the graphic in SVG don't affect the object to be transformed but transform the whole coordinate system and then draw the element in that system.
What does this help to achieve instead calculating the new cordinates for current element in consideration?
I think in canvas and CSS, its same behavior so tagging javascript and CSS also.
I am not sure I understand your question, but let me try:
The transformed element may not be a grafics element, but a container, for example a <g> element. In that case, all child elements inherit the transformed coordinate system.
The element may have associated paint servers (a pattern or a gradient, for example), or filters, masks or clipping paths that have their own sizing and positioning mechanisms. These mechanisms work in the transformed coordinate system.
Here is an example to illustrate. The first rectangle has a linear gradient, whose gradient vector is defined in user space. The second rectangle is identical, but rotated and translated to the side. The gradient is then moved together with the rectangle.
<svg width="200" height="150">
<defs>
<linearGradient id="gradient" gradientUnits="userSpaceOnUse"
x1="0" y1="50" x2="100" y1="50">
<stop stop-color="red" offset="0"/>
<stop stop-color="blue" offset="1"/>
</linearGradient>
</defs>
<rect x="0" y="20" width="100" height="30" fill="url(#gradient)" />
<rect x="0" y="20" width="100" height="30" fill="url(#gradient)" transform="translate(200, 20) rotate(90)" />
</svg>
While the transform attribute only takes userpace coordinates, i. e. unitless numbers, positioning and sizing attributes like x, y, width and height etc. may have unit identifiers like percentage, em or other CSS unit identifiers. That makes it possible to do more versatile positioning.
For example, the following rectangle will always appear at the same size in the middle of the SVG, regardless of it being resized:
<svg width="100%" height="100%">
<rect x="50%" y="50%" width="60" height="60" transform="translate(-30, -30)" />
</svg>

Count all hex colour codes in SVG/string

I have an SVG image and I want to get all the colour hex codes in an array.
<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g display="inline">
<title>Layer 1</title>
<ellipse ry="116" rx="66" id="svg_1" cy="130.7" cx="375.2" stroke-width="3" stroke="#000000" fill="#FF0000"/>
<ellipse ry="104" rx="68" id="svg_2" cy="133.7" cx="248.2" stroke-width="3" stroke="#000000" fill="#FF0000"/>
<ellipse ry="73" rx="47" id="svg_3" cy="161.7" cx="231.2" stroke-width="3" stroke="#000000" fill="#FF0000"/>
<rect id="svg_4" height="77" width="83" y="66.7" x="225.2" stroke-width="3" stroke="#000000" fill="#0000ff"/>
</g>
</svg>
So in this case I have 3 different colours. Yet in total I have 8 as there are recurrences.
I can count all with.
function count_colours(data){
return data.match(/#/g).length;
}
And obviously I get eight plus any other occurrence of #.
The issue I have is anything I can think of is too heavy. I need a light simple solution if there is one. Is there a simple way to achieve this? Getting all the hex codes in an array with no duplicates would be amazing.
EDIT:
OK, so iterate through the array and check the hex code is valid using..
var isOk = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test('#XXXXX')
Then remove duplicates using
_.uniq(hex_codes);
But how would I build the initial array, that's the part I am struggling with. Would I use indexOf() in the initial iteration. This all seems very messy.
How about this?
function count_colours(data) {
var n = 0, matches, cols = {};
if (matches = data.match(/\#[0-9A-Fa-f]{3}([0-9A-Fa-f]{3})?/g)) {
for (var i=0; i<matches.length; i++) {
if (!cols[matches[i]]) {
cols[matches[i]] = 1
n++;
}
}
}
return n;
}
Demo here
You may also need to take account of the fact that "#00F", "#00f" and "#0000FF" are all the same colour but will be counted separately. I have assumed here that these files are produced by an editor or something similar which will be consistent in how it lists colours.
Yes there is, depends on your constraints.
I'd go for underscore utility function uniq, as it's not worth writing your own implementation.
http://underscorejs.org/#uniq
There is also another answer in relation to your question here:
Removing duplicate objects with Underscore for Javascript

Animate SVG infinitely from value to value to original from value?

Is it possible to use the <animate> tag in an svg to either make a part of an svg image animation from a value to another, to another. Or even better animate infinitely from and then to and back to from and then to; and, so on?
I want to animate a circle's radius, but in that infinite fashion. If I can't do it with the <animate> tag, how else can I do it?
Here is wha tI have so far for the animation running once.
<svg>
<circle r="100" cx="100" cy="100" fill="slategrey">
<animate attributeName="r" from="0" to="100" dur="1.6s"/>
</circle>
</svg>
Instead of using from and to, use the values attribute to make it cycle back and forth, and the repeatCount attribute to make it repeat forever.
<svg>
<circle r="100" cx="100" cy="100" fill="slategrey">
<animate attributeName="r" values="0;100;0" dur="1.6s"
repeatCount="indefinite"/>
</circle>
</svg>
Demo here

How would I move an SVG pattern with an element [duplicate]

This question already has answers here:
How to make SVG image pattern fill move with object?
(4 answers)
Closed 1 year ago.
I created the svg pattern seen here:
<pattern id="t" height="20" width="20" patternUnits="userSpaceOnUse" overflow="visible">
<ellipse cx="0" cy="0" rx="20" ry="20" fill="white"/>
<ellipse cx="5" cy="5" rx="15" ry="15" fill="yellow"/>
<ellipse cx="10" cy="10" rx="10" ry="10" fill="blue"/>
<ellipse cx="15" cy="15" rx="5" ry="5" fill="red"/>
</pattern>
Then in my script I created an ellipse that uses the pattern. The problem is, when I move the ellipse around, the pattern stays still behind it instead of moving with the ellipse.
How do I configure the pattern to stay with the element?
You need to use patternContentUnits="objectBoundingBox" click on the rectangle in this example to see: http://jsfiddle.net/longsonr/x8nkz/
Change the patternContentUnits to "objectBoundingBox" (vs. userSpaceOnUse).
More: patternUnits should have no effect on how the pattern is laid out, only its dimensions (userspace units vs. boundingbox units). patternContentUnits is the attribute that you want to set to "objectBoundingBox" - note that that this will then scale your pattern if you change the size of the bounding box. If you don't want this to happen, then you need to use a viewbox attribute on your pattern - which is probably the right way to get the result you're probably looking for (fixed size pattern, positioned relative to its bounding box)
(Also please note that setting overflow to visible results in "undefined" rendering according to the spec aka - not something that you want to do)

Categories

Resources