23、网页图表创建指南

网页图表创建指南

在网页开发中,创建各种类型的图表是一项常见且重要的任务。本文将介绍如何使用相关工具和技术创建不同类型的网页图表,包括柱状图、直方图、力导向图等,并实现图表的交互功能。

1. NVD3与数据处理基础

NVD3要求数据采用特定格式。我们需要创建数据适配器函数,以及创建和配置特定图表的函数。通过调用 webviz/create-chart 函数,我们可以填充HTML元素并显示图表。 create-chart 函数接受以下参数:
- 加载数据的URL
- 创建图表的元素路径
- 创建图表的函数
- 将数据转换为点和组的函数

该函数利用这些参数请求数据、创建图表并将数据与之关联。

以下是一些相关工具的链接:
- D3官网: http://d3js.org/
- NVD3官网: http://nvd3.org/
- 替代方案C2: http://keminglabs.com/c2/

2. 使用NVD3创建柱状图

柱状图适用于比较数据集中不同类别数据的总和或计数。下面以比较四种不同饮食喂养的小鸡体重为例,介绍创建柱状图的步骤。

2.1 准备工作
2.2 操作步骤
  1. 编写处理函数 :返回一个D3页面,包含创建DIV和SVG元素的代码,并调用后续定义的ClojureScript函数。
