CSS transform on SVG text element not working in Safari - svg

Trying to position a battery indicator within a parent SVG.The SVG <svg viewBox="0 0 24 24"> element has a path for the battery and a text element showing the percentage.Its being positioned with a couple of css transforms and text attributes.The text is correctly positioned when opening in chrome/firefox but goes offshoot in safari.
<text
text-anchor="middle"
dominant-baseline="middle"
style="transform:translate(50%,98%) scale(.2);
font:700 13px sans-serif;fill:#deba78"
>24.2%</text>
Codepen https://codepen.io/niwsa/pen/rNNBKEg?editors=1000

Wrap the <text> element into a <g> element and apply the CSS transform on that, it happens to work currently even in Safari. This way, it's still CSS, so CSS transitions, animations, custom properties, selector based styling, continuous font scaling etc. work.

Instead of translating the text you may give the text some attributes like x and y. Instead of scaling the text you may change the font-size.
For the path you may choose svg transforms like this:
body {
width: 200px;
}
.bg {
fill: #beeb1b;
}
.cap {
fill: #aaa8a9;
}
.trunk {
fill: #231f20;
}
<svg xmlns="http://www.w3.org/2000/svg" tabindex="0" viewBox="0 0 351.33 722" aria-labelledby="bottletitle bottledesc" role="img">
<title id="bottletitle">
Bottle
</title>
<desc id="bottledesc">
Bottle with battery indicator inside
</desc>
<g data-name="Layer 4" class="bg">
<rect width="351.33" height="722" rx="23.33" ry="23.33"/>
</g>
<g data-name="Layer 3" class="cap">
<rect x="146.81" y="60.9" width="57.71" height="73.67"/>
</g>
<g data-name="Layer 2" class="trunk">
<path d="M173,153.25h57.75V223s1.08,25.33,30.41,56c27.06,28.29,35.34,60.33,35,71.33-.21,7,0,324.67,0,324.67s-3.33,7.33-9.33,7.33H117.12s-9-.33-9-6.66v-325s-.33-33,30.34-67c0,0,34.33-32.67,34.33-60.67S173.33,153.63,173,153.25Z" transform="translate(-26.46 -18.67)"/>
</g>
<svg viewBox="0 0 24 24">
<defs>
<linearGradient id="lg" x1="0.5" y1="1" x2="0.5" y2="0">
<stop offset="0%" stop-opacity="1" stop-color="#2ecc71"/>
<stop offset="24.2%" stop-opacity="1" stop-color="#2ecc71"/>
<stop offset="24.2%" stop-opacity="0" stop-color="#2ecc71"/>
<stop offset="100%" stop-opacity="0" stop-color="#2ecc71"/>
<animate attributeName="y2" from="1" to="0" dur="500ms" repeatCount="2s" fill="freeze"/>
</linearGradient>
</defs>
<path fill="url(#lg)" d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33v15.33C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V5.33C17 4.6 16.4 4 15.67 4z" stroke="#fff" transform="scale(.5,.5) rotate(90,12 12) translate(45,-12)"/>
<text text-anchor="middle" dominant-baseline="middle" style="font:700 2.5px sans-serif;fill:#deba78" x="12" y="23">
24.2%
</text>
</svg>
</svg>

Related

How to have image inside svg background with radialGradient

