This module implements a pannable and zoomable component.
{ viewportOffset : Coordinate
, minScale : Maybe Scale
, maxScale : Maybe Scale
, scalePerScrollPixel : Basics.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:
In your initial Config
set viewportOffset
to the default value of
viewportOffset =
{ x = 0, y = 0 }
Call Browser.Dom.getElement
and attempt the task.
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.
scalePerScrollPixel
- The factor to scale by when scrolling (per pixel).
For example setting this to 0.001
will zoom in the content box by 0.1 % when scrolling 1px and 1/(1+0.1%) when scrolling -1px.
draggableOnChildren
- If you want to disable the panning logic when dragging on child elements in the content box.
toSelf
-- Map this component's MouseEvent
s 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
, scalePerScrollPixel = 0.001
, draggableOnChildren = Exclude []
, toSelf = toSelf
}
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.List String
List of classes.
{ state : State
, config : Config msg
}
Contains config and internal state.
init : 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.
view : Model msg -> { viewportAttributes : List (Html.Attribute msg), contentAttributes : List (Html.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.
Mouse events.
getScale : Model msg -> Scale
Get the scaling of the content box.
Basics.Float
Scale of the content box.
getPosition : Model msg -> Coordinate
Get the position of the center point of the content box.
{ x : Basics.Float, y : Basics.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).
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.
scaleBy : Basics.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
The anchor point to scale the content box with respect to.