Related
I'm trying to figure out why there's a line in this SVG animation that I'm trying to make. But it's only an effect of animating it with css, and it's not part of the SVG itself. It seems to be some stroke but stroke: none doesn't affect it.
image showing the stroke
https://jsfiddle.net/bgu2e7pn/
PS: I would also appreciate if you can point out how to have the full width of the coffee content svg exceed the cup width so it looks more seamless.
You can work around this bug by using a mask instead of a clip-path.
body {
background: grey;
}
#coffee-cup .fill {
animation-name: coffeeFill;
animation-iteration-count: 1;
animation-timing-function: cubic-bezier(.2, .6, .8, .4);
animation-duration: 4s;
animation-fill-mode: forwards;
}
#coffee-cup #waveShape {
animation-name: waveMotion;
animation-iteration-count: infinite;
animation-timing-function: linear;
animation-duration: 0.5s;
fill: #8C5926;
stroke: #3D2518;
stroke-width: 10px;
}
#keyframes coffeeFill {
0% {
transform: translate(0, 150px);
}
100% {
transform: translate(0, -15px);
}
}
#keyframes waveMotion {
0% {
transform: translate(-150px, 0);
}
100% {
transform: translate(0, 0);
}
}
<div id="coffee-cup">
<div>
<svg class="position-absolute" width="259" height="180" viewBox="0 0 259 180" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M259 0H53L55.8218 23.0874C24.0777 28.8271 0 56.6017 0 90C0 127.555 30.4446 158 68 158C69.4425 158 70.8746 157.955 72.2948 157.867L75 180H238L259 0ZM57.7548 38.9028L70.4677 142.918C69.4854 142.972 68.496 143 67.5 143C38.5051 143 15 119.495 15 90.5C15 64.835 33.4162 43.4713 57.7548 38.9028Z"
fill="white" fill-opacity="0.7" />
<mask id="coffee">
<rect width="100%" height="100%" fill="black"/>
<path d="M70 49H244L232.139 156H82.2489L70 49Z" fill="white" />
</mask>
<g mask="url(#coffee)">
<g class="fill">
<path id="waveShape" d="M300,300V2.5c0,0-0.6-0.1-1.1-0.1c0,0-25.5-2.3-40.5-2.4c-15,0-40.6,2.4-40.6,2.4
c-12.3,1.1-30.3,1.8-31.9,1.9c-2-0.1-19.7-0.8-32-1.9c0,0-25.8-2.3-40.8-2.4c-15,0-40.8,2.4-40.8,2.4c-12.3,1.1-30.4,1.8-32,1.9
c-2-0.1-20-0.8-32.2-1.9c0,0-3.1-0.3-8.1-0.7V300H300z" />
</g>
</g>
</svg>
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 the following setup where multiple objects are zoomed out in a staggered delay. I am facing the following issues:
The zoom-out animation is set up to accelerate from 0% to 15% and then slow down the rest of the keyframes. But when the animation transitions from 15% to 16% the motion is visibly jarring. How can I make this transition smooth?
When the circles start out they appear from the left, not centered on their point of origin. How can I fix this?
HTML:
<div>
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<circle class="blk" cx="10" cy="10" r="5"/>
<circle class="red" cx="20" cy="10" r="5" color="red" stroke="red" fill="red"/>
<circle class="green" cx="30" cy="20" r="5" color="red" stroke="green" fill="green"/>
</svg>
</div>
CSS:
.blk{
animation-name: zoom-out;
animation-duration: 2s;
animation-timing-function: ease-in-out;
-webkit-animation-timing-function: ease-in-out;
animation-iteration-count: infinite;
animation-delay: 2.5s;
}
.red{
animation-name: zoom-out;
animation-duration: 2s;
animation-timing-function: ease-in-out;
-webkit-animation-timing-function: ease-in-out;
animation-iteration-count: infinite;
animation-delay: 1.5s;
}
.green{
animation-name: zoom-out;
animation-duration: 2s;
animation-timing-function: ease-in-out;
-webkit-animation-timing-function: ease-in-out;
animation-iteration-count: infinite;
animation-delay: 0.5s;
}
#keyframes zoom-out {
0% {
transform: scale(5,5);
}
15% {
transform: scale(2,2);
}
100% {
transform: scale(1,1);
}
}
One posible solution would be having the circles with cx="0" and cy="0" and using those circles with an x and y position like so:
circle {
animation-name: zoom-out;
animation-duration: 2s;
/*animation-timing-function: ease-in-out;
-webkit-animation-timing-function: ease-in-out;*/
animation-iteration-count: infinite;
}
#blk {
animation-delay: 2.5s;
}
#red {
stroke: red;
fill: red;
animation-delay: 1.5s;
}
#green {
stroke: green;
fill: green;
animation-delay: 0.5s;
}
#keyframes zoom-out {
0% {
transform: scale(5, 5);
}
15% {
transform: scale(2, 2);
}
100% {
transform: scale(1, 1);
}
}
<div>
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<defs>
<circle id="blk" r="5" />
<circle id="red" r="5" />
<circle id="green" r="5" />
</defs>
<use xlink:href="#blk" x="10" y="10" />
<use xlink:href="#red" x="20" y="10" />
<use xlink:href="#green" x="30" y="20" />
</svg>
</div>
Also in order to make the transition smooth you may use animation-timing-function: linear; instead of ease-in-out
UPDATE
The OP is commenting:
As for the point of origin, the actual SVG I am using is an illustator vector and each object in it has multiple paths and shapes. So, the red circle would actually be an object with 50 paths/shapes. How would I zero out the coordinates for a collection of paths?
Next comes an example where I'm using a collection of paths:
let wrap = document.querySelector("#red .wrap")
let bb = wrap.getBBox();
wrap.setAttribute("transform",`translate(${-(bb.x + bb.width/2)}, ${-(bb.y + bb.height/2)})`)
g[id] {
animation-name: zoom-out;
animation-duration: 2s;
animation-iteration-count: infinite;
}
#red {
stroke: red;
fill: red;
animation-delay: 1.5s;
}
#keyframes zoom-out {
0% {
transform: scale(5, 5);
}
15% {
transform: scale(2, 2);
}
100% {
transform: scale(1, 1);
}
}
svg{border:solid}
<svg viewBox="0 0 1000 500">
<defs>
<g id="red">
<g class="wrap">
<path id="body" fill-rule="evenodd" clip-rule="evenodd" d="M121.506,108.953c3.145-2.115,5.896-3.967,8.624-5.802 c20.948,12.522,42.66,12.281,65.725,4.354c0.778,3.128,1.687,6.18,2.285,9.291c3.208,16.677,0.616,36.326-2.758,52.719
c0,0-152.162,0.035-154.82,0.035c8.408,10.604,18.647,16.674,31.173,16.227c15.059-0.536,30.099-2.491,45.07-4.433
c26.453-3.431,50.783,0.317,70.991,19.392c1.675,1.581,7.179,9.382,3.632,13.47c-3.524,4.062-12.062-1.289-13.795-3.036
c-10.215-10.294-22.693-16.145-37.008-15.98c-14.568,0.166-29.103,2.376-43.679,3.216c-11.405,0.656-22.888,1.255-34.268,0.634
c-9.862-0.538-18.646-5.258-25.691-12.131c-15.127-14.758-26.56-31.716-26.923-53.792c-0.396-24.125,17.008-44.198,40.835-48.153
c23.477-3.897,43.372,4.666,62.051,17.569C115.82,104.515,118.537,106.717,121.506,108.953z"/>
<path id="head" fill-rule="evenodd" clip-rule="evenodd" d="M129.747,18.651c3.646,6.147,7.048,11.646,10.189,17.291
c1.404,2.523,2.761,3.481,5.888,2.718c14.09-3.439,28.227-3.932,42.396-0.046c1.308,0.358,3.815-0.733,4.608-1.923
c4.043-6.072,7.705-12.398,11.814-19.149c8.693,15.648,15.012,31.447,13.169,49.204c-1.48,14.266-9.114,24.946-22.028,31.172
c-17.641,8.503-35.969,9.511-54.067,1.823c-15.169-6.443-22.96-18.723-23.677-35.151C117.396,49.828,122.038,32.188,129.747,18.651z
M189.467,81.017c7.232,0.084,15.334-6.867,14.292-13.652c-0.832-5.418-11.566-6.019-11.732-6.025
c-7.238-0.308-13.768,6.133-14.144,13.949C177.731,78.444,182.773,80.938,189.467,81.017z M145.369,81.453
c3.597,0.294,11.258-2.441,11.324-6.992c0.079-5.443-3.357-10.158-8.897-12.255c-5.807-2.197-16.523,1.484-17.065,5.19
C129.692,74.494,138.107,81.089,145.369,81.453z"/>
</g>
</g>
</defs>
<use xlink:href="#red" x="300" y="175" />
</svg>
In order to zero out the coordinates for a collection of paths I'm using javaScript.
first I'm wrapping the paths in a group class="wrap"
I'm getting the bounding box of the wrap: let bb = wrap.getBBox();
I'm using the bounding box values to calculate the required translation and set the transform attribute of the wrap.
Hello I'am trying to chain two animations.
Now what happens is that after moveUp finished, my triangle jump back to place and then starts scaleDown. Why triangle jumps back when i specify forwards parametr which tells it should stop at last keyframes option.
I have no idea what goes wrong here.
#bottom-rect {
animation: moveUp 2s forwards, scaleDown 1s 1s forwards;
}
#keyframes moveUp {
0% {
transform: translateY(0);
}
100% {
transform: translateY(-25%);
}
}
#keyframes scaleDown {
0% {
transform: scaleY(1);
transform-origin: center;
transform-box: fill-box;
}
100% {
transform: scaleY(0);
transform-origin: center;
transform-box: fill-box;
}
}
<svg width="135" height="216" viewBox="0 0 135 216" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="svg">
<path id="bottom-rect" d="M81.2 216V189L54 216H81.2Z" fill="black"/>
<path id="top-rect" d="M54.2 0V27L81 0H54.2Z" fill="black"/>
<path id="Vector" d="M0 162H36L135 54H99L0 162Z" fill="black"/>
</g>
</svg>
The effect i need to achieve is basically this: 0sec (moveUp starts) -> 1sec (scaleDown starts) -> 2s both finished.
It's actually possible to do additive animations in CSS. In this case you can just add an element around your path tag. So with an svg we can add an additional g tag that I will name animation2 in my example.
#bottom-rect {
animation: scaleDown 1s 1s forwards;
}
#animation2{
animation: moveUp 2s forwards;
}
#keyframes moveUp {
0% {
transform: translateY(0);
}
100% {
transform: translateY(-25%);
}
}
#keyframes scaleDown {
0% {
transform: scaleY(1);
transform-origin: center;
transform-box: fill-box;
}
100% {
transform: scaleY(0);
transform-origin: center;
transform-box: fill-box;
}
}
<svg width="135" height="216" viewBox="0 0 135 216" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="svg">
<g id = "animation2">
<path id="bottom-rect" d="M81.2 216V189L54 216H81.2Z" fill="black"/>
</g>
<path id="top-rect" d="M54.2 0V27L81 0H54.2Z" fill="black"/>
<path id="Vector" d="M0 162H36L135 54H99L0 162Z" fill="black"/>
</g>
</svg>
I tried several ways to svg animation, but i can't do finish animation after hover off. I have seen some hints on stackoverflow, but in my situation it does not work. In my example I have few options animations in keyframes.
https://codepen.io/GuyDiamond/pen/QBpQze
<rect x="0" y="5" width="100" height="100" fill="red" />
<path id="heart" class="heart" d="M 10,30 A 20,20 0,0,1 50,30 A 20,20 0,0,1 90,30 Q 90,60 50,90 Q 10,60 10,30 z" fill="#fff"/>
<style type="text/css">
svg:hover .heart {
animation-timing-function: ease-in-out;
animation-duration: 4s;
animation-iteration-count: infinite;
animation-name: rotation;
transition: .5s;
animation-direction: normal;
animation-fill-mode: forwards;
}
#keyframes rotation {
50% {
transform: rotateY(180deg);
opacity:0;
}
60% {
transform: translate(100px, 0);
opacity:0;
}
100% {
transform: translate(0, 0);
opacity:1;
}
}
You can't do what you want with pure CSS. You need to use JS.
Here's one way, using the animationiteration event, which fires each time the animation loop ends.
// Get the SVG DOM object
var mysvg = document.getElementById("mysvg");
var running = false;
// On hover add the "run" class, which makes the animation run
mysvg.addEventListener("mouseenter", function(evt) {
evt.target.classList.add("run");
running = true;
});
// On mouse out, arrange to remove the "run" class when the animation loop ends
mysvg.addEventListener("mouseleave", function(evt) {
running = false;
});
// When animation loop ends, remove the "run" class if we no longer want to continue running
mysvg.addEventListener("animationiteration", function(evt) {
if (!running) {
evt.target.ownerSVGElement.classList.remove("run");
}
});
.box {
width: 300px;
height: 500px;
}
<div class="box">
<svg id="mysvg" width="100%" height="100%" viewBox="-30 0 250 500" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
<rect x="0" y="5" width="100" height="100" fill="red" />
<path id="heart" class="heart" d="M 10,30 A 20,20 0,0,1 50,30 A 20,20 0,0,1 90,30 Q 90,60 50,90 Q 10,60 10,30 z" fill="#fff"/>
<style type="text/css">
svg .heart {
}
.run .heart {
animation-timing-function: ease-in-out;
animation-duration: 4s;
animation-iteration-count: infinite;
animation-name: rotation;
animation-direction: normal;
animation-fill-mode: forwards;
}
#keyframes rotation {
50% {
transform: rotateY(180deg);
opacity:0;
}
60% {
transform: translate(100px, 0);
opacity:0;
}
100% {
transform: translate(0, 0);
opacity:1;
}
}
</style>
</svg>
</div>
You mean the animation ends suddenly? That's because you've defined the transition in the hover so as soon as the hover ends there's no active transition any more.
.box {
width: 300px;
height: 500px;
}
<div class="box">
<svg width="100%" height="100%" viewBox="-30 0 250 500" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
<rect x="0" y="5" width="100" height="100" fill="red" />
<path id="heart" class="heart" d="M 10,30 A 20,20 0,0,1 50,30 A 20,20 0,0,1 90,30 Q 90,60 50,90 Q 10,60 10,30 z" fill="#fff"/>
<style type="text/css">
svg .heart {
transition: .5s;
}
svg:hover .heart {
animation-timing-function: ease-in-out;
animation-duration: 4s;
animation-iteration-count: infinite;
animation-name: rotation;
animation-direction: normal;
animation-fill-mode: forwards; }
#keyframes rotation {
50% {
transform: rotateY(180deg);
opacity:0;
}
60% {
transform: translate(100px, 0);
opacity:0;
}
100% {
transform: translate(0, 0);
opacity:1;
}
}
</style>
</svg>
</div>
By changing the animation-play-state on hover I think it could work
https://codepen.io/dok/pen/ZjKGRE
svg #heart {
animation-timing-function: ease-in-out;
animation-duration: 4s;
animation-iteration-count: infinite;
animation-name: rotation;
animation-direction: normal;
animation-fill-mode: forwards;
animation-play-state: paused;
}
svg .heart:hover #heart {
animation-play-state: running;
}