SVG rotate animation for recycle circle wobbly - css

This appears to be an issue with the Inkscape SVG output for my SVG image. The transform matrix seems to be causing a slight wobble of the circle. Since I am not nor ever will be mathematically inclined, I am stumped on how to correct this so it does not wobble.
I have tried both CSS3 animation as well as SVG animateTransform. Both act the same. I can alter the wobble by editing the transform matrix but it only gets worse.
I've only used the webkit proprietary css here for brevity.
I've created a fiddle here:
http://jsfiddle.net/slwfleming/KGZns/
<div class="box">
<div class="rotateme">
<svg class="refresh-status1" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" xml:space="preserve" viewBox="0 0 18 18">
<g transform="translate(0,1.5)">
<g transform="matrix(3.239515,0,0,3.239515,-78.803548,-173.89306)">
<path d="m 28.973,54.655 -0.419,0.008 c 0.63,0.787 0.589,1.937 -0.14,2.667 -0.633,0.633 -1.583,0.746 -2.338,0.356 l -0.422,0.13 0.04,0.421 c 1.02,0.571 2.328,0.435 3.195,-0.433 0.968,-0.968 1.028,-2.489 0.2,-3.539 l -0.116,0.39 z"></path>
<path d="m 25.437,57.162 c -0.63,-0.787 -0.59,-1.937 0.141,-2.667 0.633,-0.633 1.583,-0.746 2.337,-0.355 l 0.422,-0.13 -0.039,-0.421 c -1.02,-0.572 -2.328,-0.435 -3.194,0.432 -0.97,0.97 -1.029,2.49 -0.201,3.54 l 0.115,-0.39 0.419,-0.009 z"></path>
</g>
</g>
</svg>
</div>
</div>
.box {
border:1px solid #c00;
width:2rem;
height:2rem;
position:relative;
}
#-webkit-keyframes rotateRefresh {
0% {
-webkit-transform: rotate(0deg) scaleX(1) scaleY(1) skewX(0deg) skewY(0deg);
}
100% {
-webkit-transform: rotate(360deg) scaleX(1) scaleY(1) skewX(0deg) skewY(0deg);
}
}
.rotateme {
width:35px;
height:35px;
-webkit-animation:rotateRefresh 4s linear infinite;
position:absolute;
top:0;
left:0;
}
.refresh-status1 {
width:35px;
height:35px;
}

Your SVG is not centred in its viewBox. You can see this if you extract it and add a contrasting background.
Demo here
Inkscape doesn't add viewBox attributes, so I am guessing you did this yourself?
If you tweak the width and height of the viewBox so it is correct (a tight fit around the diagram), it shouldn't wobble. Changing the "18" to "17.3" seems to work.
viewBox="0 0 17.3 17.3"
See corrected version here
It's also not quite centred properly in the viewBox (it's down and to the left very slightly), but that tiny error is not really visible in the final animation.

Related

CSS animation: Why is my spinning circle wobbling?

I'm trying to create a simple loading animation using SVG and CSS but for some reason the spinning circle is slightly wobbling. It's hardly noticeable but it's driving me crazy.
Here's a link to Codepen demonstrating the problem: https://codepen.io/signorbusi/pen/dyeJqmE
This is the code:
#keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
#spinner {
animation-name: spin;
margin-top: 2rem;
animation-duration: 0.5s;
animation-iteration-count: infinite;
animation-timing-function: linear;
transform-origin: center center;
}
<div class="wrapper">
<svg id="spinner" xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" stroke="black">
<circle r="23" cx="24" cy="24" stroke-width="2"/>
</svg>
</div>
Any pointers to how to get rid of the wobbling would be very helpful!
The viewbox of the <svg> element is cutting off the edges of the circle as it rotates. (Note that your stroke-width is increasing the size of the circle beyond the defined radius in the r attribute.)
If you expand the <svg> element's width and height attributes, match the viewbox attribute accordingly, and then also center the circle inside of that (using the cx and cy attributes on the <circle> element), it will not "wobble":
<div class="wrapper">
<svg id="spinner" xmlns="http://www.w3.org/2000/svg" width="60" height="60" viewBox="0 0 60 60" stroke="black">
<circle r="23" cx="30" cy="30" stroke-width="2"/>
</svg>
</div>
Working Codepen can be found here: https://codepen.io/theodorewiersema/pen/NWMXowY
This should work:
<circle r="22" cx="24" cy="24" stroke-width="2"/>
I changed the radius attribute to 22 and it doesn't wobble.

