JSFiddle - React, Tailwind, and code Playground
HTML
Overshoot: <input id="overshoot" type="number" size="4" value="1.5" step="0.1">
Elasticity: <input id="elasticity" type="number" size="3" value="3" step="0.5"><br>
<svg width="500" height="500">
<g transform="translate(30 20)" id="plot-area"></g>
</svg>
CSS
.axis, .overshootLine, .oneLine {
fill: none;
stroke-width: 1;
}
.axis {
stroke: black;
}
.overshootLine {
stroke: grey;
stroke-dasharray: 2 4;
}
.oneLine {
stroke: maroon;
}
.resultLine {
stroke-width: 2;
stroke: blue;
fill: none;
stroke-linejoin: round;
}
.xAxisLabel {
fill: grey;
font-size: 10px;
text-anchor: middle;
}
.yAxisLabel {
font-size: 12px;
text-anchor: end;
}
}
.tick {
fill: none;
stroke: grey;
stroke-width: 0.5;
}
JavaScript
var overshootField = document.getElementById('overshoot'),
elasticityField = document.getElementById('elasticity'),
plot = document.getElementById("plot-area"),
SVGNS = "http://www.w3.org/2000/svg";
function getY(x, overshoot, elasticity) {
// --------------- HELP NEEDED ------------------
// This function here needs your help!!!
// See http://brian.sol1.net/svg/ for details
//
// You can ignore the rest of the fiddle.
// However, one extension which might be helpful
// is to add a bouncing ball animation at the
// bottom and simulate the effect timing function
// (e.g. using SVG's keyTimes and keyPoints).
// ----------------------------------------------
// Sine wave
var compression = 2;
var numCycles = (Math.floor(overshoot * elasticity) + 0.5) * 2;
var cos = Math.cos(numCycles/2*Math.pow(x,compression)*Math.PI);
// Work out where the first peak lies
var firstPeak = Math.pow(numCycles/2,-1/compression);
// Adjust the amount of damping so there's none around the first peak
var dampingFactor = x > firstPeak ? (1 - x) / (1 - firstPeak)
: x / firstPeak;
var scaleFactor = Math.log(overshoot-1) / (-firstPeak*Math.E)-1;
return 1 - Math.exp(-x*(dampingFactor*scaleFactor+1)*Math.E)*cos;
}
function update() {
var points = [],
overshoot = overshootField.value,
elasticity = elasticityField.value;
for (var i = 0; i <= 100; i++) {
points.push({ x: i/100, y: getY(i/100, overshoot, elasticity) });
}
updateChart(points);
}
function updateChart(points) {
var yBounds = points.reduce(function(prev, curr) {
return { min: Math.floor(Math.min(prev.min, curr.y)),
max: Math.ceil(Math.max(prev.max, curr.y)) };
}, { min: -1, max: 2 });
var yScale = 400 / (yBounds.max - yBounds.min);
var toPlotSpace = function(y) {
return (yBounds.max - y) * yScale;
};
// Shift lines
moveObject("xAxisGroup", [ 0, toPlotSpace(0) ]);
...