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)
Related
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?
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.
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>
Imagine I have a javascript library that is arbitrarily manipulating the position of two elements. I want to draw a spline (path) that starts at one element and ends on the other one, like:
<circle cx="50" cy="50" r="5" />
<circle cx="100" cy="50" r="5" />
<path d="M 369 320 C 426 320 386 304 444 304"/>
Now, I thought about transforming the origin of the path to match the center of the circle, but that would mean "hardcoding" the coordinates. If I want to move the circle, I would have to recalculate the path data.
So, question (1) how can I do something like that without resorting to javascript? and (2) iff javascript is the sole solution for this problem, then which technique (including libraries) would you recommend that would ease the task of allowing the reference objects to be arbitrarily moved and automate the necessary path recalculations?
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