全屏、可调整大小 canvas 在 Elm 中在页面加载时不可见

Fullscreen, resizable canvas in Elm not visible on page load

我正在使用 Elm 0.19 和 joakin/elm-canvas 包。

我想要做的就是绘制一个 canvas,它跨越屏幕的整个宽度和高度,并随着 window 大小的变化动态调整大小。我花了几个小时调试和研究,但我只是被困住了。我已经尝试了多种实现,我可以让它工作,但不是在初始页面加载时。相反,它仅在调用更新函数后呈现。我觉得我遗漏了一些明显的东西,因为我对 Elm 还是很陌生。

这里 link 给正在工作的 Ellie code/demo 显示了问题:https://ellie-app.com/6JZDxnQWPLSa1 请注意在按键事件触发更新之前屏幕是如何空白的,然后 canvas 出现。

编辑:添加来自 Ellie 演示的代码。

module Main exposing (..)

import Browser
import Browser.Dom exposing (Viewport, getViewport)
import Browser.Events exposing (onKeyDown, onResize)
import Canvas exposing (..)
import Canvas.Settings exposing (..)
import Canvas.Settings.Advanced exposing (..)
import Color
import Html exposing (Html, div)
import Html.Attributes exposing (style)
import Json.Decode as Decode
import Task


main : Program () Model Msg
main =
    Browser.element
        { init = init
        , view = view
        , subscriptions = subscriptions
        , update = update
        }


-- MODEL


type alias Model =
    { screen : { width : Int, height : Int }
    }


init : () -> ( Model, Cmd Msg )
init _ =
    ( { screen = { width = 800, height = 600 }
      }
    , Task.perform (\{ viewport } -> ScreenSize (round viewport.width) (round viewport.height)) getViewport
    )


-- UPDATE


type Msg
    = TurnLeft
    | TurnRight
    | MoveForward
    | Other
    | ScreenSize Int Int


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        TurnLeft ->
            ( model, Cmd.none )

        TurnRight ->
            ( model, Cmd.none )

        MoveForward ->
            ( model, Cmd.none )

        ScreenSize w h ->
            ( { model | screen = { width = w, height = h } }
            , Cmd.none
            )

        Other ->
            ( model, Cmd.none )


-- SUBSCRIPTIONS


subscriptions : Model -> Sub Msg
subscriptions model =
    Sub.batch
        [ onKeyDown keyDecoder
        , onResize ScreenSize
        ]


keyDecoder : Decode.Decoder Msg
keyDecoder =
    Decode.map toDirection (Decode.field "key" Decode.string)


toDirection : String -> Msg
toDirection string =
    case string of
        "ArrowLeft" ->
            TurnLeft

        "ArrowRight" ->
            TurnRight

        "ArrowUp" ->
            MoveForward

        _ ->
            Other


-- VIEW


clearScreen : Float -> Float -> Renderable
clearScreen width height =
    shapes [ fill Color.black ] [ rect ( 0, 0 ) width height ]


view : Model -> Html Msg
view { screen } =
    div
        [ style "display" "flex"
        , style "justify-content" "center"
        , style "align-items" "center"
        ]
        [ Canvas.toHtml
            ( screen.width, screen.height )
            []
            [ clearScreen (toFloat screen.width) (toFloat screen.height)
              , shapes [ fill Color.red ] [ rect ( 30, 30 ) 200 200 ]
            ]
        ]

这里有一个工作示例:https://ellie-app.com/6KrhJjGLwc5a1. It's blinking a bit, but the canvas is adjusted on resize. This post 帮助我找到了这个解决方法。

我给模型添加了一个 busy 道具:

type alias Model =
    { screen : { width : Int, height : Int }
    , busy : Bool
    }

我添加了一个 PostScreenSize 以强制视图在 ScreenSize 完成后重新加载:

ScreenSize w h ->
    ( { model | screen = { width = w, height = h }, busy = True }
    , delay PostScreenSize
    )

PostScreenSize ->
    ( { model | busy = False }
    , Cmd.none
    )