SVG loading animation - can I combine <use>and <def> and <animate>? - svg

I have an SVG-based loading animation, aka "spinning weasel" but I'm wondering if I can code this more efficiently by utilizing a base animation (the fading out attributes) and referencing it in my <use> tags?
<defs>
<line
id="bit"
x1="50"
y1="25"
x2="50"
y2="10"
stroke="#000000">
</line>
</defs>
<g>
<use
xlink:href="#bit"
opacity="0.8"
transform="rotate(0 50 50)">
<animate
attributeName="opacity"
values="1;0.2"
dur="2.4s"
repeatCount="indefinite"
begin="0.0s"
/>
</use>
</g>
There are 12 of these <use> tags, I omitted them here for brevity. in the animation tag, only the begin attribute changes each time, the rest is identical.
I tried various approaches that seemed reasonable to me, but none work, so I'm hoping some SVG guru here can point me in the right direction. Or am I basically forced to repeat all the animation attributes on each of the tags?
Knowing just a little about SVG, I realize that there are many ways to accomplish the same result.
Thanks.
--> Fiddle

I don't think you can do exactly what you are trying to do because begin is an attribute of the animation elements and not a property that can be inherited from a use.
Depending on what you mean by "more efficient", there are other ways to achieve a similar effect. For example, you can use scripting:
http://jsfiddle.net/rzAwV/1/
This is a smaller file, but the animation is not as smooth because we are just rotating the spinner, rather than animating the opacity.

Related

convert or mogrify to rotate an svg file on command line : "delegate failed" error

