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多端支持不錯。