(defn bar-chart []
  (d3-page "Bar Chart"
           "webviz.barchart.bar_chart();"
           [:div#barchart.chart [:svg]]))
  1. 添加路由
(defroutes
  site-routes
  (GET "/barchart" [] (bar-chart))
  (GET "/barchart/data.json" []
       (redirect "/data/chick-weight.json"))
  (route/resources "/")
  (route/not-found "Page not found"))
  1. 创建ClojureScript文件 :在 src-cljs/webviz/barchart.cljs 中添加命名空间声明。
(ns webviz.barchart
  (:require [webviz.core :as webviz]))
  1. 数据转换 :将数据转换为NVD3期望的 Group Point 数据结构的两个类别。
(defn count-point [pair]
  (let [[diet items] pair]
    (webviz/Point. diet (count items) 1)))
(defn get-diet-counts [diet-groups]
  (apply array (map count-point diet-groups)))

(defn weight-point [pair]
  (let [[diet items] pair
        weight-total (sum-by #(.-weight %) items)]
    (webviz/Point. diet weight-total 1)))
(defn get-diet-weights [diet-groups]
  (apply array (map weight-point diet-groups)))
  1. JSON转换函数 :将JSON输入转换为NVD3的JavaScript对象。
(defn json->nv-groups [json]
  (let [diet-groups (group-by #(.-diet %) json)]
    (array (webviz/Group.
             "Chick Counts"
             (get-diet-counts diet-groups))
           (webviz/Group.
             "Chick Weights"
             (get-diet-weights diet-groups)))))
  1. 创建图表函数
(defn ^:export bar-chart []
  (webviz/create-chart "/barchart/data.json"
                       "#barchart svg"
                       #(.multiBarChart js/nv.models)
                       json->nv-groups))
2.3 工作原理

通过利用之前创建的 create-chart 函数,避免了创建图表时的大量样板代码。设置网页资源通常需要以下步骤:
1. 在服务器上定义资源(如 bar-chart )。
2. 定义访问该资源的路由。
3. 创建ClojureScript文件。
4. 在ClojureScript中编写数据转换函数。
5. 在ClojureScript中创建图表。

3. 使用NVD3创建直方图

直方图常用于展示数据的分布情况。下面以展示鲍鱼数据集的长度分布为例,介绍创建直方图的步骤。

3.1 准备工作
3.2 操作步骤
  1. 编写处理函数
(defn hist-plot []
  (d3-page "Histogram"
           "webviz.histogram.histogram();"
           [:div#histogram.chart [:svg]]))
  1. 添加路由
(defroutes
  site-routes
  (GET "/histogram" [] (hist-plot))
  (GET "/histogram/data.json" []
       (redirect "/data/abalone.json"))
  (route/resources "/")
  (route/not-found "Page not found"))
  1. 创建ClojureScript文件 :在 src-cljs/webviz/histogram.cljs 中添加命名空间声明。
(ns webviz.histogram
  (:require [webviz.core :as webviz]))
  1. 数据分组函数
(defn get-bucket-number [mn size x]
  (Math/round (/ (- x mn) size)))
(defn inc-bucket [mn size buckets x]
  (let [b (get-bucket-number mn size x)]
    (assoc buckets b (inc (buckets b)))))
(defn get-buckets [coll n]
  (let [mn (reduce min coll)
        mx (reduce max coll)
        bucket-size (/ (- mx mn) n)
        first-center (+ mn (/ bucket-size 2.0))
        centers (map #(* (inc %) first-center)
                     (range n))
        initial (reduce #(assoc %1 %2 0) {}
                        (range n))]
    (->> coll
      (reduce (partial inc-bucket mn bucket-size)
              initial)
      seq
      (sort-by first)
      (map second)
      (map vector centers))))
  1. 数据转换函数
(defn ->point [pair]
  (let [[bucket count] pair]
    (webviz/Point. (inc bucket) count 1)))
(defn data->nv-groups [data]
  (let [lengths (map #(.-length %) data)
        buckets (apply array
                       (map ->point
                            (get-buckets
                              lengths 10)))]
    (array (webviz/Group. "Abalone Lengths" buckets))))
  1. 初始化图表函数
(defn make-chart [] (.multiBarChart (.-models js/nv)))
  1. 创建图表函数
(defn ^:export histogram []
  (webviz/create-chart
    "/histogram/data.json"
    "#histogram svg"
    make-chart
    data->nv-groups
    :transition true))
3.3 工作原理

此过程与之前的类似,不同之处在于需要将数据划分为桶。通过获取桶中的最小值和最大值,计算差值并除以桶的数量,得到每个桶的宽度。利用这些信息确定每个数据点所属的桶,并进行计数。同时,获取每个桶的中心值用于标记图表中的列。

4. 使用力导向布局可视化图形

力导向布局是一种流行的可视化数据图的方法,它通过模拟带电粒子和弹簧的物理过程,创建美观的可视化效果,减少交叉线并使所有边的长度大致相同,从而清晰展示图中的关系。下面以美国人口普查种族数据的K-means聚类图为例,介绍创建力导向图的步骤。

4.1 准备工作
4.2 操作步骤
  1. 编写处理函数
(defn force-layout-plot []
  (d3-page "Force-Directed Layout"
           "webviz.force.force_layout();"
           [:div#force.chart [:svg]]))
  1. 添加路由
(defroutes
  site-routes
  (GET "/force" [] (force-layout-plot))
  (GET "/force/data.json" []
       (redirect "/data/clusters.json"))
  (route/resources "/")
  (route/not-found "Page not found"))
  1. 添加样式 :在 resources/css/style.css 中添加以下样式:
#force { width: 650px; height: 500px; }
#force .node { stroke: #fff; stroke-width: 1.5px; }
#force .link { stroke: #999; stroke-opacity: 1; }
  1. 创建ClojureScript文件 :在 src-cljs/webviz/force.cljs 中添加命名空间声明。
(ns webviz.force)
  1. 创建力导向图
(defn create-force [width height]
  (-> js/d3 .-layout
    (.force)
    (.charge -120)
    (.linkDistance 30)
    (.size (array width height))))
  1. 创建SVG元素
(defn create-svg [width height]
  (-> js/d3
    (.select "#force svg")
    (.attr "width" width)
    (.attr "height" height)))
  1. 设置节点和边并启动动画
(defn start-force [force graph]
  (-> force
    (.nodes (aget graph "nodes"))
    (.links (aget graph "links"))
    .start))
  1. 创建边和节点
(defn create-links [svg graph]
  (-> svg
    (.selectAll "line.link")
    (.data (aget graph "links"))
    (.enter)
    (.append "line")
    (.attr "class" "link")
    (.style "stroke-width"
            #(.sqrt js/Math (inc (aget % "value"))))))
(defn create-nodes [svg force color graph]
  (-> svg
    (.selectAll "circle.node")
    (.data (aget graph "nodes"))
    (.enter)
    (.append "circle")
    (.attr "class" "node")
    (.attr "r" 5)
    (.attr "data-n" #(aget % "n"))
    (.style "fill" #(color (aget % "group")))
    (.call (aget force "drag"))))
  1. 定义tick处理函数
(defn on-tick-handler [link node]
  (fn []
    (-> link
      (.attr "x1" #(-> % .-source .-x))
      (.attr "y1" #(-> % .-source .-y))
      (.attr "x2" #(-> % .-target .-x))
      (.attr "y2" #(-> % .-target .-y)))
    (-> node
      (.attr "cx" #(aget % "x"))
      (.attr "cy" #(aget % "y")))))
  1. 添加节点标题
(defn set-title [node]
  (-> node
    (.append "title")
    (.text #(aget % "name"))))
  1. 渲染图形
(def census-graph (atom nil))
(defn render-graph [color force svg graph]
  (swap! census-graph (fn [] graph))
  (start-force force graph)
  (let [link (create-links svg graph)
        node (create-nodes svg force color graph)]
    (set-title node)
    (.on force "tick" (on-tick-handler link node))))
  1. 导出函数
(defn ^:export force-layout []
  (let [width 650, height 500]
    (.json js/d3 "force/data.json"
           (partial
             render-graph
             (.category20c (aget js/d3 "scale"))
             (create-force width height)
             (create-svg width height)))))
4.3 工作原理

这是一个纯D3可视化的示例,典型的D3可视化过程包括以下步骤:
1. 创建图表控制器: create-force 函数负责创建图表的控制器。
2. 选择容器: create-svg 函数选择用于渲染图形的SVG元素。
3. 选择包含数据点的元素: create-links create-nodes 函数通过调用 selectAll 选择线条节点和圆形节点。
4. 关联数据与图表: create-links create-nodes 函数将边和节点与元素关联, start-force 函数将两者的副本传递给力对象。
5. 创建HTML元素: create-links create-nodes 函数使用 append attr 为各自的数据类型创建HTML元素。

由于力导向布局是动画形式的,还需要创建tick处理函数来更新浏览器中对象的最新位置。

总结

本文介绍了使用NVD3和D3创建不同类型网页图表的方法,包括柱状图、直方图和力导向图,并实现了图表的交互功能。通过遵循上述步骤和代码示例,你可以在网页中创建出美观且实用的图表。

流程图示例

graph TD
    A[准备数据] --> B[创建处理函数]
    B --> C[添加路由]
    C --> D[创建ClojureScript文件]
    D --> E[编写数据转换函数]
    E --> F[创建图表函数]
    F --> G[显示图表]

表格示例

图表类型 数据来源 关键步骤
柱状图 小鸡体重数据集 编写处理函数、添加路由、创建ClojureScript文件、数据转换、创建图表
直方图 鲍鱼数据集 编写处理函数、添加路由、创建ClojureScript文件、数据分组、数据转换、创建图表
力导向图 美国人口普查种族数据聚类图 编写处理函数、添加路由、添加样式、创建ClojureScript文件、创建力导向图、创建SVG元素、设置节点和边、创建边和节点、定义tick处理函数、添加节点标题、渲染图形
5. 使用D3创建交互式可视化图表

在网页开发中,使图表具有交互性可以极大地提升用户体验。下面我们将基于之前创建的美国人口普查种族数据的力导向图,为其添加交互功能,当用户悬停在节点上时,在数据面板中显示该节点的人口普查数据。

5.1 准备工作

基于之前创建的力导向图进行扩展。

5.2 操作步骤
  1. 编写处理函数 :返回一个包含数据面板的D3页面。
(defn interactive-force-plot []
  (d3-page "Interactive Force-Directive Layout"
           (str "webviz"
                ".int_force"
                ".interactive_force_layout();")
           [:div
            [:div#force.chart [:svg]]
            [:div#datapane]]))
  1. 添加路由
(defroutes
  site-routes
  (GET "/int-force" [] (interactive-force-plot))
  (GET "/int-force/data.json" []
       (redirect "/data/clusters.json"))
  (route/resources "/")
  (route/not-found "Page not found"))
  1. 添加样式 :在 resources/css/style.css 中添加数据面板的样式。
#datapane { float: right; width: 250px; }
#datapane dt { font-weight: bold; }
  1. 创建ClojureScript文件 :在 src-cljs/webviz/int_force.cljs 中添加命名空间声明。
(ns webviz.int-force
  (:require [clojure.browser.dom :as dom]
            [webviz.force :as force]
            [goog.events :as gevents]))
  1. 封装数据面板元素函数 :为每个普查项添加DT/DD元素组合。
(defn dl-item [title data key]
  (let [val2000 (aget data (str key "-2000"))]
    (str "<dt>" title "</dt>"
         "<dd>" (.round js/Math (aget data key))
         " <em>(2000: "
         (.round js/Math val2000)
         ")</em>"
         "</dd>")))
  1. 构建数据面板HTML函数
(defn update-data [node]
  (let [data (aget node "data")
        content
        (str "<h2>" (aget node "name") "</h2>"
             "<dl>"
             (dl-item "Total" data "race-total")
             (dl-item "White" data "race-white")
             (dl-item "African-American" data
                      "race-black")
             (dl-item "Native American" data
                      "race-indian")
             (dl-item "Asian" data "race-asian")
             (dl-item "Hawaiian" data "race-hawaiian")
             (dl-item "Other" data "race-other")
             (dl-item "Multi-racial" data
                      "race-two-more")
             "</dl>")]
    (dom/remove-children :datapane)
    (dom/append
      (dom/get-element :datapane)
      (dom/html->dom content))))
  1. 定义鼠标悬停事件处理函数
(defn on-mouseover [ev]
  (let [target (.-target ev)]
    (if (= (.-nodeName target) "circle")
      (let [n (+ (.getAttribute target "data-n"))]
        (update-data
          (aget (.-nodes @force/census-graph) n))))))
  1. 创建图表并添加事件处理函数
(defn ^:export interactive-force-layout []
  (force/force-layout)
  (gevents/listen (dom/get-element "force")
                  (.-MOUSEOVER gevents/EventType)
                  on-mouseover))
5.3 工作原理

此交互功能的实现原理与网页上的常规交互相同。我们监听用户在特定HTML标签上触发的事件,这里监听鼠标悬停在图中节点上的事件,并在步骤7中将事件处理函数绑定到该事件。当事件触发时,调用事件处理函数 on-mouseover ,该函数获取用户鼠标悬停节点的数据,并调用 update-data dl-item 函数构建HTML结构以显示该节点的数据。

在这个过程中,我们使用了Google Closure库的事件模块来绑定事件,使用ClojureScript的 clojure.browser.dom 命名空间来动态删除和创建HTML元素,同时通过 js: 前缀与JavaScript对象或模块进行交互。

总结与拓展

通过本文的介绍,我们掌握了使用NVD3和D3创建多种类型网页图表的方法,包括柱状图、直方图、力导向图,并为图表添加了交互功能。这些技术可以帮助我们在网页中展示数据,提升用户体验。

在实际应用中,我们可以根据具体需求对图表进行更多的定制和优化,例如调整图表的样式、添加更多的交互效果等。同时,还可以探索更多的数据可视化技术和工具,进一步丰富我们的网页应用。

流程图示例

graph TD
    A[创建力导向图] --> B[添加数据面板]
    B --> C[添加样式]
    C --> D[创建ClojureScript文件]
    D --> E[封装数据面板元素函数]
    E --> F[构建数据面板HTML函数]
    F --> G[定义鼠标悬停事件处理函数]
    G --> H[创建图表并添加事件处理函数]
    H --> I[实现交互功能]

表格示例

功能 关键代码 作用
数据面板元素封装 dl-item 函数 为每个普查项添加DT/DD元素组合
数据面板HTML构建 update-data 函数 根据节点数据构建数据面板的HTML结构
鼠标悬停事件处理 on-mouseover 函数 获取鼠标悬停节点的数据并更新数据面板
交互功能实现 interactive-force-layout 函数 创建图表并添加鼠标悬停事件处理函数
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值