weex web端使用chart图表简单,但原生端使用起比较坑,原生没有dom等问题,echart没办法在weex里面显示。
最近看到一篇文章 《聊一聊 F2 与小程序》,里面封装的思路给了启发,打算使用 @antv f2 + gcanvas 在weex里面使用chart图表。html
1.首先使用weex-toolkit新建项目,若是项目存在跳过次步骤。git
2.须要npm三个包github
npm install @antv/f2 -s
npm install wolfy87-eventemitter -s
npm install gcanvas.js -s
gcanvas.js 是相似于 H5 Canvas 标准的 JavaScript APIweb
3.F2 默认的运行环境是 HTML5,须要使用 <canvas> 标签,GCanvas 和 canvas 彻底是匹配的。
按照封装的思路新建renderer.js文件apache
import EventEmitter from 'wolfy87-eventemitter'; export default class Renderer extends EventEmitter { constructor(myCtx) { super(); const self = this; self.ctx = myCtx; self.style = {}; // just mock // self._initContext(myCtx); } getContext(type) { if (type === '2d') { return this.ctx; } } }
4.再新建个chart.js,对缺失的API进行mocknpm
import Renderer from './renderer'; import F2 from '@antv/f2'; function strLen(str) { let len = 0; for (let i = 0; i < str.length; i++) { if (str.charCodeAt(i) > 0 && str.charCodeAt(i) < 128) { len++; } else { len += 2; } } return len; } // 因为GCanvas不支持 measureText 方法,故用此方法 mock F2.Util.measureText = function (text, font) { let fontSize = 12; if (font) { fontSize = parseInt(font.split(' ')[3], 10); } fontSize /= 2; return { width: strLen(text) * fontSize }; };
因为weex的手势的回调是changedTouches,须要修改下createEvent来处理坐标。canvas
F2.Util.createEvent = function (event, chart) { const pixelRatio = chart.get('pixelRatio') || 1; const type = event.type; let x = 0; let y = 0; const touches = event.changedTouches; if (touches && touches.length > 0) { x = touches[0].pageX; y = touches[0].pageY; } return { type, chart, x: x * pixelRatio, y: y * pixelRatio }; };
另外weex的像素比和H5不同,在weex渲染出来的字体和线条很是小,须要修改总体的样式。小程序
const color1 = '#E8E8E8'; // 坐标轴线、坐标轴网格线的颜色 const color2 = '#333333'; // 字体颜色 // 坐标轴的默认样式配置 const defaultAxis = { label: { fill: color2, fontSize: 24 }, // 坐标轴文本的样式 line: { stroke: color1, lineWidth: 1, top: true }, // 坐标轴线的样式 grid: { stroke: color1, lineWidth: 1, lineDash: [ 2 ] }, // 坐标轴网格线的样式 tickLine: null, // 坐标轴刻度线,默认不展现 labelOffset: 7.5 // 坐标轴文本距离坐标轴线的距离 }; const DEFAULT_CFG = { itemMarginBottom: 12, itemGap: 10, showTitle: false, titleStyle: { fontSize: 26, fill: color2, textAlign: 'start', textBaseline: 'top' }, nameStyle: { fill: color2, fontSize: 24, textAlign: 'start', textBaseline: 'middle' }, valueStyle: { fill: color2, fontSize: 24, textAlign: 'start', textBaseline: 'middle' }, unCheckStyle: { fill: '#bfbfbf' }, itemWidth: 'auto', wordSpace: 6, selectedMode: 'multiple' // 'multiple' or 'single' }; const Theme = { fontFamily: '"Helvetica Neue", "San Francisco", Helvetica, Tahoma, Arial, "PingFang SC", "Hiragino Sans GB", "Heiti SC", "Microsoft YaHei", sans-serif', // 默认字体 defaultColor: '#1890FF', // 默认颜色 pixelRatio: 1, // 默认像素比,具体参数由用户本身设置 padding: 'auto', // 图表边距,默认自动计算 appendPadding: 18, // 默认留白,15 像素 colors: [ '#1890FF', '#2FC25B', '#FACC14', '#223273', '#8543E0', '#13C2C2', '#3436C7', '#F04864' ], // 默认色系 shapes: { line: [ 'line', 'dash' ], point: [ 'circle', 'hollowCircle' ] }, sizes: [ 4, 10 ], // 默认的大小范围 axis: { bottom: F2.Util.mix({}, defaultAxis, { grid: null }), // 底部坐标轴配置 left: F2.Util.mix({}, defaultAxis, { line: null }), // 左侧坐标轴配置 right: F2.Util.mix({}, defaultAxis, { line: null }), // 右侧坐标轴配置 circle: F2.Util.mix({}, defaultAxis, { line: null }), // 极坐标下的圆弧坐标轴配置 radius: F2.Util.mix({}, defaultAxis, { labelOffset: 4 }) // 极坐标下的半径坐标轴配置 }, // 各类坐标轴配置 shape: { line: { lineWidth: 2, // 线的默认宽度 lineJoin: 'round', lineCap: 'round' }, // 线图样式配置 point: { lineWidth: 0, size: 3 // 圆的默认半径 }, // 点图样式配置 area: { fillOpacity: 0.1 } // 区域图样式配置 }, legend: { right: F2.Util.mix({}, DEFAULT_CFG), left: F2.Util.mix({}, DEFAULT_CFG), top: F2.Util.mix({}, DEFAULT_CFG), bottom: F2.Util.mix({}, DEFAULT_CFG), marker: { symbol: 'circle', // marker 的形状 radius: 10 // 半径大小 } }, tooltip: { triggerOn: [ 'touchstart', 'touchmove' ], // triggerOff: 'touchend', showTitle: false, showCrosshairs: false, crosshairsStyle: { stroke: 'rgba(0, 0, 0, 0.25)', lineWidth: 2 }, showTooltipMarker: true, background: { radius: 1, fill: 'rgba(0, 0, 0, 0.65)', padding: [ 3, 5 ] }, titleStyle: { fontSize: 26, fill: '#fff', textAlign: 'start', textBaseline: 'top' }, nameStyle: { fontSize: 26, fill: 'rgba(255, 255, 255, 0.65)', textAlign: 'start', textBaseline: 'middle' }, valueStyle: { fontSize: 26, fill: '#fff', textAlign: 'start', textBaseline: 'middle' }, showItemMarker: true, itemMarkerStyle: { radius: 5, symbol: 'circle', lineWidth: 1, stroke: '#fff' }, layout: 'horizontal' }, _defaultAxis: defaultAxis // 用于获取默认的坐标轴配置 }; F2.Global.setTheme(Theme);
最后为F2添加一个Renderer类。weex
F2.Renderer = Renderer; export default F2;
接下来咱们就能够写个DEMO把图表渲染出来了。app
<gcanvas ref="canvas_1" style="width:750px;height:400px;"></gcanvas>
import {enable, WeexBridge, Image as GImage} from "gcanvas.js"; import F2 from './chart';
setBarChart() { let ref = this.$refs.canvas_1; ref = enable(ref, {bridge: WeexBridge}); let ctx = ref.getContext("2d"); const canvas = new F2.Renderer(ctx); //使用封装好的Renderer类匹配canvas上下文 const chart = new F2.Chart({ el: canvas, // 将第三步建立的 canvas 对象的上下文传入 width: 750, // 必选,图表宽度,同 canvas 的宽度相同 height: 400 // 必选,图表高度,同 canvas 的高度相同 }); chart.source(data1); // Step 3:建立图形语法,绘制柱状图,由 genre 和 sold 两个属性决定图形位置,genre 映射至 x 轴,sold 映射至 y 轴 chart.interval().position('genre*sold').color('genre'); chart.legend('genre', { marker: { radius: 6 // 半径大小 } }); // Step 4: 渲染图表 chart.render(); }
启起来后用palyground扫描一下就能看效果了。这是由于palyground已经集成GCanvas sdk了。若是你的weex应用没有集成GCanvas sdk,也是渲染不了的,集成方法在这里GCanvas
图表使用方法参考@antv F2,基本上H5能画的图表,weex都能画,只是一些使用Dom的功能不能使用,好比:tooltip ,因此写了 tooltip 方法去touchstart
touchstart(ev) { const plot = this.chart.get('plotRange'); const { x, y } = F2.Util.createEvent(ev, this.chart); /*if (!(x >= plot.tl.x && x <= plot.tr.x && y >= plot.tl.y && y <= plot.br.y)) { // not in chart plot this.chart.hideTooltip(); return; }*/ const lastTimeStamp = this.timeStamp; const timeStamp = +new Date(); if ((timeStamp - lastTimeStamp) > 16) { this.chart.showTooltip({ x, y }); this.timeStamp = timeStamp; } }, touchend(ev){ this.chart.hideTooltip(); }
封装比较简单,主要仍是F2多端支持不错。