I have an svg with a few elements that looks something like this:
I would like to create an animation where each element "pulses" (grows about 20% in size and then returns to its normal size without moving to a different position) one after the other, going around in a circle on repeat.
I began creating a css animation for this but my attempts to add a scale animation clearly were not using the correct center point because every scaling movement would shift that element to a different position as well.
I tried exploring different values for the property transform-origin, but none seemed to achieve the desired behaviour.
I have included a demo reprex that shows the behaviour:
#Ellipse_1 {
/* No transform-origin */
animation: pulse 2s linear infinite alternate;
animation-delay: 2.6s fill-opacity: 50%;
}
#Ellipse_2 {
/* transform-origin same as circle's center location */
transform-origin: 4 8;
fill-opacity: 50%;
animation: pulse 2s linear infinite alternate;
animation-delay: 3.4s
}
#Ellipse_3 {
/* Trying transform-origin center center keyword */
transform-origin: center center;
fill-opacity: 50%;
animation: pulse 2s linear infinite alternate;
animation-delay: 3s
}
#Path_1 {
/* Trying transform-origin center keyword */
transform-origin: center;
animation: pulse 2s linear infinite alternate;
animation-delay: 4s
}
#Path_2 {
/* This goes off screen as soon as animation starts */
animation: pulse 2s linear infinite alternate;
animation-delay: 4s
}
#keyframes pulse {
0% {
transform: scale(1);
-ms-transform: scale(1);
-webkit-transform: scale(1);
}
100% {
transform: scale(2);
-ms-transform: scale(2);
-webkit-transform: scale(2);
}
}
<svg id="shapes" data-name="shapes data" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-10 -10 100 100">
<circle id="Ellipse_1" data-name="Ellipse 1" cx="2.083" cy="2.083" r="2.083" transform="translate(14 3)" fill="red"/>
<path id="Path_1" data-name="Path 259" d="M60.749,74.282a103.267,103.267,0,0,0-5.686,23.5.459.459,0,0,1-.455.408H48.887a.46.46,0,0,1-.453-.38l-3.1-17.357a.458.458,0,0,1,.321-.519A75.754,75.754,0,0,0,60.24,73.846C60.708,73.591,60.936,73.783,60.749,74.282Z" transform="translate(-45.326 -63.944)" fill-rule="evenodd" fill="green"/>
<circle id="Ellipse_2" data-name="Ellipse 2" cx="2.083" cy="2.083" r="2.083" transform="translate(4 8)" fill="blue"/>
<circle id="Ellipse_3" data-name="Ellipse 3" cx="1.62" cy="1.62" r="1.62" transform="translate(21.942)" fill="green"/>
<path id="Path_2" data-name="Path 2" d="M97.486,54.462C94.879,58.549,90.751,66.907,88.6,81.11a.6.6,0,0,1-.572.505,16.478,16.478,0,0,0-8.995,3.049.355.355,0,0,1-.562-.322,90.68,90.68,0,0,1,6.77-25.419.966.966,0,0,1,.352-.4q1.521-.866,3.1-1.629a.016.016,0,0,1,.009,0,50.611,50.611,0,0,1,8.261-3.124h0A.465.465,0,0,1,97.486,54.462Z" transform="translate(-66.545 -51.149)" fill-rule="evenodd" fill="red"/>
</svg>
When you "overwrite" the transform using CSS the translate is removed. Here I moved the transform/translate to a parent <g>. So each element is moved using the transform in <g> and then each element is scaled using CSS.
The circles are easy to scale because their origin is in the center already, but the other shapes need either to be moved so that 0,0 is in the center (change all values in the d attribute) or use transform-origin to move the origin. I guess that the result here is a combination - that could be optimized (up to you :-)).
#Ellipse_1 {
/* No transform-origin */
animation: pulse 2s linear infinite alternate;
animation-delay: 2.6s fill-opacity: 50%;
}
#Ellipse_2 {
/* transform-origin same as circle's center location */
fill-opacity: 50%;
animation: pulse 2s linear infinite alternate;
animation-delay: 3.4s
}
#Ellipse_3 {
/* Trying transform-origin center center keyword */
fill-opacity: 50%;
animation: pulse 2s linear infinite alternate;
animation-delay: 3s
}
#Path_1 {
/* Trying transform-origin center keyword */
transform-origin: 50px 80px;
animation: pulse 2s linear infinite alternate;
animation-delay: 4s
}
#Path_2 {
/* This goes off screen as soon as animation starts */
transform-origin: 80px 70px;
animation: pulse 2s linear infinite alternate;
animation-delay: 4s
}
#keyframes pulse {
0% {
transform: scale(1);
-ms-transform: scale(1);
-webkit-transform: scale(1);
}
100% {
transform: scale(1.2);
-ms-transform: scale(1.2);
-webkit-transform: scale(1.2);
}
}
<svg id="shapes" data-name="shapes data" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<g transform="translate(14 3)">
<circle id="Ellipse_1" data-name="Ellipse 1" cx="2.083" cy="2.083" r="2.083" fill="red"/>
</g>
<g transform="translate(-45.326 -63.944)">
<path id="Path_1" data-name="Path 259" d="M60.749,74.282a103.267,103.267,0,0,0-5.686,23.5.459.459,0,0,1-.455.408H48.887a.46.46,0,0,1-.453-.38l-3.1-17.357a.458.458,0,0,1,.321-.519A75.754,75.754,0,0,0,60.24,73.846C60.708,73.591,60.936,73.783,60.749,74.282Z" fill-rule="evenodd" fill="green"/>
</g>
<g transform="translate(4 10)">
<circle id="Ellipse_2" data-name="Ellipse 2" r="2.083" fill="blue"/>
</g>
<g transform="translate(22 3)">
<circle id="Ellipse_3" data-name="Ellipse 3" r="1.62" fill="green"/>
</g>
<g transform="translate(-66.545 -51.149)">
<path id="Path_2" data-name="Path 2" d="M97.486,54.462C94.879,58.549,90.751,66.907,88.6,81.11a.6.6,0,0,1-.572.505,16.478,16.478,0,0,0-8.995,3.049.355.355,0,0,1-.562-.322,90.68,90.68,0,0,1,6.77-25.419.966.966,0,0,1,.352-.4q1.521-.866,3.1-1.629a.016.016,0,0,1,.009,0,50.611,50.611,0,0,1,8.261-3.124h0A.465.465,0,0,1,97.486,54.462Z" fill-rule="evenodd" fill="red"/>
</g>
</svg>
I have 2 Keyframes, each using "transform" property and then I tried to call both of them using animation property, however if I have the second line of animation with the "fill-mode" property set to "backwards", the first keyframe that is called using the animation property is ignored? why so?
Here is the code:
HTML
<!DOCTYPE html>
<html>
<head>
<title>SVG Animation</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="218.8 226.8 258.4 258.3">
<g class="all">
<clippath id="bg">
<circle cx="348" cy="356" r="129.2"/>
</clippath>
<g class="stars" clip-path="url(#bg)">
<circle class="stars-bg" fill="#3079AB" stroke="#95C7E8" stroke-width="6" stroke-linecap="round" cx="348" cy="356" r="129.2"/>
<path class="star" opacity="0.75" fill="#FFFFFF" d="M409.7,337.4c-1.2-1-2.2-1.2-8.4-1c-3,0-6.9,0.2-11.9,0.5c-5.7,0.2-11.4,0.7-14.6,1
c-1.2-2.5-3.7-6.9-6.4-11.6c-6.9-11.6-9.9-15.3-11.6-17.1c-1.5-2-3.2-1.7-3.7-1.7h-0.2h-0.2c-0.5,0-2.5,0.5-3.2,2.7
c-1.2,2-3,6.4-6.2,19.5c-1.2,5.2-2.2,10.1-3,12.9c-3,0.7-8.7,2-14.1,3.2c-4.7,1.2-8.7,2.2-11.6,3c-5.7,1.5-6.9,2.2-7.7,3.5
c-0.7,1-1,2.2-0.5,3.5c0.5,2,2,4.9,15.6,13.4c4.5,2.7,8.7,5.4,11.4,6.9c-0.5,3.2-1.5,9.4-2.5,15.6c-0.7,5.4-1.5,9.6-1.7,12.9
c-0.7,5.9-0.7,7.9,1.2,9.4c0.7,0.5,1.7,1,3.7,0.7c1.2-0.2,4.5-0.7,18.6-10.9c5.4-4,10.4-7.9,13.1-10.1c3.2,1.2,9.2,3.7,15.3,5.9
c16.3,5.9,19.5,5.4,20.8,5.2c1.7-0.2,2.7-1.2,3.2-1.7c1.5-2,0.7-4-1.5-9.4c-1.2-3-3-6.9-5.2-11.6c-2.7-5.7-5.4-11.4-6.9-14.3
c2-2.2,5.4-5.9,8.9-9.9c10.6-12.1,11.4-15.3,11.1-17.3C411.5,339.4,410.7,338.4,409.7,337.4z"/>
<path class="star" opacity="0.6" fill="#FFFFFF" d="M310.3,334.7c-0.5-0.7-1-1.2-4-2.5c-1.5-0.7-3.5-1.5-6.2-2.5
c-3-1.2-5.9-2.2-7.7-3c0-1.5-0.2-4.5-0.7-7.4c-0.7-7.4-1.5-10.1-2-11.1c-0.2-1.2-1.2-1.7-1.5-1.7c0,0,0,0-0.2,0c0,0,0,0-0.2,0
c-0.2,0-1.2-0.2-2.2,0.7c-1,0.7-3,2.5-7.7,8.4c-1.5,2.5-3,4.7-4,5.9c-1.7-0.2-4.7-1-7.9-1.5c-2.7-0.5-4.9-1-6.4-1.2
c-3.5-0.5-4-0.5-4.7,0c-0.7,0.2-1,1-1.2,1.5c-0.2,1.2,0,3,4.9,10.4c1.5,2.5,3.2,4.7,4.2,6.2c-1,1.5-3,4.5-4.7,7.4
c-1.7,2.5-3,4.5-3.7,6.2c-1.7,3-2.2,4-1.5,5.2c0.2,0.5,0.7,1,1.7,1.2c0.7,0.2,2.5,0.7,11.9-1.2c3.7-0.7,7.2-1.7,8.9-2.2
c1.2,1.5,3.7,4,6.4,6.4c6.9,6.7,8.7,7.2,9.4,7.4c1,0.2,1.7,0,2-0.2c1.2-0.7,1.2-1.7,1.5-5.2c0-1.7,0-4.2,0-7.2
c0-3.5-0.2-6.9-0.2-8.9c1.5-0.7,4.2-1.7,6.7-3c8.2-3.7,9.2-5.2,9.6-6.2C310.5,336.2,310.5,335.5,310.3,334.7z"/>
<path class="star" opacity="0.4" fill="#FFFFFF" d="M446.1,360.4c-0.5-0.2-0.7-0.2-3-0.2c-1,0-2.5,0.2-4.2,0.5
c-2,0.2-4,0.5-4.9,0.7c-0.5-0.7-1.5-2.2-2.5-3.7c-2.7-4-3.7-4.9-4.5-5.4c-0.5-0.7-1.2-0.5-1.5-0.5l0,0l0,0c-0.2,0-0.7,0.2-1,1
c-0.2,0.7-1,2.2-1.7,6.9c-0.2,1.7-0.5,3.5-0.7,4.5c-1,0.2-3,1-4.7,1.5c-1.7,0.5-3,1-4,1.2c-2,0.7-2.2,1-2.5,1.5
c-0.2,0.2-0.2,0.7,0,1.2c0.2,0.7,0.7,1.5,5.7,4.2c1.5,0.7,3.2,1.7,4,2c0,1.2-0.2,3.2-0.5,5.4c-0.2,1.7-0.2,3.5-0.2,4.5
c0,2,0,2.7,0.7,3.2c0.2,0.2,0.7,0.2,1.2,0.2c0.5,0,1.5-0.2,6.2-4.2c1.7-1.5,3.5-3,4.2-3.7c1.2,0.2,3.2,1,5.4,1.7
c5.7,1.5,6.9,1.2,7.2,1.2c0.5-0.2,1-0.5,1-0.7c0.5-0.7,0.2-1.2-0.7-3.2c-0.5-1-1.2-2.2-2.2-4c-1-2-2.2-3.7-2.7-4.7
c0.7-0.7,1.7-2.2,2.7-3.7c3.2-4.5,3.5-5.4,3.5-6.2C446.6,361.2,446.3,360.7,446.1,360.4z"/>
</g>
</g>
</svg>
</body>
</html>
CSS
/* --------------------------
Base
--------------------------- */
body {
padding-top: 60px;
background: #0f4e7a;
}
svg {
margin: auto;
display: block;
width: 28%;
}
/* --------------------------
Keyframes
--------------------------- */
#keyframes move{
0%{
transform: translate3d(0,300%,0);
}
60%{
transform: translate3d(0,-20px,0);
}
100%{
transform: translate3d(0,0,0);
}
}
#keyframes rotate {
0%{
transform: scale(1);
}
50%{
transform: scale(1.1);
}
100%{
transform: scale(1);
}
}
/* --------------------------
SVG Styles
--------------------------- */
.all *{
transform-origin: 50% 50%;
transform-box: fill-box;
}
.star:nth-of-type(1) {
transform: translate3d(0,0,0);
animation: move 1s 0s ease-out backwards,
rotate .4s 1.5s linear backwards;
}
.star:nth-of-type(2) {
animation: move 1s .5s ease-out backwards;
}
.star:nth-of-type(3) {
animation: move 1s 1s ease-out backwards;
}
The middle star with the class ".star:nth-of-type(1)" only scale however if I change line 57 from this
rotate .4s 1.5s linear backwards;
to that
rotate .4s 1.5s linear forwards;
the middle star both translates and scales, what is the reason behind this behavior ?
Is it possible to achieve perspective with 3d transforms on a SVG elements?
I'm talking about something similar with how the Star Wars opening titles look like with 3d perspective. This is a jsfiddle with the desired effect achieved using CSS3 3d transforms:
<section style="transform: perspective(200px) rotateX(-30deg); transform-origin: 50% 100%; text-align: justify; width: 100px;">
<p style="backface-visibility: hidden;">TEXTTEXTTEXT</p>
</section>
Update Nov 2018:
Testing the snipet from the question in latest chrome and Firefox works. Although support for 3d transforms on svg elements isn't very wide, browsers are implementing it more and more.
Origin answer :
3D transforms aren't supported on SVG elements. There are a few workarounds though :
If the svg doesn't contain elements that shouldn't be transformed, you can use CSS 3d transforms on the SVG element itself :
svg {
width: 70%;
margin: 0 auto;
display: block;
-webkit-transform: perspective(300px) rotateX(30deg);
transform: perspective(300px) rotateX(30deg);
}
<svg viewbox="0 0 100 20">
<text x="0" y="20">TEXTEXTEX</text>
</svg>
In case of polygons, you make a 2D polygon look like a 3D polygon. In the following example, the red rectangle is 3D rotated (rotateX(40deg)) and the black rectangle is a 2D SVG polygon made to look like a 3D rotated rectangle:
div{
display:inline-block;
width:200px; height:100px;
background:red;
transform:perspective(500px) rotateX(40deg);
}
svg{
display:inline-block;
width:220px; height:auto;
}
div, svg{
display:inline-block;
margin:0 10px;
}
<div></div>
<svg viewbox="0 0.5 10 4">
<polygon points="9.9 4.1 0.1 4.1 0.7 0.6 9.3 0.6" fill=""/>
</svg>
3D transforms are supported inside <svg> elements (f.e. on <circle>) (at least to some extent, it seems like perspective is isometric only).
For example, here's animation of transform: rotate3d applied to <circle> elements (tested in Chrome only):
body, html {
background: black;
width: 100%; height: 100%;
margin: 0;
padding: 0;
}
body {
display: flex;
}
svg {
width: 100%;
}
.gAExgp {
transform-origin: 50% 50% 0px;
animation-name: phEs, ipaUyp;
animation-duration: 4s, 7s;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
.PwswZ {
transform-origin: 50% 50% 0px;
animation-name: gcRPJT, ipaUyp;
animation-duration: 4s, 8s;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
#keyframes phEs {
50% {
transform: rotate3d(0, 2, 1, 180deg);
}
100% {
transform: rotate3d(0, 2, 1, 360deg);
}
}
#keyframes gcRPJT {
50% {
transform: rotate3d(2, 0, 1, 180deg);
}
100% {
transform: rotate3d(2, 0, 1, 360deg);
}
}
#keyframes ipaUyp {
0% {
stroke: magenta;
}
33% {
stroke: cyan;
}
66% {
stroke: yellow;
}
100% {
stroke: magenta;
}
}
<!-- Logo from https://rebassjs.org -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" style="display:block;max-width:100%;margin:0;fill:none;stroke:cyan" vector-effect="non-scaling-stroke" class="sc-htoDjs hCHUAb"><circle cx="32" cy="32" r="32" fill="#000" stroke="none"></circle><circle cx="32" cy="32" r="30" stroke-width="1" vector-effect="non-scaling-stroke" opacity="0.5"></circle><g><circle cx="32" cy="32" r="24" stroke-width="2" vector-effect="non-scaling-stroke" class="sc-dnqmqq gAExgp"></circle><circle cx="32" cy="32" r="24" stroke-width="2" vector-effect="non-scaling-stroke" class="sc-iwsKbI PwswZ"></circle></g><text x="32" y="34" text-anchor="middle" font-family="system-ui, sans-serif" font-weight="bold" font-size="4" stroke="none" fill="white" style="text-transform:uppercase;letter-spacing:0.5em">Rebass</text></svg>
Also available here: https://codepen.io/anon/pen/MPeyEj