I have a path which is similar to circle. The task is to fill the path with a color with animation. The fill animation should be in circle manner and not from top to bottom or bottom to top.
my svg code is :
<svg id="svg_circle" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" viewBox = '0 0 450 400'>
<g class="svg_circle" transform = "translate(0,0)">
<path class="path" stroke="#F0F0F0" fill="#fff" stroke-width="1" opacity="1" d="m-0.25,238.11061l88.59461,-82.21665l87.56445,86.40604l-33.99561,0.52367c2.72107,17.03346 46.55824,67.96739 105.37633,63.99055c70.95792,-4.79765 101.17847,-64.19902 103.74816,-97.50561c7.13262,-92.44812 -81.66575,-121.29229 -115.80064,-115.90062c-119.13463,18.8176 -96.38311,112.29843 -96.38311,112.29843l-50.54082,-49.76652l-46.48973,43.1249c-12.30406,-104.7234 83.23188,-194.53124 191.25985,-198.17803c97.87838,-3.30416 202.62703,53.17701 213.76024,178.57248c16.06879,180.98587 -165.14043,220.64431 -208.6094,218.37164c-143.15297,-7.48456 -189.38275,-115.91408 -199.33787,-158.14925l-39.14646,-1.57102z" id="svg_1">
<animate id="project_anim1" attributeName="fill" from="#fff" to="#4DAF4C" begin="1s" dur="1s" fill="freeze" repeatCount="1"></animate>
</path>
</g>
</svg>
The common "dasharray" line drawing technique would work for you, in combination with using the arrow shape as a mask.
This technique is described in this SO question
However, since the head of your arrow shape overlaps the tail, to get a "perfect" result, you may have to divide the sweep into two parts. You would draw the tail part with a tail mask, and then the second half of the shape (including the arrow head) with a separate mask.
Here's a rough imperfect version showing the technique.
<svg id="svg_circle" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" viewBox = '0 0 450 400'>
<defs>
<path id="arrow" stroke="#F0F0F0" fill="none" stroke-width="1" opacity="1" d="m-0.25,238.11061l88.59461,-82.21665l87.56445,86.40604l-33.99561,0.52367c2.72107,17.03346 46.55824,67.96739 105.37633,63.99055c70.95792,-4.79765 101.17847,-64.19902 103.74816,-97.50561c7.13262,-92.44812 -81.66575,-121.29229 -115.80064,-115.90062c-119.13463,18.8176 -96.38311,112.29843 -96.38311,112.29843l-50.54082,-49.76652l-46.48973,43.1249c-12.30406,-104.7234 83.23188,-194.53124 191.25985,-198.17803c97.87838,-3.30416 202.62703,53.17701 213.76024,178.57248c16.06879,180.98587 -165.14043,220.64431 -208.6094,218.37164c-143.15297,-7.48456 -189.38275,-115.91408 -199.33787,-158.14925l-39.14646,-1.57102z" id="svg_1"/>
<clipPath id="arrow-clip" clipPathUnits="userSpaceOnUse">
<use xlink:href="#arrow"/>
</clipPath>
</defs>
<g clip-path="url(#arrow-clip)">
<circle cx="244" cy="200" r="158" transform="rotate(-164,244,200)"
fill="none" stroke="#4DAF4C" stroke-width="175"
stroke-dasharray="993 993">
<animate attributeName="stroke-dashoffset" from="993" to="0" begin="1s" dur="1s" fill="freeze" repeatCount="1"/>
</circle>
</g>
<use xlink:href="#arrow"/>
</svg>
Here we are animating a really thick green line, but using your shape as a clipping path so it conforms to the shape you want.
But as I mentioned, the section where the head overlaps the end of the tail is not perfect, so you may need to divide the animation into two parts as described above.
Related
I'm absolutely beginner with SVG, and I need to put text around this moon...
I tried to make text around a path and could not get the right sizes and match it with the moon.
<svg viewBox="-6 -6 30 40">
<defs>
<mask id="earth">
<rect fill="white" x="-5" y="-5" width="10" height="10"></rect>
<circle fill="black" cx="3.141592654" r="5" />
</mask>
</defs>
<circle r="5" fill="currentColor" mask="url(#earth)" transform="rotate(-25)"/>
</svg>
Draw the moon with a path. (and edit in: https://yqnn.github.io/svg-path-editor/)
The path is drawn counter-clockwise, if you want to draw the innermoon text like your design, it is easier to add a 2nd path drawn clockwise.
Setting pathLength helps in positioning with startoffset
Look up all attributes you don't know in the docs.
<svg viewBox="0 0 80 60">
<rect width="100%" height="100%" fill="skyblue"/>
<path id="Moon" pathLength="10" d="m16 2a12 12 0 1018 13 1 1 0 01-18-13z"/>
<text>
<textPath href="#Moon"
startoffset="1" text-anchor="left" dominant-baseline="hanging"
fill="blue" font-size="3px">Outside moon</textPath>
</text>
<text>
<textPath href="#Moon"
startoffset="6" text-anchor="right" dominant-baseline="hanging"
fill="rebeccapurple" font-size="4">Inside moon</textPath>
</text>
</svg>
So, if you check the fiddle, (https://jsfiddle.net/xXnikosXx/6jx2rpsf/3/)
I have an SVG image, with a couple of objects, and a mask. It's masking the text (colorful lines) which are hidden below it, and the plan was to animate their top position to make the text from below appear and the text from the top to disappear, to make a text scrolling effect. The text without the mask is shown in the image below, in case it helps.
here
I thought the text would appear when the animation occurs, but it doesnt (if you remove the mask, the text is shown and animated properly but they layers are wrong so the text appears above everything else)
I cant figure out a way to make the text animate properly using the mask, is there something i can use instead of the mask to get the same result, but without the text issue?
relevant code:
<mask id="mask0" mask-type="alpha" maskUnits="userSpaceOnUse" x="354" y="174" width="733" height="344">
<rect id="laptop-screen-front" x="354" y="174" width="733" height="344" fill="white" />
</mask>
<g mask="url(#mask0)">
<!-- some of the lines that represent text: -->
<g id="text-lines">
<line id="Line 3" x1="408" y1="194.5" x2="433" y2="194.5" stroke="#E06C60" stroke-width="5" />
<line id="Line 23" x1="438" y1="194.5" x2="463" y2="194.5" stroke="#D18C4C" stroke-width="5" />
<line id="Line 35" x1="469" y1="194.5" x2="542" y2="194.5" stroke="#7BC379" stroke-width="5" />
<line id="Line 36" x1="469" y1="209.5" x2="525" y2="209.5" stroke="#7BC379" stroke-width="5" />
</g>
Your code doesn't animate the mask. It attempts to animate the position of the text-lines g element. But SVG is not HTML. It is its own thing and doesn't support many common CSS properties: e.g. most relevant for you - there is no "top" property in SVG. The easiest way to do what you want is to get rid of all the CSS animations, and add a transform and a SMIL animation directly into the following part of the SVG.
<g mask="url(#mask0)">
<g id="text-lines" transform="translate(0 0)">
<animateTransform attributeName="transform" type="translate" values="0 0; 0 -150; 0 0" dur="11s" repeatCount="indefinite"/>
<line id="Line 3" x1="408" .... etc.
BTW - one of the reasons to use transforms for animation is that browsers can often optimize things by doing the animation on the GPU - so it's better than viewBox or top animations.
2 methods:
with SMIL
svg#parent {
background-color: rgba(100, 148, 237, 0.3);
}
<svg id="parent" width="200" height="100" viewBox="0 0 200 100">
<rect x="10" y="10" width="180" height="20" fill="white" />
<svg x="10" y="10" width="180" height="20" viewBox="0 0 180 20" >
<text x="20" y="15" > hello </text>
<text x="20" y="35" > world </text>
<animate attributeName="viewBox"
values="0 0 180 20; 0 20 180 20; 0 0 180 20"
begin="1s" dur="2s" repeatCount="indefinite" />
</svg>
</svg>
with javascript - requestAnimationFrame
(function()
{
const
svgInside = document.querySelector('#insideSVG')
, moving = .7 // change the value to slow down or speed up
;
let
stepVal = moving
, stepPos = 0
;
requestAnimationFrame(step)
;
function step()
{
stepPos += stepVal
if (stepPos<=0) stepVal = moving
if (stepPos>=20) stepVal = -moving
svgInside.setAttribute('viewBox',`0 ${stepPos} 180 20`)
requestAnimationFrame(step)
}
}
)()
svg#parent {
background-color: rgba(100, 148, 237, 0.3);
}
<svg id="parent" width="200" height="100" viewBox="0 0 200 100">
<rect x="10" y="10" width="180" height="20" fill="white" />
<svg x="10" y="10" width="180" height="20" viewBox="0 0 180 20"
id="insideSVG" >
<text x="20" y="15" > hello </text>
<text x="20" y="35" > world </text>
</svg>
</svg>
For your SVG the values should be around:
<svg x="354" y="174" width="733" height="344" viewBox="354 174 733 344" >
<line x1="419" y1="187.5" ..... />
/.../
<animate attributeName="viewBox"
values="354 174 733 344; 354 520 733 344; 354 174 733 344"
begin="1s" dur="8s" repeatCount="indefinite" />
</svg>
how does <animate> work ?
– Paul LeBeau
the mdn documentation is quite complete: :
SVG animation with SMIL
<animate>
do not hesitate to consult the links to additional information pages
but if you want a general vision of the animation possibilities of your SVGs, the Wikipedia page is ideal
There is even a part showing the use of the requestAnimationFrame(), which I am using here.
PS:The Whitesmith style has been around since 1978, and no, I don't use it for aesthetic reasons but because it checks that the right pairs of braces frame the right pieces of code. For the same reasons, I put the commas in front of data lines (they are invisible [and often forgotten] at the end of the line), as well as the column alignment (both in a similar way used in Haskell)
No, the K&R is not a foolproof style, nor is it more legitimate than any other.
I'm wondering if I can animate a group of paths directly in the svg file?
I have a complex svg file and I would like to scale only one part, which I wrapped in the <g> element.
I tried something like this, but nothing happen.
<g id="Fire">
<path/>
<circle/>
<path fill="#FEC33A" d="M216.562,546.14c-0.13-0.38-0.26-0.75-0.4-1.12C216.302,545.38,216.442,545.76,216.562,546.14z"/>
<path fill="#F27D16" d="M215.032,573.37c-0.15-0.19-0.3-0.37-0.46-0.54C214.742,573.01,214.892,573.19,215.032,573.37z"/>
<animateTransform attributeName="transform"
attributeType="XML"
type="scale"
from="1"
to="1.1"
dur="2s"
repeatCount="indefinite"/>
</g>
If you want to use SMIL animations you may put the group in a <defs> element and reuse it with <use>. Then you may animate the use element
svg{border:1px solid; overflow:visible}
path{stroke:black;}
<svg viewBox="210 540 15 40" width="100">
<defs>
<g id="Fire">
<path d="M216.562,546.14
c-0.13-0.38-0.26-0.75-0.4-1.12C216.302,545.38,216.442,545.76,216.562,546.14z">
</path>
<path d="M215.032,573.37c-0.15-0.19-0.3-0.37-0.46-0.54C214.742,573.01,214.892,573.19,215.032,573.37z"/>
</g>
</defs>
<use xlink:href="#Fire" >
<animateTransform attributeName="transform"
attributeType="XML"
type="scale"
from="1"
to="1.1"
dur="2s"
repeatCount="indefinite"/>
</use>
</svg>
PS I suppose you may have a reason to use such complicated paths instead of a line
I have an SVG animation where I move an object alongside a path. At some point I am changing the motion path.
While in Firefox the animated object follows the new path, in Chrome it continue to move on the old one. Does anyone knows why it happens and how can this be fixed?
Here is an example:
function change(){
elem = document.getElementById("Zuerich_Geneva");
elem.setAttribute('d','M382500,53500 C632500,53500 549500,80000 499500,181000')
}
setTimeout(function() { change(); }, 5000);
<svg xml:space="preserve" viewBox="480000 0 360000 240000" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<symbol id="airplane" overflow="visible">
<path stroke="blue" stroke-width="100" fill="lightgray" d="M-4000,0 a1000,300 0 0,1 1000,-300 H-1000 L1500,-3000 h400 L0,-300 h2000 L3000,-1500 h500 L2500,-50 V100 L3500,1500 h-500 L2000,300 h-2000 L1900,3000 h-400 L-1000,300 H-3000 a1000,300 0 0,1 -1000,-300"/>
</symbol>
</defs>
<g id="AnimationPaths">
<path id="Zuerich_Geneva" stroke="orange" stroke-width="2000" fill="none" d="M682500,53500 C632500,53500 549500,80000 499500,181000"/>
<use id="AirplaneZurichGeneva" xlink:href="#airplane">
<animateMotion id="animMotionZurGen" dur="10s" repeatCount="indefinite" rotate="auto-reverse" keyPoints="1;0" keyTimes="0;1" calcMode="linear">
<mpath xlink:href="#Zuerich_Geneva"/>
</animateMotion>
</use>
</g>
</svg>
Thanks!
According to the SVG 1.1 spec, <mpath> is not animatable.
https://www.w3.org/TR/SVG/animate.html#MPathElementHrefAttribute
However, you are not actuall animating the <mpath>, you are animating the <path> that it is referencing. So I think it should work. It seems like a bug in Chrome, which you should report. However since Chrome have deprecated SMIL, they may not be interested in fixing it.
There are workarounds. For instance, you can define more than one <animateMotion> and switch between them. In the example below, I have programmed the switch in the animation. But you could also do it with JS.
<svg xml:space="preserve" viewBox="480000 0 360000 240000" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<symbol id="airplane" overflow="visible">
<path stroke="blue" stroke-width="100" fill="lightgray" d="M-4000,0 a1000,300 0 0,1 1000,-300 H-1000 L1500,-3000 h400 L0,-300 h2000 L3000,-1500 h500 L2500,-50 V100 L3500,1500 h-500 L2000,300 h-2000 L1900,3000 h-400 L-1000,300 H-3000 a1000,300 0 0,1 -1000,-300"/>
</symbol>
</defs>
<g id="AnimationPaths">
<path id="Zuerich_Geneva" stroke="orange" stroke-width="2000" fill="none" d="M682500,53500 C632500,53500 549500,80000 499500,181000"/>
<path id="Zuerich_Geneva2" stroke="orange" stroke-width="2000" fill="none" d="M382500,53500 C632500,53500 549500,80000 499500,181000"/>
<use id="AirplaneZurichGeneva" xlink:href="#airplane">
<animateMotion id="animMotionZurGen" dur="5s" repeatCount="indefinite"
rotate="auto-reverse" keyPoints="1;0.5" keyTimes="0;1"
calcMode="linear" begin="0s; animMotionZurGen2.begin + 5s">
<mpath xlink:href="#Zuerich_Geneva"/>
</animateMotion>
<animateMotion id="animMotionZurGen2" dur="5s" repeatCount="indefinite"
rotate="auto-reverse" keyPoints="0.5;0" keyTimes="0;1"
calcMode="linear" begin="animMotionZurGen.begin + 5s">
<mpath xlink:href="#Zuerich_Geneva2"/>
</animateMotion>
</use>
</g>
</svg>
I am trying to get #moon svg group to follow a path and apply timed transforms. I cannot seem to get animateTransform to work in this situation.
When I set the animateMotion it causes the <g> to move out of frame. I adjust that with the attribute transform="translate(-53,-130)" which is fine. When I add the addTransform, the <g> drops down and out of the frame and I cannot adjust the translate to bring it back to frame. So, is there a trick to keeping the <g> in place or re-adjusting it back into frame with the addTransform?
<svg width="100%" height="100%" viewBox="0 0 570 594" preserveAspectRatio="true" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="padding: 0 10px 0 18px;fill-rule:evenodd;clip-rule:evenodd;stroke-miterlimit:10;">
<path id="orbit" d="M146.719,183.637l-18.408,-7.796l-13.233,-6.252l-12.327,-6.302l-18.44,-9.379l-12.42,-11.695l-16.36,-10.421l-15.546,-10.511l-12.326,-12.281l-14.415,-14.728l-8.426,-16.45l-4.168,-14.276l2.084,-14.272l6.297,-11.239l8.019,-10.103l12.013,-6.302l16.682,-8.426l16.356,-4.169l22.804,-4.217l27.474,-4.168l22.03,0l21.75,1.042l24.881,1.042l20.524,1.042l26.875,3.126l27.917,5.211l41.477,9.293l37.047,10.702l41.159,12.782l35.33,14.012l19.808,8.426l25.874,12.554l18.86,11.423l18.578,11.556l18.815,14.105l17.777,16.951l12.233,16.718l8.345,17.187l1.091,27.64l-7.434,8.207l-11.194,10.466l-15.595,10.559l-24.221,7.844l-22.609,5.211l-30.925,3.265l-43.658,0l-32.546,-2.085" style="fill:none;stroke-width:0px;stroke:#ff6060;"/>
<g id="moon" transform="translate(-53, -130)">
<path d="M77.39,295.34c0,-10.683 -8.658,-19.343 -19.342,-19.343c-10.683,0 -19.344,8.66 -19.344,19.343c0,10.683 8.661,19.343 19.344,19.343c10.684,0 19.342,-8.66 19.342,-19.343" style="fill:#fff;fill-rule:nonzero;"/>
<path d="M61.54,304.476c0,-2.967 -2.404,-5.373 -5.371,-5.373c-2.969,0 -5.373,2.406 -5.373,5.373c0,2.967 2.404,5.373 5.373,5.373c2.967,0 5.371,-2.406 5.371,-5.373" style="fill:#878787;fill-opacity:0.199997;fill-rule:nonzero;"/>
<animateMotion dur="6s" repeatCount="indefinite">
<mpath xlink:href="#orbit" />
</animateMotion>
<animateTransform attributeName="transform"
type="scale"
keyTimes="0;0.2;0.8;1" values="1.5;4;4;1.5"
dur="6s" additive="replace" fill="freeze"
repeatCount="indefinite"/>
</g>
</svg>
animateTransform normally replaces the current transform with the animated value, the additive attribute controls this.
If you set additive="sum" then the animation will be applied on top of the existing transform rather than replacing it.