Skip to content

Instantly share code, notes, and snippets.

@PhilOwen
Created December 25, 2016 14:52
Show Gist options
  • Save PhilOwen/5d105e06634bb1b7fba0c17df8c9fc2f to your computer and use it in GitHub Desktop.
Save PhilOwen/5d105e06634bb1b7fba0c17df8c9fc2f to your computer and use it in GitHub Desktop.
Elmで、P5.js風にインタラクティブにイベント対応

Haskell風のAltJSであるElm言語を使ってみる。

P5.js風に、マウスの移動に合わせて円を追従させる。
円は、追従してくるものの他にもう一つあって、その円は普段は固定。 2つの円は線でつないであるので、固定の円を中心に棒がグネグネする感じ。
また、マウスをクリックすると、固定だった円がクリックした位置にジャンプする。

実行にはelm-reactorを使った。
elmは、npmでもbrewでもインストールできる。 reactorを実行して、ブラウザで http://localhost:8000/Main.elm を開けばOK。
elm-makeを使えば、HTMLやJSへもコンパイルできる。
描画にはSVGを使っている。

Elmは、Haskell風だが、where句がなかったり、 ガード文ができなかったり、パターンマッチがcase ofでしか できなかったりと、わりと基本的な面でも違う。 型クラス的のような高等な機能は、当然できない。 あと、なぜかF#から借りてきた演算子がちらほらある。

Elmアーキテクチャという、独自の方針で プログラムを書くよう推奨されている。

main = Html.program
    { init = init
    , view = view
    , update = update
    , subscriptions = subscriptions
    }

で見てわかるように、基本となるモデルを、 イベントに合わせて更新して、 それをHTMLに反映させるのが原則。
Haskellを知っている人でも、何かと慣れは必要。

描画は、React.jsやAngular.jsなどと比べても かなり高速とのこと。 React同様、Virtual DOMを使っているが、 副作用を減らして最適化しやすくなっているおかげで、より速いらしい。

References

{
"version": "0.1.0",
"summary": "",
"repository": "https://github.com/PhilOwen/dummy.git",
"license": "BSD3",
"source-directories": [
"."
],
"exposed-modules": [],
"dependencies": {
"elm-lang/core": "5.0.0 <= v < 6.0.0",
"elm-lang/html": "2.0.0 <= v < 3.0.0",
"elm-lang/mouse": "1.0.1 <= v < 2.0.0",
"elm-lang/svg": "2.0.0 <= v < 3.0.0"
},
"elm-version": "0.18.0 <= v < 0.19.0"
}
import Html exposing (Html)
import Svg exposing (..)
import Svg.Attributes exposing (..)
import Mouse exposing (..)
main = Html.program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
-- MODEL
type alias Point =
{ x: Int
, y: Int
}
type alias Model =
{ temporaryPoint: Point
, anchorPoint: Point
}
zero: Point
zero = {x = 0, y = 0}
init: (Model, Cmd Msg)
init = ( {temporaryPoint = zero, anchorPoint = zero }, Cmd.none)
-- UPDATE
type Msg = Walk Point
| Anchor Point
update: Msg -> Model -> (Model, Cmd a)
update msg model = case msg of
Walk p ->
({ model | temporaryPoint = p }, Cmd.none)
Anchor p ->
({ temporaryPoint = p, anchorPoint = p }, Cmd.none)
-- SUBSCRIPTIONS
subscriptions: Model -> Sub Msg
subscriptions model = Sub.batch
[ Mouse.moves Walk
, Mouse.clicks Anchor
]
-- VIEW
view: Model -> Html a
view model =
let
attrs1 = toCircleAttrs model.temporaryPoint
attrs2 = toCircleAttrs model.anchorPoint
attrs3 = toLineAttrs model.temporaryPoint model.anchorPoint
in
svg [ viewBox "0 0 800 800", width "800px" ]
[
circle (fill "DimGray"::attrs2) []
, line attrs3 []
, circle (fill "DodgerBlue"::attrs1) []
, text_ [x "100", y "50", fontSize "18px"] [text (toString model)]
]
toCircleAttrs: Point -> List (Attribute msg)
toCircleAttrs p = [cx <| toString p.x, cy <| toString p.y, r "30"]
toLineAttrs: Point -> Point -> List (Attribute msg)
toLineAttrs p1 p2 =
let
x1_ = x1 <| toString p1.x
y1_ = y1 <| toString p1.y
x2_ = x2 <| toString p2.x
y2_ = y2 <| toString p2.y
in
[ x1_, y1_, x2_, y2_, stroke "Gainsboro", strokeWidth "5px" ]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment