antoniandre / splitpanes

A Vue 3 (and 2) reliable, simple and touch-ready panes splitter / resizer.
https://antoniandre.github.io/splitpanes
MIT License
1.89k stars 168 forks source link

Added a `snap-to-mouse` prop + documentation #204

Open Elarcis opened 5 months ago

Elarcis commented 5 months ago

Hi.

I added an interesting feature to the package, though I do not crucially need it to be integrated to the main repo, I figured it might help some people.

Included is a new snap-to-mouse prop for the Splitpanes component, as well as an example in the documentation page showcasing what it does.

The issue

With custom CSS, splitters may be made thicker to improve touch support (or just to fit a theme). This reveals an issue with the dragging algorithm, which is that pane sizes are always calculated relative to the mouse position in the splitpanes container. To make it short, the middle of the splitter is snapped to the mouse cursor at every move.

The behaviour is especially glaring in the increased touch zone example (when trying the example with a mouse), where the splitter “jumps” right under the mouse in any circumstance.

Why this is important (to me at least)

On my company project, we are using splitpanes to manage the app layout. We have a custom case where one splitter covers the whole height of another component, so that dragging any part of that component actually resizes the panel.

With that mouse-snapping issue, it is visually annoying, as moving the mouse slightly makes the component jump by as much as dozens of pixels.

My solution

This PR adds a snap-to-mouse prop to the Splitpanes component, enabled by default (the pre-PR behaviour). When set to false, when a mousedown or touchdown event is fired, an offset is stored of the position of the mouse/pointer relative to the center of the relevant splitter. Any subsequent drag takes this offset into account, so that splitters keep the same distance from the mouse.

In my situation, dragging that wide component makes it move smoothly, as its position stays the same relative to the mouse cursor. This allows for finer size adjustments and less visual jumps.

Drawbacks

This prop, when set to false, doesn’t play super-nice with splitters that are part of the layout (i.e. splitters whose thickness is not in position: absolute): the splitter sort-of compensates for the mouse offset depending on the drag percentage. This issue is very subtle with thin splitters, but gets more noticeable with thicker ones.

This is mitigated both by the fact that the prop’s default value keeps the positioning behaviour virtually unchanged compared to before the PR, and that it goes totally away when you use position: absolute on one of the splitter’s pseudo-elements to make it thicker, which I assume would be the most common use-case.