Animate text in SVG - svg

I'm having some trouble trying to animate some text around a circular path in a repeating loop. Basically I'm trying to simulate a record player with some text on it that rotates around with the record as it spins. I just made a simple record player in Illustrator quick and saved it as an svg file then threw the relevant parts in on a webpage. I pretty new to SVG animations and was wondering how to best approach this. I'm using javascript/jQuery to handle some other events on the page so that's how I was planning on approaching the animations here too. Is there any easy way to do this in just SVG? If possible I'd really rather not have to manually calculate the proper value by which to change the rotation and x/y coordinates per animation iteration in javascript, but if so then fair enough. Here's the SVG output (from Illustrator) for my record (the circular lines show where I had planned on having text).
<svg version="1.1" id="Layer_2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="800px" height="600px" viewBox="0 0 800 600" enable-background="new 0 0 800 600" xml:space="preserve">
<path fill="#CCCCCC" stroke="#FFFFFF" stroke-width="1.24473" d="M400,5C237.07568,5,105,137.07568,105,300
c0,162.9248,132.07568,295,295,295c162.92383,0,295-132.0752,295-295C695,137.07568,562.92383,5,400,5z M400,425.7168
c-69.43164,0-125.71729-56.28418-125.71729-125.7168c0-69.43164,56.28564-125.71729,125.71729-125.71729
c69.43262,0,125.7168,56.28564,125.7168,125.71729C525.7168,369.43262,469.43262,425.7168,400,425.7168z"/>
<circle fill="#CCCCCC" stroke="#FFFFFF" stroke-width="1.24473" cx="399.99976" cy="300" r="11.20239"/>
<circle fill="none" stroke="#FFFFFF" stroke-width="1.20836" cx="399.99951" cy="300" r="265.83936"/>
<circle fill="none" stroke="#FFFFFF" stroke-width="1.05836" cx="399.99951" cy="300" r="232.83936"/>
<circle fill="none" stroke="#FFFFFF" stroke-width="0.90382" cx="399.99951" cy="300" r="198.83936"/>
<circle fill="none" stroke="#FFFFFF" stroke-width="0.75382" cx="399.99951" cy="300" r="165.83936"/>
<rect x="43.1084" y="1.00049" fill="none" stroke="#CCCCCC" stroke-width="1.09253" width="713.78223" height="597.99951"/>
<rect id="name" x="351" y="186.5" fill="none" width="96.79297" height="16"/>
</svg>
Any help would be greatly appreciated, thanks!

Something like this: http://hoffmann.bplaced.net/svgtest/textonapath02.svg or http://srufaculty.sru.edu/david.dailey/svg/SVGOpen2008/textPath1.svg perhaps. These animated the startOffset attribute of a textPath.

Related

SMIL animation of skillbars

