LoginSignup
4
4

More than 5 years have passed since last update.

Om(React)でsvgを描画する

Last updated at Posted at 2016-09-13

Omとは

facebook製 javascript frameworkであるReactのclojurescript wrapper ライブラリです.

ReactについてはQiitaにも様々な記事があげられているので詳細は割愛しますが,端的にその機能を説明するなら,「ページの状態を保持しているモデル(JSObject)」を「VirtualDOM」に流し込んで「実際に表示するDOM」を構築する,というものです(この辺りの解説はnarutoさんの記事が大変分りやすかったです).

ページの構造(VirtualDOM)とその中身(JSObject)を分離できるため,これまでJQuery等で生じてきたような,情報の追加,削除の際のAppendTo(),text(),Children()etc..DOM操作地獄を避けることができます.

OmはそうしたReactの機能のうち,Componentを作成するために組み合わせて使えるような,JS Object操作等の機能をまとめたものです.Virtual DOMの生成についてはsablonoのようなFrameworkを併用することが多いです.Omについても様々わかりやすい解説記事が出ていますので,そちらをご覧いただければと思います.

OmでSVGを描画する

上述のように,Om(React)を用いると「構造」と「内容」を分離することができるので,SVGの構造を簡潔に書くことができます.簡単なサンプルを用意してみました.Omが初めての方は公式チュートリアルも読んでみてください.

環境

  • OSX
  • Leiningen 2.7.0 on Java 1.8.0_101

プロジェクトセットアップ

Omプロジェクト作成

まず,公式チュートリアルに従って

lein new figwheel svg-test -- --om

を走らせると,プロジェクトが作成されるので,プロジェクトファイルを修正します.変更部分は各コンポーネントのバージョンと,om-toolsの追加です.


(defproject svg-test "0.1.0-SNAPSHOT"
  :description "FIXME: write this!"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}

  :min-lein-version "2.6.1"

  :dependencies [[org.clojure/clojure "1.8.0"]
                 [org.clojure/clojurescript "1.9.89"]
                 [org.clojure/core.async "0.2.385"
                  :exclusions [org.clojure/tools.reader]]
                 [cljsjs/react "15.2.1-1"]
                 [cljsjs/react-dom "15.2.1-1"]
                 [sablono "0.7.4"]
                 [org.omcljs/om "1.0.0-alpha40"]
                 [prismatic/om-tools "0.4.0"]]

  :plugins [[lein-figwheel "0.5.4-7"]
            [lein-cljsbuild "1.1.3" :exclusions [[org.clojure/clojure]]]]

  :source-paths ["src"]

  :clean-targets ^{:protect false} ["resources/public/js/compiled" "target"]

  :cljsbuild {:builds
              [{:id "dev"
                :source-paths ["src"]
                :figwheel {:on-jsload "svg-test.core/on-js-reload"
                           :open-urls ["http://localhost:3449/index.html"]}

                :compiler {:main svg-test.core
                           :asset-path "js/compiled/out"
                           :output-to "resources/public/js/compiled/svg_test.js"
                           :output-dir "resources/public/js/compiled/out"
                           :source-map-timestamp true
                           :preloads [devtools.preload]}}
               {:id "min"
                :source-paths ["src"]
                :compiler {:output-to "resources/public/js/compiled/svg_test.js"
                           :main svg-test.core
                           :optimizations :advanced
                           :pretty-print false}}]}
  :figwheel {:css-dirs ["resources/public/css"]}
:profiles {:dev {:dependencies [[binaryage/devtools "0.7.2"]
                                  [figwheel-sidecar "0.5.4-7"]
                                  [com.cemerick/piggieback "0.2.1"]]
                   :source-paths ["src" "dev"]
                   :repl-options {
                                  :init (set! *print-length* 50)
                                  :nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}}})

figwheelの実行

上記の修正が終わったら,lein figwheelでfigwheelを走らせて,http://localhost:3449/index.htmlにアクセスします.

Omが正常に動いているなら

image

が表示されているはずです.

sablonoを適用

src/svg-test/core.cljsを開き,requireを修正します.

(ns svg-test.core
  (:require [om.core :as om :include-macros true]
            [om-tools.core :refer-macros [defcomponent]]
            [sablono.core :as html :refer-macros [html]]))

applicationコンポーネントをrootから分離し,sablono,om-toolsを適用して書き換えます.

(defcomponent application [app owner]
  (render [_]
    (html [:h1 {} (:text app)])))

(om/root application  app-state
  {:target (. js/document (getElementById "app"))})

(defonce app-state (atom {:text "Hello world!"}))のtextを書き換えると表示される文字も変わるかと思います.

SVGを描画してみる

SVGで国旗の色を変えるサンプルを作成してみます.
まず,app-stateに,国名と旗の色をもたせます.

(defonce app-state (atom {:flags [{:name :germany :color ["#1D1D1F" "#E71920" "#FAB40A"]}
                                  {:name :bulgaria :color ["#FFFFFF" "#00AE0F" "#E60000"]}
                                  {:name :luxembourg :color ["#EF3340" "#00A3E0" "#FFCD00"]}]}))

次に,旗の色情報が渡すVirtualDOMを作成します.

(defcomponent flag-rect [{:keys [index color]} owner]
  (render [_]
   (html [:rect {:x 0 :y (* index 40) :width 150 :height 40
           :stroke "none" :fill color}])))

(defcomponent flag-view [{:keys [color]} owner]
  (render [_]
   (let [colors (map #(assoc {} :index %1 :color %2) (range) color)]
     (html [:svg {:width "200px" :height "120px"
                  :viewBox "0 0 200 120"}
            (om/build-all flag-rect colors {:key :index})]))))

つぎにapplicationを下記のように書き換え,flagsのfirstをflag viewに渡すと,VirtualDOMにcolor情報が流し込まれ,ドイツ国旗が表示されます.

(defcomponent application [{:keys [flags]} owner]
  (init-state [_]
   {:chosen :germany})
  (render-state [_ {:keys [chosen]}]
   (html 
    [:div
     (om/build flag-view (first flags))])))

最後に,applicationにselectorをつけてあげれば,DOM操作は行わずに国旗の色を変更できるようになります.

(defn- set-key! [val owner]
  (om/set-state! owner :chosen (keyword val)))

(defcomponent application [{:keys [flags]} owner]
  (init-state [_]
   {:chosen :germany})
  (render-state [_ {:keys [chosen]}]
   (html 
    [:div
     (om/build flag-view (first (filter #(= (:name %) chosen) flags)))
     [:div [:select {:on-change #(set-key! (.. % -target -value) owner)}
            (om/build-all selector flags {:key :name})]]])))

image

flag viewに渡すオブジェクトを変更するだけで,React側が新旧の状態を見比べて差分を計算し,必要な箇所だけを更新してくれます.

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4