I have svg element with some image inside it. I tried clipPath, but the results were not as expected
here's the code
<svg width="5396" height="829" enable-background="new" version="1.1" viewBox="0 0 1427.7 219.34" xmlns="http://www.w3.org/2000/svg">
<defs>
<radialGradient id="headera" cx="-334.2" cy="-79.465" r="713.85" gradientTransform="matrix(.35196 -.0011319 .0010719 .334 127.5 106)" gradientUnits="userSpaceOnUse">
<stop stop-color="#9d6173" offset="0"/>
<stop stop-color="#594b4f" offset="1"/>
</radialGradient>
<clipPath id="dodol">
<rect x="36.286" y="108.01" width="0" height="0" fill-opacity="0" stroke="#453030" stroke-linecap="round" stroke-linejoin="round" stroke-width=".052917"/>
<path d="m-535.38 77.813v6.0476c2.9078 113.15 92.911 174.65 152.71 167.82 169.93-23.912 248.91 44.042 387.75 45.474 56.339-1.17 204.04-22.034 204.04-22.034 78.053-5.4634 100.32 22.158 142.53 22.034 181.38-0.15553 205.7-119.12 449.25-127.14 104.33-1.7411 90.846-92.203 90.846-92.203z" fill="url(#headera)"/>
</clipPath>
</defs>
<g transform="matrix(1 0 0 .99999 535.38 -77.81)">
<rect x="36.286" y="108.01" width="0" height="0" fill-opacity="0" stroke="#453030" stroke-linecap="round" stroke-linejoin="round" stroke-width=".052917"/>
<path d="m-535.38 77.813v6.0476c2.9078 113.15 92.911 174.65 152.71 167.82 169.93-23.912 248.91 44.042 387.75 45.474 56.339-1.17 204.04-22.034 204.04-22.034 78.053-5.4634 100.32 22.158 142.53 22.034 181.38-0.15553 205.7-119.12 449.25-127.14 104.33-1.7411 90.846-92.203 90.846-92.203z" fill="url(#headera)"/>
</g>
<image clip-path="url(#dodol)" preserveAspectRatio="xMaxYMid meet" width="643px" height="50%" x="250" y="20" xlink:href="https://mdn.mozillademos.org/files/6457/mdn_logo_only_color.png"/>
</svg>
the problem with the code is I can't resize the image as it should and some of the outgoing image that exceeds the svg element remains visible
what i want to achive is
the svg shape appears as the background for the image inside it
the image appereance clipped by svg shape
I can adjust the position and size of the image
thanks
I'm not very sure this is what you need. Please take a look.
The main idea is that you have to put the image inside the transformed group.
Also I've removed some useless elements (rects with width and height 0). Also instead of using the same path twice I'm reusing it with <use>
Yet another thing: I've changed the image size so that it keeps it's width/height ratio. It may not be what you want.
<svg viewBox="0 0 1427.7 219.34" xmlns="http://www.w3.org/2000/svg">
<defs>
<radialGradient id="headera" cx="-334.2" cy="-79.465" r="713.85" gradientTransform="matrix(.35196 -.0011319 .0010719 .334 127.5 106)" gradientUnits="userSpaceOnUse">
<stop stop-color="#9d6173" offset="0"/>
<stop stop-color="#594b4f" offset="1"/>
</radialGradient>
<clipPath id="dodol">
<path id="thePath" d="m-535.38 77.813v6.0476c2.9078 113.15 92.911 174.65 152.71 167.82 169.93-23.912 248.91 44.042 387.75 45.474 56.339-1.17 204.04-22.034 204.04-22.034 78.053-5.4634 100.32 22.158 142.53 22.034 181.38-0.15553 205.7-119.12 449.25-127.14 104.33-1.7411 90.846-92.203 90.846-92.203z" />
</clipPath>
</defs>
<g transform="matrix(1 0 0 .99999 535.38 -77.81)">
<use xlink:href="#thePath" fill="url(#headera)"/>
<image clip-path="url(#dodol)" width="600" height="529" x="250" y="20" xlink:href="https://mdn.mozillademos.org/files/6457/mdn_logo_only_color.png"/>
</g>
</svg>

Fading edges of line/rect