I want to rotate an image in an svg file by x degrees in the very straightforward way, on the command line (not via a gui program), such that if a picture were on your kitchen table the picture would be simply turned around to face to the side, or where ever - i.e. real straightforward (one hopes).
[ADDED INFO]: the svg file is from gnuplot multiplot mode, in particular - but of course gnuplot has many terminals. I say this because multiplot appears unable to rotate the computed image by 90 (in the "do what I mean" sense - even though the statement in plain English does not make sense ); however, multiplot can do that to rgb files - see the famous tutorial page with Tux images.
If an svg file is given to convert to rotate by 90 degrees (for instance) :
convert -rotate 90 test_start.svg test_plus_90.svg
stderr (I guess) is reported:
convert-im6.q16: delegate failed `'potrace' --svg --output '%o' '%i'' # error/delegate.c/InvokeDelegate/1966.
no output is produced and the original file is unchanged. if mogrify is used instead, the same error plus this one is issued [ UPDATE: this has to do with the output file, but I'll leave this as it is trivial]:
mogrify-im6.q16: unable to open image `test_plus_90.svg': No such file or directory # error/blob.c/OpenBlob/2924.
the original file is then appended with a tilde, as test_start.svg~, but appears unchanged.
png and jpg files will process this way fine. It appears that some resources need to be installed, as the "Delegates" for convert do not include svg - or this is the incorrect way to process svg files, I am wrong, etc.
version and system information:
Ubuntu 22, synaptic used to install everything.
ImageMagick 6.9.11-60 Q16 x86_64 2021-01-25
Delegates do NOT include svg, so this appears to be what I need to understand.
gimp version 2.10.30
tl;dr You are better off first rendering your SVG image as a raster image, with the final dimensions for display, and only then flipping it around. Or, depending on your use case, quoting the image in a reference and rotating the result.
Flipping a SVG image by 90° is not a straightforward task, if you want the result to be still in SVG format. At least if you tried to write a tool that covered all the necessary bases for it not to produce seriously botched pictures. Let me give you an example:
<svg xmlns="http://www.w3.org/2000/svg" width="150vh" height="100vh" viewBox="0 0 400 200">
<rect x="1" y="1" width="398" height="198" fill="#ccc" stroke="#000" />
<circle r="10" cx="5%" cy="50%" fill="#00f" />
<circle r="10" cx="50%" cy="50%" fill="#00f" />
<circle r="10" cx="95%" cy="50%" fill="#00f" />
</svg>
How would you change that file to flip it around? You would exchange the width and height attributes on the root element, equally the corresponding values in the viewBox attribute. (If there was a preserveAspectRatio attribute, it would also have to be converted, but I'll leave that out.) Then, the content inside has also to be rotated by 90°. Wrap all content in a <g> element with an appropriate transform attribute:
<svg xmlns="http://www.w3.org/2000/svg" width="100vh" height="150vh" viewBox="0 0 200 400">
<g transform="rotate(90, 100,100)">
<rect x="1" y="1" width="398" height="198" fill="#ccc" stroke="#000" />
<circle r="10" cx="5%" cy="50%" fill="#00f" />
<circle r="10" cx="50%" cy="50%" fill="#00f" />
<circle r="10" cx="95%" cy="50%" fill="#00f" />
</g>
</svg>
Oops. What happened? The cx/cy values of the circles are percentages of width and height of the viewBox, which are already exchanged. But then the rotation is applied, and axis directions for the purpose of positioning are at odds.
This is obviously not the way to go about it. A conversion tool would have to sift through the complete code looking for these sort of pitfalls and correcting attributes. And let me assure you, I can think of a lot of tricky constructs that would trip implementers.
Here is a second attempt. <svg> elements can be nested, and they encapsulate their viewport completely. Instead of changing content, wrap the root element in another one and flip the now inner <svg> as a whole.
But there is a pitfall to avoid: A construct like
<svg ...>
<svg transform="rotate(90)" ...>
is only part of the SVG 2 specification. While browsers mostly have implemented that, other renderers may still disregard the transform attribute on the inner <svg>, as prescribed by the SVG 1.1 spec.
Thus you need to introduce an extra <g transform="..."> element:
<svg xmlns="http://www.w3.org/2000/svg" width="100vh" height="150vh" viewBox="0 0 200 400">
<g transform="rotate(90 100,100)">
<svg width="400" height="200" viewBox="0 0 400 200">
<rect x="1" y="1" width="398" height="198" fill="#ccc" stroke="#000" />
<circle r="10" cx="5%" cy="50%" fill="#00f" />
<circle r="10" cx="50%" cy="50%" fill="#00f" />
<circle r="10" cx="95%" cy="50%" fill="#00f" />
</svg>
</g>
</svg>
Well, it seemed to work. Note that the width/height attributes on the inner <svg> needed to be changed. (I assume there is a valid strategy even if one or two of the sizing attributes are missing in the original file, but haven't tested them all.) But imagine a tool flipping the image multiple times. Would it wrap the previous version each time with new outer elements? Would it try to analyze the structure and fold possible multiple applications of flipping into one? Most developers would probably cringe instead of writing such a tool.
This is not to say that one like this does not exist, But I don't know about it. It is so much easier to change your workflow: stall the flipping to the moment when you render the SVG image to a raster format, and do the flipping afterward and on-the-fly, where it is an easy task.
If you are using the SVG as an image in other content, it is equally easy to combine rotating with referencing. For example in HTML/CSS:
<img src="myImage.svg"
style="width:...;height:...;transform:rotate(90deg);transform-origin:...">

can't figure out why I have a border inside my SVG graphic

take a look at the picture below.
I have no clue as to why that border is showing up. I'm not using any clipping mask on that element, I stopped at this point so there's nothing on top of that layer. I'm really out of guesses.
I'm using an external svg with <symbol> elements to group the layers the way I want them. I have the 3 blurred elements in one symbol which looks like this.
<filter id="lights_glow_blur">
<feGaussianBlur in="SourceGraphic" stdDeviation="5.3" />
</filter>
<symbol viewBox="0 0 288 288" id="lights_glow_layer">
<path id="right_glow" style="fill:#54FFFC; opacity:.66; mix-blend-mode: hard-light;" filter="url(#lights_glow_blur)" d="M199.5,150.6c-3.9-0.1-7.3-2.9-8.2-6.6c-0.8-3.1-2.2-6.8-4.3-10.9c-1.9-3.9-4.4-7.9-7.2-12.2
c-2.6-3.8-5.5-7.7-8.4-11.2c-2.7-3.3-5.4-6.1-7.6-8.3c-2.9-2.7-3.6-7.1-1.7-10.6c1.6-2.9,4.6-4.7,7.8-4.7c0.7,0,1.3,0.1,2,0.2
c14.7,3.4,25.5,9.1,32.1,17c5.5,6.3,8.6,14.4,8.6,22.8c0,0.3,0,0.5,0,0.8c0,0.4,0,0.8,0,1.3c0,0.1,0,0.4,0,0.5
c-0.1,2.1-0.5,4.1-1,6.1c-0.6,2.5-1.7,5.4-2.6,7.8c-0.3,0.8-0.5,1.4-0.7,1.9c-1.2,3.6-4.6,6-8.4,6
C199.7,150.6,199.6,150.6,199.5,150.6z"/>
<path id="bottom_glow" style="fill:#54FFFC; opacity:.66; mix-blend-mode: hard-light;" filter="url(#lights_glow_blur)" d="M143.8603058,202.7382507c-11.648407,0-22.8124847-4.2978058-31.4492035-12.1112976
c-2.7723999-2.4208984-5.9336014-5.6562958-9.1493988-9.3632965c-2.6387024-3.0420074-2.8818054-7.4873047-0.5898056-10.7978058
c1.6836014-2.4346008,4.4218063-3.8027954,7.2539215-3.8027954c1.0233841,0,2.057579,0.1786957,3.0585785,0.5488892
c2.2685013,0.839798,4.7578049,1.5937042,7.3955002,2.243103c2.517601,0.6182098,5.1924057,1.1436005,7.9541016,1.5673981
c2.6112976,0.3964996,5.3036957,0.6992035,8,0.898407c2.550827,0.1904907,5.0888214,0.285202,7.5438995,0.285202
c2.5615082,0,5.2021942-0.1054993,7.8448029-0.3153992c2.7167053-0.2149048,5.4335022-0.5391083,8.075119-0.963913
c2.7197723-0.4354858,5.3710785-0.9804993,7.8798828-1.6151886c2.5741882-0.6542969,5.0097961-1.4092102,7.2372894-2.2393036
c1.0098267-0.3760071,2.0528259-0.5576019,3.0830078-0.5576019c2.8320923,0,5.569397,1.3661957,7.253891,3.7987976
c2.2979279,3.319397,2.0508118,7.7743988-0.6014862,10.8164063c-3.3018036,3.7901001-6.5293121,7.0644989-9.3408051,9.4814911
C166.6611023,198.433548,155.4989929,202.7382507,143.8603058,202.7382507z"/>
<path id="left_glow" style="fill:#54FFFC; opacity:.66; mix-blend-mode: hard-light;" filter="url(#lights_glow_blur)" d="M118,85c-0.8,0-1.5,0.1-2.3,0.3c-14.9,3.5-25.9,9.3-32.7,17.4c-5.7,6.5-8.9,14.9-8.9,23.6c0,0.3,0,0.6,0,0.8
c0,0.4,0,0.8,0,1.3c0,0.1,0,0.4,0,0.5c0.2,2.1,0.5,4.2,1,6.3c0.6,2.6,1.7,5.5,2.6,7.9l0.1,0.3c0.2,0.7,0.5,1.2,0.6,1.6
c1.4,4.1,5.1,6.8,9.4,6.8c0.1,0,0.3,0,0.4,0c4.4-0.2,8.2-3.2,9.3-7.5c0.8-3.1,2.2-6.7,4.2-10.6c1.9-3.8,4.3-7.9,7.1-12
c2.6-3.8,5.4-7.7,8.3-11.1c2.7-3.2,5.3-6.1,7.6-8.2c3.3-3.1,4.1-8,1.9-11.9C125,87,121.7,85,118,85L118,85z"/>
</symbol>
Here's a plunkr with all the code which by the way only shows up in Firefox, while making this post I discovered Chrome only displays a couple elements which I'll also be making a post for. Can anyone spot why this is happening?

Responsive SVG sprites

I am building an icon system for a site and moving away from png and font-icon sprites to SVG.
I need it to work across all major browsers (IE9+) and ideally want to use fragment identifiers or, as a second best alternative, inline. I've done extensive research, and there isn't a great deal out there, and from what I have read SVG sprites aren't particularly responsive.
For fragment identifiers, using img tag, I have to set the width/height to the same size as the viewbox. Increasing the dimensions on the img tag ends up revealing some of the next sprite. I'd like to be able to use percentage values so the sprite fragment fills the parent container.
I just want confirmation that this isn't possible, I can't find anything that suggests I am wrong to think this.
This is very much possible and is relatively easy to accomplish. SVG's are vector graphics and therefore, if done correctly, will be the most responsive graphics on your website.
Set your SVG file up as expected but put each sprite into a <g> tag with its own identifier.
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<g class="sprite" id="circle">
<ellipse cy="50" cx="50" ry="45" rx="45" stroke-width="5" stroke="#00ff00" fill="#00ff00" fill-opacity="0.75" />
</g>
<g class="sprite" id="square">
<rect y="5" x="5" width="90" height="90" stroke-width="5" stroke="#ff0000" fill="#ff0000" fill-opacity="0.75" />
</g>
<g class="sprite" id="triangle">
<path d="M20,7 L92,50 L6,93 z" stroke-width="5" stroke="#0000ff" fill="#0000ff" fill-opacity="0.75" />
</g>
</svg>
Add some CSS to say only the rargeted one needs to be displayed
<defs>
<style><![CDATA[
.sprite { display: none; }
.sprite:target { display: inline; }
]]></style>
</defs>
Then you can just call these out whenever required using an img tag or background element etc.
You can find the fully explained writeup here:
How to Use SVG Image Sprites

How to copy an SVG element's content to another SVG element and keep synchronization

Having two SVG elements ( SVG1 and SVG2 ) where SVG1 is a large area with various elements, that get added, removed and repositioned from time to time. SVG2 on the other hand needs to be used to act as an iconized reppresentation (small) version of SVG1, being quite smaller, but whatever SVG1 shows, SVG2 shows in a very small scale.
<SVG id="SVG1" width=1000 height=1000>
<g transform="scale(1)">
.... elements here....
</g>
</SVG>
<SVG id="SVG2" width=100 height=100>
<g transform="scale(0.1)">
.... elements here....
</g>
</SVG>
I believe the approach is to programmatically synchronize the element changes that end up on SVG1 so they also end up on SVG2, with unique IDs of course.
... but I wonder if there is a simpler way that ensures that, something like a mirroring feature or something that, or alternatively scan down the DOM tree of SVG1 and replicate it into SVG2.
Make the second SVG just a <use> element that points to the first. You can scale the <use> using a transform. It will always reflect whatever you do to the first SVG automatically.
<svg width="100" height="100">
<use transform="scale(0.1)" xlink:href="#SVG1"/>
</svg>

Can I apply clipping in SVG without specifying a clip-path ID?

In my markup, I have a chunk like this:
<svg width="0" height="0" style="display:none" id="dummy-wedge">
<g class="dummy" transform="translate(100, 100)">
<defs>
<clipPath id="clip1">
<polygon id="clip1Shape" points="-100,-100 0,-100 0,0 -100,-20" />
</clipPath>
</defs>
<circle id="" cx="0" cy="0" r="52" fill-opacity="0"
stroke="#ffffff" stroke-opacity="0.6" stroke-width="50"
pointer-events="visiblePainted" clip-path="url(#clip1)" />
</g>
</svg>
What I'm wanting to do is grab that chunk and clone it into a different svg root element to create a bunch of wedges, each with a different position and clip segment. That part is cool, but the frustration is that each cloned clipPath element will need to receive a new ID which will then have to be inserted into the clip-path attribute of the matching element.
I know that if all the arcs were the same length, I could have a common clip and use rotate transforms, but they aren't necessarily the same length.
Is it possible to declare a clip polygon using a topological relationship rather than by explicitly naming it? Alternatively, is there a better way to define an arc like this outside of using paths?
Thanks.
Why do you need to use clipping? Couldn't you just use path elements with the various arc segments in them?
There's no declarative way (yet) to get the behaviour you are after, but this is what the SVG Parameters specification is meant to address. Look at the examples there and the script implementation provided for processing the content (as a way to support the current browsers). NOTE: it's still a working draft, and is subject to change.

Resources