Skip to content

Commit

Permalink
Snap points fine-tuning (#73)
Browse files Browse the repository at this point in the history
* Add new example

* Fine-tune

* Add comment

* Disable scroll damping for now

* fix bug

* Responsive

* Revert hero changes

* Add zinc back
  • Loading branch information
emilkowalski committed Aug 29, 2023
1 parent 7a5038b commit bad7bd1
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 29 deletions.
2 changes: 1 addition & 1 deletion src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ interface DrawerContextValue {
setIsAnimating: (o: boolean) => void;
keyboardIsOpen: React.MutableRefObject<boolean>;
experimentalSafariThemeAnimation: boolean;
snapPointHeights: number[] | null;
snapPointsOffset: number[] | null;
snapPoints: (number | string)[] | null;
modal: boolean;
shouldFade: boolean;
Expand Down
17 changes: 9 additions & 8 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ function Root({
activeSnapPointIndex,
setActiveSnapPoint,
onRelease: onReleaseSnapPoints,
snapPointHeights,
snapPointsOffset,
onDrag: onDragSnapPoints,
shouldFade,
getPercentageDragged: getSnapPointsPercentageDragged,
Expand Down Expand Up @@ -153,6 +153,7 @@ function Root({
swipeAmount === 0
) {
lastTimeDragPrevented.current = new Date();

return false;
}

Expand All @@ -162,7 +163,7 @@ function Root({
if (element.scrollHeight > element.clientHeight) {
if (element.role === 'dialog' || element.getAttribute('vaul-drawer')) return true;

if (element.scrollTop > 0) {
if (element.scrollTop !== 0) {
lastTimeDragPrevented.current = new Date();

// The element is scrollable and not scrolled to the top, so don't drag
Expand All @@ -171,6 +172,7 @@ function Root({

if (isDraggingDown && element !== document.body && !swipeAmount) {
lastTimeDragPrevented.current = new Date();

// Element is scrolled to the top, but we are dragging down so we should allow scrolling
return false;
}
Expand Down Expand Up @@ -287,7 +289,7 @@ function Root({
}

if (snapPoints && snapPoints.length > 0) {
const activeSnapPointHeight = snapPointHeights[activeSnapPointIndex];
const activeSnapPointHeight = snapPointsOffset[activeSnapPointIndex];
diffFromInitial += activeSnapPointHeight;
}

Expand Down Expand Up @@ -384,7 +386,6 @@ function Root({
function onRelease(event: React.PointerEvent<HTMLDivElement>) {
if (!isDragging) return;

event.preventDefault();
setIsDragging(false);

dragEndTime.current = new Date();
Expand Down Expand Up @@ -553,7 +554,7 @@ function Root({
keyboardIsOpen,
setIsAnimating,
modal,
snapPointHeights,
snapPointsOffset,
experimentalSafariThemeAnimation,
}}
>
Expand Down Expand Up @@ -604,7 +605,7 @@ const Content = React.forwardRef<HTMLDivElement, ContentProps>(function (
isOpen,
keyboardIsOpen,
setIsAnimating,
snapPointHeights,
snapPointsOffset,
setActiveSnapPoint,
snapPoints,
} = useDrawerContext();
Expand Down Expand Up @@ -651,9 +652,9 @@ const Content = React.forwardRef<HTMLDivElement, ContentProps>(function (
}}
ref={composedRef}
style={
snapPointHeights
snapPointsOffset
? ({
'--snap-point-height': `${snapPointHeights[0]}px`,
'--snap-point-height': `${snapPointsOffset[0]}px`,
...style,
} as React.CSSProperties)
: style
Expand Down
39 changes: 19 additions & 20 deletions src/use-snap-points.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export function useSnapPoints({
[snapPoints, activeSnapPoint],
);

const snapPointHeights = React.useMemo(
const snapPointsOffset = React.useMemo(
() =>
snapPoints?.map((snapPoint) => {
const isPx = typeof snapPoint === 'string';
Expand All @@ -48,27 +48,27 @@ export function useSnapPoints({
snapPointAsNumber = parseInt(snapPoint, 10);
}

const height = isPx ? snapPointAsNumber : snapPoint * window.innerHeight;
const height = isPx ? snapPointAsNumber : hasWindow ? snapPoint * window.innerHeight : 0;

return hasWindow && window.innerHeight - height;
}) ?? null,
[snapPoints],
);

const activeSnapPointHeight = React.useMemo(
() => snapPointHeights?.[activeSnapPointIndex] ?? null,
[snapPointHeights, activeSnapPoint],
const activeSnapPointOffset = React.useMemo(
() => snapPointsOffset?.[activeSnapPointIndex] ?? null,
[snapPointsOffset, activeSnapPoint],
);

function snapToPoint(height: number) {
const newSnapPointIndex = snapPointHeights?.findIndex((snapPointHeight) => snapPointHeight === height) ?? null;
const newSnapPointIndex = snapPointsOffset?.findIndex((snapPointHeight) => snapPointHeight === height) ?? null;

set(drawerRef.current, {
transition: `transform ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(',')})`,
transform: `translateY(${height}px)`,
});

if (newSnapPointIndex !== snapPointHeights.length - 1 && newSnapPointIndex !== fadeFromIndex) {
if (newSnapPointIndex !== snapPointsOffset.length - 1 && newSnapPointIndex !== fadeFromIndex) {
set(overlayRef.current, {
transition: `opacity ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(',')})`,
opacity: '0',
Expand All @@ -87,7 +87,7 @@ export function useSnapPoints({
if (activeSnapPointProp) {
const newIndex = snapPoints?.findIndex((snapPoint) => snapPoint === activeSnapPointProp) ?? null;

snapToPoint(snapPointHeights[newIndex]);
snapToPoint(snapPointsOffset[newIndex]);
}
}, [activeSnapPointProp]);

Expand All @@ -100,7 +100,7 @@ export function useSnapPoints({
closeDrawer: () => void;
velocity: number;
}) {
const currentPosition = activeSnapPointHeight - draggedDistance;
const currentPosition = activeSnapPointOffset - draggedDistance;
const isOverlaySnapPoint = activeSnapPointIndex === fadeFromIndex - 1;
const isFirst = activeSnapPointIndex === 0;

Expand All @@ -116,12 +116,12 @@ export function useSnapPoints({
}

if (velocity > 2 && draggedDistance > 0) {
snapToPoint(snapPointHeights[snapPoints.length - 1]);
snapToPoint(snapPointsOffset[snapPoints.length - 1]);
return;
}

// Find the closest snap point to the current position
const closestSnapPoint = snapPointHeights?.reduce((prev, curr) => {
const closestSnapPoint = snapPointsOffset?.reduce((prev, curr) => {
return Math.abs(curr - currentPosition) < Math.abs(prev - currentPosition) ? curr : prev;
});

Expand All @@ -135,21 +135,20 @@ export function useSnapPoints({
closeDrawer();
}

snapToPoint(snapPointHeights[activeSnapPointIndex + dragDirection]);
snapToPoint(snapPointsOffset[activeSnapPointIndex + dragDirection]);
return;
}

snapToPoint(closestSnapPoint);
}

function onDrag({ draggedDistance }: { draggedDistance: number }) {
const newYValue = activeSnapPointHeight - draggedDistance;

if (isLastSnapPoint && newYValue < activeSnapPointHeight) {
const dampenedDraggedDistance = dampenValue(draggedDistance);
const newYValue = activeSnapPointOffset - draggedDistance;

if (newYValue < snapPointsOffset[snapPointsOffset.length - 1]) {
setActiveSnapPoint(snapPoints?.[snapPoints.length - 1] ?? null);
set(drawerRef.current, {
transform: `translateY(${Math.min(activeSnapPointHeight - dampenedDraggedDistance, activeSnapPointHeight)}px)`,
transform: `translateY(${0}px)`,
});
return;
}
Expand Down Expand Up @@ -177,8 +176,8 @@ export function useSnapPoints({

// Get the distance from overlaySnapPoint to the one before or vice-versa to calculate the opacity percentage accordingly
const snapPointDistance = isOverlaySnapPoint
? snapPointHeights[targetSnapPointIndex] - snapPointHeights[targetSnapPointIndex - 1]
: snapPointHeights[targetSnapPointIndex + 1] - snapPointHeights[targetSnapPointIndex];
? snapPointsOffset[targetSnapPointIndex] - snapPointsOffset[targetSnapPointIndex - 1]
: snapPointsOffset[targetSnapPointIndex + 1] - snapPointsOffset[targetSnapPointIndex];

const percentageDragged = absDraggedDistance / Math.abs(snapPointDistance);

Expand All @@ -198,6 +197,6 @@ export function useSnapPoints({
activeSnapPointIndex,
onRelease,
onDrag,
snapPointHeights,
snapPointsOffset,
};
}

0 comments on commit bad7bd1

Please sign in to comment.