weex web端使用chart图表简单,但原生端使用起比较坑,原生没有dom等问题,echart没办法在weex里面显示。
最近看到一篇文章 《聊一聊 F2 与小程序》,里面封装的思路给了启发,打算使用 @antv f2 + gcanvas 在weex里面使用chart图表。
1.首先使用weex-toolkit新建项目,如果项目存在跳过次步骤。
2.需要npm三个包
npm install @antv/f2 -s
npm install wolfy87-eventemitter -s
npm install gcanvas.js -s
gcanvas.js 是类似于 H5 Canvas 标准的 JavaScript API
3.F2 默认的运行环境是 HTML5,需要使用
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进行mock
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来处理坐标。
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类。
F2.Renderer = Renderer;
export default F2;
接下来我们就可以写个DEMO把图表渲染出来了。
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();
}
详细的demo在此,
封装比较简单,主要还是F2多端支持不错。