The only way I've been able to make the speed smoothly behave according to projectile motion (decelerating upwards, accelerating downwards) is to break the animation into its x and y components, and then use custom easing functions (splines).
The horizontal animation is easy, since the object's horizontal velocity is constant. We can animate it between beginning and end at a constant speed:
<animate id="horizontal"
attributeName="cx"
dur="5s"
values="250;462" />
The vertical component has to be broken into two animations—one for its upward path and one for its downward path. You can make them sequential by adding an id to the first and referencing it in the begin attribute of the second.
Note the different durations and values.
<animate id="vertical-up"
attributeName="cy"
dur="1.2s"
values="250;225" />
<animate id="vertical-down"
attributeName="cy"
dur="3.8s"
values="225;487"
begin="vertical-up.end" />
However, we still need to make it slow down as it ascends, and speed up as it descends. This can be done by—
- Setting the
calcMode of the animation to spline
- Adding
keyTimes of 0 and 1
- And then adding
keySplines values that map the motion of the projectile to the parabolic path.
<animate id="vertical-up"
attributeName="cy"
dur="1.2s"
values="250;225"
calcMode="spline"
keyTimes="0;1"
keySplines="0.61 1 0.88 1" />
<animate id="vertical-down"
attributeName="cy"
dur="3.8s"
values="225;487"
begin="vertical-up.end"
calcMode="spline"
keyTimes="0;1"
keySplines="0.11 0 0.5 0" />
This last step took some trial and error. I found easings.net to be a good resource in understanding what the values do.
Altogether, this results in an object that follows the parabolic curve while its horizontal velocity remains constant and its vertical velocity accelerates downward at a constant rate—just like the motion of a projectile.
Click the SVG to replay the animation.
<svg id="svg" viewBox="0 0 500 500" width="300">
<path fill="none" stroke="black" d="M250,250 Q356.06601717798213,143.93398282201787 462.13203435596427,487.86796564403573"></path>
<circle r="5" fill="red" cx="250" cy="250">
<animate attributeName="cx" dur="5s" values="250;462" begin="svg.load; svg.click"/>
<animate id="y1" attributeName="cy" dur="1.2s" values="250;225" begin="svg.load; svg.click" calcMode="spline" keyTimes="0;1" keySplines="0.61 1 0.88 1"/>
<animate attributeName="cy" dur="3.8s" values="225;487" begin="y1.end" calcMode="spline" keyTimes="0;1" keySplines="0.11 0 0.5 0"/>
</circle>
</svg>