I have an svg <g> in which I have a line (can be rect too.. have tried both). This inner element has a fill/stroke that can rotate from 8 different colors or patterns. The shape is 5px height, with a dynamic width, and I should support fading left, right or both edges.
I would like to achieve this without creating extra elements on the svg and some how apply a mask/gradient but it seems I cannot get it to work.
Since the shape's X and Y, color, and width can change, I have not found a consistent way to make this happen without creating new elements with a bunch of color combinations. Does anyone have an idea on how to achieve this in the most simple and scalable way?
UPDATE
After Paul suggestion i created a fiddle to represent some how the scenario i have:
https://jsfiddle.net/0tnc9mq5/
So multiplelines/rects in same svg with different colors, positions and dimensions. What would be the way to apply some of those fades to some of those elements? How would i implement Paul's suggestion? Is there any other approach?
Thanks
Here is one way to do it, using masks to produce the fading ends. The fades are fixed in this example to be 10% of the length of the line.
We create a shared SVG that contains <symbol> elements for each of the three variants you need. That only needs to appear once in your HTML.
Then anywhere you want one of the separators, include the mini SVG corresponding to the separator you desire. You can set the width, height and colour of the separator using CSS.
svg.sep {
width: 500px;
height: 5px;
}
.one {
color: red;
}
.two {
color: green;
}
.three {
color: blue;
}
<svg width="0" height="0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="gleft">
<stop offset="0%" stop-color="black"/>
<stop offset="10%" stop-color="white"/>
</linearGradient>
<linearGradient id="gright">
<stop offset="90%" stop-color="white"/>
<stop offset="100%" stop-color="black"/>
</linearGradient>
<linearGradient id="gboth">
<stop offset="0%" stop-color="black"/>
<stop offset="10%" stop-color="white"/>
<stop offset="90%" stop-color="white"/>
<stop offset="100%" stop-color="black"/>
</linearGradient>
<mask id="mleft">
<rect width="1" height="1" fill="url(#gleft)"/>
</mask>
<mask id="mboth">
<rect width="1" height="1" fill="url(#gboth)"/>
</mask>
<mask id="mright">
<rect width="1" height="1" fill="url(#gright)"/>
</mask>
<symbol id="left" viewBox="0 0 1 1" preserveAspectRatio="none">
<rect width="1" height="1" fill="currentColor" mask="url(#mleft)"/>
</symbol>
<symbol id="both" viewBox="0 0 1 1" preserveAspectRatio="none">
<rect width="1" height="1" fill="currentColor" mask="url(#mboth)"/>
</symbol>
<symbol id="right" viewBox="0 0 1 1" preserveAspectRatio="none">
<rect width="1" height="1" fill="currentColor" mask="url(#mright)"/>
</symbol>
</defs>
</svg>
<p> Lorem ipsum dolor sit</p>
<svg viewBox="0 0 1 1" preserveAspectRatio="none" class="sep one">
<use xlink:href="#left"/>
</svg>
<p> Lorem ipsum dolor sit</p>
<svg viewBox="0 0 1 1" preserveAspectRatio="none" class="sep two">
<use xlink:href="#right"/>
</svg>
<p> Lorem ipsum dolor sit</p>
<svg viewBox="0 0 1 1" preserveAspectRatio="none" class="sep three">
<use xlink:href="#both"/>
</svg>

Chrome Breaks SVG Pattern Fills

