77 lines
2.9 KiB
JavaScript
77 lines
2.9 KiB
JavaScript
import { warning } from "hey-listen";
|
|
import { clamp } from "../../utils/clamp";
|
|
const safeMin = 0.001;
|
|
export const minDuration = 0.01;
|
|
export const maxDuration = 10.0;
|
|
export const minDamping = 0.05;
|
|
export const maxDamping = 1;
|
|
export function findSpring({ duration = 800, bounce = 0.25, velocity = 0, mass = 1, }) {
|
|
let envelope;
|
|
let derivative;
|
|
warning(duration <= maxDuration * 1000, "Spring duration must be 10 seconds or less");
|
|
let dampingRatio = 1 - bounce;
|
|
dampingRatio = clamp(minDamping, maxDamping, dampingRatio);
|
|
duration = clamp(minDuration, maxDuration, duration / 1000);
|
|
if (dampingRatio < 1) {
|
|
envelope = (undampedFreq) => {
|
|
const exponentialDecay = undampedFreq * dampingRatio;
|
|
const delta = exponentialDecay * duration;
|
|
const a = exponentialDecay - velocity;
|
|
const b = calcAngularFreq(undampedFreq, dampingRatio);
|
|
const c = Math.exp(-delta);
|
|
return safeMin - (a / b) * c;
|
|
};
|
|
derivative = (undampedFreq) => {
|
|
const exponentialDecay = undampedFreq * dampingRatio;
|
|
const delta = exponentialDecay * duration;
|
|
const d = delta * velocity + velocity;
|
|
const e = Math.pow(dampingRatio, 2) * Math.pow(undampedFreq, 2) * duration;
|
|
const f = Math.exp(-delta);
|
|
const g = calcAngularFreq(Math.pow(undampedFreq, 2), dampingRatio);
|
|
const factor = -envelope(undampedFreq) + safeMin > 0 ? -1 : 1;
|
|
return (factor * ((d - e) * f)) / g;
|
|
};
|
|
}
|
|
else {
|
|
envelope = (undampedFreq) => {
|
|
const a = Math.exp(-undampedFreq * duration);
|
|
const b = (undampedFreq - velocity) * duration + 1;
|
|
return -safeMin + a * b;
|
|
};
|
|
derivative = (undampedFreq) => {
|
|
const a = Math.exp(-undampedFreq * duration);
|
|
const b = (velocity - undampedFreq) * (duration * duration);
|
|
return a * b;
|
|
};
|
|
}
|
|
const initialGuess = 5 / duration;
|
|
const undampedFreq = approximateRoot(envelope, derivative, initialGuess);
|
|
duration = duration * 1000;
|
|
if (isNaN(undampedFreq)) {
|
|
return {
|
|
stiffness: 100,
|
|
damping: 10,
|
|
duration,
|
|
};
|
|
}
|
|
else {
|
|
const stiffness = Math.pow(undampedFreq, 2) * mass;
|
|
return {
|
|
stiffness,
|
|
damping: dampingRatio * 2 * Math.sqrt(mass * stiffness),
|
|
duration,
|
|
};
|
|
}
|
|
}
|
|
const rootIterations = 12;
|
|
function approximateRoot(envelope, derivative, initialGuess) {
|
|
let result = initialGuess;
|
|
for (let i = 1; i < rootIterations; i++) {
|
|
result = result - envelope(result) / derivative(result);
|
|
}
|
|
return result;
|
|
}
|
|
export function calcAngularFreq(undampedFreq, dampingRatio) {
|
|
return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio);
|
|
}
|
|
//# sourceMappingURL=find-spring.js.map
|