Closed HatScripts closed 3 years ago
Love it! By the way (and please don't let this side-note derail the focus of this issue! 😄), it should be possible to avoid some repetition in the human-readable form by using <pattern>
s. In the case of the USA flag, we could replace
<rect y="0" x="0" height="512" width="512" fill="#eeeeee"/> <!-- White background -->
<g fill="#d80027"> <!-- Red stripes -->
<rect y="64" x="0" height="64" width="512"/>
<rect y="192" x="0" height="64" width="512"/>
<rect y="320" x="0" height="64" width="512"/>
<rect y="448" x="0" height="64" width="512"/>
</g>
with a repeating pattern in the <defs>
:
<pattern id="stripes" width="512" height="128" patternUnits="userSpaceOnUse">
<rect x="0" y="0" width="512" height="64" fill="#eeeeee"/> <!-- White stripe -->
<rect x="0" y="64" width="512" height="64" fill="#d80027"/> <!-- Red stripe -->
</pattern>
and then filling the entire rectangle with it:
<rect y="0" x="0" height="512" width="512" fill="url(#stripes)"/> <!-- Stripes -->
Something similar could be done for the stars, avoiding the multiple clones + translations. But again, these are just possible optimizations we could consider in the future :) for now, using the circle mask is already a nice improvement!
Another possible improvement for the future is to make sure the geometry of the flags works without the circular mask, i.e. with the flags as actual square icons. Of course, this should not sacrifice the aesthetics of the circular form, which should remain the primary representation.
Love it! By the way, it should be possible to avoid some repetition in the human-readable form by using
<pattern>
s.
Oh wow! Thanks for this information! I never knew SVG had such a feature. 😭 Looks to be extremely useful.
Another possible improvement for the future is to make sure the geometry of the flags works without the circular mask, i.e. with the flags as actual square icons. Of course, this shouls not sacrifice the aesthetics of the circular form, which should remain the primary representation.
Indeed, I agree with you. It took me a while to figure out what you were talking about. The stars, right?
I agree, this looks very wrong with the stars misaligned like that. I didn't realise (and was probably lazy) and kept the circle mask on for the entire time. But the
<pattern>
optimisation should make for an easy fix and help to prevent ugly things like this from occurring in the future.
Oh wow! Thanks for this information! I never knew SVG had such a feature. 😭 Looks to be extremely useful.
To be honest, I had a vague recollection of that functionality existing, but had never used it until the experiments that led to my comment above. I'm still learning about the syntax — MDN's reference and tutorial docs have been useful so far for that.
It took me a while to figure out what you were talking about. The stars, right?
Apologies, that was indeed why I made that comment. In usual "obvious once you figured it out" fashion, it didn't occur to me that the rationale wouldn't be clear without context. It's exactly as you put it :)
Another possible improvement for the future is to make sure the geometry of the flags works without the circular mask, i.e. with the flags as actual square icons. Of course, this should not sacrifice the aesthetics of the circular form, which should remain the primary representation.
As a follow-up to this, and for clarification, do you think flags that don't require a mask should be given one?
To choose one of the simplest examples, Japan's flag requires just two circle elements and is 165 bytes minified:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<circle cx="256" cy="256" r="256" fill="#eee"/>
<circle cx="256" cy="256" r="112" fill="#d80027"/>
</svg>
But with the clipPath it's 268 bytes 196 bytes minified:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<defs>
<clipPath id="circle-mask">
<circle r="256" cy="256" cx="256"/>
</clipPath>
</defs>
<g clip-path="url(#circle-mask)">
<!--
EDIT: Forgot to use a rect instead of a circle. This reduces the minified size to 196 bytes.
<circle cx="256" cy="256" r="256" fill="#eee"/>
-->
<rect x="0" y="0" width="512" height="512" fill="#eee"/>
<circle cx="256" cy="256" r="112" fill="#d80027"/>
</g>
</svg>
In a perfect world, SVGO would surely compress either of these SVG files down to the same 165 bytes. But I guess it just doesn't support removing redundant clipPaths. Maybe I should look into writing a plugin for SVGO.
Do you think flags that don't require a mask should be given one? To choose one of the simplest examples, Japan's flag requires just two circle elements and is 165 bytes minified (...) But with the clipPath it's 268 bytes minified.
Hmm, that's a good point. I suppose we might have to decide whether we want to prioritize consistency and semantic clarity of the design definitions — as well as the extensibility to square/squircle icons (the latter being a common trend in icons lately), as opposed to maximum compactness of the resulting images.
Perhaps unsurprisingly, I'm tempted to side with the former over the latter, especially since a total of bites in the order of hundreds is still a pretty small payload, and even orders of magnitude more compact than the equivalent PNG even in small resolutions (I tested saving a 32x32 PNG of the Japanese flag image, and even after compressing it with optipng
it is about 4KB).
That said, if we do want to keep the maximum compression possible, maybe we could add some custom logic to the optimization process — for example, storing both a canonical source foo.svg
and a "manually compressed" version foo.compact.svg
without the clippath, and wrap the svgo call in a small loop that uses the shorter file if present, and the regular one otherwise. Honestly, I'm not sure it's worth the extra effort and complexity, though.
A plugin for SVGO sounds pretty dope, actually! It would be a nice solution, but I must point out that it would only work if we hardcode circles for the background shape, which would prevent other icon shapes. (Not saying it's a deal-breaker, but I do find the possibility quite interesting.)
Btw, I haven't tested this, but perhaps we could reuse the mask code from a single file, so that we don't have to repeat the same code in all files? That would also help with the compactness angle.
I found an article saying that something like this is possible for <use>
— not sure what the story is for <clipPath>
, but it sounds plausible in principle. This guy seems to have the same question, but no answer.
Hmm, that's a good point. I suppose we might have to decide whether we want to prioritize consistency and semantic clarity of the design definitions — as well as the extensibility to square/squircle icons (the latter being a common trend in icons lately), as opposed to maximum compactness of the resulting images.
What about if we didn't use clipPath
at all, and instead added an inline style
attribute to the svg
itself (or, say, a g
containing all the elements) specifying a border-radius
? This would greatly simplify the common use cases:
A border radius of 0%
or unspecified.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="border-radius: 0%">
<rect x="0" y="0" width="512" height="512" fill="#eee"/>
<circle cx="256" cy="256" r="112" fill="#d80027"/>
</svg>
A border radius of 10%
, 25%
, 1rem
, etc.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="border-radius: 25%">
<rect x="0" y="0" width="512" height="512" fill="#eee"/>
<circle cx="256" cy="256" r="112" fill="#d80027"/>
</svg>
A border radius of 50%
.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="border-radius: 50%">
<rect x="0" y="0" width="512" height="512" fill="#eee"/>
<circle cx="256" cy="256" r="112" fill="#d80027"/>
</svg>
One drawback to this approach is of course that applying custom masks (such as a squircle) is no longer possible. A squircle can (hypothetically) be made with pure CSS, but it's far more verbose than just using a clipPath
.
Btw, I haven't tested this, but perhaps we could reuse the mask code from a single file, so that we don't have to repeat the same code in all files? That would also help with the compactness angle.
I found an article saying that something like this is possible for
<use>
— not sure what the story is for<clipPath>
, but it sounds plausible in principle. This guy seems to have the same question, but no answer.
In my recent experiments with <use>
, it, unfortunately, appears to have a couple big issues:
<use>
seems to ignore clipPath
s within the linked SVG.The style
approach is ingenious! And yeah, I agree that custom shapes, and a squircle in particular, would not be that much of an advantage once we're able to easily produce squares, rounded squares, and circles.
One caveat of using style
is that we might need to stick with width
and height
instead of viewBox
, because the border radius appears to be applied to the entire viewport's dimensions, not the SVG document's dimensions (which may be a browser bug). Here's how both Firefox and Chrome display the 50% border-radius SVG you listed above:
I tried adding a preserveAspectRatio
attribute but none of its configurations seemed to help. That said, it may be possible with further CSS+SVG tweaking. I played a bit with object-fit
and box-sizing
, but no luck so far...
Oh damn, nice catch! I thought the border-radius
solution had to be too good to be true. Are you aware of any downsides to using width
and height
over viewBox
? And on a related note I realised I never properly responded to your original post on #7 where you suggested this, so for that I apologise.
I tried adding a
preserveAspectRatio
attribute but none of its configurations seemed to help. That said, it may be possible with further CSS+SVG tweaking. I played a bit withobject-fit
andbox-sizing
, but no luck so far...
Thanks for going to such lengths looking for a solution. You are right that this seems to be a browser bug. Strange that it renders incorrectly in both Chrome and Firefox. I get the same results on my end.
Are you aware of any downsides to using
width
andheight
overviewBox
? And on a related note I realised I never properly responded to your original post on #7 where you suggested this, so for that I apologise.
No problem :) I'll respond there.
You are right that this seems to be a browser bug.
Yeah, it's possible that it's been reported; otherwise, we should probably do so to make sure it's addressed eventually. It's not the first edge case I've found while trying to do smarter stuff with SVGs (some of the experiments I did with
In the meantime, we should be fine working with width
and height
, as I suggest in my response at #7.
Hey @waldyrious, sorry it's taken me so long to get back to you. Just to keep you in the loop, @climech's PR #18 (also related: #19) has effectively resolved this issue, i.e., applying a circular mask to all flags. So it's probably safe to close this issue now.
@climech opted for using mask
s over clipPath
s to save a byte on each file. Since border-radius
wasn't used, converting the viewBox
to width
/height
(as per #7, and to avoid the browser bug you mentioned) isn't strictly necessary, but I still agree that it would be a worthwhile thing to do.
@HatScripts, thanks! The new flags show up correctly in GitHub:
But look square on my system:
I have also tried embedding them in my application and they look square too (native app).
Hello, @Shatur95!
Some more details would be appreciated. What system was this observed on, and what kind of rendering library was used to display the images in your application? The files validate against the W3 (SVG 1.1) spec, but I suppose it's up to the libraries to adhere to it 😞
Also, can you save the following 3 snippets as separate files and test if anything changes?
1.svg:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<defs>
<mask id="a">
<circle cx="256" cy="256" r="256" fill="#fff"/>
</mask>
</defs>
<g mask="url(#a)">
<path fill="#0052b4" d="M0 0h144.7l36 254.6-36 257.4H0z"/>
<path fill="#d80027" d="M367.3 0H512v512H367.3l-29.7-257.3z"/>
<path fill="#ffda44" d="M144.7 0h222.6v512H144.7z"/>
<path fill="#d80027" d="M256 354.5V256h66.8v47.3zm-66.8-165.3H256V256h-66.8z"/>
<path fill="#ff9811" d="M289.4 167a22.3 22.3 0 0 0-33.4-19.3 22.1 22.1 0 0 0-11.1-3c-12.3 0-22.3 10-22.3 22.3H167v111.3c0 41.4 32.9 65.4 58.7 77.8a22.1 22.1 0 0 0-3 11.2 22.3 22.3 0 0 0 33.3 19.3 22.1 22.1 0 0 0 11.1 3 22.3 22.3 0 0 0 19.2-33.5c25.8-12.4 58.7-36.4 58.7-77.8V167zm22.3 111.3c0 5.8 0 23.4-27.5 40.9a136.5 136.5 0 0 1-28.2 13.3c-7-2.4-17.8-6.7-28.2-13.3-27.5-17.5-27.5-35.1-27.5-41v-77.9h111.4z"/>
</g>
</svg>
2.svg:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<clipPath id="a">
<circle cx="256" cy="256" r="256"/>
</clipPath>
<g clip-path="url(#a)">
<path fill="#0052b4" d="M0 0h144.7l36 254.6-36 257.4H0z"/>
<path fill="#d80027" d="M367.3 0H512v512H367.3l-29.7-257.3z"/>
<path fill="#ffda44" d="M144.7 0h222.6v512H144.7z"/>
<path fill="#d80027" d="M256 354.5V256h66.8v47.3zm-66.8-165.3H256V256h-66.8z"/>
<path fill="#ff9811" d="M289.4 167a22.3 22.3 0 0 0-33.4-19.3 22.1 22.1 0 0 0-11.1-3c-12.3 0-22.3 10-22.3 22.3H167v111.3c0 41.4 32.9 65.4 58.7 77.8a22.1 22.1 0 0 0-3 11.2 22.3 22.3 0 0 0 33.3 19.3 22.1 22.1 0 0 0 11.1 3 22.3 22.3 0 0 0 19.2-33.5c25.8-12.4 58.7-36.4 58.7-77.8V167zm22.3 111.3c0 5.8 0 23.4-27.5 40.9a136.5 136.5 0 0 1-28.2 13.3c-7-2.4-17.8-6.7-28.2-13.3-27.5-17.5-27.5-35.1-27.5-41v-77.9h111.4z"/>
</g>
</svg>
3.svg:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<defs>
<clipPath id="a">
<circle cx="256" cy="256" r="256"/>
</clipPath>
</defs>
<g clip-path="url(#a)">
<path fill="#0052b4" d="M0 0h144.7l36 254.6-36 257.4H0z"/>
<path fill="#d80027" d="M367.3 0H512v512H367.3l-29.7-257.3z"/>
<path fill="#ffda44" d="M144.7 0h222.6v512H144.7z"/>
<path fill="#d80027" d="M256 354.5V256h66.8v47.3zm-66.8-165.3H256V256h-66.8z"/>
<path fill="#ff9811" d="M289.4 167a22.3 22.3 0 0 0-33.4-19.3 22.1 22.1 0 0 0-11.1-3c-12.3 0-22.3 10-22.3 22.3H167v111.3c0 41.4 32.9 65.4 58.7 77.8a22.1 22.1 0 0 0-3 11.2 22.3 22.3 0 0 0 33.3 19.3 22.1 22.1 0 0 0 11.1 3 22.3 22.3 0 0 0 19.2-33.5c25.8-12.4 58.7-36.4 58.7-77.8V167zm22.3 111.3c0 5.8 0 23.4-27.5 40.9a136.5 136.5 0 0 1-28.2 13.3c-7-2.4-17.8-6.7-28.2-13.3-27.5-17.5-27.5-35.1-27.5-41v-77.9h111.4z"/>
</g>
</svg>
Some more details would be appreciated. What system was this observed on, and what kind of rendering library was used to display the images in your application? The files validate against the W3 (SVG 1.1) spec, but I suppose it's up to the libraries to adhere to it
I use KDE Plasma. My application written in Qt framework that should support SVG 1.1.
Also, can you save the following 3 snippets as separate files and test if anything changes?
Tested, looks same:
Oh, it looks like Qt just not support clipPath
because it not included in SVG Tiny 1.2.
Spec.
@Shatur95 Could you please test the following?
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" style="border-radius: 50%">
<path fill="#0052b4" d="M0 0h144.7l36 254.6-36 257.4H0z"/>
<path fill="#d80027" d="M367.3 0H512v512H367.3l-29.7-257.3z"/>
<path fill="#ffda44" d="M144.7 0h222.6v512H144.7z"/>
<path fill="#d80027" d="M256 354.5V256h66.8v47.3zm-66.8-165.3H256V256h-66.8z"/>
<path fill="#ff9811" d="M289.4 167a22.3 22.3 0 0 0-33.4-19.3 22.1 22.1 0 0 0-11.1-3c-12.3 0-22.3 10-22.3 22.3H167v111.3c0 41.4 32.9 65.4 58.7 77.8a22.1 22.1 0 0 0-3 11.2 22.3 22.3 0 0 0 33.3 19.3 22.1 22.1 0 0 0 11.1 3 22.3 22.3 0 0 0 19.2-33.5c25.8-12.4 58.7-36.4 58.7-77.8V167zm22.3 111.3c0 5.8 0 23.4-27.5 40.9a136.5 136.5 0 0 1-28.2 13.3c-7-2.4-17.8-6.7-28.2-13.3-27.5-17.5-27.5-35.1-27.5-41v-77.9h111.4z"/>
</svg>
In the meantime I'll draft a new release v2.0.0 so that you can revert back to v1.0.0 if you like.
@Shatur95 Could you please test the following?
Same here.
But not problem, there is no issue here. The icons conform to SVG 1.1 and Qt implementation conform to Tiny 1.2, so rounding just not supported by Qt. I will use square icons, they look good too as to me :)
In the meantime I'll draft a new release v2.0.0 so that you can revert back to v1.0.0 if you like.
Yes, it would be nice if you draft a new release.
Same here.
But not problem, there is no issue here. The icons conform to SVG 1.1 and Qt implementation conform to Tiny 1.2, so rounding just not supported by Qt. I will use square icons, they look good too as to me :)
Hmm, that is very strange. So even the border-radius
trick didn't work, despite it conforming to the spec? 😦 I'll have to do some further investigating.
Yes, it would be nice if you draft a new release.
Done :)
Hmm, that is very strange. So even the border-radius trick didn't work, despite it conforming to the spec? frowning I'll have to do some further investigating.
Yes, I double checked it. It looks square both in the system and in the Qt application.
Done :)
Thanks!
Per waldyrious's suggestion on #8, here are some experiments with converting flags to use a circular
<clipPath>
. This helps to avoid having longer than necessary<path d="...">
s, and generally makes the code more human readable. It also seems to result in smaller files when minified with svgo (For these examples I'm using svgo 1.3.2; the latest release at the time of writing).We also trying to avoid the anti-aliasing/color-bleeding issues described here and on the graphic design SE.
Original (302 bytes minified):
New (298 bytes minified):
Original (836 bytes minified):
16px, 24px, 32px, 48px (zoomed to 400%)
New (784 bytes minified):
16px, 24px, 32px, 48px (zoomed to 400%)