I've been searching for hours and hours and I just don't comprehend how I cannot find a solution to animate skillbars in SMIL.
So I've got an SVG composed of two paths, one for the outter border and the other for the fill and I want to animate the fill onload of the page to start at 0 and reach its final width or position after a given amount of time (say 800ms).
Have a look at the HTML:
https://codepen.io/anon/pen/PaOqrr
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="80%" viewBox="0.626 346.31 226.762 13.796" enable-background="new 0.626 346.31 226.762 13.796" xml:space="preserve">
<path fill="#4A929D" d="M159.155,353.208c0,3.536-2.868,6.398-6.398,6.398H7.524c-3.533,0-6.397-2.864-6.397-6.398l0,0 c0-3.531,2.865-6.397,6.397-6.397h145.233C156.289,346.81,159.155,349.676,159.155,353.208L159.155,353.208z"/>
<path fill="none" stroke="#88C2C8" stroke-linecap="round" stroke-miterlimit="10" stroke-dasharray="0,2" d="M226.888,353.208 c0,3.536-2.867,6.398-6.397,6.398H7.524c-3.533,0-6.397-2.864-6.397-6.398l0,0c0-3.531,2.865-6.397,6.397-6.397H220.49 C224.021,346.81,226.888,349.676,226.888,353.208L226.888,353.208z"/>
I really want to achieve this only with SMIL. I know how to do with JS and CSS but I can't believe this won't work with animate.
I have tried the attribute "x" and "width" but it doesn't budge.
Any idea?
Paths don't have a x or width attribute, so animating those won't do anything.
There are several ways to achieve what you want. But the simplest would probably be use a mask or a clip path to define the area between the dots and then animate the position of your progress bar. You would start with it off to the left (ie not visible through the clip) and then slowly move it to the right, so that it appears to grow in length.
Your current bar only covers about two thirds of our progress. So we can't really use it. Scaling or moving it won't help us. So we might as well discard it.
However we can use the dots path, as that matches what we need. If we copy the path definition across, we get the following:
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="80%" viewBox="0.626 346.31 226.762 13.796">
<path fill="#4A929D"
d="M226.888,353.208 c0,3.536-2.867,6.398-6.397,6.398H7.524c-3.533,0-6.397-2.864-6.397-6.398l0,0c0-3.531,2.865-6.397,6.397-6.397H220.49 C224.021,346.81,226.888,349.676,226.888,353.208L226.888,353.208z"/>
<path fill="none" stroke="#88C2C8" stroke-linecap="round" stroke-miterlimit="10" stroke-dasharray="0,2"
d="M226.888,353.208 c0,3.536-2.867,6.398-6.397,6.398H7.524c-3.533,0-6.397-2.864-6.397-6.398l0,0c0-3.531,2.865-6.397,6.397-6.397H220.49 C224.021,346.81,226.888,349.676,226.888,353.208L226.888,353.208z"/>
</svg>
We can now animate the progress bar by animating the transform of that path. We use a translate() transform to move it from left to right by an appropriate amount.
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="80%" viewBox="0.626 346.31 226.762 13.796">
<path fill="#4A929D"
d="M226.888,353.208 c0,3.536-2.867,6.398-6.397,6.398H7.524c-3.533,0-6.397-2.864-6.397-6.398l0,0c0-3.531,2.865-6.397,6.397-6.397H220.49 C224.021,346.81,226.888,349.676,226.888,353.208L226.888,353.208z">
<animateTransform attributeName="transform" attributeType="XML"
type="translate" from="-226, 0" to="0, 0"
dur="3s" fill="freeze"/>
</path>
<path fill="none" stroke="#88C2C8" stroke-linecap="round" stroke-miterlimit="10" stroke-dasharray="0,2"
d="M226.888,353.208 c0,3.536-2.867,6.398-6.397,6.398H7.524c-3.533,0-6.397-2.864-6.397-6.398l0,0c0-3.531,2.865-6.397,6.397-6.397H220.49 C224.021,346.81,226.888,349.676,226.888,353.208L226.888,353.208z"/>
</svg>
Now moving it alone is not enough. We need to hide the part of the moving bar that is outside our channel of dots. We can do that by applying a <mask> or a <clipPath>. I'm going to use a clip path. Since the clip path will be the same shape as the progress bar path, and the dots path, we will use the same definition.
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="80%" viewBox="0.626 346.31 226.762 13.796">
<defs>
<clipPath id="bar-clip">
<path d="M226.888,353.208 c0,3.536-2.867,6.398-6.397,6.398H7.524c-3.533,0-6.397-2.864-6.397-6.398l0,0c0-3.531,2.865-6.397,6.397-6.397H220.49 C224.021,346.81,226.888,349.676,226.888,353.208L226.888,353.208z"/>
</clipPath>
</defs>
<g clip-path="url(#bar-clip)">
<path fill="#4A929D"
d="M226.888,353.208 c0,3.536-2.867,6.398-6.397,6.398H7.524c-3.533,0-6.397-2.864-6.397-6.398l0,0c0-3.531,2.865-6.397,6.397-6.397H220.49 C224.021,346.81,226.888,349.676,226.888,353.208L226.888,353.208z">
<animateTransform attributeName="transform" attributeType="XML"
type="translate" from="-226, 0" to="0, 0"
dur="3s" fill="freeze"/>
</path>
</g>
<path fill="none" stroke="#88C2C8" stroke-linecap="round" stroke-miterlimit="10" stroke-dasharray="0,2"
d="M226.888,353.208 c0,3.536-2.867,6.398-6.397,6.398H7.524c-3.533,0-6.397-2.864-6.397-6.398l0,0c0-3.531,2.865-6.397,6.397-6.397H220.49 C224.021,346.81,226.888,349.676,226.888,353.208L226.888,353.208z"/>
</svg>
When you look at the above, you may wonder why we applied the clip to a group (<g>) rather than directly to the progress bar path itself. The reason is because, if we apply it to the path, it will be affected by the animated transform. It will move with the path, and so no clipping will happen.
Finally, we are using the same path three times here. You may wonder if we can do anything to make the file smaller. The answer is yes. We can define the path just once, then refer to it everywhere else it is needed. We can do that by using the <use> element.
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="80%" viewBox="0.626 346.31 226.762 13.796">
<defs>
<clipPath id="bar-clip">
<path id="bar-shape" d="M226.888,353.208 c0,3.536-2.867,6.398-6.397,6.398H7.524c-3.533,0-6.397-2.864-6.397-6.398l0,0c0-3.531,2.865-6.397,6.397-6.397H220.49 C224.021,346.81,226.888,349.676,226.888,353.208L226.888,353.208z"/>
</clipPath>
</defs>
<g clip-path="url(#bar-clip)">
<use xlink:href="#bar-shape" fill="#4A929D">
<animateTransform attributeName="transform" attributeType="XML"
type="translate" from="-226, 0" to="0, 0"
dur="3s" fill="freeze"/>
</use>
</g>
<use xlink:href="#bar-shape" fill="none" stroke="#88C2C8" stroke-linecap="round" stroke-miterlimit="10" stroke-dasharray="0,2"/>
</svg>

