<h1>ゼミ課題2</h1> Elmを使ったプログラムを進めるために基本構文を知る必要があるため調べることからはじめその後開発を進めていく。 <h2>Elmについて</h2> <h4>Elmを使うメリットとデメリット</h4> メリット <li>実行時エラーが実質起きない</li> <li>コンパイル速度がはやい</li> <br> <br> デメリット <li>関数型言語なため手続き型に慣れている人には使いにくい</li> <li>jsのライブラリを使うのに工夫が必要</li> <li>複雑なUIを作るのは苦手</li> <br> このようなメリットが出てきてjsと違いコンパイル時にエラーを吐き出してくれることでおかしな動きを防いでくれることがわかった。 複雑なものを作らない限りはelmが使いやすいことがわかった。 <h4>Elmのを書く時の特徴</h4> <li>基本的にmodel,update,viewに分けて書く。</li> <ul>modelはアプリケーションの状態、updateは状態を更新する方法、viewはhtmlとして状態を閲覧する方法。</ul> <li>{}や()は基本的に使わない</li> <li> <h2>今回使う開発環境</h2> elmオンラインエディタ <h2>進める道筋</h2> マウス座標取得のプログラム→図形がマウスに追尾するプログラム→ドラッグアンドドロップ <h2>マウス座標を取得するプログラム</h2> https://blog.emattsan.org/entry/2019/05/26/093114 のサイトに乗っているプログラムをコピペして実際に動かしてみる ``` import Browser import Html exposing (Html, div, span, text) import Html.Events exposing (on) import Html.Attributes exposing (style) import Json.Decode exposing (map2, field, int) main = Browser.sandbox { init = init , update = update , view = view } type alias Model = { x: Int , y: Int } init : Model init = { x = 0 , y = 0 } type Msg = Move Int Int update : Msg -> Model -> Model update msg model = case msg of Move x y -> {x = x, y = y} view : Model -> Html Msg view model = div [] [ span [] [ text ("(" ++ (String.fromInt model.x) ++ ", " ++ String.fromInt model.y ++ ")") ] , div [ style "background-color" "gray" , style "height" "80vh" , on "mousemove" (map2 Move (field "clientX" int) (field "clientY" int)) ] [] ] ``` <h3>動作</h3> ![](https://i.imgur.com/VATHhku.png) 説明 mousemoveを使いマウスの位置座標を取得、その座標を表示するプログラム <h3>図形の追加</h3> ドラッグアンドドロップをするためには動かすものが必要なため図形を作る ``` import Browser import Html exposing (Html, div, span, text) import Html.Events exposing (on) import Html.Attributes exposing (style) import Json.Decode exposing (map2, field, int) import Html.Events exposing (onClick) main = Browser.sandbox { init = init , update = update , view = view } type alias Model = { x: Int , y: Int } init : Model init = { x = 0 , y = 0 } type Msg = Move Int Int update : Msg -> Model -> Model update msg model = case msg of Move x y -> {x = x, y = y} view : Model -> Html Msg view model = div [] [ span [] [ text ("(" ++ (String.fromInt model.x) ++ ", " ++ String.fromInt model.y ++ ")") ] , div [ style "background-color" "gray" , style "height" "80vh" ,style "width" "1200px" ,style "position" "absolute" , onMouseMove Move ] [] ,div [ style "background-color" "red" , style "height" "30px" ,style "width" "30px" ,style "top" "0px" ,style "left" "0px" ,style "position" "relative" , onMouseMove Move ] [] ] onMouseMove : (Int -> Int -> msg) -> Html.Attribute msg onMouseMove f = on "mousemove" (map2 f (field "clientX" int) (field "clientY" int)) ``` <h3>実行結果</h3> ![](https://i.imgur.com/YfZAY1L.png) 説明 新しくdivを追加し四角を追加。position:relativeにすることで親要素の右端を0として参照している。 <h2>マウスに図形が追尾するプログラム</h2> プログラムのtopとleftに取得したマウスのx,y座標を入れることができれば図形は追尾すると考えられる。 なのでstyle "top" (StringformInt model.x++"px"),style "left" (StringformInt model.y++"px")とすることで取得したx,y座標を入れることができる。 結果 ![](https://i.imgur.com/s9IO9cJ.png) 図形が追尾するプログラムが完成しました。 <h2>ドラッグアンドドロップ</h2> ドラッグアンドドロップを行う際にonMouseDownとonMouseUpを使う onMouseDownとはボタンを押下し続けている状態を取得するもので onMouseupはボタンを押下していない状態を取得するものである 以上の2つを使うことで押している間ドラッグし離したときにドロップするという命令を与えることができる。 そして作っていった結果以下のプログラムになった ``` import Browser import Html exposing (..) import Html.Events exposing (..) import Html.Attributes exposing (..) import Json.Decode exposing (..) main = Browser.sandbox { init = init , update = update , view = view } type alias Model = { x: Int , y: Int ,drag:Bool} init : Model init = { x = 0 , y = 0 ,drag=False} type Msg = Down | Up | Move Int Int update : Msg -> Model -> Model update msg model = case msg of Down ->{model | drag=True} Up ->{model | drag=False} Move x y -> {model | x = if(model.drag==True) then x else model.x, y = if(model.drag==True) then y else model.y} view : Model -> Html Msg view model = div [ style "height" "100vh" ,style "width" "1200px" ,style "position" "absolute" , onMouseMove Move ,onMouseUp Up ] [ div [ style "background-color" "red" , style "height" "30px" ,style "width" "30px" ,style "position" "relative" ,style "top" (String.fromInt model.y++"px") ,style "left" (String.fromInt model.x++"px") ,onMouseDown Down ] [] ] onMouseMove : (Int -> Int -> msg) -> Html.Attribute msg onMouseMove f = on "mousemove" (map2 f (field "clientX" int) (field "clientY" int)) ``` とすることで動かしていくことができ変更点として modelにBoolean型のdragを入れることで押されたときにTrue、離したときにFalseの値になるようにする。 updateの部分でdragがTrueの時カーソルの値をMoveに入れ続けfalseの時は離された場所の値を代入するようになっている。 viewでは動かす図形にonMouseDownを入れ親要素にonMouseUpとonMouseMoveを貼り付けた。 問題点 clientで図形をドラッグアンドドロップしているため図形を動かす際どの部分をクリックしても図形の左上を持つようになってしまっている。 解決策としてoffsetを使うと聞いたため試行錯誤してみましたができませんでした。 以下考えたプログラム ``` import Browser import Html exposing (..) import Html.Events exposing (..) import Html.Attributes exposing (..) import Json.Decode exposing (..) main = Browser.sandbox { init = init , update = update , view = view } type alias Model = { x: Int , y: Int ,drag:Bool,offsetX:Int,offsetY:Int} init : Model init = { x = 0 , y = 0 ,drag=False,offsetX=0,offsetY=0} type Msg = Down | Up | Move Int Int | Offset Int Int update : Msg -> Model -> Model update msg model = case msg of Down ->{model | drag=True} Up ->{model | drag=False} Move x y -> {model | x = if(model.drag==True) then x-model.offsetX else model.x , y = if(model.drag==True) then y-model.offsetY else model.y} Offset x y -> {model | offsetX = if(model.drag==True) then x else model.offsetX , offsetY = if(model.drag==True) then y else model.offsetY} view : Model -> Html Msg view model = div [ style "height" "100vh" ,style "width" "1200px" ,style "position" "absolute" , onMouseMove Move ,onMouseUp Up ] [ div [ style "background-color" "red" , style "height" "50px" ,style "width" "50px" ,style "position" "relative" ,style "top" (String.fromInt model.y++"px") ,style "left" (String.fromInt model.x++"px") ,onMouseDown Down ,onMouseOffset Offset ] [] ] onMouseMove : (Int -> Int -> msg) -> Html.Attribute msg onMouseMove f = on "mousemove" (map2 f (field "clientX" int) (field "clientY" int)) onMouseOffset : (Int -> Int -> msg) -> Html.Attribute msg onMouseOffset f = on "mousemove" (map2 f (field "offsetX" int) (field "offsetY" int)) ```