Split the bill by dragging
Drag each item off the receipt onto whoever had it, and their share rolls up, service included.
Expo · Reanimated · Gesture Handler · Expo UI (SwiftUI) · React Native Skia · Expo Haptics
1Demo
2Flow
- 1Receipt printsAppout of a slot in line-sized feeds, a haptic tick each
- 2Long-press a lineYoua ghost of the row lifts and follows your finger
- 3Drop it on a faceYouor on Everyone to split it evenly between all three
- 4The total rollsAppthe ghost flies in, the face bumps, a firm tap, then the digits roll
- 5Tap SettleYouthe receipt tears off with a heavy tap and everyone gets a stub with their share
3How it fits
4Code
const drop = useCallback(
(x: number, y: number) => {
const index = dragging;
if (index < 0) return;
const hit = layout.targets.findIndex((t) => Math.hypot(t.x - x, t.y - y) < 58);
if (hit < 0) {
gx.value = withSpring(layout.rowX, { damping: 16 });
gy.value = withSpring(layout.rowY(index), { damping: 16 }, () => {
go.value = 0;
});
gs.value = withSpring(1);
setTimeout(() => setDragging(-1), 320);
return;
}
const t = layout.targets[hit];
const ease = { duration: 360, easing: Easing.in(Easing.cubic) };
gx.value = withTiming(t.x - layout.rowW / 2, ease);
gy.value = withTiming(t.y - ROW / 2, ease);
gs.value = withTiming(0.12, ease);
go.value = withTiming(0, { duration: 380 });
setTimeout(() => {
feedback.land();
bumps[hit].value = withSequence(withTiming(1.16, { duration: 120 }), withSpring(1, { damping: 9 }));
if (hit === 3) for (let i = 0; i < 3; i += 1) bumps[i].value = withSequence(withTiming(1.1, { duration: 140 }), withSpring(1, { damping: 10 }));
setAssigned((a) => ({ ...a, [ITEMS[index].id]: hit === 3 ? EVERYONE : PEOPLE[hit].id }));
setDragging(-1);
}, 340);
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[dragging, go, gs, gx, gy, layout],
);The ghost flies into the face first; the item is only assigned 340 ms later, so the total rolls as it lands.
5Decisions
Assign the item 340 ms after the drop over assigning it the moment you let go
the ghost reaches the face first, so the total rolls as it arrives, not before
Reveal the receipt in line-sized feeds, a tick each over one smooth slide out of the slot
it judders out like thermal paper
SwiftUI's numeric text, through Expo UI over rolling digits rebuilt in Reanimated
it is the system's own digit roll, not an imitation of it
Whole cents, service pro rata over dollars with service split evenly
whoever ordered more pays more of the service, and every share is whole cents
