基於f2從零實現移動端可視化編輯器

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"前言"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"筆者之前花了大量的時間在思考如何設計和實現H5頁面可視化編輯器"},{"type":"text","marks":[{"type":"strong"}],"text":"H5-Dooring"},{"type":"text","text":",從第一個版本到現在經歷了很多次版本迭代和優化,也收到了很多寶貴的建議,目前剛好完成了移動端數據可視化的基本設計和落地方案,在這裏特地總結和覆盤一下。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們先來看看實現的基本預覽圖:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/94/945a38f274a81dfb2978f9a6f50702e2.gif","alt":"","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"你將收穫"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基於"},{"type":"text","marks":[{"type":"strong"}],"text":"antv/f2"},{"type":"text","text":"實現可視化圖形組件的封裝"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如何設計表格編輯器並集成到"},{"type":"text","marks":[{"type":"strong"}],"text":"antd"},{"type":"text","text":"的Form中"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"數據可視化組件的"},{"type":"text","marks":[{"type":"strong"}],"text":"schema"},{"type":"text","text":"約定"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"利用"},{"type":"text","marks":[{"type":"strong"}],"text":"js-xlsx"},{"type":"text","text":"解析"},{"type":"text","marks":[{"type":"strong"}],"text":"Excel"},{"type":"text","text":"文件並導入到"},{"type":"text","marks":[{"type":"strong"}],"text":"Table"},{"type":"text","text":"中作爲可視化組件的"},{"type":"text","marks":[{"type":"strong"}],"text":"數據源"}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"正文"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在開始正式實現之前筆者先對"},{"type":"text","marks":[{"type":"strong"}],"text":"H5數據可視化"},{"type":"text","text":"做一個基本的介紹,方便大家理解其價值。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1. H5數據可視化方案的應用場景"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"隨着人工智能和大數據的快速發展,數據可視化設計在移動端的應用越來越多,主要體現在"},{"type":"text","marks":[{"type":"strong"}],"text":"數據圖表"},{"type":"text","text":",也就是我們常見的"},{"type":"text","marks":[{"type":"strong"}],"text":"柱狀圖"},{"type":"text","text":","},{"type":"text","marks":[{"type":"strong"}],"text":"折線圖"},{"type":"text","text":","},{"type":"text","marks":[{"type":"strong"}],"text":"條形圖"},{"type":"text","text":","},{"type":"text","marks":[{"type":"strong"}],"text":"雷達圖"},{"type":"text","text":"等。它們能很形象的展示不同產品或者某類特徵的變化趨勢,從而爲我們決策提供依據。比如說我們常見的"},{"type":"text","marks":[{"type":"strong"}],"text":"性格測試雷達圖"},{"type":"text","text":",各類金融app比較愛玩的某某g票的"},{"type":"text","marks":[{"type":"strong"}],"text":"趨勢預測折線圖"},{"type":"text","text":",運營人比較喜歡用的"},{"type":"text","marks":[{"type":"strong"}],"text":"漏斗模型"},{"type":"text","text":"等,幾乎任何領域都有自己的可視化應用。如下圖幾個例子:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/8e/8e2954de37767ca7c93fc64c655eb2df.png","alt":"","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/24/24b27965b46640204d02b5579b471eff.png","alt":"","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/95/950fe3a468117b3a389bb5628feb8af8.png","alt":"","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以爲了滿足企業對移動端場景下的可視化需求,能設計一款針對移動端的傻瓜式可視化搭建平臺是非常有實際意義的,目前也有很多公司在做,在"},{"type":"text","marks":[{"type":"strong"}],"text":"商業智能"},{"type":"text","text":"領域也有不錯的應用。接下來筆者就來帶大家一起實現一個這樣的"},{"type":"text","marks":[{"type":"strong"}],"text":"H5數據可視化"},{"type":"text","text":"搭建平臺。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2. H5數據可視化設計平臺的實現方式"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"目前市面上已有的比較流行的可視化庫有"},{"type":"text","marks":[{"type":"strong"}],"text":"echart"},{"type":"text","text":","},{"type":"text","marks":[{"type":"strong"}],"text":"antv"},{"type":"text","text":","},{"type":"text","marks":[{"type":"strong"}],"text":"D3.js"},{"type":"text","text":"等,針對於移動端而言,筆者還是覺得"},{"type":"text","marks":[{"type":"strong"}],"text":"antv/f2"},{"type":"text","text":"更加適合,其官網介紹如下:"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"F2 是一個專注於移動,開箱即用的可視化解決方案,完美支持 H5 環境同時兼容多種環境(Node, 小程序,Weex),完備的圖形語法理論,滿足你的各種可視化需求,專業的移動設計指引爲你帶來最佳的移動端圖表體驗。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們就暫且相信它官網的描述,接下來的技術實現筆者也會基於"},{"type":"text","marks":[{"type":"strong"}],"text":"f2"},{"type":"text","text":"做可視化組件的二次封裝。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.1 需求設計"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"筆者在開發產品之前的一貫風格就是先要理清需求,只有在需求確定之後我們才能做更加合適的技術選型和方案,所以筆者在此帶大家分析一下"},{"type":"text","marks":[{"type":"strong"}],"text":"移動端可視化編輯器"},{"type":"text","text":"的需求設計。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/bc/bc4f15fe5212029aefe20fbf180d08de.png","alt":"","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上圖爲一個可視化組件編輯器的基本模型,組成結構大致爲:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"圖表組件"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"數據源"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"屬性編輯器(組件標題,顏色,對齊等屬性)"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以說我們大致可以抽象爲如下原型:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f1/f16cb7878b75724c0c25627ced7564ed.png","alt":"","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"組件列表爲我們提供選擇不同組件的能力,畫布區域主要用來拖拽圖形和調整圖形位置,大小,編輯器用來定製圖形的“形狀”和數據源導入。在瞭解基本的需求之後我們來進行接下來的開發工作。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.2 基於antv/f2實現可視化圖形組件的封裝"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於市場上暫時沒有比較成熟的基於"},{"type":"text","marks":[{"type":"strong"}],"text":"f2"},{"type":"text","text":"的"},{"type":"text","marks":[{"type":"strong"}],"text":"react"},{"type":"text","text":"組件等封裝, 所以這裏筆者對其做一個簡單的二次封裝來實現我們的組件定製的需求。對於組件列表,爲了提高加載性能,筆者用"},{"type":"text","marks":[{"type":"strong"}],"text":"圖片佔位符"},{"type":"text","text":"代替。數據傳遞方式和"},{"type":"link","attrs":{"href":"https://github.com/MrXujiang/h5-Dooring","title":null},"content":[{"type":"text","text":"H5-Dooring"}]},{"type":"text","text":"已有組件的拖拽一致,這裏就不一一介紹了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在開發組件之前我們先安裝一下"},{"type":"text","marks":[{"type":"strong"}],"text":"f2"},{"type":"text","text":":"}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"yarn add antv/f2\n複製代碼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了進一步降低移動端代碼體積和提高加載性能,我們在引入組件時可以按需引入:"}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"// 引入核心包\nconst Core = require('@antv/f2/lib/core');\nrequire('@antv/f2/lib/geom/line'); // 只加載折線圖\nrequire('@antv/f2/lib/geom/area'); // 只加載面積圖\n複製代碼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"按需引入的方式官網上有詳細的說明,感興趣的可以學習瞭解一下。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在上面的需求分析中我們大致瞭解了可視化組件需要設置的屬性,這裏我們先整理一下以便接下來對可視化組件的封裝:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ea/ea9610b75ec74fc2bef69e973a9efc2d.png","alt":"","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們來看看"},{"type":"text","marks":[{"type":"strong"}],"text":"Chart"},{"type":"text","text":"組件的實現:"}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"// components/Chart\nimport { Chart } from '@antv/f2';\nimport React, { memo, PropsWithChildren, useEffect, useRef } from 'react';\nimport ChartImg from '@/assets/chart.png';\n\nimport styles from './index.less';\n\ntype DataItem = {\n name: string;\n value: number;\n};\n\ninterface XChartProps {\n isTpl: boolean;\n title: string;\n color: string;\n size: number;\n paddingTop: number;\n data: Array;\n}\n\nconst XChart = (props: PropsWithChildren) => {\n const { isTpl, data, color, size, paddingTop, title } = props;\n const chartRef = useRef(null);\n useEffect(() => {\n if (!isTpl) {\n const chart = new Chart({\n el: chartRef.current || undefined,\n pixelRatio: window.devicePixelRatio, // 指定分辨率\n });\n\n // step 2: 處理數據\n const dataX = data.map(item => ({ ...item, value: Number(item.value) }));\n\n // Step 2: 載入數據源\n chart.source(dataX);\n\n // Step 3:創建圖形語法,繪製柱狀圖,由 genre 和 sold 兩個屬性決定圖形位置,genre 映射至 x 軸,sold 映射至 y 軸\n chart\n .interval()\n .position('name*value')\n .color('name');\n\n // Step 4: 渲染圖表\n chart.render();\n }\n }, []);\n return (\n
\n
\n {title}\n
\n {isTpl ? \"dooring : }\n
\n );\n};\n\nexport default memo(XChart);\n複製代碼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以上Chart組件就基本封裝完畢,如果有更多定製化需求,也可以自行添加。代碼中我們採用"},{"type":"text","marks":[{"type":"strong"}],"text":"typescript"},{"type":"text","text":"和"},{"type":"text","marks":[{"type":"strong"}],"text":"React Hooks"},{"type":"text","text":"開發,爲了對組件進行做優化,我們用了"},{"type":"text","marks":[{"type":"strong"}],"text":"memo"},{"type":"text","text":",如果對這些技術點不熟悉的,稍後可以移步我的"},{"type":"text","marks":[{"type":"strong"}],"text":"react hooks"},{"type":"text","text":"和"},{"type":"text","marks":[{"type":"strong"}],"text":"typescript"},{"type":"text","text":"相關的文章學習。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以上只是完成了基本的可視化組件的封裝,接下來的重點是實現可視化組件和表單編輯器之間的聯動。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.3 設計表格編輯器並集成到antd的Form中"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"表格編輯器的實現我們主要基於"},{"type":"text","marks":[{"type":"strong"}],"text":"antd"},{"type":"text","text":"的"},{"type":"text","marks":[{"type":"strong"}],"text":"Table"},{"type":"text","text":"組件來實現,當我們點擊數據源的時候,會彈出表格編輯器,我們先來看看效果:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/61/613a6d11bcbbe22c14e10f8687049ee7.png","alt":"","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們可以直接對數據源進行編輯,比如修改數據,刪除數據,添加數據,也即是"},{"type":"text","marks":[{"type":"strong"}],"text":"CURD"},{"type":"text","text":"的那套流程。並且支持導入excel數據,這塊筆者將在下一章節來實現。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可編輯表格實現原理就是在表格中加入狀態,分爲查看模式和編輯模式,編輯模式採用input框,在失焦時進行保存/切換查看狀態。添加行的邏輯主要是動態插入一條數據,這塊實現也比較簡單,具體實現感興趣的朋友可參考我的"},{"type":"link","attrs":{"href":"https://github.com/MrXujiang/h5-Dooring/tree/master/src/components/Table","title":null},"content":[{"type":"text","text":"源碼"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"部分代碼參考如下:"}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"// 添加行\nhandleAdd = () => {\n const { count, dataSource } = this.state;\n const newData = {\n key: count,\n name: `dooring ${count}`,\n value: 32,\n };\n const newDataSource = [...dataSource, newData];\n this.setState({\n dataSource: newDataSource,\n count: count + 1,\n });\n this.props.onChange && this.props.onChange(newDataSource);\n};\n\n// 保存行數據\nhandleSave = row => {\n const newData = [...this.state.dataSource];\n const index = newData.findIndex(item => row.key === item.key);\n const item = newData[index];\n newData.splice(index, 1, {\n ...item,\n ...row,\n });\n this.setState({ dataSource: newData });\n this.props.onChange && this.props.onChange(newData);\n};\n複製代碼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面代碼的this.props.onChange主要是爲了"},{"type":"text","marks":[{"type":"strong"}],"text":"antd"},{"type":"text","text":"的"},{"type":"text","marks":[{"type":"strong"}],"text":"Form"},{"type":"text","text":"能接受到變化,使"},{"type":"text","marks":[{"type":"strong"}],"text":"Table Editor"},{"type":"text","text":"成爲"},{"type":"text","marks":[{"type":"strong"}],"text":"Form"},{"type":"text","text":"的“受控組件”。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.4 利用js-xlsx解析Excel文件並導入到Table中作爲可視化組件的數據源"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於上面介紹的數據源錄入,我們有兩種模式:"},{"type":"text","marks":[{"type":"strong"}],"text":"手動錄入"},{"type":"text","text":"和"},{"type":"text","marks":[{"type":"strong"}],"text":"文件導入"},{"type":"text","text":"。設計文件導入主要是爲了更好的用戶體驗,這裏爲了實現該功能我們可以採用社區比較火的"},{"type":"text","marks":[{"type":"strong"}],"text":"js-xlsx"},{"type":"text","text":",一款專業的解析excel數據的插件,而且可以輸出多種數據類型。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們先來安裝一下:"}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":" npm install xlsx\n複製代碼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"讀取excel文件數據代碼如下:"}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"// 讀取本地excel文件\nfunction readLocalFile(file, callback) {\n\tlet reader = new FileReader();\n\treader.onload = function(e) {\n\t\tlet data = e.target.result;\n\t\tlet formData = XLSX.read(data, {type: 'binary'});\n\t\tif(callback) callback(formData);\n\t};\n\treader.readAsBinaryString(file);\n}\n複製代碼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有了以上代碼,我們只需要在導入excel的按鈕上綁定事件並解析數據即可實現導入功能。大家可以嘗試一下。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以上就基本實現了我們的真個體系設計,後面的雷達圖,折線圖等實現原理也類似。我們看看用"},{"type":"text","marks":[{"type":"strong"}],"text":"H5-Dooring"},{"type":"text","text":"配置出的幾個案例:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/77/773d35c77b786ab78e8a8cd582871d74.png","alt":"","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d3/d39815f02766453256047c2b56f46642.png","alt":"","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當然大家也可以在"},{"type":"text","marks":[{"type":"strong"}],"text":"H5-Dooring"},{"type":"text","text":"定製自己的"},{"type":"text","marks":[{"type":"strong"}],"text":"H5數據可視化面板"},{"type":"text","text":"。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"3. 總結"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以上教程筆者已經集成到"},{"type":"text","marks":[{"type":"strong"}],"text":"H5-Dooring"},{"type":"text","text":"中,對於一些更復雜的交互功能,通過合理的設計也是可以實現的,大家可以自行探索研究。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"github地址:"},{"type":"link","attrs":{"href":"https://github.com/MrXujiang/h5-Dooring","title":null},"content":[{"type":"text","text":"H5在線編輯器H5-Dooring"}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"最後"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果想學習更多H5遊戲, webpack,node,gulp,css3,javascript,nodeJS,canvas數據可視化等前端知識和實戰,歡迎在《趣談前端》一起學習討論,共同探索前端的邊界。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章