{"id":946,"date":"2021-04-22T09:01:36","date_gmt":"2021-04-22T09:01:36","guid":{"rendered":"https:\/\/potatodie.nl\/diffuse-write-ups\/?p=946"},"modified":"2021-10-03T08:14:22","modified_gmt":"2021-10-03T08:14:22","slug":"organize-svg-with-use-but-beware-of-transforms","status":"publish","type":"post","link":"https:\/\/potatodie.nl\/diffuse-write-ups\/organize-svg-with-use-but-beware-of-transforms\/","title":{"rendered":"Organize SVG with &lt;use&gt;, but beware of transforms"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Organizing SVG files with <code>use<\/code> and <code>symbol <\/code>elements offers many advantages, see for instance <a href=\"https:\/\/www.sarasoueidan.com\/blog\/structuring-grouping-referencing-in-svg\/\">Sara Soueidan&#8217;s article<\/a>. I did encounter some quirks though, especially with Safari, when using transforms in animations.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Quick solution<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Let <a href=\"https:\/\/greensock.com\/\">Greensock <\/a>handle <code>transformOrigin <\/code>for you and don&#8217;t use the <code>x<\/code> and <code>y<\/code> attribute on <code>&lt;use&gt;<\/code>. Set a transform instead.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Read on if you want to know how I arrived at this conclusion.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What&#8217;s the test case?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The test case is derived from a <a href=\"https:\/\/potatodie.nl\/coronation\">little game<\/a> I was creating when I encountered these issues.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/potatodie.nl\/diffuse-write-ups\/wp-content\/uploads\/2021\/04\/layout-2.svg\" alt=\"\" class=\"wp-image-950\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The above SVG image, exported from Illustrator (4.37 kB) contains four similar crowns. Three encircled at the bottom (representing coins) and one on the head of the figure at the top. It would be nice to use the same code for each crown, instead of repeating the full graphic in the SVG file. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s what <code>use <\/code>and <code>symbol<\/code> are meant for. This would not only minimize the file size, it would also be more maintainable, and more semantic. Here&#8217;s a more structured SVG (only 1,87 kB):<\/p>\n\n\n<div class=\"wp-block-advanced-gutenberg-blocks-code\">\n  <header class=\"wp-block-advanced-gutenberg-blocks-code__header\">\n    <div class=\"wp-block-advanced-gutenberg-blocks-code__lang is-lang-html\">\n      HTML    <\/div>\n    <div class=\"wp-block-advanced-gutenberg-blocks-code__file\">\n          <\/div>\n  <\/header>\n  <textarea \n    class=\"wp-block-advanced-gutenberg-blocks-code__source\" \n    name=\"codemirror-785691345\" \n    id=\"codemirror-785691345\"\n  >&lt;svg version=&quot;1.1&quot;\n    xmlns=&quot;http:\/\/www.w3.org\/2000\/svg&quot; viewBox=&quot;0 0 360 430&quot;&gt;\n    &lt;defs&gt;\n        &lt;symbol id=&quot;crown&quot; viewBox=&quot;0 0 100 100&quot; width=&quot;100&quot; height=&quot;100&quot; style=&quot;stroke-width: .8;&quot;&gt;\n            &lt;rect x=&quot;34.7&quot; y=&quot;65.4&quot; width=&quot;30.2&quot; height=&quot;7.8&quot;\/&gt;\n            &lt;rect x=&quot;34.7&quot; y=&quot;73.2&quot; width=&quot;30.2&quot; height=&quot;2.7&quot;\/&gt;\n            &lt;circle cx=&quot;40.1&quot; cy=&quot;69.2&quot; r=&quot;1.5&quot;\/&gt;\n            &lt;circle cx=&quot;50&quot; cy=&quot;69.2&quot; r=&quot;1.5&quot;\/&gt;\n            &lt;circle cx=&quot;59.9&quot; cy=&quot;69.2&quot; r=&quot;1.5&quot;\/&gt;\n            &lt;polygon points=&quot;34.7,65.4 64.9,65.4 73.9,37.2 58.9,48.7 49.9,29.1 40.8,48.7 26.1,37.2 &quot;\/&gt;\n            &lt;circle cx=&quot;26.1&quot; cy=&quot;37.2&quot; r=&quot;4.9&quot;\/&gt;\n            &lt;circle cx=&quot;49.9&quot; cy=&quot;29.1&quot; r=&quot;4.9&quot;\/&gt;\n            &lt;circle cx=&quot;73.9&quot; cy=&quot;37.2&quot; r=&quot;4.9&quot;\/&gt;\n        &lt;\/symbol&gt;\n        &lt;symbol id=&quot;coin&quot; style=&quot;stroke: inherit; fill: inherit;&quot; viewBox=&quot;0 0 100 100&quot;&gt;\n            &lt;circle cx=&quot;49.1&quot; cy=&quot;50.5&quot; r=&quot;40.8&quot;\/&gt;\n            &lt;use href=&quot;#crown&quot;\/&gt;\n        &lt;\/symbol&gt;\n    &lt;\/defs&gt;\n    &lt;g id=&quot;Coins&quot; transform=&quot;translate(40, 300)&quot; style=&quot;fill:none;stroke:#0080A4;&quot;&gt;\n        &lt;use href=&quot;#coin&quot; width=&quot;100&quot; height=&quot;100&quot;\/&gt;\n        &lt;use href=&quot;#coin&quot; x=&quot;92&quot; width=&quot;100&quot; height=&quot;100&quot;\/&gt;\n        &lt;use href=&quot;#coin&quot; x=&quot;184&quot; width=&quot;100&quot; height=&quot;100&quot; id=&quot;special_coin&quot;\/&gt;\n    &lt;\/g&gt;\n    &lt;g id=&quot;Crown_Pipo&quot; style=&quot;fill:#0080A3;stroke:#89CAB5;stroke-width:2;&quot;&gt;\n        &lt;circle cx=&quot;190.08&quot; cy=&quot;176.96&quot; r=&quot;100.96&quot;\/&gt;\n\n        &lt;ellipse transform=&quot;matrix(0.1602 -0.9871 0.9871 0.1602 -18.7071 330.942)&quot; cx=&quot;185.13&quot; cy=&quot;176.46&quot; rx=&quot;43.88&quot; ry=&quot;43.88&quot;\/&gt;\n        &lt;circle cx=&quot;175.08&quot; cy=&quot;169.23&quot; r=&quot;8.58&quot;\/&gt;\n        &lt;polygon points=&quot;146.89,186.92 156.74,201.43 70.44,238.69 \t&quot;\/&gt;\n        &lt;path d=&quot;M269.71,238.69l-23.86-35.34l-88.71,37.14c0,0,2.49,15.2,11.81,35.22&quot;\/&gt;\n        &lt;path d=&quot;M162.58,214.1c0,0,22.31,6.21,29.21-22.72&quot;\/&gt;\n\n        &lt;use href=&quot;#crown&quot; transform-origin=&quot;50px 50px&quot; transform=&quot;translate(160, 38) scale(2.4) rotate(16)&quot;\/&gt;\n    &lt;\/g&gt;\n&lt;\/svg&gt;<\/textarea>\n  <script>\n    CodeMirror.fromTextArea( document.getElementById('codemirror-785691345'), {\n      mode: 'xml',\n      readOnly: true,\n      theme: 'hopscotch', \n      lineNumbers: false,\n      firstLineNumber: 1,\n      matchBrackets: true,\n      indentUnit: 4,\n      tabSize: 4,\n      lineWrapping: false,\n    } ); \n  <\/script>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\">The last coin was given an id <code>special_coin<\/code>, in order to transform (or animate) that coin with Greensock:<\/p>\n\n\n<div class=\"wp-block-advanced-gutenberg-blocks-code\">\n  <header class=\"wp-block-advanced-gutenberg-blocks-code__header\">\n    <div class=\"wp-block-advanced-gutenberg-blocks-code__lang is-lang-js\">\n      JS    <\/div>\n    <div class=\"wp-block-advanced-gutenberg-blocks-code__file\">\n          <\/div>\n  <\/header>\n  <textarea \n    class=\"wp-block-advanced-gutenberg-blocks-code__source\" \n    name=\"codemirror-1112598837\" \n    id=\"codemirror-1112598837\"\n  >gsap.to(&#039;#special_coin&#039;, {\n    transformOrigin: &quot;50% 50%&quot;,\n\tscale: 1,\n    rotate: 45,\n    duration: .4\n});<\/textarea>\n  <script>\n    CodeMirror.fromTextArea( document.getElementById('codemirror-1112598837'), {\n      mode: 'javascript',\n      readOnly: true,\n      theme: 'hopscotch', \n      lineNumbers: true,\n      firstLineNumber: 1,\n      matchBrackets: true,\n      indentUnit: 4,\n      tabSize: 4,\n      lineWrapping: true,\n    } ); \n  <\/script>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\">One of the things that makes Greensock indispensible is it handles transform-origin for you. Doing it yourself is hard. I managed to do it in the last <code>use <\/code>(in Crown_Pipo), but only by fiddling the translation. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Quirks<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">All is well, until you check how things look on iPhone.<\/p>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"688\" height=\"815\" src=\"https:\/\/potatodie.nl\/diffuse-write-ups\/wp-content\/uploads\/2021\/04\/bird-crowned-1.jpg\" alt=\"\" class=\"wp-image-958\" srcset=\"https:\/\/potatodie.nl\/diffuse-write-ups\/wp-content\/uploads\/2021\/04\/bird-crowned-1.jpg 688w, https:\/\/potatodie.nl\/diffuse-write-ups\/wp-content\/uploads\/2021\/04\/bird-crowned-1-253x300.jpg 253w\" sizes=\"auto, (max-width: 688px) 100vw, 688px\" \/><\/figure>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"448\" height=\"529\" src=\"https:\/\/potatodie.nl\/diffuse-write-ups\/wp-content\/uploads\/2021\/04\/bird-bald-2.jpg\" alt=\"\" class=\"wp-image-957\" srcset=\"https:\/\/potatodie.nl\/diffuse-write-ups\/wp-content\/uploads\/2021\/04\/bird-bald-2.jpg 448w, https:\/\/potatodie.nl\/diffuse-write-ups\/wp-content\/uploads\/2021\/04\/bird-bald-2-254x300.jpg 254w\" sizes=\"auto, (max-width: 448px) 100vw, 448px\" \/><\/figure>\n<\/div>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\">The first image shows how it is intended, in FireFox. The other Safari on iPhone. Three things have gone wrong. 1. The crown has disappeared from the head of the fellow with the nose. 2. The most right coin has transformed out of the picture and 3. The color of the coins don&#8217;t seem right. You may check the <a href=\"https:\/\/potatodie.nl\/coronation\/usetestcase\/\">test page<\/a> yourself.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Shadow DOM<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Apart from that, if you inspect the code in Firefox with dev tools you&#8217;ll see that the shadow DOM doesn&#8217;t align with the real DOM, which is confusing.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The fix<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The solution is to omit the <code>x<\/code> attribute on the use element and replace it with a transform instead. Actually it&#8217;s not so strange browsers get confused, since the <code>x<\/code> and<code> y<\/code> attributes are a shorthand for a transform (a translation). The order of transforms matters and may differ from browser to browser?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here&#8217;s my final SVG, in which I also omitted the <code>&lt;defs&gt;<\/code> tag, because symbols don&#8217;t need that:<\/p>\n\n\n<div class=\"wp-block-advanced-gutenberg-blocks-code\">\n  <header class=\"wp-block-advanced-gutenberg-blocks-code__header\">\n    <div class=\"wp-block-advanced-gutenberg-blocks-code__lang is-lang-html\">\n      HTML    <\/div>\n    <div class=\"wp-block-advanced-gutenberg-blocks-code__file\">\n          <\/div>\n  <\/header>\n  <textarea \n    class=\"wp-block-advanced-gutenberg-blocks-code__source\" \n    name=\"codemirror-1059462693\" \n    id=\"codemirror-1059462693\"\n  >&lt;svg version=&quot;1.1&quot;\n    xmlns=&quot;http:\/\/www.w3.org\/2000\/svg&quot; viewBox=&quot;0 0 360 430&quot;&gt;\n    &lt;symbol id=&quot;crown&quot; width=&quot;100&quot; height=&quot;100&quot; style=&quot;stroke-width: .8;&quot;&gt;\n        &lt;rect x=&quot;34.7&quot; y=&quot;65.4&quot; width=&quot;30.2&quot; height=&quot;7.8&quot;\/&gt;\n        &lt;rect x=&quot;34.7&quot; y=&quot;73.2&quot; width=&quot;30.2&quot; height=&quot;2.7&quot;\/&gt;\n        &lt;circle cx=&quot;40.1&quot; cy=&quot;69.2&quot; r=&quot;1.5&quot;\/&gt;\n        &lt;circle cx=&quot;50&quot; cy=&quot;69.2&quot; r=&quot;1.5&quot;\/&gt;\n        &lt;circle cx=&quot;59.9&quot; cy=&quot;69.2&quot; r=&quot;1.5&quot;\/&gt;\n        &lt;polygon points=&quot;34.7,65.4 64.9,65.4 73.9,37.2 58.9,48.7 49.9,29.1 40.8,48.7 26.1,37.2 &quot;\/&gt;\n        &lt;circle cx=&quot;26.1&quot; cy=&quot;37.2&quot; r=&quot;4.9&quot;\/&gt;\n        &lt;circle cx=&quot;49.9&quot; cy=&quot;29.1&quot; r=&quot;4.9&quot;\/&gt;\n        &lt;circle cx=&quot;73.9&quot; cy=&quot;37.2&quot; r=&quot;4.9&quot;\/&gt;\n    &lt;\/symbol&gt;\n    &lt;symbol id=&quot;coin&quot; style=&quot;stroke: inherit; fill: inherit;&quot;&gt;\n        &lt;circle cx=&quot;49.1&quot; cy=&quot;50.5&quot; r=&quot;40.8&quot;\/&gt;\n        &lt;use href=&quot;#crown&quot;\/&gt;\n    &lt;\/symbol&gt;\n    &lt;g id=&quot;Coins&quot; transform=&quot;translate(40, 300)&quot; style=&quot;fill:none;stroke:#0080A4;&quot;&gt;\n        &lt;use href=&quot;#coin&quot;\/&gt;\n        &lt;use href=&quot;#coin&quot; x=&quot;92&quot;\/&gt;\n        &lt;use href=&quot;#coin&quot; id=&quot;special_coin&quot; transform=&quot;translate(184, 0)&quot;\/&gt;\n    &lt;\/g&gt;\n    &lt;g id=&quot;Crown_Pipo&quot; style=&quot;fill:#0080A3;stroke:#89CAB5;stroke-width:2;&quot;&gt;\n        &lt;circle cx=&quot;190.08&quot; cy=&quot;176.96&quot; r=&quot;100.96&quot;\/&gt;\n        &lt;ellipse transform=&quot;matrix(0.1602 -0.9871 0.9871 0.1602 -18.7071 330.942)&quot; cx=&quot;185.13&quot; cy=&quot;176.46&quot; rx=&quot;43.88&quot; ry=&quot;43.88&quot;\/&gt;\n        &lt;circle cx=&quot;175.08&quot; cy=&quot;169.23&quot; r=&quot;8.58&quot;\/&gt;\n        &lt;polygon points=&quot;146.89,186.92 156.74,201.43 70.44,238.69 \t&quot;\/&gt;\n        &lt;path d=&quot;M269.71,238.69l-23.86-35.34l-88.71,37.14c0,0,2.49,15.2,11.81,35.22&quot;\/&gt;\n        &lt;path d=&quot;M162.58,214.1c0,0,22.31,6.21,29.21-22.72&quot;\/&gt;\n\n        &lt;use href=&quot;#crown&quot; id=&quot;king&quot;\/&gt;\n    &lt;\/g&gt;\n&lt;\/svg&gt;<\/textarea>\n  <script>\n    CodeMirror.fromTextArea( document.getElementById('codemirror-1059462693'), {\n      mode: 'xml',\n      readOnly: true,\n      theme: 'hopscotch', \n      lineNumbers: false,\n      firstLineNumber: 1,\n      matchBrackets: true,\n      indentUnit: 4,\n      tabSize: 4,\n      lineWrapping: false,\n    } ); \n  <\/script>\n<\/div>\n\n\n\n<p class=\"wp-block-paragraph\">I also handed the crown on the figure&#8217;s head over to Greensock:<\/p>\n\n\n<div class=\"wp-block-advanced-gutenberg-blocks-code\">\n  <header class=\"wp-block-advanced-gutenberg-blocks-code__header\">\n    <div class=\"wp-block-advanced-gutenberg-blocks-code__lang is-lang-js\">\n      JS    <\/div>\n    <div class=\"wp-block-advanced-gutenberg-blocks-code__file\">\n          <\/div>\n  <\/header>\n  <textarea \n    class=\"wp-block-advanced-gutenberg-blocks-code__source\" \n    name=\"codemirror-666739672\" \n    id=\"codemirror-666739672\"\n  >gsap.set(&#039;#king&#039;, {\n    transformOrigin: &quot;50% 50%&quot;,\n    scale: 2.4,\n    rotate: 16,\n    translateX: 160,\n    translateY: 38\n})<\/textarea>\n  <script>\n    CodeMirror.fromTextArea( document.getElementById('codemirror-666739672'), {\n      mode: 'javascript',\n      readOnly: true,\n      theme: 'hopscotch', \n      lineNumbers: false,\n      firstLineNumber: 1,\n      matchBrackets: true,\n      indentUnit: 4,\n      tabSize: 4,\n      lineWrapping: false,\n    } ); \n  <\/script>\n<\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Do the test<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">You may check the <a href=\"https:\/\/potatodie.nl\/coronation\/usetestcase\/\">final test case<\/a> for your device yourself.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Organizing SVG files with use and symbol elements offers many advantages but some quirks may arise when using transforms\/animations.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[21,49,38,48,15,12],"class_list":["post-946","post","type-post","status-publish","format-standard","hentry","category-web-development","tag-animation","tag-game-design","tag-greensock","tag-quirks","tag-svg","tag-transform"],"acf":[],"_links":{"self":[{"href":"https:\/\/potatodie.nl\/diffuse-write-ups\/wp-json\/wp\/v2\/posts\/946","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/potatodie.nl\/diffuse-write-ups\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/potatodie.nl\/diffuse-write-ups\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/potatodie.nl\/diffuse-write-ups\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/potatodie.nl\/diffuse-write-ups\/wp-json\/wp\/v2\/comments?post=946"}],"version-history":[{"count":10,"href":"https:\/\/potatodie.nl\/diffuse-write-ups\/wp-json\/wp\/v2\/posts\/946\/revisions"}],"predecessor-version":[{"id":995,"href":"https:\/\/potatodie.nl\/diffuse-write-ups\/wp-json\/wp\/v2\/posts\/946\/revisions\/995"}],"wp:attachment":[{"href":"https:\/\/potatodie.nl\/diffuse-write-ups\/wp-json\/wp\/v2\/media?parent=946"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/potatodie.nl\/diffuse-write-ups\/wp-json\/wp\/v2\/categories?post=946"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/potatodie.nl\/diffuse-write-ups\/wp-json\/wp\/v2\/tags?post=946"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}