Improve SVG so pin is centered inside circle, without multiple viewboxes

I have a pin that needs to be shown inside a circle in Svg.
My current code is the following:
<svg viewBox="0 0 20 20" preserveAspectRatio="xMinYMin meet">
<circle cx="50%" cy="1.5" r="1.5" style="fill: green;"></circle>
<svg x="47.5%" y="5%" viewBox="0 0 10000 10000" fill="#fff" preserveAspectRatio="none">
<g>
<path d="M250,124.3c-35,0-63.4,28.8-63.4,64.1c0,35.3,28.5,64,63.4,64s63.4-28.8,63.4-64.1C313.4,153,285,124.3,250,124.3z
M250,222c-18.3,0-33.2-15.1-33.2-33.7s14.9-33.7,33.2-33.7s33.2,15.1,33.2,33.7S268.3,222,250,222z">
</path>
<path d="M250,50.9c-74.9,0-135.8,61.6-135.8,137.4c0,31.3,22.5,84.4,66.9,157.7c32.9,54.4,66.2,100.3,66.6,100.7l2.4,3.3l2.4-3.3
c0.3-0.5,33.7-46.3,66.6-100.7c44.4-73.3,66.9-126.4,66.9-157.7C385.8,112.5,324.9,50.9,250,50.9z M250,397.6
c-16.5-24.3-45.5-68.4-69.9-114c-23.5-44-35.9-77-35.9-95.4c0-59,47.4-107,105.8-107s105.8,48,105.8,107
c0,18.4-12.4,51.4-35.9,95.4C295.4,329.3,266.5,373.4,250,397.6z">
</path>
</g>
</svg>
</svg>
which works somewhat but seems inelegant, and perhaps also buggy. What I would like is a better way to center the group 'inside' the circle without using JavaScript
It would be nice if I could get rid of the extra SVG element in the middle with its really big viewBox that I'm using to place the pin. So if you can show me how to do it with just a g and make a scaling function that would be good.
If you want to use coordinates that contain percentage values, you need an element that has x and y attributes. <use> is such an element, <g> is not.
Your live will be easier if you draw your pin centered on the origin of the coordinate system: translate(-250 -230).
After that, you can easily scale it to the size you need: scale(0.0025) (remember: multiple transform commands are processed right-to-left.)
Finally, you use the pin template with the same x and y coordinates as your circle.
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 20 20" preserveAspectRatio="xMinYMin meet">
<defs>
<!--center the pin around the origin and scale it to final size-->
<g id="pin" transform="scale(0.0025) translate(-250 -230)">
<path d="M250,124.3c-35,0-63.4,28.8-63.4,64.1c0,35.3,28.5,64,63.4,64s63.4-28.8,63.4-64.1C313.4,153,285,124.3,250,124.3z
M250,222c-18.3,0-33.2-15.1-33.2-33.7s14.9-33.7,33.2-33.7s33.2,15.1,33.2,33.7S268.3,222,250,222z" />
<path d="M250,50.9c-74.9,0-135.8,61.6-135.8,137.4c0,31.3,22.5,84.4,66.9,157.7c32.9,54.4,66.2,100.3,66.6,100.7l2.4,3.3l2.4-3.3
c0.3-0.5,33.7-46.3,66.6-100.7c44.4-73.3,66.9-126.4,66.9-157.7C385.8,112.5,324.9,50.9,250,50.9z M250,397.6
c-16.5-24.3-45.5-68.4-69.9-114c-23.5-44-35.9-77-35.9-95.4c0-59,47.4-107,105.8-107s105.8,48,105.8,107
c0,18.4-12.4,51.4-35.9,95.4C295.4,329.3,266.5,373.4,250,397.6z" />
</g>
</defs>
<!--use the same coordinates for the center of the circle and the pin-->
<circle cx="50%" cy="1.5" r="1.5" fill="green" />
<use xlink:href="#pin" x="50%" y="1.5" fill="white" />
</svg>

