I just changed to use a Browser.document instead, and now it works. I guess that makes sense - given that getViewport is a Task and can therefore handle errors might it be better if the signature were something like:
getViewport : Task Error Viewport
with the Error type extended to:
= NotFound String
| NotSupported String
This creates another problem, I set up some attributes on my body:
<body id="diagram" style="overflow: hidden;">
but there is no way to construct a body like this with Browser.document. It might be better if body had its own type and constructor:
type Body msg = ...
body : List (Attribute msg) -> List (Html msg) -> Body msg
I fairly often want to create SVG applications that are full screen, but where the viewport is larger than the available screen size, so you can scroll around and zoom in and out and so on. To do this nicely you have to set overflow: hidden otherwise the scroll bars will flicker on and off.
Why is it required to set these attributes on the body rather than, say, a div immediately inside the body? And at least the styling can be done inside dedicated CSS, rather than as an Elm attribute, unless you want to toggle them.
Thanks, I tried putting it in a div, and happily that seems to work better when the screen is resized.
I set the SVG viewport to exactly match the pixel size it is rendered at, as this makes it easier to pixel align a drawing and render it nice and sharp looking. Directly in the body, when the screen is resized the drawing stretches before the new viewport size kicks in. Done in a div, this does not seem to happen, so the whole drawing does not jump around during a resize.
I think the reason for this is that I set the div to a fixed pixel size, so when the window is resized, it does not change the size of the div. The div is then set to a new size in the next view.
It would be helpful if there was a subscription like this:
module Browser.Events ...
onResizeOf : String -> (Int -> Int -> msg) -> Sub msg
Then I would just use getViewportOf and onResizeOf on the div containing the drawing. I think this would work equally with Browser.element as Browser.document.
Its nice to be able to set overflow: hidden on the body, as sometimes the browser will show the scroll bar even when the content fits, and sometimes not. Its just a way of ensuring it is never shown, but yes, can put that in a global style.
onResizeOf would take the id of some node, and notify every time that it changes size - a ResizeObserver basically. But I realize this is only wishful thinking with the current level of support for ResizeObserver: https://caniuse.com/#feat=resizeobserver
What I do currently is watch for mutations on some div that contains things I am interested in tracking size of, and get their size with getViewportOf. I also do this if the overall window changes size, and on any other actions that I think may change the size of something I am interested in - its a bit messy but seems to work ok.
When you talk about the “viewport” I guess you talk about the viewport property of the svg element, and not the visual viewport of the page? All this gets sometime confusing. Especially with svg sizing. Anyway, it’s not always necessary to use the “viewport” property to manage positionning and zooming in an SVG drawing. In fact I’ve had a lot of dealing with this for specific use cases where the SVG element is filling the space available in the page, (with flexbox or elm-ui) so it never displays scroll bars.
Yes, I mean the SVG viewport. I generally set that so that in a normal (not-zoomed) drawing, its size is 1:1 with the pixel size it is being rendered. The drawing may extend over a much larger area of which only a smaller viewport is being shown. If the zoom changes, then obviously the size will no longer be 1:1 with the pixels.