I am trying to set up an animation where, on hover, an arrow within my circular button animates out of the button to the right and then comes back in from the left. It would start animating slowly to the right and speed up while leaving the boundary of the circle and then it would re-enter from the left and slow down as it reaches the starting point again. I was able to set up some keyframes to mostly accomplish this, but it is not very smooth when it re-enters from the left.
button {
display: inline-flex;
width:50px;
height:50px;
align-items:center;
justify-content:center;
border-radius:50px;
border:none;
-webkit-appearance:none;
appearance:none;
position:relative;
overflow:hidden;
}
button img {
display:block;
width:30px;
}
button:hover img {
animation: train;
animation-duration: 3s;
animation-timing-function: cubic-bezier(0.785, 0.135, 0.06, 1.01);
}
@keyframes train {
50% {
transform: translateX(calc(100% + 30px));
}
50.001% {
transform: translateX(-100%);
}
100% {
transform:translateX(0);
}
}
<button><img alt="" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjIuODggMTA4LjA2IiB4bWxuczp2PSJodHRwczovL3ZlY3RhLmlvL25hbm8iPjxwYXRoIGQ9Ik01OC45NCAyNC4yOGExNC4yNyAxNC4yNyAwIDAgMSAyMC4zNS0yMGwzOS40OSA0MC4xNmExNC4yOCAxNC4yOCAwIDAgMSAwIDIwbC0zOC42OSAzOS4zNWExNC4yNyAxNC4yNyAwIDEgMS0yMC4zNS0yMGwxNS4wOC0xNS4zOC02MC42Ny0uMjlhMTQuMjcgMTQuMjcgMCAwIDEgLjI0LTI4LjU0bDU5Ljg1LjI4LTE1LjMtMTUuNTh6Ii8+PC9zdmc+" /></button>
You have to use half the sum of both width (50px + 30px)/2 = 40px
to make sure your element fully exit its container. Also note that 100% in translate refers to the arrow width, not the button width.
button {
display: inline-grid;
width: 50px;
aspect-ratio: 1;
place-items: center;
border-radius: 50%;
border: none;
appearance: none;
overflow: hidden;
}
button img {
display: block;
width: 30px;
}
button:hover img {
animation: train 3s cubic-bezier(0.785, 0.135, 0.06, 1.01);
}
@keyframes train {
50% {
translate: 40px;
}
50.1% {
translate: -40px;
}
}
<button><img alt="" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjIuODggMTA4LjA2IiB4bWxuczp2PSJodHRwczovL3ZlY3RhLmlvL25hbm8iPjxwYXRoIGQ9Ik01OC45NCAyNC4yOGExNC4yNyAxNC4yNyAwIDAgMSAyMC4zNS0yMGwzOS40OSA0MC4xNmExNC4yOCAxNC4yOCAwIDAgMSAwIDIwbC0zOC42OSAzOS4zNWExNC4yNyAxNC4yNyAwIDEgMS0yMC4zNS0yMGwxNS4wOC0xNS4zOC02MC42Ny0uMjlhMTQuMjcgMTQuMjcgMCAwIDEgLjI0LTI4LjU0bDU5Ljg1LjI4LTE1LjMtMTUuNTh6Ii8+PC9zdmc+" /></button>
To avoid pixel values, express the width in percentage and update the code like below:
button {
display: inline-grid;
width: 50px;
aspect-ratio: 1;
place-items: center;
border-radius: 50%;
border: none;
padding: 0;
appearance: none;
overflow: hidden;
}
button img {
display: block;
width: 60%;
}
button:hover img {
animation: train 3s cubic-bezier(0.785, 0.135, 0.06, 1.01);
}
@keyframes train {
50% {
translate: calc( 50%*(1 + 1/.6)); /* .6 = 60% */
}
50.1% {
translate: calc(-50%*(1 + 1/.6));
}
}
<button><img alt="" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjIuODggMTA4LjA2IiB4bWxuczp2PSJodHRwczovL3ZlY3RhLmlvL25hbm8iPjxwYXRoIGQ9Ik01OC45NCAyNC4yOGExNC4yNyAxNC4yNyAwIDAgMSAyMC4zNS0yMGwzOS40OSA0MC4xNmExNC4yOCAxNC4yOCAwIDAgMSAwIDIwbC0zOC42OSAzOS4zNWExNC4yNyAxNC4yNyAwIDEgMS0yMC4zNS0yMGwxNS4wOC0xNS4zOC02MC42Ny0uMjlhMTQuMjcgMTQuMjcgMCAwIDEgLjI0LTI4LjU0bDU5Ljg1LjI4LTE1LjMtMTUuNTh6Ii8+PC9zdmc+" /></button>
<button style="width: 100px"><img alt="" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjIuODggMTA4LjA2IiB4bWxuczp2PSJodHRwczovL3ZlY3RhLmlvL25hbm8iPjxwYXRoIGQ9Ik01OC45NCAyNC4yOGExNC4yNyAxNC4yNyAwIDAgMSAyMC4zNS0yMGwzOS40OSA0MC4xNmExNC4yOCAxNC4yOCAwIDAgMSAwIDIwbC0zOC42OSAzOS4zNWExNC4yNyAxNC4yNyAwIDEgMS0yMC4zNS0yMGwxNS4wOC0xNS4zOC02MC42Ny0uMjlhMTQuMjcgMTQuMjcgMCAwIDEgLjI0LTI4LjU0bDU5Ljg1LjI4LTE1LjMtMTUuNTh6Ii8+PC9zdmc+" /></button>