Reputation: 14545
The question was initiated by a topic
In that question, the bounces were vertical
<svg id="svg1" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="200" height="200" viewBox="0 0 200 200" preserveAspectRatio="xMinYMin meet" style="border:1px solid" >
<image xlink:href="https://i.sstatic.net/hXyA5.png" x="82" width="25px" height="25px" >
<animateTransform id="anT"
attributeName="transform"
type="translate"
dur="3s"
begin="svg1.click+0.5s;anT.end+1s"
values="
0,0;
0,168;
0,84;
0,168;
0,126;
0,168;
0,148;
0,168;
0,158;
0,168;
0,163;
0,168;
0,166;
0,168;
"
keyTimes="0;0.066;0.13;0.198;0.264;0.33;0.396;0.462;0.528;0.594;0.66;0.726;0.792;1"
repeatCount="1"
fill="freeze"
restart="whenNotActive" />
</image>
<polyline points="5,193 194,193" stroke="silver" stroke-width="4" />
</svg>
This question concerns bounces with different amounts of offset in height and length
But it is difficult to realize the unevenness of movement and speed.
How to implement realistic parabolic ball movement?
Any idea and solution would be appreciated.
Upvotes: 4
Views: 626
Reputation: 272744
CSS animation can approximate this better. You need to:
Then you need to correctly calculate the delay based on the duration (the previous animation delay + 2*previous aniamation duration
).
The main trick is that using altenate
and running each animation twice will give you the mirror effect and a perfect parabolic curve (half of it defined by the bezier curve)
.box {
width: 200px;
height: 190px;
border: 1px solid;
display: flex;
justify-content: center;
align-items: flex-end;
}
img {
--d:0.8s; /*duration factor*/
width:30px;
animation-timing-function:cubic-bezier(.17,.76,.58,1); /* control this: https://cubic-bezier.com/#.17,.76,.58,1*/
animation-iteration-count:2; /* don't change*/
animation-direction: alternate; /* don't change */
animation-name:t1,t2,t3,t4,t5; /* don't change unless you need more steps*/
animation-duration:
var(--d),
calc(var(--d)/2),
calc(var(--d)/3),
calc(var(--d)/4),
calc(var(--d)/5);
animation-delay:
calc(var(--d)*-1),
calc(var(--d)),
calc(var(--d) + 2*var(--d)/2),
calc(var(--d)*2 + 2*var(--d)/3),
calc(var(--d)*2 + 2*var(--d)/3 + 2*var(--d)/4);
}
@keyframes t1{to {transform:translateY(-160px)}}
@keyframes t2{to {transform:translateY(-110px)}}
@keyframes t3{to {transform:translateY(-60px)}}
@keyframes t4{to {transform:translateY(-30px)}}
@keyframes t5{to {transform:translateY(-10px)}}
<div class="box">
<img src="https://i.sstatic.net/hXyA5.png">
<div>
Adding the horizontal movement:
.box {
--d:0.8s; /*duration factor*/
width: 200px;
height: 190px;
border: 1px solid;
display: flex;
align-items: flex-end;
overflow:hidden;
}
img {
width:30px;
animation-timing-function:cubic-bezier(.17,.76,.58,1); /* control this: https://cubic-bezier.com/#.17,.76,.58,1*/
animation-iteration-count:2; /* don't change*/
animation-direction: alternate; /* don't change */
animation-name:t1,t2,t3,t4,t5; /* don't change unless you need more steps*/
animation-duration:
var(--d),
calc(var(--d)/2),
calc(var(--d)/3),
calc(var(--d)/4),
calc(var(--d)/5);
animation-delay:
calc(var(--d)*-1),
calc(var(--d)),
calc(var(--d) + 2*var(--d)/2),
calc(var(--d)*2 + 2*var(--d)/3),
calc(var(--d)*2 + 2*var(--d)/3 + 2*var(--d)/4);
}
@keyframes t1{to {transform:translateY(-160px)}}
@keyframes t2{to {transform:translateY(-110px)}}
@keyframes t3{to {transform:translateY(-60px)}}
@keyframes t4{to {transform:translateY(-30px)}}
@keyframes t5{to {transform:translateY(-10px)}}
.box > span {
animation:m calc(var(--d)*2 + 2*var(--d)/3 + 2*var(--d)/4 + 2*var(--d)/5) linear forwards;
}
@keyframes m{to {transform:translateX(150px)}}
<div class="box">
<span><img src="https://i.sstatic.net/hXyA5.png"></span>
<div>
Upvotes: 4
Reputation: 14545
Svg smil solution
The trajectory of the ball was created from the picture in the question in the vector editor Inkscape
For animation of movement along the trajectory I used - animateMotion
The app is responsive and looks more interesting on full screen
.container {
width:60%;
height:60%;
}
<div class="container">
<svg id="svg1" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 488 232" preserveAspectRatio="xMinYMin meet" style="border:1px solid" >
<defs>
<!--Parabolic trajectory of movement and bounce of the ball -->
<path id="trace" d="m 25.652812,16.016049 v 181.392771 0 0 l -0.596577,25.31062 c 0,0 12.411076,-62.72171 21.476772,-93.33069 5.109455,-17.25132 11.107049,-34.303277 18.493888,-50.709043 6.68532,-14.84773 11.981393,-31.214941 23.266504,-42.953546 5.128983,-5.335092 11.703068,-10.892125 19.090461,-11.334963 9.33174,-0.559391 18.66956,5.119788 25.65281,11.334963 12.5853,11.201058 18.46109,28.314233 25.65282,43.550123 7.68011,16.270538 13.21524,33.508736 18.49388,50.709046 9.35008,30.46698 17.6561,61.30229 23.26651,92.73411 0,0 7.63247,-46.2686 13.72127,-68.87103 4.15486,-15.42339 7.4192,-31.5679 15.511,-45.33985 5.42122,-9.226722 10.78091,-23.515729 21.47677,-23.863083 11.38465,-0.369722 18.27651,14.296709 24.45966,23.863083 8.5031,13.15573 11.7205,29.10909 16.10758,44.1467 6.69922,22.96293 9.48984,49.49348 15.511,70.06418 5.7106,-34.92901 6.57028,-45.00528 16.10758,-65.29157 5.26265,-11.19392 9.73289,-28.98491 22.07335,-29.82885 11.92521,-0.81554 20.99188,14.41196 25.05624,25.65281 4.04949,11.19974 6.57393,20.61892 9.54523,31.02201 3.6399,12.74398 5.25389,18.16556 10.14181,38.4456 2.98041,-18.27579 1.7071,-16.73913 4.17604,-24.72433 1.6821,-5.44034 3.72502,-10.96841 7.15892,-15.511 3.31674,-4.3876 7.05439,-11.27709 12.52812,-10.73839 7.04712,0.69355 9.92525,10.387 13.12469,16.70416 5.35774,10.57863 5.4736,18.42066 9.54524,34.26956 0.57788,-7.32318 1.59738,-10.47263 1.78973,-13.98594 0.5125,-9.36095 6.36879,-18.32474 10.84695,-17.16023 9.45261,2.45807 13.01612,16.09152 13.01612,16.09152" style="fill:none;stroke-opacity:0.9;stroke-width:2;stroke:#e8204f"/>
</defs>
<!-- Tennis ball -->
<image xlink:href="https://i.sstatic.net/hXyA5.png" x="0" y="0" width="25px" height="25px" >
<animateMotion id="anT"
dur="1.5s"
begin="svg1.click+0.5s;anT.end+1s"
repeatCount="1"
fill="freeze"
restart="whenNotActive" >
<mpath xlink:href="#trace" />
</animateMotion>
</image>
</svg>
</div>
Upvotes: 2
Reputation: 101800
I allowed for this behaviour in my previous answer (in the linked question). Just set dx
to a non-zero value.
ball = {x: 82, y: 0, dx: 1, dy: 0};
x
and y
are the starting position of the balldx
and dy
are the initial velocity of the balllet ballElem = document.getElementById("ball");
let GRAVITY = 40; // Acceleration due to gravity (pixels / sec /sec)
let FLOOR_Y = 200 - 25; // Y coord of floor. The 25 here is because ball.y is the top of the ball.
let BOUNCINESS = 0.8; // Velocity retained after a bounce
let LIMIT = 0.1; // Minimum velocity required to keep animation running
let ball = {};
let lastFrameTime = null;
ballElem.addEventListener("click", startAnim);
function startAnim()
{
ball = {x: 82, y: 0, dx: 1, dy: 0};
lastFrameTime = null;
requestAnimationFrame(animStep);
}
function animStep(timestamp)
{
if (lastFrameTime === null)
lastFrameTime = timestamp;
// Milliseconds elapsed since last step
const elapsed = timestamp - lastFrameTime;
lastFrameTime = timestamp;
ball.dy += GRAVITY * elapsed / 1000;
ball.y += ball.dy;
ball.x += ball.dx; // not really used in this example
if (ball.y > FLOOR_Y) {
// Step has taken us below the floor, so we need to rebound the ball.
ball.y -= (ball.y - FLOOR_Y);
ball.dy = -ball.dy * BOUNCINESS;
}
// Update the <image> element x and y
ballElem.x.baseVal.value = ball.x;
ballElem.y.baseVal.value = ball.y;
// Request another animation step
if (Math.abs(ball.y - FLOOR_Y) > LIMIT || // Not on ground
Math.abs(ball.dy) > LIMIT || // or still moving
Math.abs(ball.dx) > LIMIT) {
requestAnimationFrame(animStep);
}
}
<svg id="svg1"
width="200" height="200" viewBox="0 0 200 200" preserveAspectRatio="xMinYMin meet" style="border:1px solid" >
<image id="ball" xlink:href="https://i.sstatic.net/hXyA5.png" x="82" width="25px" height="25px"/>
</svg>
Upvotes: 2