SVG absolute line marker size

I've got a bunch of SVGs representing a trip using a polyline.
I'm using non-scaling-stroke vector effect to make sure that each of the polylines is rendered with an absolute width. So when the viewBox's dimension changes, the width of the polyline does not.
At the ends of the mentioned polylines, I wanted to put markers. I would like to have the markers absolutely sized as well. I thought this should be easy by setting the marker units to strokeWidth.
In contrast to what happens with the stroke of the polyline, the size of the markers at the end of a polyline, does change along with the size of the viewBox.
Below I've included an example in where I used simple circles for markers.
<svg preserveAspectRatio="xMidYMid" xmlns="http://www.w3.org/2000/svg" viewBox="131890.80533333332 85178.93015415945 198.25991111111944 205.10300513348193">
<defs>
<marker id="end" markerHeight="4" markerUnits="strokeWidth" markerWidth="4" orient="0deg" refX="8" refY="16" viewBox="0 0 16 20">
<circle cx="8" cy="16" fill="#000" r="4"></circle>
</marker>
<marker id="start" markerHeight="4" markerUnits="strokeWidth" markerWidth="4" orient="0deg" refX="8" refY="16" viewBox="0 0 16 20">
<circle cx="8" cy="16" fill="#000" r="4"></circle>
</marker>
</defs>
<g>
<polyline fill="none" marker-end="url(#end)" marker-start="url(#start)" stroke="#000" stroke-linejoin="round" stroke-width="3" vector-effect="non-scaling-stroke" points="132089.06524444444, 85384.03315929293 131890.80533333332, 85178.93015415945">
</polyline>
</g>
</svg>
<svg preserveAspectRatio="xMidYMid" xmlns="http://www.w3.org/2000/svg" viewBox="132038.56071111112 85364.68902323228 50.557866666669725 19.330493533576373">
<defs>
<marker id="end" markerHeight="4" markerUnits="strokeWidth" markerWidth="4" orient="0deg" refX="8" refY="16" viewBox="0 0 16 20">
<circle cx="8" cy="16" fill="#000" r="4"></circle>
</marker>
<marker id="start" markerHeight="4" markerUnits="strokeWidth" markerWidth="4" orient="0deg" refX="8" refY="16" viewBox="0 0 16 20">
<circle cx="8" cy="16" fill="#000" r="4"></circle>
</marker>
</defs>
<g>
<polyline fill="none" marker-end="url(#end)" marker-start="url(#start)" stroke="#000" stroke-linejoin="round" stroke-width="3" vector-effect="non-scaling-stroke" points="132038.56071111112, 85364.68902323228 132089.1185777778, 85384.01951676585">
</polyline>
</g>
</svg>
See also: https://jsfiddle.net/u4bmupza/3/
Am I missing some SVG attributes or should I calculate the size of the markers manually?
vector-effect="non-scaling-stroke was first introduced in SVG 1.2 Tiny. That was a subset of SVG intended for for mobile devices with limited power. SVG 1.2 didn't have markers, so this issue wasn't a problem.
vector-effect was about the only thing from SVG 1.2 Tiny that browsers ended up implementing. Unfortunately, this problem was obviously missed at that time, and I guess no one has bothered to report it as a bug. Though I've seen it asked about on S.O. previously.
The good news is that the SVG2 spec specifically notes it as something that should be addressed, though that doesn't help you now.

