How to get exact amount of stroke-dasharray on circle and rect? - javascript
I am trying to figure out how an svg file works. I successfully found out the amount of stroke-dasharray of the path using JavaScript:
var path = document.querySelector(".aa")
path.getTotalLength();
When you checkout the svg file there are three elements. The first one is
a path, the second one is a rect, and the last is a circle.
The console keeps showing error messages on the rect and circle. Is there any solution for this?
Here is my original code:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="imacSVG" x="0" y="0" viewBox="0 0 1800 1200" xml:space="preserve" enable-background="new 0 0 1800 1200">
<path d="M423.5 251.1c0 0 1.2-25.2 23.6-25.2 22.3 0 909.9 0.7 909.9 0.7s19.5-0.6 19.5 21.4 0 597 0 597 1.5 21.5-21.5 21.5 -909 0-909 0 -22.5-1.4-22.5-21.8C423.5 824.2 423.5 251.1 423.5 251.1z" class="aa"/>
<rect x="466.6" y="271.6" width="865.5" height="540" class="bb"/>
<circle cx="900.5" cy="246" r="8.2" class"cc"/>
</svg>
The getTotalLength() function is only available for <path> elements. You will need to find a different solution for the <rect> and <circle>.
Obviously, for the circle, you can use (2 * r * PI), and for the rect you can use (2 * (width + height)).
Related
How can I load a custom svg in SVG JS
I'm ripping apart a pong demo (https://css-tricks.com/pong-svg-js/) to learn about Javascript, SVGs, and SVG.js. My version will draw a ball that bounces back and forth in the window. Using the "var ball = draw.circle(ballsize)" it works correctly, but when I try to substitute a custom SVG, it fails. How can I correct this to draw my custom svg in place of the ball? <html> <body translate="no" > <div id="svg-data" hidden> <svg id="svg2" width="720" height="720" xmlns="http://www.w3.org/2000/svg"> <g> <title>background</title> <rect fill="none" id="canvas_background" height="602" width="802" y="-1" x="-1"/> </g> <g> <title>Layer 1</title> <g id="layer1"> <path d="m339.03,161.54001c166.28,1.498 323.57,64.413 335.55,203.73c8.98,101.9 -125.84,194.73 -292.11,194.73c-167.78,-1.5 -323.57,-62.91 -337.05,-202.18c-8.987,-103.45 127.24,-196.28 293.61,-196.28zm40.352,364.01c71.903,0 118.34,-68.954 110.85,-124.33c-16.478,-109.35 -53.928,-197.78 -152.79,-197.78c-71.903,0 -113.85,70.405 -104.86,125.83c16.571,109.4 48.029,196.28 146.8,196.28z" id="path270"/> </g> </g> </svg> </div> <div id="pong"></div> <script src='https://cdnjs.cloudflare.com/ajax/libs/svg.js/2.7.1/svg.min.js'></script> <script id="rendered-js" > // define document width and height var width = 450,height = 300; // create SVG document and set its size var draw = SVG('pong').size(width, height); draw.viewbox(0, 0, 450, 300); // draw background var background = draw.rect(width, height).fill('#dde3e1'); // define ball size var ballSize = 10; // original image - create ball //var ball = draw.circle(ballSize); // custom image - fails var ball = draw.svg(document.getElementById('svg2').innerHTML); ball.size(30,30); ball.center(width / 2, height / 2).fill('#7f7f7f'); var ballDirection = -1; var ballSpeed = 100; function update(dt) { ball.dmove( ballDirection * ballSpeed * dt, 0); if (ball.cx() < 0 || ball.cx() > width) { if (ball.cx() < 0) { ball.cx(0); } else if (ball.cx() > width) { ball.cx(width); } ballDirection *= -1; } } var lastTime, animFrame; function callback(ms) { cancelAnimationFrame(animFrame); if (lastTime) { update((ms - lastTime) / 1000); } lastTime = ms; animFrame = requestAnimationFrame(callback); } callback(); </script> </body> </html>
Let's say you have a file containing the grafics of a tennis ball. I have selected that file for its minimal size, the markup content of something you would like to use could be much larger: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"> <circle fill="#77B255" cx="18" cy="18" r="18"/> <path fill="#A6D388" d="M26 18c0 6.048 2.792 10.221 5.802 11.546C34.42 26.42 36 22.396 36 18c0-4.396-1.58-8.42-4.198-11.546C28.792 7.779 26 11.952 26 18z"/> <path fill="#FFF" d="M27 18c0-6.048 1.792-10.221 4.802-11.546-.445-.531-.926-1.028-1.428-1.504C27.406 6.605 25 10.578 25 18c0 7.421 2.406 11.395 5.374 13.05.502-.476.984-.973 1.428-1.504C28.792 28.221 27 24.048 27 18z"/> <path fill="#A6D388" d="M10 18c0-6.048-2.792-10.22-5.802-11.546C1.58 9.58 0 13.604 0 18c0 4.396 1.58 8.42 4.198 11.546C7.208 28.22 10 24.048 10 18z"/><path fill="#FFF" d="M4.198 6.454C7.208 7.78 9 11.952 9 18c0 6.048-1.792 10.22-4.802 11.546.445.531.926 1.027 1.428 1.504C8.593 29.395 11 25.421 11 18c0-7.421-2.406-11.395-5.374-13.049-.502.476-.984.972-1.428 1.503z"/> </svg> SVG.js would be able to reference that file in an SVG <image> tag: var ball = draw.image('/path/to/Twemoji12 1f3be.svg').size(ballsize, ballsize) But that has a drawback: the ball is loaded from a separate file, you need a request for that that takes time to fullfill (just like with every image file). But the ball is just something you can write into your main file. That is where the size of your grafic markup will make a difference. Variant 1: draw every tag separately This is what SVG.js is really meant to do. // draw a wrapper element, here you would use a nested `<svg>` element var ball = draw.svg().size(ballsize, ballsize).viewbox(0 0 36 3) // draw the circle ball.circle(18).cx(18).cy(18).fill('#77B255') // and so on for every tag and every attribute Frankly: this is just more work than you probably would like to do. Variant 2: construct the elements from strings var ball = SVG(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"> <circle fill="#77B255" cx="18" cy="18" r="18"/> <path fill="#A6D388" d="M26 18c0 6.048 2.792 10.221 5.802 11.546C34.42 26.42 36 22.396 36 18c0-4.396-1.58-8.42-4.198-11.546C28.792 7.779 26 11.952 26 18z"/> <path fill="#FFF" d="M27 18c0-6.048 1.792-10.221 4.802-11.546-.445-.531-.926-1.028-1.428-1.504C27.406 6.605 25 10.578 25 18c0 7.421 2.406 11.395 5.374 13.05.502-.476.984-.973 1.428-1.504C28.792 28.221 27 24.048 27 18z"/> <path fill="#A6D388" d="M10 18c0-6.048-2.792-10.22-5.802-11.546C1.58 9.58 0 13.604 0 18c0 4.396 1.58 8.42 4.198 11.546C7.208 28.22 10 24.048 10 18z"/><path fill="#FFF" d="M4.198 6.454C7.208 7.78 9 11.952 9 18c0 6.048-1.792 10.22-4.802 11.546.445.531.926 1.027 1.428 1.504C8.593 29.395 11 25.421 11 18c0-7.421-2.406-11.395-5.374-13.049-.502.476-.984.972-1.428 1.503z"/> </svg>`) draw.add(ball).size(ballsize, ballsize) A bit better, but at least I don't like mixing Javascript and markup inside the same file. I would go with a Variant 3: bypass SVG.js and just write the markup in your HTML markup directly I am saddling you with a high learning curve here, but ultimately you wanted to get that out of it, didn't you? SVG contains its own templating mechanism, whith an element called <symbol>. Here is how your initial markup could look: <div id="pong"> <svg xmlns="http://www.w3.org/2000/svg" width="0" height="0"> <symbol id="ball" viewBox="0 0 36 36"> <circle fill="#77B255" cx="18" cy="18" r="18"/> <path fill="#A6D388" d="M26 18c0 6.048 2.792 10.221 5.802 11.546C34.42 26.42 36 22.396 36 18c0-4.396-1.58-8.42-4.198-11.546C28.792 7.779 26 11.952 26 18z"/> <path fill="#FFF" d="M27 18c0-6.048 1.792-10.221 4.802-11.546-.445-.531-.926-1.028-1.428-1.504C27.406 6.605 25 10.578 25 18c0 7.421 2.406 11.395 5.374 13.05.502-.476.984-.973 1.428-1.504C28.792 28.221 27 24.048 27 18z"/> <path fill="#A6D388" d="M10 18c0-6.048-2.792-10.22-5.802-11.546C1.58 9.58 0 13.604 0 18c0 4.396 1.58 8.42 4.198 11.546C7.208 28.22 10 24.048 10 18z"/><path fill="#FFF" d="M4.198 6.454C7.208 7.78 9 11.952 9 18c0 6.048-1.792 10.22-4.802 11.546.445.531.926 1.027 1.428 1.504C8.593 29.395 11 25.421 11 18c0-7.421-2.406-11.395-5.374-13.049-.502.476-.984.972-1.428 1.503z"/> </symbol> </svg> </div> What did I do here? I copied the file content inside your wrapper <div>. Then I inserted a <symbol> element so that it wraps the grafical elements. The <svg> element gets zero size, because it initially only contains the template (I'm simplifying.) The viewBox attribute gets moved to the <symbol> and a unique id is added. Now, your script can work just as before. The line var draw = SVG('pong').size(width, height) would just insert a second <svg> element after that first, invisible one. But you could also pickup the first one and draw there. The initial zero size gets overwritten. var draw = SVG('#pong svg').size(width, height) In both variants, the ball is then used in the form of a reference: var ball = draw.use('ball').size(ballsize, ballsize)
Get the content of an SVG string
Let's say that we have the following simple SVG element: const svg = `<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg"> <g clip-path="url(#clip0)"> <path d="M100 100C100 44.7715 55.2285 -1.95703e-06 0 -4.37114e-06L-4.37114e-06 100L100 100Z" fill="current"/> </g> </svg>` The svg variable is a string and I want to extract all the child nodes from the svg (the <g> element for the specific example but can be more elements). How can I do that without using a DOM manipulation library like JQuery?
Version 2 You can use str.split() to extract all the child elements from the <svg> as a string. Working Example: const svg = `<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg"> <g clip-path="url(#clip0)"> <path d="M100 100C100 44.7715 55.2285 -1.95703e-06 0 -4.37114e-06L-4.37114e-06 100L100 100Z" fill="current"/> </g> </svg>`; let g; g = svg.split('xmlns="http://www.w3.org/2000/svg">')[1]; g = g.split('</svg>')[0]; g = g.trim(); console.log(g); Version 1 I want to extract the content from the <svg> which is the element inside it. If I've understood correctly: you want to extract only the <g> element you know that the <svg> contains a <g> element you know that there is only one <g> element you can use str.split() to extract the <g> from the <svg>. Working Example: const svg = `<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg"> <g clip-path="url(#clip0)"> <path d="M100 100C100 44.7715 55.2285 -1.95703e-06 0 -4.37114e-06L-4.37114e-06 100L100 100Z" fill="current"/> </g> </svg>`; let g; g = svg.split('<g ')[1]; g = g.split('</g>')[0]; g = '<g ' + g + '</g>' console.log(g);
Either use JavaScripts built in DOMParser var p= new DOMParser() var d= p.parseFromString(svg,"text/html") var g= d.querySelector("svg g") OR make your own html/xml parser from scratch by looping through each character, checking for <, if found look for some letters next for the tag, if found look for other >, get all content in between for attributes, rinse and repeat
How to animate SVG fill and path in a similar style to vivus.js
I am trying to animate an SVG with vivus.js but it doesn't seem to be doing anything. Vivus seems to be able to read the SVG correctly, I can run the following code: var test = new Vivus('welcome_message', {duration: 200}); console.table(test.map); And get the following result in the console (this is how vivus recommends you debug it): (index) el startAt duration progress 0 path 0 133.33333333333331 0.0075000000000000015 1 path 5.555555555555556 133.33333333333331 0 2 path 11.111111111111112 133.33333333333331 0 3 path 16.666666666666668 133.33333333333331 0 4 path 22.222222222222225 133.33333333333331 0 5 path 27.777777777777782 133.33333333333331 0 6 path 33.333333333333336 133.33333333333331 0 7 path 38.88888888888889 133.33333333333331 0 8 path 44.44444444444445 133.33333333333331 0 9 path 50.00000000000001 133.33333333333331 0 10 path 55.555555555555564 133.33333333333331 0 11 path 61.11111111111112 133.33333333333331 0 12 path 66.66666666666667 133.33333333333331 0 This says to me the vivus is working, but when the code runs, nothing happens. Please see the following very simple jsfiddle example: https://jsfiddle.net/zzgnkwtn/2/ All I can thinks is that perhaps there is something wrong with my SVGs but seeing as it is picking up on the paths correctly this doesn't seem right. Here is a link to the vivus documentation incase anyone wants to have a look, I am stumped. https://github.com/maxwellito/vivus#vivusjs Thanks in advance for any help. EDIT (changed title from 'SVG animation with vivus.js not working' to 'How to animate SVG fill and path in a similar style to vivus.js') I have realised that the end goal I am trying to achieve isn't actually the above question, this is because I am new to SVG animation and started on the wrong path. What I am more looking to achieve is something like the following tutorial: https://medium.com/#gordonnl/stylised-line-animations-ded23320ffe5#.v6va1kc1w The key difference and the main thing I am struggling is I want that exact type of animation but I want to animate the fill and the path, not just the path. With help from #Alvin I have realised that some kind of clipPath or mask is required, I have tried to do this by following the above tutorial but this is the result I am getting: http://codepen.io/anon/pen/dMeQLB This seems to animate some of the paths but it is definitely not doing what I want. Any help from anyone would be amazing! FINAL RESULT I finally managed to achieve what I was trying to do, most of the problem is that the SVG was not crated in the best way for animation in Illustrator, I needed to convert my object into a Compound Path so I could use it as clipPath and then have a simple path underneath which I can easily animate. Demo: http://codepen.io/anon/pen/zqjyzX
See my demo, click on blue circle to restart animation. var sign = new Vivus('mySign', { type: 'scenario-sync', duration: 20, start: 'autostart', dashGap: 20, forceRender: false }, function() { if (window.console) { console.log('Animation finished. [log triggered from callback]'); } $('#mySign').find('path').attr('class', 'fill_path'); }); function runme() { $('#mySign').find('path').attr('class', ''); sign.reset().play(); }; svg * { stroke: blue; stroke-width: 1; fill: none; } .fill_path { fill: blue; } <script src="//code.jquery.com/jquery-2.2.2.min.js"></script> <script src="//cdn.jsdelivr.net/vivus/latest/vivus.min.js"></script> <svg id="mySign" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="200px" height="200px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve" > <path data-duration="30" d="M27.9,9.6c0,0,0.2,1.1,0.4,2.7c0.1,0.4,0.2,1.1,0,1.6c-0.2,0.6-0.7,0.8-1.1,0.9c-0.4,0.1-1,0.1-1.3-0.2c-0.7,0.7-0.6,0.6-1.1,0.7c0,0-0.3,0.1-0.4,0.1c-0.1,0-0.2-0.1-0.3-0.1c-0.6,0.1-0.9-1-1-1.4c-0.3-1.4-0.6-2.8-0.9-4.2C22.2,9.5,22,9,22,8.9c-0.1-0.2-0.1-0.5-0.2-0.7c0.1,0,0.4-0.1,0.5-0.1c0.4-0.1,0.5,0.1,0.6,0.5c0.4,1.5,0.7,3,1.1,4.5c0.1,0.2,0.4,1.4,0.9,1.3c0.3-0.1,0.1-1.1,0.1-1.3c-0.2-0.8-0.3-1.6-0.5-2.4c-0.1-0.8-0.2-1.5-0.4-2.3c-0.2-0.7-0.1-0.8,0.3-0.9c0.2,0,0.6,0,0.6,0.2c0,0,0,0.4,0,0.4c0,0.2,0.1,0.4,0.1,0.6c0.3,1.4,0.6,2.9,0.8,4.3c0.1,0.4,0.6,1,1.1,0.9c0.4-0.1,0.1-2,0.1-2.1c0-0.2-0.1-0.4-0.1-0.6c0,0-0.1-1.3-0.2-2c-0.1-0.7-0.2-1.3-0.4-1.9C26.8,7.1,27.4,7,27.4,7c0.3-0.1,0.2,0.2,0.3,0.2C27.8,8,27.9,8.8,27.9,9.6z"></path> <path data-duration="30" d="M32.3,9.4c-0.1,0-0.8,0.1-0.9,0.1c-0.3,0-0.6,0.1-0.8,0.1c0.2,1.1,0.4,2.2,0.6,3.2c0.4-0.1,0.9-0.2,1.3-0.2c0.1,0,0.3,0,0.4,0c0.1,0.1,0.2,0.7,0.3,0.9c0.1,0.4-0.1,0.3-0.4,0.4c-0.1,0-0.7,0.1-0.8,0.1c-0.5,0.1-1,0.2-1.5,0.3c-0.1-0.1-0.2-0.2-0.2-0.2c0-0.2,0-0.1,0-0.4c-0.2-1-0.4-2.1-0.6-3.1c-0.2-0.9-0.3-1.9-0.5-2.8C29.1,7.4,29,7.1,29,6.8c0.3-0.1,0.7-0.2,1.1-0.3c0.5-0.1,1-0.2,1.5-0.3c0.3-0.1,0.5,0.8,0.5,1c0,0-0.3,0.2-0.3,0.2c-0.4,0-0.8,0.1-1.2,0.1c-0.3,0.1-0.3,0-0.3,0.3c0,0,0.1,0.5,0.2,0.7c0.2,0.1,0.3,0,0.6,0c1.3-0.2,1.4-0.3,1.5,0.5C32.6,9.1,32.5,9.3,32.3,9.4z"></path> <path data-duration="30" d="M36.6,13.2c-0.5,0.1-1.6,0.4-1.7,0.4c-0.2,0-0.2-0.1-0.3-0.2c-0.3-2.4-0.7-4.8-1-7.1c0,0-0.2-0.4,0.1-0.5c1.1-0.2,1.2-0.4,1.3,0.7c0,0.3,0.1,0.7,0.1,1c0,0.1,0,0.4,0,0.5c0.2,1.1,0.4,2.2,0.5,3.3c0.1,0.1,0,0.7,0.2,0.6c0.1,0,0.5-0.1,0.5-0.1c0.2,0,0.4,1.2,0.4,1.2C36.8,13,36.7,13.1,36.6,13.2z"></path> <path data-duration="30" d="M39.4,6.9c-0.3,0.6-0.5,1.2-0.6,1.7c0,0.2-0.1,0.5-0.1,0.7c0,0.1,0,1,0,1.2c0.1,0.8,0.6,1.3,1.5,1.2c0.1,0,0.1,0,0.1,0c0.3,0,0.5,0.8,0.5,0.9c0,0.1-0.1,0.2-0.2,0.2c-0.1,0-0.1,0-0.2,0c-0.1,0-0.5,0-0.7-0.1c-0.1,0-0.3,0-0.3,0c-1.4-0.4-1.6-1.3-1.7-2c0,0-0.1-1-0.1-1.4c0-0.4,0.1-1.3,0.2-1.9c0-0.1,1-2.3,1.7-2.4c0.1,0,0.1,0,0.2,0.1c0.1,0.3,0.2,0.6,0.2,0.6C40.1,5.7,39.6,6.5,39.4,6.9z"></path> <path data-duration="30" d="M43.9,12.4c-1.5,0.1-2.2-2.5-2.2-3.1c0-0.4-0.1-0.7-0.1-1.1c0-0.1,0.1-0.7,0.1-1c0.1-1.4,0.9-3,2.4-3.1c0.6-0.1,1.3,0.6,1.6,1.1c0.6,1,0.8,2.1,0.9,3.2C46.5,10.1,46.2,12.2,43.9,12.4z M44.5,5.7c-0.2-0.2-0.5-0.5-0.7-0.5c-1,0.1-1.2,3.1-1.1,3.6c0,0.2,0.3,2.7,1.4,2.6c0.3,0,0.7-0.4,0.9-0.7c0.5-0.8,0.5-1.6,0.4-2.3C45.3,7.7,45,6.2,44.5,5.7z"></path> <path data-duration="30" d="M55.1,11.9c-0.2,0-0.4,0-0.6-0.1c-0.3-1.1-0.4-1.5-0.5-2.3c-0.2-0.9-0.4-1.9-0.6-2.8c0,0,0,0,0,0c-0.4,1.2-1,3.3-1.3,5c-0.1,0.1-0.9,0.3-1.1,0.3c-0.1,0-0.1,0-0.2,0c-0.4-1.3-0.9-2.9-1.3-4.3c-0.3,1.1-0.3,2.5-0.6,4.2c0,0-0.2,0.1-0.2,0.1c-0.4,0-0.2,0-1,0.1c0-0.1,0-0.1,0-0.2c0,0,0.1-0.9,0.1-1.4c0.1-0.8,0.4-3.4,0.5-4.8c0-0.1,0.1-0.3,0.1-0.5c0-0.1,0-1,0.3-1c0.3,0,1.1,0.1,1.1,0.1c0.5,1.5,0.9,3.1,1.6,5c0,0,0,0,0,0c0.5-1.7,1.1-3.5,1.4-4.4C52.8,4.6,53,4.3,53,4.1C53.1,4,53.8,4,53.9,4c0.4,0,0.5,1,0.5,1.4c0.1,0.3,0.2,0.7,0.2,1.1c0.1,0.3,0.2,0.9,0.2,1.3c0.1,0.6,0.3,1.4,0.4,2.2c0.1,0.4,0.4,1.4,0.4,1.5C55.8,11.9,55.2,11.8,55.1,11.9z"></path> <path data-duration="30" d="M59.7,7.2c-0.1,0-0.8-0.1-0.9-0.1c-0.3,0-0.6,0-0.8,0c0,1.1,0,2.2,0,3.3c0.5,0,0.9,0,1.4,0c0.1,0,0.3,0,0.4,0.1c0,0.1,0.1,0.7,0.1,0.9c0,0.4-0.2,0.3-0.5,0.3c-0.1,0-0.7-0.1-0.8-0.1c-0.5,0-1,0-1.6,0c-0.1-0.1-0.1-0.2-0.1-0.3c0-0.2,0-0.1,0-0.4c0-1.1,0-2.1,0-3.2c0-0.9,0-1.9,0-2.8c0-0.3,0-0.6,0-0.9C57.3,4,57.7,4,58.1,4c0.5,0,1,0,1.5,0c0.3,0,0.3,0.9,0.3,1c0,0-0.3,0.1-0.4,0.1c-0.4,0-0.8-0.1-1.2-0.1C58.1,5,58,4.9,58,5.3c0,0,0,0.5,0,0.7c0.2,0.1,0.3,0.1,0.6,0.1C60,6.1,60,6,60,6.9C60,7,59.9,7.2,59.7,7.2z"></path> <path data-duration="30" d="M68.6,5.4c-0.4,0-0.9-0.1-1.3-0.1c-0.1,1.9-0.2,3.8-0.4,5.7c0,0.1,0.1,0.6,0.1,0.7c0,0.1-0.1,0.2-0.1,0.2c-0.2,0-0.7,0-0.8,0c-0.1,0-0.2-0.1-0.3-0.1c-0.1-0.1-0.1-0.6-0.1-0.8c0.1-1.5,0.2-3.1,0.3-4.6c0.1-1.4,0.2-0.9-1.7-1.2c0-0.1-0.1-0.7-0.1-0.8c0,0,0-0.2,0.1-0.3c1.1,0.1,2.1,0.1,3.2,0.2c0.4,0,1.3,0.1,1.7,0.1c0,0.1,0.1,0.2,0,0.4C69.1,5.1,69.1,5.4,68.6,5.4z"></path> <path data-duration="30" d="M71.6,12.5C70.1,12.3,69.9,9.6,70,9c0-0.4,0.1-0.7,0.1-1.1c0-0.1,0.2-0.7,0.2-1c0.3-1.4,1.4-2.8,2.9-2.6c0.6,0.1,1.2,0.9,1.4,1.4C74.9,6.8,75,7.9,74.8,9C74.7,10.6,73.9,12.7,71.6,12.5z M73.5,6c-0.1-0.2-0.3-0.6-0.6-0.7c-1-0.1-1.7,2.8-1.8,3.4c0,0.2-0.2,2.7,0.9,2.8c0.3,0,0.8-0.3,1-0.6c0.6-0.7,0.7-1.5,0.8-2.2C73.9,8.1,73.9,6.5,73.5,6z"></path> <path data-duration="30" d="M83.6,7.2c-0.4-0.1-0.9-0.1-1.3-0.2c-0.3,1.9-0.6,3.8-0.9,5.6c0,0.1,0,0.6,0,0.7c0,0.1-0.1,0.1-0.1,0.2c-0.2,0-0.7,0-0.8,0c-0.1,0-0.2-0.1-0.3-0.2c0-0.1,0-0.6,0-0.8c0.2-1.5,0.5-3.1,0.7-4.6c0.2-1.4,0.3-0.8-1.6-1.3c0-0.1,0-0.7,0-0.8c0,0,0.1-0.2,0.1-0.3c1,0.2,2.1,0.3,3.1,0.5c0.4,0.1,1.3,0.2,1.6,0.3c0,0.1,0,0.3,0,0.4C84.1,7,84,7.3,83.6,7.2z"></path> <path data-duration="30" d="M87.8,14.4c0,0.1-0.1,0.2-0.1,0.2c-0.1,0.1-0.8,0.1-1,0c-0.2,0-0.2-0.3-0.1-0.4c0.2-0.8,0.4-1.6,0.6-2.4c0.1-0.5,0.2-0.9,0.3-1.4c-0.4-0.1-0.9-0.2-1.3-0.2c-0.1,0.6-0.2,1.3-0.4,1.9c0,0,0,0.4,0,0.4c0,0.2-0.1,0.4-0.1,0.7c0,0.1,0,0.6-0.1,0.6c0,0.1-0.1,0.3-0.2,0.2c-0.1,0-0.6-0.1-0.6-0.1C84.2,14,84,14,84.3,13.4c0.4-2.1,0.7-4.2,1.1-6.2c0-0.2,0.1-0.5,0.1-0.7c0.1,0,0.3-0.1,0.3,0c0.1,0,0.2,0.1,0.3,0.1c0.1,0,0.1,0,0.1,0c0.1,0,0.4,0.1,0.4,0.2c0.1,0.5-0.1,1.2-0.2,1.8c0,0.2-0.1,0.5-0.1,0.8c0.2,0.1,0.4,0.1,0.5,0.2c0.9,0.2,0.9,0.2,1-0.3c0.1-0.3,0.1-0.5,0.2-0.8c0-0.1,0.1-0.9,0.2-1.3c0.1,0,0.3,0,0.4,0c0.2,0,0.8,0.2,0.6,0.6C88.7,9.9,88.3,12.1,87.8,14.4z"></path> <path data-duration="30" d="M92.7,11.3c-0.1,0-0.8-0.2-0.8-0.3c-0.3-0.1-0.6-0.1-0.8-0.2c-0.2,1.1-0.5,2.1-0.7,3.2c0.4,0.1,0.9,0.2,1.3,0.3c0.1,0,0.3,0.1,0.4,0.2c0,0.1-0.1,0.7-0.1,0.9c-0.1,0.4-0.2,0.3-0.5,0.2c-0.1,0-0.7-0.2-0.7-0.2c-0.5-0.1-1-0.2-1.5-0.3c0-0.1-0.1-0.3-0.1-0.3c0-0.2,0.1-0.1,0.1-0.4c0.2-1,0.5-2.1,0.7-3.1c0.2-0.9,0.4-1.8,0.6-2.8c0-0.3,0.1-0.6,0.2-0.9c0.4,0,0.7,0.1,1.1,0.1c0.5,0.1,1,0.2,1.5,0.3c0.3,0.1,0.1,1,0.1,1.1c0,0-0.3,0-0.4,0c-0.4-0.1-0.8-0.2-1.1-0.3c-0.3-0.1-0.3-0.2-0.4,0.2c0,0-0.1,0.5-0.1,0.7c0.1,0.1,0.3,0.1,0.6,0.2c1.3,0.3,1.4,0.3,1.2,1.1C93.1,11.2,92.9,11.4,92.7,11.3z"></path> <path transform="translate(-270,30)" data-duration="30" d="M324.6,61.2c16.6,0,29.5-12.9,29.5-29.5c0-16.6-12.9-29.5-29.5-29.5c-16.6,0-29.5,12.9-29.5,29.5C295.1,48.4,308,61.2,324.6,61.2z" onclick="runme();" /> </svg>
svg-sprite: symbol conversion to single svg icon
I have a svg sprite like the following: <svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" style="width:0;height:0;visibility:hidden;"> <symbol viewBox="0 0 500 500" id="test-icon"><title>test</title><ellipse class="background" id="test-background" fill="#fff" cx="248.8" cy="251.4" rx="246.2" ry="244.8"/> <path class="test-frame" d="M249.4 18C121.1 18 16.8 122.3 16.8 250.6S121.3 483 249.4 483 482 378.8 482 250.5 377.7 18 249.4 18m0 480.8c-66 0-128.3-26.4-176.2-73.1C27.6 378.9 1.3 316.5 1.3 249.3s26.3-128.2 73.1-175 109.1-73.1 175-73.1 128.3 26.4 176.2 73.1c46.7 46.8 73.1 109.2 73.1 176.2s-26.4 128.3-73.1 176.2c-46.7 45.6-109 72.1-176.2 72.1"/> <path class="test-figure" d="M133.8 103.5v293h232.5V148.9l-22-22-23.4-23.4H133.8zm17.9 22h108.7v12.4H151.7v-12.4zm0 24.8h108.7v12.4H151.7v-12.4zm192.5 211.8H151.7v-24.7h191.2v24.7h1.3zm0-41.2H151.7v-24.7h191.2v24.7h1.3zm0-42.7H151.7v-24.7h191.2v24.7h1.3zm0-44H151.7v-23.4h191.2v23.4h1.3z"/> </symbol> <symbol viewBox="0 0 500 500" id="test-icon"><title>test</title><ellipse class="background" id="test-background" fill="#fff" cx="248.8" cy="251.4" rx="246.2" ry="244.8"/> <path class="test-frame" d="M249.4 18C121.1 18 16.8 122.3 16.8 250.6S121.3 483 249.4 483 482 378.8 482 250.5 377.7 18 249.4 18m0 480.8c-66 0-128.3-26.4-176.2-73.1C27.6 378.9 1.3 316.5 1.3 249.3s26.3-128.2 73.1-175 109.1-73.1 175-73.1 128.3 26.4 176.2 73.1c46.7 46.8 73.1 109.2 73.1 176.2s-26.4 128.3-73.1 176.2c-46.7 45.6-109 72.1-176.2 72.1"/> <path class="test-figure" d="M133.8 103.5v293h232.5V148.9l-22-22-23.4-23.4H133.8zm17.9 22h108.7v12.4H151.7v-12.4zm0 24.8h108.7v12.4H151.7v-12.4zm192.5 211.8H151.7v-24.7h191.2v24.7h1.3zm0-41.2H151.7v-24.7h191.2v24.7h1.3zm0-42.7H151.7v-24.7h191.2v24.7h1.3zm0-44H151.7v-23.4h191.2v23.4h1.3z"/> </symbol> <symbol viewBox="0 0 500 500" id="test-icon"><title>test</title><ellipse class="background" id="test-background" fill="#fff" cx="248.8" cy="251.4" rx="246.2" ry="244.8"/> <path class="test-frame" d="M249.4 18C121.1 18 16.8 122.3 16.8 250.6S121.3 483 249.4 483 482 378.8 482 250.5 377.7 18 249.4 18m0 480.8c-66 0-128.3-26.4-176.2-73.1C27.6 378.9 1.3 316.5 1.3 249.3s26.3-128.2 73.1-175 109.1-73.1 175-73.1 128.3 26.4 176.2 73.1c46.7 46.8 73.1 109.2 73.1 176.2s-26.4 128.3-73.1 176.2c-46.7 45.6-109 72.1-176.2 72.1"/> <path class="test-figure" d="M133.8 103.5v293h232.5V148.9l-22-22-23.4-23.4H133.8zm17.9 22h108.7v12.4H151.7v-12.4zm0 24.8h108.7v12.4H151.7v-12.4zm192.5 211.8H151.7v-24.7h191.2v24.7h1.3zm0-41.2H151.7v-24.7h191.2v24.7h1.3zm0-42.7H151.7v-24.7h191.2v24.7h1.3zm0-44H151.7v-23.4h191.2v23.4h1.3z"/> </symbol> </svg> Now my question is: can I just make a svg out of the symbol, just by extracting the symbol and then replacing the symbol-tag with an svg-tag? Or how can I achieve this conversion to separate svg? I thought of a little script, that can inject several svg icons from an svg-sprite like above, so the icons can be style by css better/individually. The thing here is that in the sprite there are symbols, which (as far as I read) can only be used with <use xlink:href>. But then the icon can't be styled individually like inline svg could be (as it is a clone). Edit: I found open-iconic svgIncetor that uses an img-src and single svgs to inject into the html. Also to mention is that I alread read following articles by Chris Coyier: http://css-tricks.com/svg-use-external-source/ http://css-tricks.com/svg-symbol-good-choice-icons/ http://css-tricks.com/icon-fonts-vs-svg/ http://css-tricks.com/svg-sprites-use-better-icon-fonts/ Basically I want to combine the technics used there to have an icon system, that can have multi-color icons based on css and icon-sprite (with inline svg through js-inject). The open-iconic injector can only inject sinlge svgs and not from sprites afaik.
There isn't a lot you need to change (see http://jsfiddle.net/yo8bhxfu/): Remove the view box from the SVG element Adapt the style of the SVG element so it's visible and specifies the proper size (taken from the symbol's view box) Convert the symbol tags into g tags You might be on the safe side if you additionally remove the class attributes, the id attributes and the title. <svg xmlns="http://www.w3.org/2000/svg" style="width:500px; height:500px;"> <g viewBox="0 0 500 500" id="test-icon"> <title>test</title> <ellipse class="background" id="test-background" fill="#fff" cx="248.8" cy="251.4" rx="246.2" ry="244.8"/> <path class="test-frame" d="M249.4 18C121.1 18 16.8 122.3 16.8 250.6S121.3 483 249.4 483 482 378.8 482 250.5 377.7 18 249.4 18m0 480.8c-66 0-128.3-26.4-176.2-73.1C27.6 378.9 1.3 316.5 1.3 249.3s26.3-128.2 73.1-175 109.1-73.1 175-73.1 128.3 26.4 176.2 73.1c46.7 46.8 73.1 109.2 73.1 176.2s-26.4 128.3-73.1 176.2c-46.7 45.6-109 72.1-176.2 72.1"/> <path class="test-figure" d="M133.8 103.5v293h232.5V148.9l-22-22-23.4-23.4H133.8zm17.9 22h108.7v12.4H151.7v-12.4zm0 24.8h108.7v12.4H151.7v-12.4zm192.5 211.8H151.7v-24.7h191.2v24.7h1.3zm0-41.2H151.7v-24.7h191.2v24.7h1.3zm0-42.7H151.7v-24.7h191.2v24.7h1.3zm0-44H151.7v-23.4h191.2v23.4h1.3z"/> </g> </svg>
I have created a small script using Nodejs and svg-parser, to split a SVG icons sprite file: <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <symbol id="act" viewBox="0 0 48 48"> <path d="M24 22c0.552 0 1-0.448 1-1v-18c0-0.552-0.448-1-1-1s-1 0.448-1 1v18c0 0.552 0.448 1 1 1z" /> <path d="M33.027 8.148c0 0-0.010 0.013-0.012 0.015-0.152-0.094-0.322-0.163-0.515-0.163-0.552 0-1 0.448-1 1 0 0.377 0.217 0.693 0.525 0.864 0.004 0.004-0.001 0.015 0.004 0.018 5.503 2.817 9.971 8.868 9.971 16.118 0 9.625-7.866 18-18 18-10.17 0-18-8.375-18-18 0-7.33 4.443-13.31 9.972-16.118 0.005-0.002 0-0.014 0.003-0.018 0.308-0.171 0.525-0.487 0.525-0.864 0-0.552-0.448-1-1-1-0.191 0-0.359 0.068-0.511 0.16-0.002-0.002-0.011-0.015-0.011-0.015-6.513 3.298-10.978 10.055-10.978 17.855 0 11.046 8.954 20 20 20s20-8.954 20-20c0-7.798-4.462-14.553-10.973-17.852z" /> </symbol> ... </defs> </svg> const fs = require('fs'); const { parse } = require('svg-parser'); // Read from sprite file fs.readFile('file.svg', 'utf8', function (err, contents) { console.log(err); const parsed = parse(contents); const symbols = parsed.children[0].children[0].children; symbols.forEach(symbol => { let paths = ''; const name = symbol.properties.id; symbol.children.forEach(path => { paths = paths + `<path d="${path.properties.d}" />` }); // Build SVG content const newIcon = `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 50 50"> <g> ${paths} </g> </svg>` // White to file fs.writeFile(`export/${name}.svg`, newIcon, () => { console.log(name); }); }); });
I improved Bogdan's code and created a script to parse any sprite to it's original svgs. import {toHtml} from "hast-util-to-html"; import {parse} from "svg-parser"; import * as fs from 'fs'; fs.readFile('input/path/sprite.svg', 'utf8', function (err, contents) { const parsed = parse(contents); const symbols = parsed.children[0].children; symbols.forEach(symbol => { const name = symbol.properties.id; symbol.tagName = "svg"; let newIcon = toHtml(symbol); fs.writeFile(`output/path/${name}.svg`, newIcon, () => { console.log(name); }); }); }) Just add your sprite path and the output path and it will generate the inner svgs with it's ids in the output folder. you can find the code in this gist
I used Codo's answer, but it didn't fully work in my case. A solution that worked for the SVG i came across: (which was <use xlink:href= and the svg optimised to just <path>s) (using Chrome or similar dev tools) Inspect the svg, find the <use xlink:href= element expand the element, and expand the #shadow-root (you may need to enable the Shadow DOM in your dev tools settings) copy the svg element inside into a text editor ensure there is a structure <g> containing the paths and shapes, like so: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 480"> <g> ... </g> </svg> (optional) clean up the svg using https://jakearchibald.github.io/svgomg/ save the file as .svg
Inline SVG Animation: Compatibility Oddities
I've been working on this for some time, but can't seem to find an adequate solution yet. The page is XHTML with inline SVG which is animated with ECMAscript once the SVG is finished loading. There is a 0.5s pause, then an eye in the middle opens. It works on Safari, Firefox, and Chrome on MacOS X. The problem is in Chrome on Windows the animation doesn't start, and in Opera (on OS X) the animation behaves erratically. What is going on?! You can see it live at this page. I'd love to hear your thoughts. Here are the most relevant bits of code: <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 1200 800" preserveAspectRatio="xMidYMid" style="width:100%; height:100%; position:absolute; top:0; left:0; z-index:2;" onload="startup(evt);"> <script> function startup(evt){ setTimeout("doEyeOpen(1200,20);",500); } function doEyeOpen(dur,msrate) { eye=document.getElementById("eyehole"); var totStep = dur/msrate, step=0; window.timer = window.setInterval( function() { var start=580, d1=start-175.087, d2=624.913-start; var prog = easeOut(step,totStep); var y2=start-(d1*prog); var y1=start+(d2*prog); var d="M 1200, 800 H 0 V 0 h 1200 V 800 z M 1183.589 400 C 1037.727 400 813.41 "+y1+" 600 "+y1+" C 386.591 "+y1+" 162.274 400 16.412 400 C 162.274 400 386.591 "+y2+" 600 "+y2+" C 813.41 "+y2+" 1037.727 400 1183.589 400 z"; eye.setAttribute("d", d); step++; if (step > totStep) {window.clearInterval(window.timer); return} } ,msrate) } function easeOut(step,totStep){ return (1 - Math.pow(((step/totStep)-1),8)); } </script> <rect fill="#FFF" x="10" y="10" width="1180" height="780" id="white-bg"/> <g id="Iris"> <circle transform="translate(600, 400)" r="260" fill="#e50000" id="c-iris" onclick="changeColor(evt)" /> <circle transform="translate(600, 400)" r="130" fill="#000" id="c-pupil"/> </g> <g id="Gray"> <path fill="#999999" d="M138.363,397.25c0-90.911,26.295-175.674,71.671-247.127H78.419 c-35.555,74.909-55.457,158.691-55.457,247.124c0,90.606,20.891,176.329,58.105,252.629h132.529 C166.042,577.29,138.363,490.509,138.363,397.25z"/> <path fill="#999999" d="M1121.58,150.124H989.963c45.377,71.453,71.671,156.216,71.671,247.127 c0,93.258-27.68,180.039-75.233,252.626h132.53c37.215-76.3,58.107-162.023,58.107-252.629 C1177.039,308.815,1157.137,225.033,1121.58,150.124z"/> </g> <clipPath id="CP" clip-rule="evenodd"> <path id="eyehole" d="M 1200, 800 H 0 V 0 h 1200 V 800 z M 1183.589 400 C 1037.727 400 813.41 400.000 600 400.000 C 386.591 400.000 162.274 400 16.412 400 C 162.274 400 386.591 400.000 600 400.000 C 813.41 400.000 1037.727 400 1183.589 400 z"/> </clipPath> <rect fill="#000" x="0" y="0" width="1200" height="800" id="black-cover" clip-path="url(#CP)"/> </svg>
The startup method gets called twice in opera it seems, I've filed a bugreport (CORE-31399) on this. A workaround could be to use only one inline svg fragment, or to have a condition in the startup function such that you don't get two timers going (which is I believe the cause of the odd rendering behaviour).
The problem in Chrome is not the animation but your clipping path. When you test your image without animation and the final eyehole path, you only see black in Chrome. Somehow the hole isn't cut out of the black box. EDIT: Seems like Chrome can't render the evenodd clip-rule (you can test here: http://srufaculty.sru.edu/david.dailey/cs427/StateOfArt-Dailey.html#clipPath)