基于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}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章