JohanWinther/elm-pan-and-zoom/2.0.1

NOTE: the latest version is 3.0.0

PanZoom

This module implements a pannable and zoomable component.

Config

type alias Config msg =
{ viewportOffset : Coordinate
, minScale : Maybe Scale
, maxScale : Maybe Scale
, scrollScaleFactor : Float
, draggableOnChildren : ElementFilter
, toSelf : MouseEvent -> msg
}

Configuration options for the component.

  • viewportOffset - Tell the component the offset of the viewport's the top left corner (needed to properly convert between the internal and browser coordinate systems).

    NOTE: this will not set the offset, only inform the component of the offset. This will allow you to set the viewport offset with CSS yourself.

    If your CSS positional logic is too complicated to specify the offset beforehand you can perform these steps:

    1. In your initial Config set viewportOffset to the default value of

      viewportOffset =
          { x = 0, y = 0 }
    2. Call Browser.Dom.getElement and attempt the task.

    3. In the logic receiving the Msg containing the Browser.Dom.Element, extract the fields element.x and element.y and override the config of your PanZoom.Model

      let
          oldConfig = panZoomModel.config
          newConfig = { oldConfig | viewportOffset = {x = element.x, y = element.y }
      in
      { panZoomModel | config = newConfig }
  • minScale - Set a minimum possible scale. This will decide how far you can zoom out.

  • maxScale - Set a maximum possible scale. This will decide how far you can zoom in.

  • scrollScaleFactor - The factor to scale by when scrolling. For example setting this to 1.1 will scale the content box by 10 % on every scroll.

  • draggableOnChildren - If you want to disable the panning logic when dragging on child elements in the content box.

  • toSelf -- Map this component's MouseEvents to a Msg so that the parent can properly update the component.

Note: the viewport's default dimensions are 100vw and 100vh respectively but can be overriden by adding your own style attribute in view.

defaultConfig : (MouseEvent -> msg) -> Config msg

Default configuration:

{ viewportOffset = { x = 0, y = 0 }
, minScale = Nothing
, maxScale = Nothing
, scrollScaleFactor = 1.1
, draggableOnChildren = Exclude []
, toSelf = toSelf
}
= Include ClassList
| Exclude ClassList

Filters elements by class name.

  • Include - dragging the content box will only be possible on child elements with any of these class names. The viewport itself needs to have a class in this list to be draggable.
  • Exclude - dragging the content box will not be possible on child elements with any of these class names. The viewport itself can have a class in this list to be non-draggable.
type alias ClassList =
List String

List of classes.

State

type alias Model msg =
{ state : State
, config : Config msg
}

Contains config and internal state.

Config msg
-> { scale : Scale
, position : Coordinate
}
-> Model msg

Creates a Model from a Config and an initial scale and position for the content box.

scale will be clamped by minScale and maxScale if they are set.

position sets the position of the center point of the content box. So in order to position the content box at point p with respect to its top left corner you will need to set

position =
    { x = p.x + width / 2, y = p.y + height / 2 }

where the width and height are the dimensions of the content box specified in pixels.

Model msg
-> { viewportAttributes : List (Attribute msg)
, contentAttributes : List (Attribute msg)
}
-> List (Html msg)
-> Html msg

Show the component with some content.

The elements provided as content will become direct descendants of the content box.

It is possible to provide additional HTML attributes to the viewport and the content box. For example it is recommended to add the user-select CSS property to the viewport to prevent text from being selected while dragging.

WARNING: These styles should not be overriden for proper function:

  • overflow: hidden (viewport)
  • box-sizing: border-box (viewport)
  • box-sizing: border-box (content box)
  • transform (content box)
update : MouseEvent -> Model msg -> Model msg

Process an event and return a new model.

= MouseMoved Position
| MousePressed Position ClassList
| MouseReleased
| WheelScrolled Position ScrollDirection

Mouse events.

Getters

getScale : Model msg -> Scale

Get the scaling of the content box.

type alias Scale =
Float

Scale of the content box.

getPosition : Model msg -> Coordinate

Get the position of the center point of the content box.

type alias Coordinate =
{ x : Float
, y : Float
}

Two dimensional vector that can represent a point or direction.

getMousePosition : Model msg -> Maybe Coordinate

Get the mouse position (if the content box is being dragged).

Transformations

Panning

moveBy : Coordinate -> Model msg -> Model msg

Move the content box by a relative distance.

Given a model where

getPosition model == { x = 10, y = 20 }

the following holds

getPosition (model |> moveBy { x = -15, y = 5 }) == { x = -5, y = 25 }
moveTo : Coordinate -> Model msg -> Model msg

Move the content box center point to a new position.

Given a model where

getPosition model == { x = 10, y = 20 }

the following holds

getPosition (model |> moveTo { x = -15, y = 5 }) == { x = -15, y = 5 }

In order to position the content box at point p with respect to its top left corner you will need to call

moveTo { x = p.x + width / 2, y = p.y + height / 2 }

where the width and height are the dimensions of the content box specified in pixels.

Zooming

scaleBy : Float -> Anchor -> Model msg -> Model msg

Scale the content box by a relative value (if within the minScale/maxScale bounds).

Given a model where

getScale model == scale

the following holds

getScale (model |> scaleBy factor ContentCenter) == scale * factor
scaleTo : Scale -> Anchor -> Model msg -> Model msg

Scale the content box to an absolute scaling (if within the minScale/maxScale bounds).

Given a model where

getScale model == scale

the following holds

getScale (model |> scaleTo newScale ContentCenter) == newScale
type Anchor
= Point Coordinate
| ContentCenter

The anchor point to scale the content box with respect to.