View this SVG in the latest version of Chrome and Safari.
In Safari, you will see a colorful fill. In Chrome, the fill doesn't render.
Any ideas on how I can fix this bug? It seems to be a new bug from the latest Chrome release (https://code.google.com/p/chromium/issues/detail?id=452235)
I removed the d coordinates for to be concise
<?xml version="1.0" standalone="no"?>
<svg width="2000" height="2000" xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<linearGradient id="Gradient1">
<stop offset="5%" stop-color="white"/>
<stop offset="95%" stop-color="blue"/>
</linearGradient>
<linearGradient id="Gradient2" x1="0" x2="0" y1="0" y2="1">
<stop offset="5%" stop-color="red"/>
<stop offset="95%" stop-color="orange"/>
</linearGradient>
<pattern id="Pattern" x="0" y="0" width=".25" height=".25">
<rect x="0" y="0" width="50" height="50" fill="skyblue"/>
<rect x="0" y="0" width="25" height="25" fill="url(#Gradient2)"/>
<circle cx="25" cy="25" r="20" fill="url(#Gradient1)" fill-opacity="0.5"/>
</pattern>
</defs>
<g typename="Graphic" artname="PRAYING HANDS" min_size_x="0" min_size_y="0" size_locked="false" transform="matrix(1 0 0 1 100 100)"><g artname="PRAYING HANDS" data-artwork-id="1041" transform="">
<title>Praying Hands</title>
<g transform="matrix(1 0 0 -1 -2401 2972)" style="text-rendering:optimizeLegibility;shape-rendering:default;image-rendering:optimizeQuality" artname="PRAYING HANDS" data-artwork-id="1041">
<path fill="url(#Pattern)" d="..." opacity="1"></path>
</g>
</g>
<!-- outline -->
<g transform="matrix(1 0 0 -1 -2400.16 2971.63)" style="text-rendering:optimizeLegibility;
shape-rendering:default;
image-rendering:optimizeQuality" artname="PRAYING HANDS" data-artwork-id="1041">
<desc>Untitled</desc>
<path style="fill:purple;stroke:#000000;fill-rule: evenodd;stroke-width:0.000001" d="..." fill="none" opacity=""></path>
</g>
</g>
</svg>
This is a known bug in Chrome.
https://code.google.com/p/chromium/issues/detail?id=447707
Looks like it will be fixed in Chrome 41.
This is somehow caused by transforming the to-be-filled things or their parents, try without it. For my case, a "scale(-1,1)" caused the issue. I worked around this by doing the transformation manually, which is easy for scale(-1,1). Translates & rotates where no issues.
Cheers,
Kay

How to make an SVG "line" with a border around it?

I have a little svg widget whose purpose is to display a list of angles (see image).
Right now, the angles are line elements, which only have a stroke and no fill. But now I'd like to have an "inside fill" color and a "stroke/border" around it. I'm guessing the line element can't handle this, so what should I use instead?
Notice that the line-endcap of the line's stroke is rounded. I'd like to maintain this effect in the solution.
<svg height="160" version="1.1" viewBox="-0.6 -0.6 1.2 1.2" width="160" xmlns="http://www.w3.org/2000/svg">
<g>
<g>
<circle class="sensorShape" cx="0" cy="0" fill="#FFF" r="0.4" stroke="black" stroke-width="0.015"/>
<line stroke="black" stroke-width="0.015" x1="0" x2="0" y1="-0.4" y2="0.4"/>
<line stroke="black" stroke-width="0.015" x1="-0.4" x2="0.4" y1="0" y2="0"/>
</g>
<g class="lsNorthAngleHandsContainer">
<line data-angle="348" stroke="red" stroke-linecap="round" stroke-width="0.04" transform="rotate(348)" x1="0" x2="0" y1="0" y2="-0.4"/>
<text font-size="0.08" transform="translate(-0.02316467632710395,-0.45125904029352226)">
348
</text>
</g>
</g>
</svg>
Add a second line, with same coordinates but thinner line width:
<g class="lsNorthAngleHandsContainer">
<line data-angle="348" stroke="red" stroke-linecap="round" stroke-width="0.04" transform="rotate(348)" x1="0" x2="0" y1="0" y2="-0.4"/>
<line data-angle="348" stroke="yellow" stroke-linecap="round" stroke-width="0.025" transform="rotate(348)" x1="0" x2="0" y1="0" y2="-0.4"/>
I found elegant solution inspired by illustration to W3C article about filling and stroking. Basically, you move path to definitions and use this definition to draw two elements:
<svg width="200" height="200" viewBox="0 0 100 100">
<defs>
<line id="line1" x1="25" x2="75" y1="25" y2="75"/>
</defs>
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#line1" class="stroke"></use>
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#line1" class="line"></use>
</svg>
By using <defs> and <use> you can change only path element to update both lines. JSFiddle demo
It looks like your line is opaque, so you can just draw a thin line with the "inside" color on top of the thicker line with the "outside" color.
You could use a rect with rounded corners, but the math changes a bit:
<rect data-angle="348" stroke="red" stroke-linecap="round" stroke-width="0.02" fill="#FF0" transform="rotate(348, 0, 0)" x="-0.02" y="-0.4" width=".06" height=".4" rx=".03" ry=".03"/>
http://jsfiddle.net/RNAuP/
You can also do it with a path, even though it's tricky around the round bits:
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!-- I often use entities to be able to change lot of numbers at once in static SVG, also kind of makes the paths more readable.
Obvisouly, if you're generating the path you can use the same variables in code to append to d -->
<!ENTITY handFill "0.01">
<!ENTITY handFill2 "0.02"><!-- Should be 2 * handFill to be centered -->
<!ENTITY handStroke "0.005"><!-- Should be less than handFill2 to not hide fill -->
]>
<svg height="160" version="1.1" viewBox="-0.6 -0.6 1.2 1.2" width="160" xmlns="http://www.w3.org/2000/svg">
<g>
<g>
<circle class="sensorShape" cx="0" cy="0" fill="#FFF" r="0.4" stroke="black" stroke-width="0.015"/>
<line stroke="black" stroke-width="0.015" x1="0" x2="0" y1="-0.4" y2="0.4"/>
<line stroke="black" stroke-width="0.015" x1="-0.4" x2="0.4" y1="0" y2="0"/>
</g>
<g class="lsNorthAngleHandsContainer">
<path d="
M -&handFill;,0 l0,-0.4
a &handFill;,&handFill; 0 0,1 &handFill2;,0
l 0,0.4
a &handFill;,&handFill; 0 0,1 -&handFill2;,0
" stroke="red" stroke-linecap="round" stroke-width="&handStroke;" fill="yellow" transform="rotate(348)" />
<text font-size="0.08" transform="translate(-0.02316467632710395,-0.45125904029352226)">
348
</text>
</g>
</g>
</svg>

