My problem is that when i am rotating this svg i counter rotate the text to keep it horizontal. however this makes it look like it is further from the circle when the degree is for example 0 or 180 when its high above or below. but when its 90 its right next to it. i believe this is actually correct since the center of the text is the same distance away. is there a way to fix this?
<svg
height="200"
viewBox="0 0 100 100"
fill="none"
transform="rotate(45, -16, -16)"
>
<circle
cx="30"
cy="30"
r="5"
fill="#FFFFFF"
stroke="#3D3D3D"
stroke-width="1.5"
/>
<g transform="rotate(90, 20, 20)">
<path
d="M15.6528 7.06945L15.4939 7.36025L15.602 7.67352L17.8504 14.1941L2.3372 6.25279L18.9596 1.01652L15.6528 7.06945Z"
fill="white"
stroke="#3D3D3D"
stroke-width="1.5"
/>
</g>
<text
x="30"
y="70"
text-anchor="middle"
font-size="14px"
fill="#3D3D3D"
pointer-events="none"
font-weight={500}
transform="rotate(-45, 30, 70)"
>
some text here
</text>
</svg>
Remember to set your transform-origin, because they help solv this: if you want the text at a fixed distance, put it in a <g> with the circle's center as transform-origin, and a rotation transform, and then give the text element a rotation by the same angle in the opposite direction so that it stays upright.
I am trying to build a javascript experiment of a dynamic chain which follows the mouse cursor.
Therefore I am useing a SVG wwith the following path:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 240">
<defs>
<path id="a" d="M143 158q-31-139 9-99" fill="none" stroke-linecap="round"/>
<mask id="b">
<rect x="0%" y="0%" width="100%" height="100%" fill="#fff"/>
<use href="#a" stroke-width="4" stroke-dasharray="6 14" stroke-dashoffset="7" stroke="#000"/>
</mask>
</defs>
<use href="#a" stroke-width="8" stroke-dasharray="6 14" stroke-dashoffset="7" stroke="#333" stroke-opacity=".8" mask="url(#b)"/>
<use href="#a" stroke-width="2" stroke-dasharray="12 8" stroke="#333" stroke-opacity=".8"/>
</svg>
Unfortunately the chain is cropped:
Why is this happening?
You can see the full experiment here (desktop only).
Long story short: A mask works within an object's bounding box, but the bounding box of any element doesn't include the stroke width.
Therefore, the default mask adds 10% padding around the bounding box with its x, y, width, height and maskUnits attributes. This works in most cases, but fails when an element is slim and almost horizontal or vertical.
See the below image: The blue rectangle is your path's bounding box, and the green is the area where the mask does its job. You can see that some of the path sticks out to the left and right.
So you must change the mask attributes to work for you. For example, make it cover the whole image:
<mask id="b" maskUnits="userSpaceOnUse" x="0" y="0" width="100%" height="100%">
I fixed it by adding maskUnits="userSpaceOnUse"
https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/maskUnits
(changed the viewBox to better display it in an SO snippet)
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 40 300 240">
<defs>
<path id="a" d="M143 158q-31-139 9-99" fill="none" stroke-linecap="round"/>
<mask id="b" maskUnits="userSpaceOnUse">
<rect x="0%" y="0%" width="100%" height="100%" fill="#fff"/>
<use href="#a" stroke-width="4" stroke-dasharray="6 14" stroke-dashoffset="7" stroke="#000"/>
</mask>
</defs>
<use href="#a" stroke-width="8" stroke-dasharray="6 14" stroke-dashoffset="7" stroke="#333" stroke-opacity=".8" mask="url(#b)"/>
<use href="#a" stroke-width="2" stroke-dasharray="12 8" stroke="#333" stroke-opacity=".8"/>
</svg>
I'm trying to achieve a svg path fill animation like the gif below, tried clipPath but no luck, any help, idea how to achieve that kind of animation (gif image below)? tried gsap or any svg animation library but none of them cater my needs.
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 263.42 187.39">
<defs>
<clipPath id="clip1">
<path class="cls-1" fill="#c2a01e" d="M1889.36,77.11v3.47L1853,111.81q-10.32,2.86-21.35,7.09l-5.2,2c-8.68-2.49-17.65-5-26.73-7.37l90.6-109.08L1891.48,3V0H1737.59V4.49h0V51.65h2.23V48.17l50.94-43.68h44l-82.46,99.29-4,4.76c25.65,2.89,53.48,10.52,78.8,17.75l5.2-2c22.16-8.58,42.53-12.81,59.26-13.69V77.11Z" transform="translate(-1677.9)"/>
</clipPath>
<clipPath id="clip2">
<path class="cls-1" fill="#c2a01e" d="M1932.74,126.17c-13.82-10.71-52.11-11.68-98.2,6.14l-5.19,2c-53.78-15.35-118.75-32.47-150.47-2.43a3.15,3.15,0,0,0-1,2.51v0a2.32,2.32,0,0,0,3.67,1.69c24.48-17.77,57.75-16.76,132.38,4.48l-1.75.75c-8.06,3.49-21,9.57-34.68,15.79-20.34-9.54-48.19-17.44-66.13-6.65-26.21,15.75-19.06,51.2,38.47,30.66,8.47-3,48.84-21.17,83-35,2.93.89,5.92,1.79,9,2.73,16.51,5.06,38.3,8.93,57.55,10.6C1945.4,163.47,1948.48,138.37,1932.74,126.17ZM1712,157.37c15.48-11.94,39.91-5.59,58.83,2.82-6.94,3.12-13.89,6.17-20.3,8.84C1699.64,190.21,1697.33,168.67,1712,157.37Zm149.51-14c-4.11-1.09-8.38-2.27-12.78-3.51,9.34-3.58,17.56-6.5,23.64-8.22,29.63-8.41,58.5-3.24,61,6.19C1936.54,149.74,1912.3,156.79,1861.48,143.37Z" transform="translate(-1677.9)"/>
</clipPath>
</defs>
<path class="cls-1" fill="#FFF" clip-path="url(#clip1)" transform="translate(-1677.9)"/>
<path class="cls-1" fill="#FFF" clip-path="url(#clip2)" transform="translate(-1677.9)"/>
</svg>
The general idea is this: you draw the path you clip it as you need and next you animate the stroke-dashoffset of the clipped path. You make sure that the animation for the next path begins after the previous one ends: begin="a.end + .5s".
However in this case you will need to rewrite the paths. For example in the case of the lace I would use 2 paths and 2 different clip-paths or even 3. Otherwise you get an unaesthetic bleeding effect where the path overlaps.
<svg viewBox="0 0 500 500">
<defs>
<clipPath id="theZ">
<path d="M401.374,215.788v6.586l-69.015,59.278c-13.06,3.619-26.567,8.105-40.525,13.457l-9.869,3.797
c-16.477-4.727-33.502-9.49-50.737-13.989L403.196,77.872l2.202-2.752v-5.694h-292.1v8.522l0,0v89.515h4.232v-6.605l96.69-82.91
h83.517L141.219,266.41l-7.592,9.035c48.687,5.486,101.511,19.969,149.57,33.691l9.871-3.796
c42.062-16.286,80.727-24.315,112.481-25.985v-63.567H401.374z"/>
</clipPath>
<clipPath id="theLace">
<path d="M483.714,308.909c-26.231-20.329-98.91-22.17-186.394,11.654l-9.852,3.796
c-102.081-29.135-225.4-61.631-285.608-4.611c-1.316,1.222-2.013,2.971-1.898,4.764l0,0c0.201,2.424,2.328,4.227,4.751,4.025
c0.799-0.065,1.565-0.348,2.215-0.817c46.466-33.729,109.616-31.812,251.271,8.503l-3.321,1.424
c-15.298,6.625-39.86,18.165-65.826,29.971c-38.607-18.107-91.47-33.103-125.522-12.621c-49.75,29.895-36.178,97.183,73.02,58.195
c16.077-5.694,92.704-40.184,157.544-66.434c5.561,1.689,11.236,3.397,17.082,5.182c31.338,9.604,72.698,16.95,109.236,20.12C507.744,379.709,513.591,332.066,483.714,308.909z M64.726,368.131c29.383-22.664,75.753-10.611,111.666,5.352
c-13.173,5.923-26.365,11.712-38.531,16.779C41.265,430.464,36.88,389.579,64.726,368.131z M348.512,341.557
c-7.801-2.068-15.906-4.309-24.258-6.662c17.729-6.795,33.331-12.338,44.871-15.603c56.241-15.963,111.04-6.149,115.785,11.749
c6.017,22.607-39.993,35.988-136.455,10.516H348.512z"/>
</clipPath>
</defs>
<path fill="none" stroke="#C2A01E" clip-path="url(#theLace)" stroke-width="25" stroke-dasharray="1206" stroke-dashoffset="1206" d="M-0.038,324.512c0,0,49.523-27.071,69.538-27.012 c23.35,0.069,74.84,1.785,121.646,11.637C242.583,319.965,288.5,338.5,288.5,338.5s134.99,34.916,169,27s36.999-16.612,35-33.425
s-44.115-67.954-226,12.425s-172,69-172,69s-52.867,8.077-52-16s7.277-37.219,43.205-46.984c38.84-10.557,110.17,30.986,110.17,30.986" >
<animate id="a"
attributeName="stroke-dashoffset"
attributeType="XML"
from="1206" to="0"
dur="1s"
fill="freeze"
repeatCount="1"/>
</path>
<path fill="none" stroke="#C2A01E" d="M259.424,309.137V64.778" clip-path="url(#theZ)" stroke-width="300" stroke-dasharray="244.36" stroke-dashoffset="244.36">
<animate
attributeName="stroke-dashoffset"
attributeType="XML"
begin="a.end + .5s"
from="244.36" to="0"
dur="1s"
fill="freeze"
repeatCount="1"
/>
</path>
</svg>
I know how to use SVG masks to completely "cut out" the mask in another shape, if the mask is monochrome.
How can I use a multicolored SVG definition X as the mask so that the outer shape of X defines the "hole" to be cut out?
Here are three images that illustrate what I am trying to achieve:
svg #1 to be used as mask
svg #2 on which the outer shape of #1 should be used as a cut-out
result
Creating a white-filled version of the shape as #enxaneta proposed is not applicable to my problem, as I have many "complicated" external SVG definitions, and I don't want to change every single one of them.
Is there another, simpler way to achieve what I want?
You need to define your paths with no fill. Then you use your paths for the mask and fill them with white. To draw the image you fill those paths with the colors of your choice.
svg{border:1px solid; width:49vw}
svg:nth-child(2){background:red;}
mask use{fill:white;}
<svg viewBox="0 0 100 50">
<defs>
<polygon id="a" points="30,5 70,20 75,40 20,20" />
<circle id="b" cx="50" cy="25" r="15" />
<circle id="c" cx="60" cy="35" r="10" />
<mask id="m">
<use xlink:href="#a"/>
<use xlink:href="#b"/>
<use xlink:href="#c"/>
</mask>
</defs>
<g id="complexShape">
<use xlink:href="#a" fill="lightblue" />
<use xlink:href="#b" fill="gold"/>
<use xlink:href="#c" fill="red"/>
</g>
</svg>
<svg viewBox="0 0 100 50">
<rect width="100" height="50" style="mask: url(#m)" />
</svg>
The colour of a mask determines the final opacity of the masked object at that point. The R, G, B, and A components of the mask colour are combined in a formula to determine a luminance value that is used to set the final transparency that the mask will be a that point. So, for example, if the mask is red, the final masked result will be semi transparent.
There is no way to make a coloured object be a solid (not translucent) mask. Only full white will do that.
Update
Assuming you have an external SVG image that looks like the following:
<svg viewBox="0 0 100 50">
<polygon id="a" points="30,5 70,20 75,40 20,20" fill="lightblue"/>
<circle id="b" cx="50" cy="25" r="15" fill="gold"/>
<circle id="c" cx="60" cy="35" r="10" fill="red" stroke="blue" stroke-width="4"/>
</svg>
You can turn this into a "mask" version by adding three lines to the start of your SVG.
<svg viewBox="0 0 100 50">
<filter id="blacken"><feFlood flood-color="black"/><feComposite operator="in" in2="SourceGraphic"/></filter>
<style>svg :not(#maskbg) { filter: url(#blacken); }</style>
<rect id="maskbg" x="-100%" y="-100%" width="300%" height="300%" fill="white"/>
<polygon id="a" points="30,5 70,20 75,40 20,20" fill="lightblue"/>
<circle id="b" cx="50" cy="25" r="15" fill="gold"/>
<circle id="c" cx="60" cy="35" r="10" fill="red" stroke="blue" stroke-width="4"/>
</svg>
This is something that could easily be scripted. This method should work for almost all SVGs.
Once you have all the mask variants built, you can apply them using mask-image.
https://developer.mozilla.org/en-US/docs/Web/CSS/mask-image