tossable
Version:
Generated by ambients-cli
116 lines (98 loc) • 3.02 kB
text/typescript
import { inertia, pointer, value, calc, ColdSubscription } from "popmotion";
import { Cancellable } from "@lincode/promiselikes";
import "../assets/style.css";
import TouchLib from "touchlib";
import { extendFunction } from "@lincode/utils";
export class TossableHandle extends Cancellable {
public constructor(
cb: () => void,
public set: (val: number) => void,
public enable: () => void,
public disable: () => void
) {
super(cb);
}
}
type Options = {
min?: number,
max?: number,
start?: number,
tug?: number,
power?: number,
bounceStiffness?: number,
bounceDamping?: number,
speed?: number,
touchTarget: HTMLElement,
current?: () => number,
step: (val: number) => void,
onComplete?: () => void,
axis?: "x" | "y"
}
export default ({
min = 0,
max = 500,
start = min,
tug = 0.2,
power = 0.6,
bounceStiffness = 400,
bounceDamping = 20,
speed = 1,
touchTarget,
current,
step,
onComplete,
axis = "x"
}: Options) => {
const reaction = value(start, step);
onComplete && (reaction.complete = extendFunction(reaction.complete, onComplete));
let trackerStart: ColdSubscription | undefined;
let trackerStop: ColdSubscription | undefined;
const touch = new TouchLib(touchTarget);
touchTarget.classList.add("tossable-" + axis);
const handle0 = touch.on("panstart", e => {
trackerStart?.stop();
trackerStop?.stop();
const isVertical = Math.abs(e.deltaX) < Math.abs(e.deltaY) / 2;
if ((axis === "x" && isVertical) || (axis === "y" && !isVertical))
return;
const reactionStart = current?.() ?? reaction.get() as number;
const applyOverdrag = (v: number) => {
if (v < min) return calc.getValueFromProgress(min, v, tug);
if (v > max) return calc.getValueFromProgress(max, v, tug);
return v;
}
trackerStop = undefined;
trackerStart = pointer({ preventDefault: false })
.pipe((v: { x: number, y: number }) => reactionStart + (v[axis] - e[axis]) * speed, applyOverdrag)
.start(reaction);
});
const handle1 = touch.on("panend", () => {
trackerStart?.stop();
trackerStop?.stop();
trackerStart = undefined;
//@ts-ignore
trackerStop = inertia({
min,
max,
from: reaction.get(),
velocity: reaction.getVelocity(),
power,
bounceStiffness,
bounceDamping
}).start(reaction);
});
return new TossableHandle(
() => {
handle0.cancel();
handle1.cancel();
}, val => {
trackerStart?.stop();
trackerStop?.stop();
trackerStart = undefined;
trackerStop = undefined;
reaction.update(val);
},
() => touch.enable(),
() => touch.disable()
);
}