Transform-origin for an SVG group in Electron is ignored

I have a simple SVG icon which I animate in CSS. Here is the SVG:
<svg width="100" height="100" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" className={this.state.isTimewatchActive ? "active" : ""}>
<rect fill="lightgrey" id="background" ry="20" rx="20" height="512" width="512" y="0" x="0" />
<g id="icon">
<path
d="m352.35001,180.85625l-1.2375,-1.16875l12.65,0l5.84375,5.3625l15.46875,-15.675l-28.05,-27.70625l-15.95,15.33125l6.05,6.325l0,12.7875l-3.4375,-3.23125c-21.51875,-19.45625 -48.125,-31.075 -77.6875,-33.61875l0,-26.2625l-22,0l0,26.125l-1.375,0c-30.04375,2.0625 -58.4375,15.46875 -79.75,36.4375l0,-12.2375l6.11875,-6.325l-15.74375,-15.2625l-28.05,27.6375l15.46875,15.675l5.84375,-5.3625l12.2375,0c-0.20625,0 -0.48125,0.48125 -0.6875,0.75625c-22.55,24.13125 -35.0625,55.61875 -35.0625,88.34375c0,71.775 59.19375,130.2125 132.06875,130.2125c72.7375,0 131.93125,-58.36875 131.93125,-130.14375c0,-32.725 -12.375,-64.00625 -34.65,-88zm-91.85,117.90625l-5.5,12.2375l-5.5,-12.2375c-9.625,-2.40625 -16.5,-10.8625 -16.5,-20.96875c0,-9.4875 7.90625,-17.875 16.5,-20.83125l0,-88.9625l11,0l0,88.89375c9.28125,2.54375 16.5,10.93125 16.5,20.9c0,10.10625 -6.875,18.5625 -16.5,20.96875z"
fill="#FFFFFF"
stroke="null"
/>
</g>
</svg>
(I am working in the context of React, so the className attribute is really a class and the expression is evaluated to output the class string.)
This SVG has two states, active (with class="active" on the svg tag) and inactive (the code block above).
Here is the (post)CSS for the two states:
svg {
#icon {
transform: scale(0.8);
transform-origin: center center;
transition: all 0.2s;
}
#shadow {
opacity: 0;
transition: all 0.2s;
}
#background {
transition: all 0.2s;
}
&.active {
#background {
fill: $greenHn;
}
#icon {
transform: scale(1);
}
#shadow {
opacity: 0.2;
}
}
}
So at first, the icon should display at scale 0.8 in the center of the background. In Electron, it is scaled down but the transform origin is top left. The weird thing is that if I open the dev tools in Electron and toggle the transform-origin: center center rule (with the checkbox in the right-hand side panel), and toggle it back on, the rule is correctly applied.
So it doesn't seem to be a CSS problem but then I don't know what to do.
Does anyone have any idea?
Thanks :)
EDIT
As I read that SVG isn't supposed to support transform-origin, I tried using a transform: translate() instead, but I have the same behaviour.

Override SVG transform attributes with CSS

I have a SVG element that looks like this:
<image
xmlns="http://www.w3.org/2000/svg"
overflow="visible"
width="54"
height="54"
id="Content-Author-Off_xA0_Image"
xlink:href="data:image/png;base64,..."
transform="matrix(1 0 0 1 0 108)">
</image>
Is it possible to override transform attribute with CSS? I've tried this and it doesn't work:
image {
transform: none;
}
Looks like transform: none doesn't quite work in all browsers (see bugreport) (fixed jan 2017), but it's possible to work around that by using an identity matrix, e.g transform: scale(1). The stylerules will override the attributes, as demonstrated by the following snippet:
circle {
transform-origin: 50%;
transform: scale(1);
transition: transform 0.5s;
}
circle:hover {
transform: scale(2);
}
<svg>
<circle cx="50" cy="50" r="25" transform="translate(500 500)"/>
</svg>
Same as fiddle.
CSS cannot affect an attribute only a CSS property. You could try using a CSS transform instead of an SVG attribute transform in the first place, although not all UAs support CSS transforms on SVG elements yet.
Here's an example of a CSS transform with a rect element. Put the mouse over it to see it change.
rect {
transform: translate(50px, 0) rotate(30deg);
}
rect:hover {
transform: none;
}
<svg>
<rect x="30" y="30" width="50" height="50" fill="red"/>
</svg>