Convert new svg elements to path

I'm working with a new illustrator svg file I guess, and I want to animate the path inside it, but I've incurred in few issues which means that I don't know how to interact with ellipse, polyline and circle. I'd like to know how to transform at least how to convert an ellipse element into a path element. Here's my file:
<svg version="1.1" id="Layer_1" class="snap" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 106.2 107" style="enable-background:new 0 0 106.2 107;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;stroke:#8DC63F;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
</style>
<g>
<ellipse transform="matrix(0.7071 -0.7071 0.7071 0.7071 -22.2783 53.2037)" class="st0" cx="53.1" cy="53.5" rx="38.1" ry="38.1"/>
<polyline class="st0" points="32,48.8 32,35.8 41.6,48.8 41.6,35.8 "/>
<polyline class="st0" points="65.2,48.8 65.2,35.8 74.9,48.8 74.9,35.8 "/>
<polyline class="st0" points="58.2,69.7 58.2,56.7 52.8,64 47.5,56.7 47.5,69.7 "/>
<circle class="st0" cx="53.6" cy="42.3" r="6.5"/>
<circle class="st0" cx="70.7" cy="63.2" r="6.5"/>
<path class="st0" d="M41.1,58.2c-1.1-0.9-2.6-1.5-4.1-1.5c-3.6,0-6.5,2.9-6.5,6.5c0,3.6,2.9,6.5,6.5,6.5c1.6,0,3.1-0.6,4.3-1.6
v-4.6H37"/>
</g>
</svg>
Does it exist any converter online which can do this job for me?
If I understand ok, you want to animate the svg path? by that do you mean to animate the stroke of it? if yes, I made a plugin just for that, it supports rect, circle, line and polygon : zPath.js
Or if you only want a solution to convert any element into a path yes there is Flatten.js as far as I know, I did not use it I found it on this post

Make SVG element transparent (like a mask)

How can I use the polygon as a mask, which will make the area in the circle transparent?
I cannot manage it
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50" />
<polygon points="20,30 25,20 80,40 80,60 25,80, 20,70 70,50"/>
</svg>
Masks are quite simple. They are described here: http://www.w3.org/TR/SVG11/masking.html#Masking
In your case, it is just a matter of adding a few lines.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50" mask="url(#hole)"/>
<mask id="hole">
<rect x="0" y="0" width="100" height="100" fill="white"/>
<polygon points="20,30 25,20 80,40 80,60 25,80, 20,70 70,50" fill="black"/>
</mask>
</svg>
In the mask definition, we have to add a white rectangle the size of the circle to make the <circle> visible (white means opaque), and we make the <polygon> black (transparent) to create the hole.
Fiddle here
I'd say try using Inkscape to make an SVG file, draw your items on each other, select them both and use the Path->Exclusion menu to do so. Save your file and then you can look at the code of the .svg file to see what it did.

Resources