# プロジェクト2 Elmを使って、ドラッグ・アンド・ドロップを実現する。 # 一回目: まず、最終的の目標を確認する。 https://jovial-bassi-4f05d7.netlify.app/ 要素を確認してみると、中には四角形が入っていることがわかる。 長方形はマウスを使って、移動させることはきるようになっている。 また、四角形の左上のx、y座標を常に更新して、表示されていることがわかる。 中身を確認した後、まずは四角形を作ってみたいと考えている。 https://elm-lang.org/examples/shapes こちらのサイトを参考に、簡単に作れる。 自分なりに四角形の要素を変えてみた。 最終的にこのような四角形を作った。 ![](https://i.imgur.com/JIkESUo.png) 次に、四角形のx、y座標を表示させてみる。 ![](https://i.imgur.com/oqy6JfV.png) 文字を表示するところまで作った。 # 二回目: 前回は四角形の描画ができた。 今回は前回やったことを踏まえて、書き方を改善する。 プログラムの内容が全て、MAINに入っている状態ではなく、 MAIN : MODEL: UPDATE: VIEW: のように分けて、書いていこうと思う。 MAINでは、他のどのサンプルと同じようにかく。 ``` main = Browser.sandbox { init = init ,update = update , view = view } ``` MAINは一番書きやすい! また、MODELのところは他のサンプルを参考に、この様に書くことができる。 ``` type alias Model = Int init : Model init = 0 ``` 次に、UPDATEのところを書く。 今の作ったプログラムは特にアップするデータはないが、一応どのような形するかをイメージしながらこの様に作った。 ``` type Msg = Increment | Decrement update : msg -> Model -> Model update msg model = case width of width -> model +200 ``` 最後にVIEWのところ 前回MAINに入った内容を今回VIEWの中に入れる。 ``` view : Model -> Html Msg view model = svg [] [ rect [ x "0" , y "0" , width "120" , height "120" , fill "green" ] [] ] ``` なるべく簡潔に作ることが目的であるため、不要なコードは消しておいた。 この様にプログラムを書いて、実行してみると、一応正しく実行できる。 ![](https://i.imgur.com/CsJOrC0.png) 結果としては、この様な感じになっている。 # 三回目: 今回は前回のプログラムを改善してマウスの追跡、及び長方形とマウスと一緒に移動できるプログラムを作成していきたいと思う。 考え方としては、まずマウスの追跡ができるようにプログラムを作成する。その後、四角形のx、y座標をマウスと同じ座標に変化したら、完成できると考えている。 https://blog.emattsan.org/entry/2019/05/26/093114 こちらのサイトを参考にVIEWを書き換える。 自分なりに要素をつけたり、消したりして、最終的にVIEWがこの様に書くことになった。 ``` view : Model -> Html Msg view model = let onMouseMove f = on "mousemove" (map2 Move (field "clientX" int) (field "clientY" int)) in div [] [ div [ Html.Attributes.style "height" "80vh" , onMouseMove Move ] [] ,span [] [ Html.text ("(" ++ String.fromInt model.x ++ ", " ++ String.fromInt model.y ++ ")") ] ] ``` let文を使うことで、とても簡潔に見える様に書き換えることができた。 let文の書き方については下のサイトを参考に書いた。 https://elm-lang.org/examples/clock 実行してみると、追跡ができるようになったことが確認された。 ここで、四角形を描くプログラムと連携できたら、四角形の移動も実現できる。 https://bombrary.github.io/blog/posts/elm-drag01/ こちらのサイトを参考に、四角形を描くコードを入れる。 ``` viewSvg : Model -> Svg Msg viewSvg model = svg [] [ rect [ x "++ String.fromInt model.x ++" , y "++ String.fromInt model.y ++" , width "120" , height "120" , fill "green" ] [] ] ``` 四角形を描くコードを直接VIEWに書く方法を探しても、参考になりそうなサイトが見つからないため、viewsvgに書くことにした。 実行すると、エラーがないけど、四角形の描画ができてない… 参考したサイトを見て、原因を探してみたら、 viewSvg model  この文は書いてないから、描画ができないと推測した。 viewに上のコードを入れて実行すると、描画ができるようになった。 しかし、連携はうまくできてないみたい。 # 四回目: 前回、先生から「svgを使わなくても、divだけで四角形の描画ができるよ」というコメントをいただいた。 今回はまず、svgを使わずに、四角の描画をできるようにしたい。 そのために、divでどうやって描画できるかを調べて、divで、四角形を描画してみる。 よく考えたら、前に書いたdivは全部四角形を作って、その中でsvgを使って図形を書いてきた。 もうしかしたら、divの中に、divを書くことによって、二つの四角形が出来上がるのではないかと考えた。 その考えを基づいて、viewを書き換える。 ``` div [] [ div [ style "height" "500px" , style "width" "500px" , style "background" "green" [ div [ style "background" "red" , style "height" "50px" , style "width" "50px" , onMouseMove Move ] ] ] ``` このように、書き換えることができた。 ![](https://i.imgur.com/hkEOuS1.png) これで、svgを使わずに描画することができる様になった。 また、座標の表示をできる様に作りたい。 textの書き方をし調べてみた。 https://qiita.com/Yametaro/items/4118ecdf808d128d56c6 こちらを参考に、text文を書いた。 ``` div [][text ("(" ++ String.fromInt model.x ++ ", " ++ String.fromInt model.y ++ ")") ``` こちらの文を適切の場所に入れることで、この様に座標の表示ができる様になった。 ![Uploading file..._u9k1sql93]() そして、四角形を動かせる様にしたい。 その前に、まず四角の位置情報をどうやって指定できるのかを調べた。 Elmではないが、似たような感じのサイトが出てきた。 http://lab.agr.hokudai.ac.jp/useful/CSS/b6_position.htm このサイトを参考にして、四角の位置情報をとりあえず変えてみる。 先生のサンプルの様に、最初は100,100に位置にしてみる。 ``` style "top" "100" style "left" "100" ``` こちらの文を追加することで、初期位置の指定はできると考えていた。 しかし、実行してみると、うまく動作しない。 # 五回目: 前回で作ったコードを基づいて、mouseupとmousedownを追加する。 まず、mouseupとmousedownを反映するために、新たに二つの変数を作る。 ``` type alias Model = { x: Int , y: Int ,ax: Int , ay: Int } ``` この様にaxとayを追加する。 そのあと、初期化を行う。 ``` init = { x = 0 , y = 0 , ax = 0 , ay = 0 } ``` そして、Moveの様に、DownとUpのイベントの追加をする。 ``` type Msg = Move Int Int | Down Int Int | Up Int Int ``` イベントが追加されて、その後、updateの中でどう変化するかを定義する。 ``` Down dx dy -> {x = model.x, y = model.y, ax = dx, ay = dy} Up ux uy -> {x = model.x, y = model.y, ax = ux, ay = uy} ``` 最後に、viewを書き換える。 let文の書き方よくわからないため、とりあえずonMouseMoveをの書き方を真似して書く。 ``` onMouseDown i = on "mousedown" (map2 Down (field "clientX" int) (field "clientY" int)) onMouseUp j = on "mouseup" (map2 Up (field "clientX" int) (field "clientY" int)) ``` textの部分も前の書き方を真似して、書くことができる。ここで、x、yではなく、ax,ayを使う。 ``` div [][text ("(" ++ String.fromInt model.x ++ ", " ++ String.fromInt model.y ++ ")") ,div [][text ("(" ++ String.fromInt model.ax ++ ", " ++ String.fromInt model.ay ++ ")") ``` 正しく追加されているかを確認するために、moveを消して、DownとUpだけでみる。 ``` , onMouseDown Down , onMouseUp Up ``` ![](https://i.imgur.com/llXx8RI.png) マウスをクリックした瞬間、座標が変化する。 離したら、もう一回座標が変化する。 結果から、正しく追加されていることがわかる。 # 六回目: ここまでできて、残りとしては条件分岐して、クリックしているかを判別させる。 クリックしたら、mousemoveを動作させる。 それ以外の場合、mousemoveを動作させない。 そのために、Bool型のisDraggingという変数をModelに追加する。 そして、初期値としてFalseを入れる。 updateで条件分岐を書いていく。 Moveの中身では、もし、model.isDraggingの場合、x、yの座標計算を行なって、更新する。 Downの中身では、リリックしたとき、mouseが四角形の中にあるのかを判別する。 もし、中にあるのなら、ax,ayよisDraggingの値を更新する。 UPではクリックじゃない場合、isDraggingをFalseにする。 この考えに従い、コードを書くと最終的に以下のコードが出来上がる。 ``` module Sample exposing (..) import Browser import Html exposing (Html, div, text) import Html.Events exposing (on) import Html.Attributes exposing (style) import Json.Decode exposing (map2, field, int) -- MAIN main = Browser.sandbox { init = init ,update = update , view = view } -- MODEL type alias Model = { isDragging : Bool, x : Int, y : Int, ax : Int , ay : Int } init : Model init = { isDragging = False, x = 0, y = 0, ax = 0, ay = 0 } -- UPDATE type Msg = Move Int Int | Down Int Int | Up Int Int update : Msg -> Model -> Model update msg model = case msg of Move cx cy -> if model.isDragging then let x = cx - model.ax y = cy - model.ay in { model | x = x, y = y } else model Down cx cy -> let isDragging = cx > model.x && cx < model.x + 70 && cy > model.y && cy < model.y + 70 in if isDragging then let ax = cx - model.x ay = cy - model.y in { model | isDragging = isDragging, ax = ax, ay = ay } else model Up cx cy -> if model.isDragging then { model | isDragging = False } else model -- VIEW view : Model -> Html Msg view model = let onMouseMove f = on "mousemove" (map2 Move (field "clientX" int) (field "clientY" int)) onMouseDown i = on "mousedown" (map2 Down (field "clientX" int) (field "clientY" int)) onMouseUp j = on "mouseup" (map2 Up (field "clientX" int) (field "clientY" int)) in div [] [ div [ style "height" "100vh" , style "width" "100%" , onMouseMove Move , onMouseUp Up ] [ div [ style "background" "red" , style "position" "absolute" , style "top" <| String.fromInt model.y ++ "px" , style "left" <| String.fromInt model.x ++ "px" , style "height" "70px" , style "width" "70px" , onMouseDown Down ] [ div [][text ("(" ++ String.fromInt model.x ++ ", " ++ String.fromInt model.y ++ ")") ,div [][text ("(" ++ String.fromInt model.ax ++ ", " ++ String.fromInt model.ay ++ ")") ] ] ] ] ] ```