Vertically Align Rect Elements Inside SVG By Center of Rect

Goal:
I found a tutorial online to make a CSS3 infinite loading icon, using rectangles which grow and shrink in height. They end up appearing like:
It uses 5 divs wrapped in an outer div, and works perfectly. I want to try and recreate the effect using an SVG though, so that I can reference it with just an image, not having to add 5 HTML elements.
What I Have:
I started off with the same CSS animation code, and used 5 rects inside the svg tags. The animation works perfect, but I can't get the rectangles to be centered vertically. Since they are placed using x and y coordinates, which correspond to the top/left point of each rectangle, they are fixed to that location.
<svg version="1.1" baseProfile="full" width="250" height="250" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink">
<rect class="rect1" fill="black" height="30" width="6" x="0" />
<rect class="rect2" fill="black" height="30" width="6" x="10" />
<rect class="rect3" fill="black" height="30" width="6" x="20" />
<rect class="rect4" fill="black" height="30" width="6" x="30" />
<rect class="rect5" fill="black" height="30" width="6" x="40" />
</svg>
rect {
-webkit-animation: stretchdelay 1.2s infinite ease-in-out;
animation: stretchdelay 1.2s infinite ease-in-out;
alignment-baseline:bottom;
}
.rect2 {
-webkit-animation-delay: -1.1s;
animation-delay: -1.1s;
}
.rect3 {
-webkit-animation-delay: -1.0s;
animation-delay: -1.0s;
}
.rect4 {
-webkit-animation-delay: -0.9s;
animation-delay: -0.9s;
}
.rect5 {
-webkit-animation-delay: -0.8s;
animation-delay: -0.8s;
}
#-webkit-keyframes stretchdelay {
0%, 40%, 100% { -webkit-transform: scaleY(0.4) }
20% { -webkit-transform: scaleY(1.0) }
}
#keyframes stretchdelay {
0%, 40%, 100% {
transform: scaleY(0.4);
-webkit-transform: scaleY(0.4);
} 20% {
transform: scaleY(1.0);
-webkit-transform: scaleY(1.0);
}
}
See Working Example: http://codepen.io/Kelderic/pen/vuipF
One thing that is odd, it's running in CodePen, the same code doesn't run in JSFiddle. It also doesn't run when I embed the CodePen as an SVG.
Question
Is using CSS animations like this supposed to work for SVG elements, and can I fix them in the center to match the original?
Edit
js1568's answer provides what SHOULD be a fix, and it does make things work in Chrome. It has no effect in Firefox though, and after some research I've found that I'm not the only one who has had the same problem with Firefox.
Setting transform-origin on SVG group not working in FireFox
I think the only answer here is that at this time this won't work in all browsers. (If anyone does know a way, feel free to add an answer though!)
Edit 2
I figured a way to make this work in Firefox, explained in an answer below. Still no IE9-11 though.
Okay, so I figured out the answer to this, and it works in Chrome and Firefox. IE doesn't support CSS transforms on SVG elements (though it does support the transform attribute, and I'm trying to figure out a workaround).
Instead of trying to set the baseline to the center of the rect element, I just use a second animation. I move the element up and down, sync'd up time-wise. This creates the appearance that the element is centered vertically.
I had some issues getting it to sync perfectly when using the 0.4 scale, so I changed over to 0.5, which still looks good.
HTML:
<svg version="1.1" baseProfile="full" width="250" height="250" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink">
<rect class="rect1" fill="black" height="30" transform="translate(2,2)" width="6" x="0" y="0" />
<rect class="rect2" fill="black" height="30" width="6" x="10" y="0" />
<rect class="rect3" fill="black" height="30" width="6" x="20" y="0" />
<rect class="rect4" fill="black" height="30" width="6" x="30" y="0" />
<rect class="rect5" fill="black" height="30" width="6" x="40" y="0" />
</svg>
CSS:
#-webkit-keyframes stretchdelay {
0% {
-webkit-transform: scaleY(1.0) translate(0,0);
} 30%,70% {
-webkit-transform: scaleY(0.5) translate(0,15px);
} 100% {
-webkit-transform: scaleY(1.0) translate(0,0);
}
}
#keyframes stretchdelay {
0% {
transform: scaleY(1.0) translate(0,0);
-webkit-transform: scaleY(1.0) translate(0,0);
} 30%,70% {
transform: scaleY(0.5) translate(0,15px);
-webkit-transform: scaleY(0.5) translate(0,15px);
} 100% {
transform: scaleY(1.0) translate(0,0);
-webkit-transform: scaleY(1.0) translate(0,0);
}
}
rect {
-webkit-animation: stretchdelay 1.2s infinite ease-in-out;
animation: stretchdelay 1.2s infinite ease-in-out;
-ms-animation: stretchdelay 1.2s infinite ease-in-out;
}
.rect2 {
-webkit-animation-delay: -1.1s;
animation-delay: -1.1s;
}
.rect3 {
-webkit-animation-delay: -1.0s;
animation-delay: -1.0s;
}
.rect4 {
-webkit-animation-delay: -0.9s;
animation-delay: -0.9s;
}
.rect5 {
-webkit-animation-delay: -0.8s;
animation-delay: -0.8s;
}
Result:
(Note, recently CodePen added a feature allowing you to embed SVGs created inline as SVG images. I found out today as I created this, that all CSS must be placed in <style> tags inside the HTML input box. If it is placed in the CSS box, it won't work.)
I have edited your codepen to include transform-origin styles:
http://codepen.io/anon/pen/ojgwr
Is this the effect you were going for?

Animate image independent of mask?

I'm trying to find a way to animate an image which has a masked applied to it without affecting the mask itself.
This has been the best cross-browser way I've found to apply a mask, but I'm uncertain how to apply CSS animations to it in such a way I can have the center image rotate inside the mask, and not both at the same time.
For example, my current code just rotates the entire image and mask together.
HTML
<div class="svgMask">
<svg width="726" height="726" baseProfile="full" version="1.2">
<defs>
<mask id="svgmask2" maskUnits="userSpaceOnUse" maskContentUnits="userSpaceOnUse" transform="scale(1)">
<image width="100%" height="100%" xlink:href="http://www.mikerezl.com/img/testmask.png"/>
</mask>
</defs>
<image id="interior" mask="url(#svgmask2)" width="100%" height="100%" y="0" x="0" xlink:href="http://www.mikerezl.com/img/valknut.jpg"/>
</svg>
</div>
CSS
#interior {
-webkit-animation-name: rotate;
-webkit-animation-duration:2s;
-webkit-animation-iteration-count:infinite;
-webkit-animation-timing-function:linear;
-moz-animation-name: rotate;
-moz-animation-duration:2s;
-moz-animation-iteration-count:infinite;
-moz-animation-timing-function:linear;
}
#-webkit-keyframes rotate {
from {-webkit-transform:rotate(0deg);}
to { -webkit-transform:rotate(360deg);}
}
#-moz-keyframes rotate {
from {-moz-transform:rotate(0deg);}
to { -moz-transform:rotate(360deg);}
}
Fiddle link
You need to set the origin like this (http://jsfiddle.net/icodeforlove/f5RE4/1/)
-webkit-transform-origin: 50% 50%;
-moz-transform-origin: 50% 50%;
transform-origin: 50% 50%;
but for me performance is horrible and firefox isnt working. i would suggest using a canvas to composite your image and animating on that.
context.save();
context.drawImage(valknut, 0, 0);
context.globalCompositeOperation = 'destination-in';
context.drawImage(mask, 0, 0);
context.restore();
by using a canvas you gain a lot of control! heres an example of how you would achieve this with a canvas (http://jsfiddle.net/f5RE4/)

Resources