gampleman / elm-visualization

A data visualization library for Elm

Home Page:http://package.elm-lang.org/packages/gampleman/elm-visualization/latest/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Differentiate between scroll and pinch-to-zoom

elizabethprins opened this issue · comments

Hi, thank you for all the great work! I use this package for zooming and panning in a user interface, and it works wonderfully.

The only issue I have is that the onWheel event handles both the scrolling and the pinch-to-zoom gestures, whereas it would be preferable if scrolling were still possible, as it is for example in the zoom-and-pan interfaces of Sketch and Metro.io.

According to this article, the solution could be to add (D.field "ctrlKey" D.bool) to the decoder in the onWheel function:

When you perform a pinch-zoom gesture, Firefox and Chrome produce a wheel event with a deltaY: ±scale, ctrlKey: true.

If ctrlKey is False, the message could be a NoOp and the preventDefault in the event listener could be removed to allow scrolling.

Probably it would make sense to make this customisable. For some use cases scrolling to zoom is kind of what you want (i.e. maps), others not so much.

Alternatively we could do the Google Maps sort of thing where a little message pops up when you scroll over the interactive area telling the user to hold ctrl to zoom.

Thanks for responding! Yes, I agree that scrolling to zoom can still be the best choice in some use cases.

Also, it turns out I oversimplified the scrolling solution. Because with a regular scroll window, it's possible to drag your canvas out of view and not be able to scroll back to it. So you need a function that actually handles 'panning on scroll'.

It would be great if the onWheel event could be customized to allow for this! I wouldn't know the best way to implement this, but here is a (still non-customizable) solution that I found works for my case:

onWheel : Zoom -> (OnZoom -> msg) -> Attribute msg
onWheel _ tagger =
    custom "wheel"
        (D.field "ctrlKey" D.bool
            |> D.andThen
                (\isCtrlKeyPressed ->
                    let
                        decoder =
                            if isCtrlKeyPressed then
                                zoomOnWheelDecoder

                            else
                                panOnWheelDecoder
                    in
                    D.map
                        (\onZoom ->
                            { message = tagger onZoom
                            , stopPropagation = True
                            , preventDefault = True
                            }
                        )
                        decoder
                )
        )


zoomOnWheelDecoder : Decoder OnZoom
zoomOnWheelDecoder =
    D.map3
        (\deltaY deltaMode position ->
            Wheeled
                (-deltaY
                    * (if deltaMode == 0 then
                        0.004

                       else if deltaMode == 1 then
                        0.05

                       else
                        1
                      )
                )
                position
        )
        (D.field "deltaY" D.float)
        (D.field "deltaMode" D.int)
        Events.decodeMousePosition


panOnWheelDecoder : Decoder OnZoom
panOnWheelDecoder =
    D.map3
        (\scrollX scrollY position ->
            Scrolled position scrollX scrollY
        )
        (D.field "wheelDeltaX" D.int)
        (D.field "wheelDeltaY" D.int)
        Events.decodeMousePosition

where Scrolled does the following:

Scrolled position0 scrollX scrollY ->
    let
        position1 =
            Transform.invert
                ( Tuple.first position0 - (toFloat scrollX / 4)
                , Tuple.second position0 - (toFloat scrollY / 4)
                )
                model.transform

        transform_ =
            translate position0 position1 model.transform
    in
    Zoom
        { model
            | transform = transform_ |> constrain model.extent model.translateExtent
            , transition = Nothing
        }