SVG angular gradient

Is there a way to do an 'angular gradient' in SVG?
(I don't know the official term -- it's the kind of gradient you see in color-pickers, where it varies by angle.)
SVG seems to support only linear and radial gradients, but I'm thinking there might be some way to use a transform to simulate what I want.
thanks!
...10 years later...
CSS now supports conical gradients, although browser support is mixed at the time of writing this.
You could apply a <clipPath /> to a <foreignObject /> whose contents use a CSS conical gradient to achieve the desired effect.
https://codepen.io/eastonium/pen/abOpdEm
There's no standard support to do angular (conical) gradients.
But see http://wiki.inkscape.org/wiki/index.php/Advanced_Gradients#Conical_gradient for some approximation methods (source code not included, though). Examples on that link do not work.
Here is how to do it using patterns: https://jsfiddle.net/prozoroff/8eodzrke/
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="800" width="800">
<defs>
<linearGradient id="Gradient1" gradientTransform="rotate(90)">
<stop offset="0%" stop-color="#ff0000"/>
<stop offset="100%" stop-color="#00ff00"/>
</linearGradient>
<linearGradient id="Gradient2" gradientTransform="rotate(90)">
<stop offset="0%" stop-color="#0000ff"/>
<stop offset="100%" stop-color="#00ff00"/>
</linearGradient>
<pattern id="Pattern" x="0" y="0" width="600" height="600" patternUnits="userSpaceOnUse">
<g transform="rotate(0, 300, 300)">
<rect shape-rendering="crispEdges" x="0" y="0" width="300" height="600" fill="url(#Gradient1)"/>
<rect shape-rendering="crispEdges" x="300" y="0" width="300" height="600" fill="url(#Gradient2)"/>
</g>
</pattern>
</defs>
<path id='arc5' style="stroke: url(#Pattern);" fill='transparent' stroke-width='60' d='M 364 58 A 250 250 0 1 1 235 58'/>
</svg>
In my answer to this similar question, I used six linear gradients to approximate a conical gradient. If you are only needing the gradient for the stroke/perimeter of a circle, rather than the fill, then it should be a good enough approximation.
svg multiple color on circle stroke
Here is a possible vector conical gradient, but only VML (+IE) can do it...:
http://midiwebconcept.free.fr/Demos/degradeconique.htm
If you dig into this page, you'll find code that approximates a conic gradient in SVG by drawing it as a series of 1 degree arcs.
Add a patern with 100% width and height so its just a one repetition pattern
<div style="width:100px">
<svg viewBox="0 0 35 35" style="transform: scale(1) rotate(-90deg)">
<defs>
<pattern
id="p1"
patternUnits="userSpaceOnUse"
width="100%"
height="100%"
patternTransform="rotate(90)"
>
<image href="https://blogs.igalia.com/dpino/files/2020/06/conic-gradient.png" width="36" height="36" />
</pattern>
</defs>
<g>
<circle
cx="50%"
cy="50%"
stroke-width="2"
r="15.915"
stroke-dasharray="89, 100"
stroke="url(#p1)"
fill="none"
/>
</g>
</svg>
</div>
http://en.wikipedia.org/wiki/File:Blended_colour_wheel.svg uses an innovative technique to approximate it.

Resources