李兴辉 89b04fb058 init
2025-03-10 13:49:13 +08:00

5047 lines
169 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* uCharts v1.8.5.20190815
* uni-app平台高性能跨全端图表支持H5、APP、小程序微信/支付宝/百度/头条/QQ/360
* Copyright (c) 2019 QIUN秋云 https://www.ucharts.cn All rights reserved.
* Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
*
* uCharts官方网站
* https://www.uCharts.cn
*
* 开源地址:
* https://gitee.com/uCharts/uCharts
*
* uni-app插件市场地址
* http://ext.dcloud.net.cn/plugin?id=271
*
*/
'use strict';
var config = {
yAxisWidth: 15,
yAxisSplit: 5,
xAxisHeight: 15,
xAxisLineHeight: 15,
legendHeight: 15,
yAxisTitleWidth: 15,
padding: [10, 10, 10, 10],
pixelRatio: 1,
rotate: false,
columePadding: 3,
fontSize: 13,
//dataPointShape: ['diamond', 'circle', 'triangle', 'rect'],
dataPointShape: ['circle', 'circle', 'circle', 'circle'],
colors: ['#1890ff', '#2fc25b', '#facc14', '#f04864', '#8543e0', '#90ed7d'],
pieChartLinePadding: 15,
pieChartTextPadding: 5,
xAxisTextPadding: 3,
titleColor: '#333333',
titleFontSize: 20,
subtitleColor: '#999999',
subtitleFontSize: 15,
toolTipPadding: 3,
toolTipBackground: '#000000',
toolTipOpacity: 0.7,
toolTipLineHeight: 20,
radarGridCount: 3,
radarLabelTextMargin: 15,
gaugeLabelTextMargin: 15
};
let assign;
if (Object.assign) {
assign = Object.assign;
} else {
// 使用polyfill
assign = function(target, varArgs) {
if (target == null) {
throw new TypeError('Cannot convert undefined or null to object');
}
var to = Object(target);
for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index];
if (nextSource != null) {
for (var nextKey in nextSource) {
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
}
}
var util = {
toFixed: function toFixed(num, limit) {
limit = limit || 2;
if (this.isFloat(num)) {
num = num.toFixed(limit);
}
return num;
},
isFloat: function isFloat(num) {
return num % 1 !== 0;
},
approximatelyEqual: function approximatelyEqual(num1, num2) {
return Math.abs(num1 - num2) < 1e-10;
},
isSameSign: function isSameSign(num1, num2) {
return Math.abs(num1) === num1 && Math.abs(num2) === num2 || Math.abs(num1) !== num1 && Math.abs(num2) !== num2;
},
isSameXCoordinateArea: function isSameXCoordinateArea(p1, p2) {
return this.isSameSign(p1.x, p2.x);
},
isCollision: function isCollision(obj1, obj2) {
obj1.end = {};
obj1.end.x = obj1.start.x + obj1.width;
obj1.end.y = obj1.start.y - obj1.height;
obj2.end = {};
obj2.end.x = obj2.start.x + obj2.width;
obj2.end.y = obj2.start.y - obj2.height;
var flag = obj2.start.x > obj1.end.x || obj2.end.x < obj1.start.x || obj2.end.y > obj1.start.y || obj2.start.y < obj1.end.y;
return !flag;
}
};
//兼容H5点击事件
function getH5Offset(e) {
e.mp = {
changedTouches: []
};
e.mp.changedTouches.push({
x: e.offsetX,
y: e.offsetY
});
return e;
}
// hex 转 rgba
function hexToRgb(hexValue, opc) {
var rgx = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
var hex = hexValue.replace(rgx, function(m, r, g, b) {
return r + r + g + g + b + b;
});
var rgb = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
var r = parseInt(rgb[1], 16);
var g = parseInt(rgb[2], 16);
var b = parseInt(rgb[3], 16);
return 'rgba(' + r + ',' + g + ',' + b + ',' + opc + ')';
}
function findRange(num, type, limit) {
if (isNaN(num)) {
throw new Error('[uCharts] unvalid series data!');
}
limit = limit || 10;
type = type ? type : 'upper';
var multiple = 1;
while (limit < 1) {
limit *= 10;
multiple *= 10;
}
if (type === 'upper') {
num = Math.ceil(num * multiple);
} else {
num = Math.floor(num * multiple);
}
while (num % limit !== 0) {
if (type === 'upper') {
num++;
} else {
num--;
}
}
return num / multiple;
}
function calCandleMA(dayArr, nameArr, colorArr, kdata) {
let seriesTemp = [];
for (let k = 0; k < dayArr.length; k++) {
let seriesItem = {
data: [],
name: nameArr[k],
color: colorArr[k]
};
for (let i = 0, len = kdata.length; i < len; i++) {
if (i < dayArr[k]) {
seriesItem.data.push(null);
continue;
}
let sum = 0;
for (let j = 0; j < dayArr[k]; j++) {
sum += kdata[i - j][1];
}
seriesItem.data.push(+(sum / dayArr[k]).toFixed(3));
}
seriesTemp.push(seriesItem);
}
return seriesTemp;
}
function calValidDistance(distance, chartData, config, opts) {
var dataChartAreaWidth = opts.width - opts.area[1] - opts.area[3];
var dataChartWidth = chartData.eachSpacing * (opts.chartData.xAxisData.xAxisPoints.length-1);
var validDistance = distance;
if (distance >= 0) {
validDistance = 0;
} else if (Math.abs(distance) >= dataChartWidth - dataChartAreaWidth) {
validDistance = dataChartAreaWidth - dataChartWidth;
}
return validDistance;
}
function isInAngleRange(angle, startAngle, endAngle) {
function adjust(angle) {
while (angle < 0) {
angle += 2 * Math.PI;
}
while (angle > 2 * Math.PI) {
angle -= 2 * Math.PI;
}
return angle;
}
angle = adjust(angle);
startAngle = adjust(startAngle);
endAngle = adjust(endAngle);
if (startAngle > endAngle) {
endAngle += 2 * Math.PI;
if (angle < startAngle) {
angle += 2 * Math.PI;
}
}
return angle >= startAngle && angle <= endAngle;
}
function calRotateTranslate(x, y, h) {
var xv = x;
var yv = h - y;
var transX = xv + (h - yv - xv) / Math.sqrt(2);
transX *= -1;
var transY = (h - yv) * (Math.sqrt(2) - 1) - (h - yv - xv) / Math.sqrt(2);
return {
transX: transX,
transY: transY
};
}
function createCurveControlPoints(points, i) {
function isNotMiddlePoint(points, i) {
if (points[i - 1] && points[i + 1]) {
return points[i].y >= Math.max(points[i - 1].y, points[i + 1].y) || points[i].y <= Math.min(points[i - 1].y,
points[
i + 1].y);
} else {
return false;
}
}
var a = 0.2;
var b = 0.2;
var pAx = null;
var pAy = null;
var pBx = null;
var pBy = null;
if (i < 1) {
pAx = points[0].x + (points[1].x - points[0].x) * a;
pAy = points[0].y + (points[1].y - points[0].y) * a;
} else {
pAx = points[i].x + (points[i + 1].x - points[i - 1].x) * a;
pAy = points[i].y + (points[i + 1].y - points[i - 1].y) * a;
}
if (i > points.length - 3) {
var last = points.length - 1;
pBx = points[last].x - (points[last].x - points[last - 1].x) * b;
pBy = points[last].y - (points[last].y - points[last - 1].y) * b;
} else {
pBx = points[i + 1].x - (points[i + 2].x - points[i].x) * b;
pBy = points[i + 1].y - (points[i + 2].y - points[i].y) * b;
}
if (isNotMiddlePoint(points, i + 1)) {
pBy = points[i + 1].y;
}
if (isNotMiddlePoint(points, i)) {
pAy = points[i].y;
}
return {
ctrA: {
x: pAx,
y: pAy
},
ctrB: {
x: pBx,
y: pBy
}
};
}
function convertCoordinateOrigin(x, y, center) {
return {
x: center.x + x,
y: center.y - y
};
}
function avoidCollision(obj, target) {
if (target) {
// is collision test
while (util.isCollision(obj, target)) {
if (obj.start.x > 0) {
obj.start.y--;
} else if (obj.start.x < 0) {
obj.start.y++;
} else {
if (obj.start.y > 0) {
obj.start.y++;
} else {
obj.start.y--;
}
}
}
}
return obj;
}
function fillSeries(series, opts, config) {
var index = 0;
return series.map(function(item) {
if (!item.color) {
item.color = config.colors[index];
index = (index + 1) % config.colors.length;
}
if (!item.type) {
item.type = opts.type;
}
if (typeof item.show == "undefined") {
item.show = true;
}
if (!item.type) {
item.type = opts.type;
}
if (!item.pointShape) {
item.pointShape = "circle";
}
if (!item.legendShape) {
switch (item.type) {
case 'line':
item.legendShape = "line";
break;
case 'column':
item.legendShape = "rect";
break;
case 'area':
item.legendShape = "triangle";
break;
default:
item.legendShape = "circle";
}
}
return item;
});
}
function getDataRange(minData, maxData) {
var limit = 0;
var range = maxData - minData;
if (range >= 10000) {
limit = 1000;
} else if (range >= 1000) {
limit = 100;
} else if (range >= 100) {
limit = 10;
} else if (range >= 10) {
limit = 5;
} else if (range >= 1) {
limit = 1;
} else if (range >= 0.1) {
limit = 0.1;
} else if (range >= 0.01) {
limit = 0.01;
} else if (range >= 0.001) {
limit = 0.001;
} else if (range >= 0.0001) {
limit = 0.0001;
} else if (range >= 0.00001) {
limit = 0.00001;
} else {
limit = 0.000001;
}
return {
minRange: findRange(minData, 'lower', limit),
maxRange: findRange(maxData, 'upper', limit)
};
}
function measureText(text) {
var fontSize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : config.fontSize;
text = String(text);
var text = text.split('');
var width = 0;
for (let i = 0; i < text.length; i++) {
let item = text[i];
if (/[a-zA-Z]/.test(item)) {
width += 7;
} else if (/[0-9]/.test(item)) {
width += 5.5;
} else if (/\./.test(item)) {
width += 2.7;
} else if (/-/.test(item)) {
width += 3.25;
} else if (/[\u4e00-\u9fa5]/.test(item)) {
width += 10;
} else if (/\(|\)/.test(item)) {
width += 3.73;
} else if (/\s/.test(item)) {
width += 2.5;
} else if (/%/.test(item)) {
width += 8;
} else {
width += 10;
}
}
return width * fontSize / 10;
}
function dataCombine(series) {
return series.reduce(function(a, b) {
return (a.data ? a.data : a).concat(b.data);
}, []);
}
function dataCombineStack(series, len) {
var sum = new Array(len);
for (var j = 0; j < sum.length; j++) {
sum[j] = 0;
}
for (var i = 0; i < series.length; i++) {
for (var j = 0; j < sum.length; j++) {
sum[j] += series[i].data[j];
}
}
return series.reduce(function(a, b) {
return (a.data ? a.data : a).concat(b.data).concat(sum);
}, []);
}
function getTouches(touches, opts, e) {
let x, y;
if (touches.clientX) {
if (opts.rotate) {
y = opts.height - touches.clientX * opts.pixelRatio;
x = (touches.pageY - e.currentTarget.offsetTop - (opts.height / opts.pixelRatio / 2) * (opts.pixelRatio - 1)) *
opts.pixelRatio;
} else {
x = touches.clientX * opts.pixelRatio;
y = (touches.pageY - e.currentTarget.offsetTop - (opts.height / opts.pixelRatio / 2) * (opts.pixelRatio - 1)) *
opts.pixelRatio;
}
} else {
if (opts.rotate) {
y = opts.height - touches.x * opts.pixelRatio;
x = touches.y * opts.pixelRatio;
} else {
x = touches.x * opts.pixelRatio;
y = touches.y * opts.pixelRatio;
}
}
return {
x: x,
y: y
}
}
function getSeriesDataItem(series, index) {
var data = [];
for (let i = 0; i < series.length; i++) {
let item = series[i];
if (item.data[index] !== null && typeof item.data[index] !== 'undefined' && item.show) {
let seriesItem = {};
seriesItem.color = item.color;
seriesItem.type = item.type;
seriesItem.style = item.style;
seriesItem.pointShape = item.pointShape;
seriesItem.disableLegend = item.disableLegend;
seriesItem.name = item.name;
seriesItem.show = item.show;
seriesItem.data = item.format ? item.format(item.data[index]) : item.data[index];
data.push(seriesItem);
}
}
return data;
}
function getMaxTextListLength(list) {
var lengthList = list.map(function(item) {
return measureText(item);
});
return Math.max.apply(null, lengthList);
}
function getRadarCoordinateSeries(length) {
var eachAngle = 2 * Math.PI / length;
var CoordinateSeries = [];
for (var i = 0; i < length; i++) {
CoordinateSeries.push(eachAngle * i);
}
return CoordinateSeries.map(function(item) {
return -1 * item + Math.PI / 2;
});
}
function getToolTipData(seriesData, calPoints, index, categories) {
var option = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
var textList = seriesData.map(function(item) {
return {
text: option.format ? option.format(item, categories[index]) : item.name + ': ' + item.data,
color: item.color
};
});
var validCalPoints = [];
var offset = {
x: 0,
y: 0
};
for (let i = 0; i < calPoints.length; i++) {
let points = calPoints[i];
if (typeof points[index] !== 'undefined' && points[index] !== null) {
validCalPoints.push(points[index]);
}
}
for (let i = 0; i < validCalPoints.length; i++) {
let item = validCalPoints[i];
offset.x = Math.round(item.x);
offset.y += item.y;
}
offset.y /= validCalPoints.length;
return {
textList: textList,
offset: offset
};
}
function getMixToolTipData(seriesData, calPoints, index, categories) {
var option = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
var textList = seriesData.map(function(item) {
return {
text: option.format ? option.format(item, categories[index]) : item.name + ': ' + item.data,
color: item.color,
disableLegend: item.disableLegend ? true : false
};
});
textList = textList.filter(function(item) {
if (item.disableLegend !== true) {
return item;
}
});
var validCalPoints = [];
var offset = {
x: 0,
y: 0
};
for (let i = 0; i < calPoints.length; i++) {
let points = calPoints[i];
if (typeof points[index] !== 'undefined' && points[index] !== null) {
validCalPoints.push(points[index]);
}
}
for (let i = 0; i < validCalPoints.length; i++) {
let item = validCalPoints[i];
offset.x = Math.round(item.x);
offset.y += item.y;
}
offset.y /= validCalPoints.length;
return {
textList: textList,
offset: offset
};
}
function getCandleToolTipData(series, seriesData, calPoints, index, categories, extra) {
var option = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : {};
let upColor = extra.color.upFill;
let downColor = extra.color.downFill;
//颜色顺序为开盘,收盘,最低,最高
let color = [upColor, upColor, downColor, upColor];
var textList = [];
let text0 = {
text: categories[index],
color: null
};
textList.push(text0);
seriesData.map(function(item) {
if (index == 0 && item.data[1] - item.data[0] < 0) {
color[1] = downColor;
} else {
if (item.data[0] < series[index - 1][1]) {
color[0] = downColor;
}
if (item.data[1] < item.data[0]) {
color[1] = downColor;
}
if (item.data[2] > series[index - 1][1]) {
color[2] = upColor;
}
if (item.data[3] < series[index - 1][1]) {
color[3] = downColor;
}
}
let text1 = {
text: '开盘:' + item.data[0],
color: color[0]
};
let text2 = {
text: '收盘:' + item.data[1],
color: color[1]
};
let text3 = {
text: '最低:' + item.data[2],
color: color[2]
};
let text4 = {
text: '最高:' + item.data[3],
color: color[3]
};
textList.push(text1, text2, text3, text4);
});
var validCalPoints = [];
var offset = {
x: 0,
y: 0
};
for (let i = 0; i < calPoints.length; i++) {
let points = calPoints[i];
if (typeof points[index] !== 'undefined' && points[index] !== null) {
validCalPoints.push(points[index]);
}
}
offset.x = Math.round(validCalPoints[0][0].x);
return {
textList: textList,
offset: offset
};
}
function filterSeries(series) {
let tempSeries = [];
for (let i = 0; i < series.length; i++) {
if (series[i].show == true) {
tempSeries.push(series[i])
}
}
return tempSeries;
}
function findCurrentIndex(currentPoints, xAxisPoints, opts, config) {
var offset = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;
var currentIndex = -1;
var spacing = 0;
if((opts.type=='line' || opts.type=='area') && opts.xAxis.boundaryGap=='justify'){
spacing = opts.chartData.eachSpacing/2;
}
if (isInExactChartArea(currentPoints, opts, config)) {
xAxisPoints.forEach(function(item, index) {
if (currentPoints.x + offset + spacing > item) {
currentIndex = index;
}
});
}
return currentIndex;
}
function findLegendIndex(currentPoints, legendData, opts) {
let currentIndex = -1;
if (isInExactLegendArea(currentPoints, legendData.area)) {
let points = legendData.points;
let index = -1;
for (let i = 0, len = points.length; i < len; i++) {
let item = points[i];
for (let j = 0; j < item.length; j++) {
index += 1;
let area = item[j]['area'];
if (currentPoints.x > area[0] && currentPoints.x < area[2] && currentPoints.y > area[1] && currentPoints.y < area[3]) {
currentIndex = index;
break;
}
}
}
return currentIndex;
}
return currentIndex;
}
function isInExactLegendArea(currentPoints, area) {
return currentPoints.x > area.start.x && currentPoints.x < area.end.x && currentPoints.y > area.start.y &&
currentPoints.y < area.end.y;
}
function isInExactChartArea(currentPoints, opts, config) {
return currentPoints.x < opts.width - opts.area[1] + 10 && currentPoints.x > opts.area[3] -10 && currentPoints.y > opts.area[0] && currentPoints.y < opts.height - opts.area[2];
}
function findRadarChartCurrentIndex(currentPoints, radarData, count) {
var eachAngleArea = 2 * Math.PI / count;
var currentIndex = -1;
if (isInExactPieChartArea(currentPoints, radarData.center, radarData.radius)) {
var fixAngle = function fixAngle(angle) {
if (angle < 0) {
angle += 2 * Math.PI;
}
if (angle > 2 * Math.PI) {
angle -= 2 * Math.PI;
}
return angle;
};
var angle = Math.atan2(radarData.center.y - currentPoints.y, currentPoints.x - radarData.center.x);
angle = -1 * angle;
if (angle < 0) {
angle += 2 * Math.PI;
}
var angleList = radarData.angleList.map(function(item) {
item = fixAngle(-1 * item);
return item;
});
angleList.forEach(function(item, index) {
var rangeStart = fixAngle(item - eachAngleArea / 2);
var rangeEnd = fixAngle(item + eachAngleArea / 2);
if (rangeEnd < rangeStart) {
rangeEnd += 2 * Math.PI;
}
if (angle >= rangeStart && angle <= rangeEnd || angle + 2 * Math.PI >= rangeStart && angle + 2 * Math.PI <=
rangeEnd) {
currentIndex = index;
}
});
}
return currentIndex;
}
function findFunnelChartCurrentIndex(currentPoints, funnelData) {
var currentIndex = -1;
for (var i = 0, len = funnelData.series.length; i < len; i++) {
var item = funnelData.series[i];
if (currentPoints.x > item.funnelArea[0] && currentPoints.x < item.funnelArea[2] && currentPoints.y > item.funnelArea[1] && currentPoints.y < item.funnelArea[3]) {
currentIndex = i;
break;
}
}
return currentIndex;
}
function findWordChartCurrentIndex(currentPoints, wordData) {
var currentIndex = -1;
for (var i = 0, len = wordData.length; i < len; i++) {
var item = wordData[i];
if (currentPoints.x > item.area[0] && currentPoints.x < item.area[2] && currentPoints.y > item.area[1] && currentPoints.y < item.area[3]) {
currentIndex = i;
break;
}
}
return currentIndex;
}
function findMapChartCurrentIndex(currentPoints, opts) {
var currentIndex = -1;
var cData=opts.chartData.mapData;
var data=opts.series;
var tmp=pointToCoordinate(currentPoints.y, currentPoints.x,cData.bounds,cData.scale,cData.xoffset,cData.yoffset);
var poi=[tmp.x, tmp.y];
for (var i = 0, len = data.length; i < len; i++) {
var item = data[i].geometry.coordinates;
if(isPoiWithinPoly(poi,item)){
currentIndex = i;
break;
}
}
return currentIndex;
}
function findPieChartCurrentIndex(currentPoints, pieData) {
var currentIndex = -1;
if (isInExactPieChartArea(currentPoints, pieData.center, pieData.radius)) {
var angle = Math.atan2(pieData.center.y - currentPoints.y, currentPoints.x - pieData.center.x);
angle = -angle;
for (var i = 0, len = pieData.series.length; i < len; i++) {
var item = pieData.series[i];
if (isInAngleRange(angle, item._start_, item._start_ + item._proportion_ * 2 * Math.PI)) {
currentIndex = i;
break;
}
}
}
return currentIndex;
}
function isInExactPieChartArea(currentPoints, center, radius) {
return Math.pow(currentPoints.x - center.x, 2) + Math.pow(currentPoints.y - center.y, 2) <= Math.pow(radius, 2);
}
function splitPoints(points) {
var newPoints = [];
var items = [];
points.forEach(function(item, index) {
if (item !== null) {
items.push(item);
} else {
if (items.length) {
newPoints.push(items);
}
items = [];
}
});
if (items.length) {
newPoints.push(items);
}
return newPoints;
}
function calLegendData(series, opts, config, chartData) {
let legendData = {
area: {
start: {
x: 0,
y: 0
},
end: {
x: 0,
y: 0
},
width: 0,
height: 0,
wholeWidth: 0,
wholeHeight: 0
},
points: [],
widthArr: [],
heightArr: []
};
if (opts.legend.show === false) {
chartData.legendData = legendData;
return legendData;
}
let padding = opts.legend.padding;
let margin = opts.legend.margin;
let fontSize = opts.legend.fontSize;
let shapeWidth = 15 * opts.pixelRatio;
let shapeRight = 5 * opts.pixelRatio;
let lineHeight = Math.max(opts.legend.lineHeight * opts.pixelRatio, fontSize);
if (opts.legend.position == 'top' || opts.legend.position == 'bottom') {
let legendList = [];
let widthCount = 0;
let widthCountArr = [];
let currentRow = [];
for (let i = 0; i < series.length; i++) {
let item = series[i];
let itemWidth = shapeWidth + shapeRight + measureText(item.name || 'undefined', fontSize) + opts.legend.itemGap;
if (widthCount + itemWidth > opts.width - opts.padding[1] - opts.padding[3]) {
legendList.push(currentRow);
widthCountArr.push(widthCount - opts.legend.itemGap);
widthCount = itemWidth;
currentRow = [item];
} else {
widthCount += itemWidth;
currentRow.push(item);
}
}
if (currentRow.length) {
legendList.push(currentRow);
widthCountArr.push(widthCount - opts.legend.itemGap);
legendData.widthArr = widthCountArr;
let legendWidth = Math.max.apply(null, widthCountArr);
switch (opts.legend.float) {
case 'left':
legendData.area.start.x = opts.padding[3];
legendData.area.end.x = opts.padding[3] + 2 * padding;
break;
case 'right':
legendData.area.start.x = opts.width - opts.padding[1] - legendWidth - 2 * padding;
legendData.area.end.x = opts.width - opts.padding[1];
break;
default:
legendData.area.start.x = (opts.width - legendWidth) / 2 - padding;
legendData.area.end.x = (opts.width + legendWidth) / 2 + padding;
}
legendData.area.width = legendWidth + 2 * padding;
legendData.area.wholeWidth = legendWidth + 2 * padding;
legendData.area.height = legendList.length * lineHeight + 2 * padding;
legendData.area.wholeHeight = legendList.length * lineHeight + 2 * padding + 2 * margin;
legendData.points = legendList;
}
} else {
let len = series.length;
let maxHeight = opts.height - opts.padding[0] - opts.padding[2] - 2 * margin - 2 * padding;
let maxLength = Math.min(Math.floor(maxHeight / lineHeight), len);
legendData.area.height = maxLength * lineHeight + padding * 2;
legendData.area.wholeHeight = maxLength * lineHeight + padding * 2;
switch (opts.legend.float) {
case 'top':
legendData.area.start.y = opts.padding[0] + margin;
legendData.area.end.y = opts.padding[0] + margin + legendData.area.height;
break;
case 'bottom':
legendData.area.start.y = opts.height - opts.padding[2] - margin - legendData.area.height;
legendData.area.end.y = opts.height - opts.padding[2] - margin;
break;
default:
legendData.area.start.y = (opts.height - legendData.area.height) / 2;
legendData.area.end.y = (opts.height + legendData.area.height) / 2;
}
let lineNum = len % maxLength === 0 ? len / maxLength : Math.floor((len / maxLength) + 1);
let currentRow = [];
for (let i = 0; i < lineNum; i++) {
let temp = series.slice(i * maxLength, i * maxLength + maxLength);
currentRow.push(temp);
}
legendData.points = currentRow;
if (currentRow.length) {
for (let i = 0; i < currentRow.length; i++) {
let item = currentRow[i];
let maxWidth = 0;
for (let j = 0; j < item.length; j++) {
let itemWidth = shapeWidth + shapeRight + measureText(item[j].name || 'undefined', fontSize) + opts.legend.itemGap;
if (itemWidth > maxWidth) {
maxWidth = itemWidth;
}
}
legendData.widthArr.push(maxWidth);
legendData.heightArr.push(item.length * lineHeight + padding * 2);
}
let legendWidth = 0
for (let i = 0; i < legendData.widthArr.length; i++) {
legendWidth += legendData.widthArr[i];
}
legendData.area.width = legendWidth - opts.legend.itemGap + 2 * padding;
legendData.area.wholeWidth = legendData.area.width + padding;
}
}
switch (opts.legend.position) {
case 'top':
legendData.area.start.y = opts.padding[0] + margin;
legendData.area.end.y = opts.padding[0] + margin + legendData.area.height;
break;
case 'bottom':
legendData.area.start.y = opts.height - opts.padding[2] - legendData.area.height - margin;
legendData.area.end.y = opts.height - opts.padding[2] - margin;
break;
case 'left':
legendData.area.start.x = opts.padding[3];
legendData.area.end.x = opts.padding[3] + legendData.area.width;
break;
case 'right':
legendData.area.start.x = opts.width - opts.padding[1] - legendData.area.width;
legendData.area.end.x = opts.width - opts.padding[1];
break;
}
chartData.legendData = legendData;
return legendData;
}
function calCategoriesData(categories, opts, config, eachSpacing) {
var result = {
angle: 0,
xAxisHeight: config.xAxisHeight
};
var categoriesTextLenth = categories.map(function(item) {
return measureText(item);
});
var maxTextLength = Math.max.apply(this, categoriesTextLenth);
if (opts.xAxis.rotateLabel == true && maxTextLength + 2 * config.xAxisTextPadding > eachSpacing) {
result.angle = 45 * Math.PI / 180;
result.xAxisHeight = 2 * config.xAxisTextPadding + maxTextLength * Math.sin(result.angle);
}
return result;
}
function getRadarDataPoints(angleList, center, radius, series, opts) {
var process = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 1;
var radarOption = opts.extra.radar || {};
radarOption.max = radarOption.max || 0;
var maxData = Math.max(radarOption.max, Math.max.apply(null, dataCombine(series)));
var data = [];
for (let i = 0; i < series.length; i++) {
let each = series[i];
let listItem = {};
listItem.color = each.color;
listItem.data = [];
each.data.forEach(function(item, index) {
let tmp = {};
tmp.angle = angleList[index];
tmp.proportion = item / maxData;
tmp.position = convertCoordinateOrigin(radius * tmp.proportion * process * Math.cos(tmp.angle), radius * tmp.proportion *
process * Math.sin(tmp.angle), center);
listItem.data.push(tmp);
});
data.push(listItem);
}
return data;
}
function getPieDataPoints(series, radius) {
var process = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
var count = 0;
var _start_ = 0;
for (let i = 0; i < series.length; i++) {
let item = series[i];
item.data = item.data === null ? 0 : item.data;
count += item.data;
}
for (let i = 0; i < series.length; i++) {
let item = series[i];
item.data = item.data === null ? 0 : item.data;
if (count === 0) {
item._proportion_ = 1 / series.length * process;
} else {
item._proportion_ = item.data / count * process;
}
item._radius_ = radius;
}
for (let i = 0; i < series.length; i++) {
let item = series[i];
item._start_ = _start_;
_start_ += 2 * item._proportion_ * Math.PI;
}
return series;
}
function getFunnelDataPoints(series, radius) {
var process = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
series = series.sort(function(a,b){return parseInt(b.data)-parseInt(a.data);});
for (let i = 0; i < series.length; i++) {
series[i].radius = series[i].data/series[0].data*radius*process;
series[i]._proportion_ = series[i].data/series[0].data;
}
return series.reverse();
}
function getRoseDataPoints(series, type, minRadius, radius) {
var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
var count = 0;
var _start_ = 0;
var dataArr = [];
for (let i = 0; i < series.length; i++) {
let item = series[i];
item.data = item.data === null ? 0 : item.data;
count += item.data;
dataArr.push(item.data);
}
var minData = Math.min.apply(null, dataArr);
var maxData = Math.max.apply(null, dataArr);
var radiusLength = radius - minRadius;
for (let i = 0; i < series.length; i++) {
let item = series[i];
item.data = item.data === null ? 0 : item.data;
if (count === 0 || type == 'area') {
item._proportion_ = item.data / count * process;
item._rose_proportion_ = 1 / series.length * process;
} else {
item._proportion_ = item.data / count * process;
item._rose_proportion_ = item.data / count * process;
}
item._radius_ = minRadius + radiusLength * ((item.data - minData) / (maxData - minData));
}
for (let i = 0; i < series.length; i++) {
let item = series[i];
item._start_ = _start_;
_start_ += 2 * item._rose_proportion_ * Math.PI;
}
return series;
}
function getArcbarDataPoints(series, arcbarOption) {
var process = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
if (process == 1) {
process = 0.999999;
}
for (let i = 0; i < series.length; i++) {
let item = series[i];
item.data = item.data === null ? 0 : item.data;
let totalAngle;
if (arcbarOption.type == 'default') {
if (arcbarOption.endAngle < arcbarOption.startAngle) {
totalAngle = 2 + arcbarOption.endAngle - arcbarOption.startAngle;
} else{
totalAngle = arcbarOption.startAngle - arcbarOption.endAngle;
}
} else {
totalAngle = 2;
}
item._proportion_ = totalAngle * item.data * process + arcbarOption.startAngle;
if (item._proportion_ >= 2) {
item._proportion_ = item._proportion_ % 2;
}
}
return series;
}
function getGaugeAxisPoints(categories, startAngle, endAngle) {
let totalAngle = startAngle - endAngle + 1;
let tempStartAngle = startAngle;
for (let i = 0; i < categories.length; i++) {
categories[i].value = categories[i].value === null ? 0 : categories[i].value;
categories[i]._startAngle_ = tempStartAngle;
categories[i]._endAngle_ = totalAngle * categories[i].value + startAngle;
if (categories[i]._endAngle_ >= 2) {
categories[i]._endAngle_ = categories[i]._endAngle_ % 2;
}
tempStartAngle = categories[i]._endAngle_;
}
return categories;
}
function getGaugeDataPoints(series, categories, gaugeOption) {
let process = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1;
for (let i = 0; i < series.length; i++) {
let item = series[i];
item.data = item.data === null ? 0 : item.data;
if (gaugeOption.pointer.color == 'auto') {
for (let i = 0; i < categories.length; i++) {
if (item.data <= categories[i].value) {
item.color = categories[i].color;
break;
}
}
} else {
item.color = gaugeOption.pointer.color;
}
let totalAngle = gaugeOption.startAngle - gaugeOption.endAngle + 1;
item._endAngle_ = totalAngle * item.data + gaugeOption.startAngle;
item._oldAngle_ = gaugeOption.oldAngle;
if (gaugeOption.oldAngle < gaugeOption.endAngle) {
item._oldAngle_ += 2;
}
if (item.data >= gaugeOption.oldData) {
item._proportion_ = (item._endAngle_ - item._oldAngle_) * process + gaugeOption.oldAngle;
} else {
item._proportion_ = item._oldAngle_ - (item._oldAngle_ - item._endAngle_) * process;
}
if (item._proportion_ >= 2) {
item._proportion_ = item._proportion_ % 2;
}
}
return series;
}
function getPieTextMaxLength(series) {
series = getPieDataPoints(series);
let maxLength = 0;
for (let i = 0; i < series.length; i++) {
let item = series[i];
let text = item.format ? item.format(+item._proportion_.toFixed(2)) : util.toFixed(item._proportion_ * 100) + '%';
maxLength = Math.max(maxLength, measureText(text));
}
return maxLength;
}
function fixColumeData(points, eachSpacing, columnLen, index, config, opts) {
return points.map(function(item) {
if (item === null) {
return null;
}
item.width = Math.ceil((eachSpacing - 2 * config.columePadding) / columnLen);
if (opts.extra.column && opts.extra.column.width && +opts.extra.column.width > 0) {
item.width = Math.min(item.width, +opts.extra.column.width);
}
if (item.width <= 0) {
item.width = 1;
}
item.x += (index + 0.5 - columnLen / 2) * item.width;
return item;
});
}
function fixColumeMeterData(points, eachSpacing, columnLen, index, config, opts, border) {
return points.map(function(item) {
if (item === null) {
return null;
}
item.width = Math.ceil((eachSpacing - 2 * config.columePadding) / 2);
if (opts.extra.column && opts.extra.column.width && +opts.extra.column.width > 0) {
item.width = Math.min(item.width, +opts.extra.column.width);
}
if (index > 0) {
item.width -= 2 * border;
}
return item;
});
}
function fixColumeStackData(points, eachSpacing, columnLen, index, config, opts, series) {
return points.map(function(item, indexn) {
if (item === null) {
return null;
}
item.width = Math.ceil((eachSpacing - 2 * config.columePadding) / 2);
if (opts.extra.column && opts.extra.column.width && +opts.extra.column.width > 0) {
item.width = Math.min(item.width, +opts.extra.column.width);
}
return item;
});
}
function getXAxisPoints(categories, opts, config) {
var yAxisTotalWidth = config.yAxisWidth + config.yAxisTitleWidth;
var spacingValid = opts.width - opts.area[1] - opts.area[3];
var dataCount = opts.enableScroll ? Math.min(opts.xAxis.itemCount, categories.length) : categories.length;
if((opts.type=='line' || opts.type=='area') && dataCount>1 && opts.xAxis.boundaryGap=='justify'){
dataCount -=1;
}
var eachSpacing = spacingValid / dataCount;
var xAxisPoints = [];
var startX = opts.area[3];
var endX = opts.width - opts.area[1];
categories.forEach(function(item, index) {
xAxisPoints.push(startX + index * eachSpacing);
});
if(opts.xAxis.boundaryGap !=='justify'){
if (opts.enableScroll === true) {
xAxisPoints.push(startX + categories.length * eachSpacing);
} else {
xAxisPoints.push(endX);
}
}
return {
xAxisPoints: xAxisPoints,
startX: startX,
endX: endX,
eachSpacing: eachSpacing
};
}
function getCandleDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config) {
var process = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : 1;
var points = [];
var validHeight = opts.height - opts.area[0] - opts.area[2];
data.forEach(function(item, index) {
if (item === null) {
points.push(null);
} else {
var cPoints = [];
item.forEach(function(items, indexs) {
var point = {};
point.x = xAxisPoints[index] + Math.round(eachSpacing / 2);
var value = items.value || items;
var height = validHeight * (value - minRange) / (maxRange - minRange);
height *= process;
point.y = opts.height - Math.round(height) - opts.area[2];
cPoints.push(point);
});
points.push(cPoints);
}
});
return points;
}
function getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config) {
var process = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : 1;
var boundaryGap='center';
if (opts.type == 'line'||opts.type == 'area'){
boundaryGap=opts.xAxis.boundaryGap;
}
var points = [];
var validHeight = opts.height - opts.area[0] - opts.area[2];
data.forEach(function(item, index) {
if (item === null) {
points.push(null);
} else {
var point = {};
point.color = item.color;
point.x = xAxisPoints[index];
if(boundaryGap=='center'){
point.x += Math.round(eachSpacing / 2);
}
var value = item;
if (typeof item === 'object' && item !== null) {
value = item.value
}
var height = validHeight * (value - minRange) / (maxRange - minRange);
height *= process;
point.y = opts.height - Math.round(height) - opts.area[2];
points.push(point);
}
});
return points;
}
function getStackDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, seriesIndex, stackSeries) {
var process = arguments.length > 9 && arguments[9] !== undefined ? arguments[9] : 1;
var points = [];
var validHeight = opts.height - opts.area[0] - opts.area[2];
data.forEach(function(item, index) {
if (item === null) {
points.push(null);
} else {
var point = {};
point.color = item.color;
point.x = xAxisPoints[index] + Math.round(eachSpacing / 2);
if (seriesIndex > 0) {
var value = 0;
for (let i = 0; i <= seriesIndex; i++) {
value += stackSeries[i].data[index];
}
var value0 = value - item;
var height = validHeight * (value - minRange) / (maxRange - minRange);
var height0 = validHeight * (value0 - minRange) / (maxRange - minRange);
} else {
var value = item;
var height = validHeight * (value - minRange) / (maxRange - minRange);
var height0 = 0;
}
var heightc = height0;
height *= process;
heightc *= process;
point.y = opts.height - Math.round(height) - opts.area[2];
point.y0 = opts.height - Math.round(heightc) - opts.area[2];
points.push(point);
}
});
return points;
}
function getYAxisTextList(series, opts, config, stack) {
var data;
if (stack == 'stack') {
data = dataCombineStack(series, opts.categories.length);
} else {
data = dataCombine(series);
}
var sorted = [];
// remove null from data
data = data.filter(function(item) {
//return item !== null;
if (typeof item === 'object' && item !== null) {
if (Array.isArray(item)) {
return item !== null;
} else {
return item.value !== null;
}
} else {
return item !== null;
}
});
data.map(function(item) {
if (typeof item === 'object') {
if (Array.isArray(item)) {
item.map(function(subitem) {
sorted.push(subitem);
})
} else {
sorted.push(item.value);
}
} else {
sorted.push(item);
}
})
var minData = 0;
var maxData = 0;
if (sorted.length > 0) {
minData = Math.min.apply(this, sorted);
maxData = Math.max.apply(this, sorted);
}
if (typeof opts.yAxis.min === 'number') {
minData = Math.min(opts.yAxis.min, minData);
}
if (typeof opts.yAxis.max === 'number') {
maxData = Math.max(opts.yAxis.max, maxData);
}
if (minData === maxData) {
var rangeSpan = maxData || 10;
maxData += rangeSpan;
}
var dataRange = getDataRange(minData, maxData);
var minRange = dataRange.minRange;
var maxRange = dataRange.maxRange;
var range = [];
var eachRange = (maxRange - minRange) / config.yAxisSplit;
for (var i = 0; i <= config.yAxisSplit; i++) {
range.push(minRange + eachRange * i);
}
return range.reverse();
}
function calYAxisData(series, opts, config) {
//堆叠图重算Y轴
var columnstyle = assign({}, {
type: ""
}, opts.extra.column);
var ranges = getYAxisTextList(series, opts, config, columnstyle.type);
var yAxisWidth = config.yAxisWidth;
var yAxisFontSize = opts.yAxis.fontSize || config.fontSize;
var rangesFormat = ranges.map(function(item) {
item = util.toFixed(item, 6);
item = opts.yAxis.format ? opts.yAxis.format(Number(item)) : item;
yAxisWidth = Math.max(yAxisWidth, measureText(item, yAxisFontSize) + 5);
return item;
});
if (opts.yAxis.disabled === true) {
yAxisWidth = 0;
}
return {
rangesFormat: rangesFormat,
ranges: ranges,
yAxisWidth: yAxisWidth
};
}
function calTooltipYAxisData(point, series, opts, config, eachSpacing) {
var ranges = getYAxisTextList(series, opts, config);
var spacingValid = opts.height - opts.area[0] - opts.area[2];
let maxVal = ranges[0];
let minVal = ranges[ranges.length - 1];
let minAxis = opts.padding[3];
let maxAxis = opts.padding[1] + spacingValid;
let item = maxVal - (maxVal - minVal) * (point - minAxis) / (maxAxis - minAxis);
item = opts.yAxis.format ? opts.yAxis.format(Number(item)) : item;
return item;
}
function calMarkLineData(minRange, maxRange, points, opts) {
let spacingValid = opts.height - opts.area[0] - opts.area[2];
for (let i = 0; i < points.length; i++) {
let height = spacingValid * (points[i].value - minRange) / (maxRange - minRange);
points[i].y = opts.height - Math.round(height) - opts.area[2];
}
return points;
}
function contextRotate(context, opts) {
if (opts.rotateLock !== true) {
context.translate(opts.height, 0);
context.rotate(90 * Math.PI / 180);
} else if (opts._rotate_ !== true) {
context.translate(opts.height, 0);
context.rotate(90 * Math.PI / 180);
opts._rotate_ = true;
}
}
function drawPointShape(points, color, shape, context, opts) {
context.beginPath();
context.setStrokeStyle("#ffffff");
context.setLineWidth(1 * opts.pixelRatio);
context.setFillStyle(color);
if (shape === 'diamond') {
points.forEach(function(item, index) {
if (item !== null) {
context.moveTo(item.x, item.y - 4.5);
context.lineTo(item.x - 4.5, item.y);
context.lineTo(item.x, item.y + 4.5);
context.lineTo(item.x + 4.5, item.y);
context.lineTo(item.x, item.y - 4.5);
}
});
} else if (shape === 'circle') {
points.forEach(function(item, index) {
if (item !== null) {
context.moveTo(item.x + 3.5 * opts.pixelRatio, item.y);
context.arc(item.x, item.y, 4 * opts.pixelRatio, 0, 2 * Math.PI, false);
}
});
} else if (shape === 'rect') {
points.forEach(function(item, index) {
if (item !== null) {
context.moveTo(item.x - 3.5, item.y - 3.5);
context.rect(item.x - 3.5, item.y - 3.5, 7, 7);
}
});
} else if (shape === 'triangle') {
points.forEach(function(item, index) {
if (item !== null) {
context.moveTo(item.x, item.y - 4.5);
context.lineTo(item.x - 4.5, item.y + 4.5);
context.lineTo(item.x + 4.5, item.y + 4.5);
context.lineTo(item.x, item.y - 4.5);
}
});
}
context.closePath();
context.fill();
context.stroke();
}
function drawRingTitle(opts, config, context, center) {
var titlefontSize = opts.title.fontSize || config.titleFontSize;
var subtitlefontSize = opts.subtitle.fontSize || config.subtitleFontSize;
var title = opts.title.name || '';
var subtitle = opts.subtitle.name || '';
var titleFontColor = opts.title.color || config.titleColor;
var subtitleFontColor = opts.subtitle.color || config.subtitleColor;
var titleHeight = title ? titlefontSize : 0;
var subtitleHeight = subtitle ? subtitlefontSize : 0;
var margin = 5;
if (subtitle) {
var textWidth = measureText(subtitle, subtitlefontSize);
var startX = center.x - textWidth / 2 + (opts.subtitle.offsetX || 0);
var startY = center.y + subtitlefontSize / 2 + (opts.subtitle.offsetY || 0);
if (title) {
startY += (titleHeight + margin) / 2;
}
context.beginPath();
context.setFontSize(subtitlefontSize);
context.setFillStyle(subtitleFontColor);
context.fillText(subtitle, startX, startY);
context.closePath();
context.stroke();
}
if (title) {
var _textWidth = measureText(title, titlefontSize);
var _startX = center.x - _textWidth / 2 + (opts.title.offsetX || 0);
var _startY = center.y + titlefontSize / 2 + (opts.title.offsetY || 0);
if (subtitle) {
_startY -= (subtitleHeight + margin) / 2;
}
context.beginPath();
context.setFontSize(titlefontSize);
context.setFillStyle(titleFontColor);
context.fillText(title, _startX, _startY);
context.closePath();
context.stroke();
}
}
function drawPointText(points, series, config, context) {
// 绘制数据文案
var data = series.data;
points.forEach(function(item, index) {
if (item !== null) {
//var formatVal = series.format ? series.format(data[index]) : data[index];
context.beginPath();
context.setFontSize(series.textSize || config.fontSize);
context.setFillStyle(series.textColor || '#666666');
var value = data[index]
if (typeof data[index] === 'object' && data[index] !== null) {
value = data[index].value
}
var formatVal = series.format ? series.format(value) : value;
context.fillText(String(formatVal), item.x - measureText(formatVal, series.textSize || config.fontSize) / 2, item.y -
2);
context.closePath();
context.stroke();
}
});
}
function drawGaugeLabel(gaugeOption, radius, centerPosition, opts, config, context) {
radius -= gaugeOption.width / 2 + config.gaugeLabelTextMargin;
let totalAngle = gaugeOption.startAngle - gaugeOption.endAngle + 1;
let splitAngle = totalAngle / gaugeOption.splitLine.splitNumber;
let totalNumber = gaugeOption.endNumber - gaugeOption.startNumber;
let splitNumber = totalNumber / gaugeOption.splitLine.splitNumber;
let nowAngle = gaugeOption.startAngle;
let nowNumber = gaugeOption.startNumber;
for (let i = 0; i < gaugeOption.splitLine.splitNumber + 1; i++) {
var pos = {
x: radius * Math.cos(nowAngle * Math.PI),
y: radius * Math.sin(nowAngle * Math.PI)
};
var labelText = gaugeOption.labelFormat ? gaugeOption.labelFormat(nowNumber) : nowNumber;
pos.x += centerPosition.x - measureText(labelText) / 2;
pos.y += centerPosition.y;
var startX = pos.x;
var startY = pos.y;
context.beginPath();
context.setFontSize(config.fontSize);
context.setFillStyle(gaugeOption.labelColor || '#666666');
context.fillText(labelText, startX, startY + config.fontSize / 2);
context.closePath();
context.stroke();
nowAngle += splitAngle;
if (nowAngle >= 2) {
nowAngle = nowAngle % 2;
}
nowNumber += splitNumber;
}
}
function drawRadarLabel(angleList, radius, centerPosition, opts, config, context) {
var radarOption = opts.extra.radar || {};
radius += config.radarLabelTextMargin;
angleList.forEach(function(angle, index) {
var pos = {
x: radius * Math.cos(angle),
y: radius * Math.sin(angle)
};
var posRelativeCanvas = convertCoordinateOrigin(pos.x, pos.y, centerPosition);
var startX = posRelativeCanvas.x;
var startY = posRelativeCanvas.y;
if (util.approximatelyEqual(pos.x, 0)) {
startX -= measureText(opts.categories[index] || '') / 2;
} else if (pos.x < 0) {
startX -= measureText(opts.categories[index] || '');
}
context.beginPath();
context.setFontSize(config.fontSize);
context.setFillStyle(radarOption.labelColor || '#666666');
context.fillText(opts.categories[index] || '', startX, startY + config.fontSize / 2);
context.closePath();
context.stroke();
});
}
function drawPieText(series, opts, config, context, radius, center) {
var lineRadius = config.pieChartLinePadding;
var textObjectCollection = [];
var lastTextObject = null;
var seriesConvert = series.map(function(item) {
var text = item.format ? item.format(+item._proportion_.toFixed(2)) : util.toFixed(item._proportion_.toFixed(4) * 100) +'%';
if(item._rose_proportion_) item._proportion_=item._rose_proportion_;
var arc = 2 * Math.PI - (item._start_ + 2 * Math.PI * item._proportion_ / 2);
var color = item.color;
var radius = item._radius_;
return {
arc: arc,
text: text,
color: color,
radius: radius,
textColor: item.textColor,
textSize: item.textSize,
};
});
for (let i = 0; i < seriesConvert.length; i++) {
let item = seriesConvert[i];
// line end
let orginX1 = Math.cos(item.arc) * (item.radius + lineRadius);
let orginY1 = Math.sin(item.arc) * (item.radius + lineRadius);
// line start
let orginX2 = Math.cos(item.arc) * item.radius;
let orginY2 = Math.sin(item.arc) * item.radius;
// text start
let orginX3 = orginX1 >= 0 ? orginX1 + config.pieChartTextPadding : orginX1 - config.pieChartTextPadding;
let orginY3 = orginY1;
let textWidth = measureText(item.text);
let startY = orginY3;
if (lastTextObject && util.isSameXCoordinateArea(lastTextObject.start, {
x: orginX3
})) {
if (orginX3 > 0) {
startY = Math.min(orginY3, lastTextObject.start.y);
} else if (orginX1 < 0) {
startY = Math.max(orginY3, lastTextObject.start.y);
} else {
if (orginY3 > 0) {
startY = Math.max(orginY3, lastTextObject.start.y);
} else {
startY = Math.min(orginY3, lastTextObject.start.y);
}
}
}
if (orginX3 < 0) {
orginX3 -= textWidth;
}
let textObject = {
lineStart: {
x: orginX2,
y: orginY2
},
lineEnd: {
x: orginX1,
y: orginY1
},
start: {
x: orginX3,
y: startY
},
width: textWidth,
height: config.fontSize,
text: item.text,
color: item.color,
textColor: item.textColor,
textSize: item.textSize
};
lastTextObject = avoidCollision(textObject, lastTextObject);
textObjectCollection.push(lastTextObject);
}
for (let i = 0; i < textObjectCollection.length; i++) {
let item = textObjectCollection[i];
let lineStartPoistion = convertCoordinateOrigin(item.lineStart.x, item.lineStart.y, center);
let lineEndPoistion = convertCoordinateOrigin(item.lineEnd.x, item.lineEnd.y, center);
let textPosition = convertCoordinateOrigin(item.start.x, item.start.y, center);
context.setLineWidth(1 * opts.pixelRatio);
context.setFontSize(config.fontSize);
context.beginPath();
context.setStrokeStyle(item.color);
context.setFillStyle(item.color);
context.moveTo(lineStartPoistion.x, lineStartPoistion.y);
let curveStartX = item.start.x < 0 ? textPosition.x + item.width : textPosition.x;
let textStartX = item.start.x < 0 ? textPosition.x - 5 : textPosition.x + 5;
context.quadraticCurveTo(lineEndPoistion.x, lineEndPoistion.y, curveStartX, textPosition.y);
context.moveTo(lineStartPoistion.x, lineStartPoistion.y);
context.stroke();
context.closePath();
context.beginPath();
context.moveTo(textPosition.x + item.width, textPosition.y);
context.arc(curveStartX, textPosition.y, 2, 0, 2 * Math.PI);
context.closePath();
context.fill();
context.beginPath();
context.setFontSize(item.textSize || config.fontSize);
context.setFillStyle(item.textColor || '#666666');
context.fillText(item.text, textStartX, textPosition.y + 3);
context.closePath();
context.stroke();
context.closePath();
}
}
function drawToolTipSplitLine(offsetX, opts, config, context) {
var toolTipOption = opts.extra.tooltip || {};
toolTipOption.gridType = toolTipOption.gridType == undefined ? 'solid' : toolTipOption.gridType;
toolTipOption.dashLength = toolTipOption.dashLength == undefined ? 4 : toolTipOption.dashLength;
var startY = opts.area[0];
var endY = opts.height - opts.area[2];
if (toolTipOption.gridType == 'dash') {
context.setLineDash([toolTipOption.dashLength, toolTipOption.dashLength]);
}
context.setStrokeStyle(toolTipOption.gridColor || '#cccccc');
context.setLineWidth(1 * opts.pixelRatio);
context.beginPath();
context.moveTo(offsetX, startY);
context.lineTo(offsetX, endY);
context.stroke();
context.setLineDash([]);
if (toolTipOption.xAxisLabel) {
let labelText = opts.categories[opts.tooltip.index];
context.setFontSize(config.fontSize);
let textWidth = measureText(labelText, config.fontSize);
let textX = offsetX - 0.5 * textWidth;
let textY = endY;
context.beginPath();
context.setFillStyle(hexToRgb(toolTipOption.labelBgColor || config.toolTipBackground, toolTipOption.labelBgOpacity || config.toolTipOpacity));
context.setStrokeStyle(toolTipOption.labelBgColor || config.toolTipBackground);
context.setLineWidth(1 * opts.pixelRatio);
context.rect(textX - config.toolTipPadding, textY, textWidth + 2 * config.toolTipPadding, config.fontSize + 2 * config.toolTipPadding);
context.closePath();
context.stroke();
context.fill();
context.beginPath();
context.setFontSize(config.fontSize);
context.setFillStyle(toolTipOption.labelFontColor || config.fontColor);
context.fillText(String(labelText), textX, textY + config.toolTipPadding + config.fontSize);
context.closePath();
context.stroke();
}
}
function drawMarkLine(minRange, maxRange, opts, config, context) {
let markLineOption = assign({}, {
type: 'solid',
dashLength: 4,
data: []
}, opts.extra.markLine);
let startX = opts.area[3];
let endX = opts.width - opts.padding[1];
let points = calMarkLineData(minRange, maxRange, markLineOption.data, opts);
for (let i = 0; i < points.length; i++) {
let item = assign({}, {
lineColor: '#DE4A42',
showLabel: false,
labelFontColor: '#666666',
labelBgColor: '#DFE8FF',
labelBgOpacity: 0.8,
yAxisIndex: 0
}, points[i]);
if (markLineOption.type == 'dash') {
context.setLineDash([markLineOption.dashLength, markLineOption.dashLength]);
}
context.setStrokeStyle(item.lineColor);
context.setLineWidth(1 * opts.pixelRatio);
context.beginPath();
context.moveTo(startX, item.y);
context.lineTo(endX, item.y);
context.stroke();
context.setLineDash([]);
if (item.showLabel) {
let labelText = opts.yAxis.format ? opts.yAxis.format(Number(item.value)) : item.value;
context.setFontSize(config.fontSize);
let textWidth = measureText(labelText, config.fontSize);
let bgStartX = opts.padding[3] + config.yAxisTitleWidth - config.toolTipPadding;
let bgEndX = Math.max(opts.area[3], textWidth + config.toolTipPadding * 2);
let bgWidth = bgEndX - bgStartX;
let textX = bgStartX + (bgWidth - textWidth) / 2;
let textY = item.y;
context.setFillStyle(hexToRgb(item.labelBgColor, item.labelBgOpacity));
context.setStrokeStyle(item.labelBgColor);
context.setLineWidth(1 * opts.pixelRatio);
context.beginPath();
context.rect(bgStartX, textY - 0.5 * config.fontSize - config.toolTipPadding, bgWidth, config.fontSize + 2 * config.toolTipPadding);
context.closePath();
context.stroke();
context.fill();
context.beginPath();
context.setFontSize(config.fontSize);
context.setFillStyle(item.labelFontColor);
context.fillText(String(labelText), textX, textY + 0.5 * config.fontSize);
context.stroke();
}
}
}
function drawToolTipHorizentalLine(opts, config, context, eachSpacing, xAxisPoints) {
var toolTipOption = assign({}, {
gridType: 'solid',
dashLength: 4
}, opts.extra.tooltip);
var startX = opts.area[3];
var endX = opts.width - opts.padding[1];
if (toolTipOption.gridType == 'dash') {
context.setLineDash([toolTipOption.dashLength, toolTipOption.dashLength]);
}
context.setStrokeStyle(toolTipOption.gridColor || '#cccccc');
context.setLineWidth(1 * opts.pixelRatio);
context.beginPath();
context.moveTo(startX, opts.tooltip.offset.y);
context.lineTo(endX, opts.tooltip.offset.y);
context.stroke();
context.setLineDash([]);
if (toolTipOption.yAxisLabel) {
let labelText = calTooltipYAxisData(opts.tooltip.offset.y, opts.series, opts, config, eachSpacing);
context.setFontSize(config.fontSize);
let textWidth = measureText(labelText, config.fontSize);
let bgStartX = opts.padding[3] + config.yAxisTitleWidth - config.toolTipPadding;
let bgEndX = Math.max(opts.area[3], textWidth + config.toolTipPadding * 2);
let bgWidth = bgEndX - bgStartX;
let textX = bgStartX + (bgWidth - textWidth) / 2;
let textY = opts.tooltip.offset.y;
context.beginPath();
context.setFillStyle(hexToRgb(toolTipOption.labelBgColor || config.toolTipBackground, toolTipOption.labelBgOpacity || config.toolTipOpacity));
context.setStrokeStyle(toolTipOption.labelBgColor || config.toolTipBackground);
context.setLineWidth(1 * opts.pixelRatio);
context.rect(bgStartX, textY - 0.5 * config.fontSize - config.toolTipPadding, bgWidth, config.fontSize + 2 * config.toolTipPadding);
context.closePath();
context.stroke();
context.fill();
context.beginPath();
context.setFontSize(config.fontSize);
context.setFillStyle(toolTipOption.labelFontColor || config.fontColor);
context.fillText(labelText, textX, textY + 0.5 * config.fontSize);
context.closePath();
context.stroke();
}
}
function drawToolTipSplitArea(offsetX, opts, config, context, eachSpacing) {
var toolTipOption = assign({}, {
activeBgColor: '#000000',
activeBgOpacity: 0.08
}, opts.extra.tooltip);
var startY = opts.area[0];
var endY = opts.height - opts.area[2];
context.beginPath();
context.setFillStyle(hexToRgb(toolTipOption.activeBgColor, toolTipOption.activeBgOpacity));
context.rect(offsetX - eachSpacing / 2, startY, eachSpacing, endY - startY);
context.closePath();
context.fill();
}
function drawToolTip(textList, offset, opts, config, context, eachSpacing, xAxisPoints) {
var toolTipOption = assign({}, {
bgColor: '#000000',
bgOpacity: 0.7,
fontColor: '#FFFFFF'
}, opts.extra.tooltip);
var legendWidth = 4 * opts.pixelRatio;
var legendMarginRight = 5 * opts.pixelRatio;
var arrowWidth = 8 * opts.pixelRatio;
var isOverRightBorder = false;
if (opts.type == 'line' || opts.type == 'area' || opts.type == 'candle' || opts.type == 'mix') {
drawToolTipSplitLine(opts.tooltip.offset.x, opts, config, context);
}
offset = assign({
x: 0,
y: 0
}, offset);
offset.y -= 8 * opts.pixelRatio;
var textWidth = textList.map(function(item) {
return measureText(item.text, config.fontSize);
});
var toolTipWidth = legendWidth + legendMarginRight + 4 * config.toolTipPadding + Math.max.apply(null, textWidth);
var toolTipHeight = 2 * config.toolTipPadding + textList.length * config.toolTipLineHeight;
// if beyond the right border
if (offset.x - Math.abs(opts._scrollDistance_) + arrowWidth + toolTipWidth > opts.width) {
isOverRightBorder = true;
}
if (toolTipHeight + offset.y > opts.height) {
offset.y = opts.height - toolTipHeight;
}
// draw background rect
context.beginPath();
context.setFillStyle(hexToRgb(toolTipOption.bgColor || config.toolTipBackground, toolTipOption.bgOpacity || config.toolTipOpacity));
if (isOverRightBorder) {
context.moveTo(offset.x, offset.y + 10 * opts.pixelRatio);
context.lineTo(offset.x - arrowWidth, offset.y + 10 * opts.pixelRatio - 5 * opts.pixelRatio);
context.lineTo(offset.x - arrowWidth, offset.y);
context.lineTo(offset.x - arrowWidth - Math.round(toolTipWidth), offset.y);
context.lineTo(offset.x - arrowWidth - Math.round(toolTipWidth), offset.y + toolTipHeight);
context.lineTo(offset.x - arrowWidth, offset.y + toolTipHeight);
context.lineTo(offset.x - arrowWidth, offset.y + 10 * opts.pixelRatio + 5 * opts.pixelRatio);
context.lineTo(offset.x, offset.y + 10 * opts.pixelRatio);
} else {
context.moveTo(offset.x, offset.y + 10 * opts.pixelRatio);
context.lineTo(offset.x + arrowWidth, offset.y + 10 * opts.pixelRatio - 5 * opts.pixelRatio);
context.lineTo(offset.x + arrowWidth, offset.y);
context.lineTo(offset.x + arrowWidth + Math.round(toolTipWidth), offset.y);
context.lineTo(offset.x + arrowWidth + Math.round(toolTipWidth), offset.y + toolTipHeight);
context.lineTo(offset.x + arrowWidth, offset.y + toolTipHeight);
context.lineTo(offset.x + arrowWidth, offset.y + 10 * opts.pixelRatio + 5 * opts.pixelRatio);
context.lineTo(offset.x, offset.y + 10 * opts.pixelRatio);
}
context.closePath();
context.fill();
// draw legend
textList.forEach(function(item, index) {
if (item.color !== null) {
context.beginPath();
context.setFillStyle(item.color);
var startX = offset.x + arrowWidth + 2 * config.toolTipPadding;
var startY = offset.y + (config.toolTipLineHeight - config.fontSize) / 2 + config.toolTipLineHeight * index +
config.toolTipPadding + 1;
if (isOverRightBorder) {
startX = offset.x - toolTipWidth - arrowWidth + 2 * config.toolTipPadding;
}
context.fillRect(startX, startY, legendWidth, config.fontSize);
context.closePath();
}
});
// draw text list
textList.forEach(function(item, index) {
var startX = offset.x + arrowWidth + 2 * config.toolTipPadding + legendWidth + legendMarginRight;
if (isOverRightBorder) {
startX = offset.x - toolTipWidth - arrowWidth + 2 * config.toolTipPadding + +legendWidth + legendMarginRight;
}
var startY = offset.y + (config.toolTipLineHeight - config.fontSize) / 2 + config.toolTipLineHeight * index +
config.toolTipPadding;
context.beginPath();
context.setFontSize(config.fontSize);
context.setFillStyle(toolTipOption.fontColor);
context.fillText(item.text, startX, startY + config.fontSize);
context.closePath();
context.stroke();
});
}
function drawYAxisTitle(title, opts, config, context) {
var startX = config.xAxisHeight + (opts.height - config.xAxisHeight - measureText(title)) / 2;
context.save();
context.beginPath();
context.setFontSize(config.fontSize);
context.setFillStyle(opts.yAxis.titleFontColor || '#333333');
context.translate(0, opts.height);
context.rotate(-90 * Math.PI / 180);
context.fillText(title, startX, opts.padding[3] + 0.5 * config.fontSize);
context.closePath();
context.stroke();
context.restore();
}
function drawColumnDataPoints(series, opts, config, context) {
let process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
let ranges = [].concat(opts.chartData.yAxisData.ranges);
let xAxisData = opts.chartData.xAxisData,
xAxisPoints = xAxisData.xAxisPoints,
eachSpacing = xAxisData.eachSpacing;
let columnOption = assign({}, {
type: 'group',
width: eachSpacing / 2,
meter: {
border: 4,
fillColor: '#FFFFFF'
}
}, opts.extra.column);
let minRange = ranges.pop();
let maxRange = ranges.shift();
let calPoints = [];
context.save();
if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) {
context.translate(opts._scrollDistance_, 0);
}
if (opts.tooltip && opts.tooltip.textList && opts.tooltip.textList.length && process === 1) {
drawToolTipSplitArea(opts.tooltip.offset.x, opts, config, context, eachSpacing);
}
series.forEach(function(eachSeries, seriesIndex) {
var data = eachSeries.data;
switch (columnOption.type) {
case 'group':
var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
var tooltipPoints = getStackDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, seriesIndex, series, process);
calPoints.push(tooltipPoints);
points = fixColumeData(points, eachSpacing, series.length, seriesIndex, config, opts);
points.forEach(function(item, index) {
if (item !== null) {
context.beginPath();
context.setStrokeStyle(item.color || eachSeries.color);
context.setLineWidth(1)
context.setFillStyle(item.color || eachSeries.color);
var startX = item.x - item.width / 2;
var height = opts.height - item.y - opts.area[2];
context.moveTo(startX-1, item.y);
context.lineTo(startX+item.width-2,item.y);
context.lineTo(startX+item.width-2,opts.height - opts.area[2]);
context.lineTo(startX,opts.height - opts.area[2]);
context.lineTo(startX,item.y);
//context.rect(startX, item.y, item.width, height);
context.closePath();
context.stroke();
context.fill();
}
});
break;
case 'stack':
// 绘制堆叠数据图
var points = getStackDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config,
seriesIndex,
series, process);
calPoints.push(points);
points = fixColumeStackData(points, eachSpacing, series.length, seriesIndex, config, opts, series);
points.forEach(function(item, index) {
if (item !== null) {
context.beginPath();
context.setFillStyle(item.color || eachSeries.color);
var startX = item.x - item.width / 2 + 1;
var height = opts.height - item.y - opts.area[2];
var height0 = opts.height - item.y0 - opts.area[2];
if (seriesIndex > 0) {
height -= height0;
}
context.moveTo(startX, item.y);
context.fillRect(startX, item.y, item.width - 2, height);
context.closePath();
context.fill();
}
});
break;
case 'meter':
// 绘制温度计数据图
var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
calPoints.push(points);
points = fixColumeMeterData(points, eachSpacing, series.length, seriesIndex, config, opts, columnOption.meter
.border);
if (seriesIndex == 0) {
points.forEach(function(item, index) {
if (item !== null) {
//画背景颜色
context.beginPath();
context.setFillStyle(columnOption.meter.fillColor);
var startX = item.x - item.width / 2;
var height = opts.height - item.y - opts.area[2];
context.moveTo(startX, item.y);
context.fillRect(startX, item.y, item.width, height);
context.closePath();
context.fill();
//画边框线
if (columnOption.meter.border > 0) {
context.beginPath();
context.setStrokeStyle(eachSeries.color);
context.setLineWidth(columnOption.meter.border * opts.pixelRatio);
context.moveTo(startX + columnOption.meter.border * 0.5, item.y + height);
context.lineTo(startX + columnOption.meter.border * 0.5, item.y + columnOption.meter.border * 0.5);
context.lineTo(startX + item.width - columnOption.meter.border * 0.5, item.y + columnOption.meter.border * 0.5);
context.lineTo(startX + item.width - columnOption.meter.border * 0.5, item.y + height);
context.stroke();
}
}
});
} else {
points.forEach(function(item, index) {
if (item !== null) {
context.beginPath();
context.setFillStyle(item.color || eachSeries.color);
var startX = item.x - item.width / 2;
var height = opts.height - item.y - opts.area[2];
context.moveTo(startX, item.y);
context.fillRect(startX, item.y, item.width, height);
context.closePath();
context.fill();
}
});
}
break;
}
});
if (opts.dataLabel !== false && process === 1) {
series.forEach(function(eachSeries, seriesIndex) {
var data = eachSeries.data;
switch (columnOption.type) {
case 'group':
var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
points = fixColumeData(points, eachSpacing, series.length, seriesIndex, config, opts);
drawPointText(points, eachSeries, config, context);
break;
case 'stack':
var points = getStackDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config,
seriesIndex,
series, process);
drawPointText(points, eachSeries, config, context);
break;
case 'meter':
var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
drawPointText(points, eachSeries, config, context);
break;
}
});
}
context.restore();
return {
xAxisPoints: xAxisPoints,
calPoints: calPoints,
eachSpacing: eachSpacing,
minRange: minRange,
maxRange: maxRange
};
}
function drawCandleDataPoints(series, seriesMA, opts, config, context) {
var process = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 1;
var candleOption = assign({}, {
color: {},
average: {}
}, opts.extra.candle);
candleOption.color = assign({}, {
upLine: '#f04864',
upFill: '#f04864',
downLine: '#2fc25b',
downFill: '#2fc25b'
}, candleOption.color);
candleOption.average = assign({}, {
show: false,
name: [],
day: [],
color: config.colors
}, candleOption.average);
opts.extra.candle = candleOption;
let ranges = [].concat(opts.chartData.yAxisData.ranges);
let xAxisData = opts.chartData.xAxisData,
xAxisPoints = xAxisData.xAxisPoints,
eachSpacing = xAxisData.eachSpacing;
let minRange = ranges.pop();
let maxRange = ranges.shift();
let calPoints = [];
context.save();
if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) {
context.translate(opts._scrollDistance_, 0);
}
//画均线
if (candleOption.average.show) {
seriesMA.forEach(function(eachSeries, seriesIndex) {
var data = eachSeries.data;
var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
//calPoints.push(points);
var splitPointList = splitPoints(points);
splitPointList.forEach(function(points, index) {
context.beginPath();
context.setStrokeStyle(eachSeries.color);
context.setLineWidth(1);
if (points.length === 1) {
context.moveTo(points[0].x, points[0].y);
context.arc(points[0].x, points[0].y, 1, 0, 2 * Math.PI);
} else {
context.moveTo(points[0].x, points[0].y);
points.forEach(function(item, index) {
if (index > 0) {
var ctrlPoint = createCurveControlPoints(points, index - 1);
context.bezierCurveTo(ctrlPoint.ctrA.x, ctrlPoint.ctrA.y, ctrlPoint.ctrB.x, ctrlPoint.ctrB.y,
item.x, item
.y);
}
});
context.moveTo(points[0].x, points[0].y);
}
context.closePath();
context.stroke();
});
});
}
//画K线
series.forEach(function(eachSeries, seriesIndex) {
var data = eachSeries.data;
var points = getCandleDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
calPoints.push(points);
var splitPointList = splitPoints(points);
splitPointList = splitPointList[0];
splitPointList.forEach(function(points, index) {
context.beginPath();
//如果上涨
if (data[index][1] - data[index][0] > 0) {
context.setStrokeStyle(candleOption.color.upLine);
context.setFillStyle(candleOption.color.upFill);
context.setLineWidth(1 * opts.pixelRatio);
context.moveTo(points[3].x, points[3].y); //顶点
context.lineTo(points[1].x, points[1].y); //收盘中间点
context.lineTo(points[1].x - eachSpacing / 4, points[1].y); //收盘左侧点
context.lineTo(points[0].x - eachSpacing / 4, points[0].y); //开盘左侧点
context.lineTo(points[0].x, points[0].y); //开盘中间点
context.lineTo(points[2].x, points[2].y); //底点
context.lineTo(points[0].x, points[0].y); //开盘中间点
context.lineTo(points[0].x + eachSpacing / 4, points[0].y); //开盘右侧点
context.lineTo(points[1].x + eachSpacing / 4, points[1].y); //收盘右侧点
context.lineTo(points[1].x, points[1].y); //收盘中间点
context.moveTo(points[3].x, points[3].y); //顶点
} else {
context.setStrokeStyle(candleOption.color.downLine);
context.setFillStyle(candleOption.color.downFill);
context.setLineWidth(1 * opts.pixelRatio);
context.moveTo(points[3].x, points[3].y); //顶点
context.lineTo(points[0].x, points[0].y); //开盘中间点
context.lineTo(points[0].x - eachSpacing / 4, points[0].y); //开盘左侧点
context.lineTo(points[1].x - eachSpacing / 4, points[1].y); //收盘左侧点
context.lineTo(points[1].x, points[1].y); //收盘中间点
context.lineTo(points[2].x, points[2].y); //底点
context.lineTo(points[1].x, points[1].y); //收盘中间点
context.lineTo(points[1].x + eachSpacing / 4, points[1].y); //收盘右侧点
context.lineTo(points[0].x + eachSpacing / 4, points[0].y); //开盘右侧点
context.lineTo(points[0].x, points[0].y); //开盘中间点
context.moveTo(points[3].x, points[3].y); //顶点
}
context.closePath();
context.fill();
context.stroke();
});
});
context.restore();
return {
xAxisPoints: xAxisPoints,
calPoints: calPoints,
eachSpacing: eachSpacing,
minRange: minRange,
maxRange: maxRange
};
}
function drawAreaDataPoints(series, opts, config, context) {
var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
var areaOption = assign({},{
type: 'straight',
opacity: 0.2,
addLine: false,
width: 2
},opts.extra.area);
let ranges = [].concat(opts.chartData.yAxisData.ranges);
let xAxisData = opts.chartData.xAxisData,
xAxisPoints = xAxisData.xAxisPoints,
eachSpacing = xAxisData.eachSpacing;
let minRange = ranges.pop();
let maxRange = ranges.shift();
let endY = opts.height - opts.area[2];
let calPoints = [];
context.save();
if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) {
context.translate(opts._scrollDistance_, 0);
}
series.forEach(function(eachSeries, seriesIndex) {
let data = eachSeries.data;
let points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
calPoints.push(points);
let splitPointList = splitPoints(points);
for (let i = 0; i < splitPointList.length; i++) {
let points = splitPointList[i];
// 绘制区域数
context.beginPath();
context.setStrokeStyle(hexToRgb(eachSeries.color, areaOption.opacity));
context.setFillStyle(hexToRgb(eachSeries.color, areaOption.opacity));
context.setLineWidth(areaOption.width * opts.pixelRatio);
if (points.length > 1) {
let firstPoint = points[0];
let lastPoint = points[points.length - 1];
context.moveTo(firstPoint.x, firstPoint.y);
if (areaOption.type === 'curve') {
points.forEach(function(item, index) {
if (index > 0) {
let ctrlPoint = createCurveControlPoints(points, index - 1);
context.bezierCurveTo(ctrlPoint.ctrA.x, ctrlPoint.ctrA.y, ctrlPoint.ctrB.x, ctrlPoint.ctrB.y,item.x, item.y);
}
});
} else {
points.forEach(function(item, index) {
if (index > 0) {
context.lineTo(item.x, item.y);
}
});
}
context.lineTo(lastPoint.x, endY);
context.lineTo(firstPoint.x, endY);
context.lineTo(firstPoint.x, firstPoint.y);
} else {
let item = points[0];
context.moveTo(item.x - eachSpacing / 2, item.y);
context.lineTo(item.x + eachSpacing / 2, item.y);
context.lineTo(item.x + eachSpacing / 2, endY);
context.lineTo(item.x - eachSpacing / 2, endY);
context.moveTo(item.x - eachSpacing / 2, item.y);
}
context.closePath();
context.fill();
//画连线
if (areaOption.addLine) {
context.beginPath();
context.setStrokeStyle(eachSeries.color);
context.setLineWidth(areaOption.width * opts.pixelRatio);
if (points.length === 1) {
context.moveTo(points[0].x, points[0].y);
context.arc(points[0].x, points[0].y, 1, 0, 2 * Math.PI);
} else {
context.moveTo(points[0].x, points[0].y);
if (areaOption.type === 'curve') {
points.forEach(function(item, index) {
if (index > 0) {
let ctrlPoint = createCurveControlPoints(points, index - 1);
context.bezierCurveTo(ctrlPoint.ctrA.x, ctrlPoint.ctrA.y, ctrlPoint.ctrB.x, ctrlPoint.ctrB.y,item.x,item.y);
}
});
} else {
points.forEach(function(item, index) {
if (index > 0) {
context.lineTo(item.x, item.y);
}
});
}
context.moveTo(points[0].x, points[0].y);
}
context.closePath();
context.stroke();
}
}
//画点
if (opts.dataPointShape !== false) {
var shape = config.dataPointShape[seriesIndex % config.dataPointShape.length];
drawPointShape(points, eachSeries.color, shape, context, opts);
}
});
if (opts.dataLabel !== false && process === 1) {
series.forEach(function(eachSeries, seriesIndex) {
var data = eachSeries.data;
var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
drawPointText(points, eachSeries, config, context);
});
}
context.restore();
return {
xAxisPoints: xAxisPoints,
calPoints: calPoints,
eachSpacing: eachSpacing,
minRange: minRange,
maxRange: maxRange
};
}
function drawLineDataPoints(series, opts, config, context) {
var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
var lineOption = opts.extra.line || {
type: 'straight',
width: 2
};
lineOption.type = lineOption.type ? lineOption.type : 'straight';
lineOption.width = lineOption.width ? lineOption.width : 2;
let ranges = [].concat(opts.chartData.yAxisData.ranges);
let xAxisData = opts.chartData.xAxisData,
xAxisPoints = xAxisData.xAxisPoints,
eachSpacing = xAxisData.eachSpacing;
var minRange = ranges.pop();
var maxRange = ranges.shift();
var calPoints = [];
context.save();
if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) {
context.translate(opts._scrollDistance_, 0);
}
series.forEach(function(eachSeries, seriesIndex) {
var data = eachSeries.data;
var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
calPoints.push(points);
var splitPointList = splitPoints(points);
splitPointList.forEach(function(points, index) {
context.beginPath();
context.setStrokeStyle(eachSeries.color);
context.setLineWidth(lineOption.width * opts.pixelRatio);
if (points.length === 1) {
context.moveTo(points[0].x, points[0].y);
context.arc(points[0].x, points[0].y, 1, 0, 2 * Math.PI);
} else {
context.moveTo(points[0].x, points[0].y);
if (lineOption.type === 'curve') {
points.forEach(function(item, index) {
if (index > 0) {
var ctrlPoint = createCurveControlPoints(points, index - 1);
context.bezierCurveTo(ctrlPoint.ctrA.x, ctrlPoint.ctrA.y, ctrlPoint.ctrB.x, ctrlPoint.ctrB.y,
item.x, item
.y);
}
});
} else {
points.forEach(function(item, index) {
if (index > 0) {
context.lineTo(item.x, item.y);
}
});
}
context.moveTo(points[0].x, points[0].y);
}
context.closePath();
context.stroke();
});
if (opts.dataPointShape !== false) {
var shape = config.dataPointShape[seriesIndex % config.dataPointShape.length];
drawPointShape(points, eachSeries.color, shape, context, opts);
}
});
if (opts.dataLabel !== false && process === 1) {
series.forEach(function(eachSeries, seriesIndex) {
var data = eachSeries.data;
var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
drawPointText(points, eachSeries, config, context);
});
}
context.restore();
return {
xAxisPoints: xAxisPoints,
calPoints: calPoints,
eachSpacing: eachSpacing,
minRange: minRange,
maxRange: maxRange
};
}
function drawMixDataPoints(series, opts, config, context) {
let process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
let ranges = [].concat(opts.chartData.yAxisData.ranges);
let xAxisData = opts.chartData.xAxisData,
xAxisPoints = xAxisData.xAxisPoints,
eachSpacing = xAxisData.eachSpacing;
let minRange = ranges.pop();
let maxRange = ranges.shift();
let endY = opts.height - opts.area[2];
let calPoints = [];
var columnIndex = 0;
var columnLength = 0;
series.forEach(function(eachSeries, seriesIndex) {
if (eachSeries.type == 'column') {
columnLength += 1;
}
});
context.save();
if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) {
context.translate(opts._scrollDistance_, 0);
}
series.forEach(function(eachSeries, seriesIndex) {
var data = eachSeries.data;
var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
calPoints.push(points);
// 绘制柱状数据图
if (eachSeries.type == 'column') {
points = fixColumeData(points, eachSpacing, columnLength, columnIndex, config, opts);
points.forEach(function(item, index) {
if (item !== null) {
context.beginPath();
context.setStrokeStyle(item.color || eachSeries.color);
context.setLineWidth(1)
context.setFillStyle(item.color || eachSeries.color);
var startX = item.x - item.width / 2;
var height = opts.height - item.y - opts.area[2];
context.moveTo(startX, item.y);
context.moveTo(startX-1, item.y);
context.lineTo(startX+item.width-2,item.y);
context.lineTo(startX+item.width-2,opts.height - opts.area[2]);
context.lineTo(startX,opts.height - opts.area[2]);
context.lineTo(startX,item.y);
//context.rect(startX, item.y, item.width, height);
context.closePath();
context.stroke();
context.fill();
context.closePath();
context.fill();
}
});
columnIndex += 1;
}
//绘制区域图数据
if (eachSeries.type == 'area') {
let splitPointList = splitPoints(points);
for (let i = 0; i < splitPointList.length; i++) {
let points = splitPointList[i];
// 绘制区域数据
context.beginPath();
context.setStrokeStyle(eachSeries.color);
context.setFillStyle(hexToRgb(eachSeries.color, 0.2));
context.setLineWidth(2 * opts.pixelRatio);
if (points.length > 1) {
var firstPoint = points[0];
let lastPoint = points[points.length - 1];
context.moveTo(firstPoint.x, firstPoint.y);
if (eachSeries.style === 'curve') {
points.forEach(function(item, index) {
if (index > 0) {
var ctrlPoint = createCurveControlPoints(points, index - 1);
context.bezierCurveTo(ctrlPoint.ctrA.x, ctrlPoint.ctrA.y, ctrlPoint.ctrB.x, ctrlPoint.ctrB.y, item.x, item.y);
}
});
} else {
points.forEach(function(item, index) {
if (index > 0) {
context.lineTo(item.x, item.y);
}
});
}
context.lineTo(lastPoint.x, endY);
context.lineTo(firstPoint.x, endY);
context.lineTo(firstPoint.x, firstPoint.y);
} else {
let item = points[0];
context.moveTo(item.x - eachSpacing / 2, item.y);
context.lineTo(item.x + eachSpacing / 2, item.y);
context.lineTo(item.x + eachSpacing / 2, endY);
context.lineTo(item.x - eachSpacing / 2, endY);
context.moveTo(item.x - eachSpacing / 2, item.y);
}
context.closePath();
context.fill();
}
}
// 绘制折线数据图
if (eachSeries.type == 'line') {
var splitPointList = splitPoints(points);
splitPointList.forEach(function(points, index) {
context.beginPath();
context.setStrokeStyle(eachSeries.color);
context.setLineWidth(2 * opts.pixelRatio);
if (points.length === 1) {
context.moveTo(points[0].x, points[0].y);
context.arc(points[0].x, points[0].y, 1, 0, 2 * Math.PI);
} else {
context.moveTo(points[0].x, points[0].y);
if (eachSeries.style == 'curve') {
points.forEach(function(item, index) {
if (index > 0) {
var ctrlPoint = createCurveControlPoints(points, index - 1);
context.bezierCurveTo(ctrlPoint.ctrA.x, ctrlPoint.ctrA.y, ctrlPoint.ctrB.x, ctrlPoint.ctrB.y,
item.x,
item.y);
}
});
} else {
points.forEach(function(item, index) {
if (index > 0) {
context.lineTo(item.x, item.y);
}
});
}
context.moveTo(points[0].x, points[0].y);
}
context.closePath();
context.stroke();
});
}
// 绘制点数据图
if (eachSeries.type == 'point') {
points.forEach(function(pointsa, index) {
if (pointsa) {
context.beginPath();
context.setFillStyle(eachSeries.color);
context.setStrokeStyle('#FFFFFF');
context.setLineWidth(1 * opts.pixelRatio);
context.moveTo(pointsa.x + 3.5 * opts.pixelRatio, pointsa.y);
context.arc(pointsa.x, pointsa.y, 4 * opts.pixelRatio, 0, 2 * Math.PI);
context.closePath();
context.fill();
context.stroke();
}
});
}
if (eachSeries.addPoint == true && eachSeries.type !== 'column') {
var shape = config.dataPointShape[seriesIndex % config.dataPointShape.length];
drawPointShape(points, eachSeries.color, shape, context, opts);
}
});
if (opts.dataLabel !== false && process === 1) {
var columnIndex = 0;
series.forEach(function(eachSeries, seriesIndex) {
var data = eachSeries.data;
var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
if (eachSeries.type !== 'column') {
drawPointText(points, eachSeries, config, context);
} else {
points = fixColumeData(points, eachSpacing, columnLength, columnIndex, config, opts);
drawPointText(points, eachSeries, config, context);
columnIndex += 1;
}
});
}
context.restore();
return {
xAxisPoints: xAxisPoints,
calPoints: calPoints,
eachSpacing: eachSpacing,
minRange: minRange,
maxRange: maxRange
};
}
function drawToolTipBridge(opts, config, context, process, eachSpacing, xAxisPoints) {
var toolTipOption = opts.extra.tooltip || {};
if (toolTipOption.horizentalLine && opts.tooltip && process === 1 && (opts.type == 'line' || opts.type == 'area' || opts.type == 'column' || opts.type == 'candle' || opts.type == 'mix')) {
drawToolTipHorizentalLine(opts, config, context, eachSpacing, xAxisPoints)
}
context.save();
if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) {
context.translate(opts._scrollDistance_, 0);
}
if (opts.tooltip && opts.tooltip.textList && opts.tooltip.textList.length && process === 1) {
drawToolTip(opts.tooltip.textList, opts.tooltip.offset, opts, config, context, eachSpacing, xAxisPoints);
}
context.restore();
}
function drawXAxis(categories, opts, config, context) {
let xAxisData = opts.chartData.xAxisData,
xAxisPoints = xAxisData.xAxisPoints,
startX = xAxisData.startX,
endX = xAxisData.endX,
eachSpacing = xAxisData.eachSpacing;
var boundaryGap='center';
if (opts.type == 'line'||opts.type == 'area'){
boundaryGap=opts.xAxis.boundaryGap;
}
var startY = opts.height - opts.area[2];
var endY = opts.area[0];
//绘制滚动条
if (opts.enableScroll && opts.xAxis.scrollShow) {
var scrollY = opts.height - opts.area[2] + config.xAxisHeight;
var scrollScreenWidth = endX - startX;
var scrollTotalWidth = eachSpacing * (xAxisPoints.length - 1);
var scrollWidth = scrollScreenWidth * scrollScreenWidth / scrollTotalWidth;
var scrollLeft = 0;
if (opts._scrollDistance_) {
scrollLeft = -opts._scrollDistance_ * (scrollScreenWidth) / scrollTotalWidth;
}
context.beginPath();
context.setLineCap('round');
context.setLineWidth(6 * opts.pixelRatio);
context.setStrokeStyle(opts.xAxis.scrollBackgroundColor || "#EFEBEF");
context.moveTo(startX, scrollY);
context.lineTo(endX, scrollY);
context.stroke();
context.closePath();
context.beginPath();
context.setLineCap('round');
context.setLineWidth(6 * opts.pixelRatio);
context.setStrokeStyle(opts.xAxis.scrollColor || "#A6A6A6");
context.moveTo(startX + scrollLeft, scrollY);
context.lineTo(startX + scrollLeft + scrollWidth, scrollY);
context.stroke();
context.closePath();
context.setLineCap('butt');
}
context.save();
if (opts._scrollDistance_ && opts._scrollDistance_ !== 0) {
context.translate(opts._scrollDistance_, 0);
}
if (opts.xAxis.disableGrid !== true) {
context.setStrokeStyle(opts.xAxis.gridColor || "#cccccc");
context.setLineCap('butt');
context.setLineWidth(1 * opts.pixelRatio);
if (opts.xAxis.gridType == 'dash') {
context.setLineDash([opts.xAxis.dashLength, opts.xAxis.dashLength]);
}
if (opts.xAxis.type === 'calibration') {
xAxisPoints.forEach(function(item, index) {
if (index > 0) {
context.beginPath();
context.moveTo(item - eachSpacing / 2, startY);
context.lineTo(item - eachSpacing / 2, startY + 4 * opts.pixelRatio);
context.closePath();
context.stroke();
}
});
} else {
opts.xAxis.gridEval = opts.xAxis.gridEval || 1;
xAxisPoints.forEach(function(item, index) {
if (index % opts.xAxis.gridEval == 0) {
context.beginPath();
context.moveTo(item, startY);
context.lineTo(item, endY);
context.stroke();
}
});
}
context.setLineDash([]);
}
//不绘制X轴
if (opts.xAxis.disabled !== true) {
// 对X轴列表做抽稀处理
let validWidth = opts.width - opts.padding[1] - opts.padding[3] - config.yAxisWidth - config.yAxisTitleWidth;
//默认全部显示X轴标签
let maxXAxisListLength = categories.length;
//如果设置了X轴单屏数量
if (opts.xAxis.labelCount) {
//如果设置X轴密度
if (opts.xAxis.itemCount) {
maxXAxisListLength = Math.ceil(categories.length / opts.xAxis.itemCount * opts.xAxis.labelCount);
} else {
maxXAxisListLength = opts.xAxis.labelCount;
}
maxXAxisListLength -= 1;
}
let ratio = Math.ceil(categories.length / maxXAxisListLength);
let newCategories = [];
let cgLength = categories.length;
for (let i = 0; i < cgLength; i++) {
if (i % ratio !== 0) {
newCategories.push("");
} else {
newCategories.push(categories[i]);
}
}
newCategories[cgLength - 1] = categories[cgLength - 1];
var xAxisFontSize = opts.xAxis.fontSize || config.fontSize;
if (config._xAxisTextAngle_ === 0) {
newCategories.forEach(function(item, index) {
var offset = - measureText(item, xAxisFontSize) / 2;
if(boundaryGap == 'center'){
offset+=eachSpacing / 2;
}
context.beginPath();
context.setFontSize(xAxisFontSize);
context.setFillStyle(opts.xAxis.fontColor || '#666666');
context.fillText(item, xAxisPoints[index] + offset, startY + xAxisFontSize + (config.xAxisHeight - xAxisFontSize) / 2);
context.closePath();
context.stroke();
});
} else {
newCategories.forEach(function(item, index) {
context.save();
context.beginPath();
context.setFontSize(xAxisFontSize);
context.setFillStyle(opts.xAxis.fontColor || '#666666');
var textWidth = measureText(item);
var offset = - textWidth;
if(boundaryGap == 'center'){
offset+=eachSpacing / 2;
}
var _calRotateTranslate = calRotateTranslate(xAxisPoints[index] + eachSpacing / 2, startY + xAxisFontSize / 2 + 5, opts.height),
transX = _calRotateTranslate.transX,
transY = _calRotateTranslate.transY;
context.rotate(-1 * config._xAxisTextAngle_);
context.translate(transX, transY);
context.fillText(item, xAxisPoints[index] + offset, startY + xAxisFontSize + 5);
context.closePath();
context.stroke();
context.restore();
});
}
}
context.restore();
}
function drawYAxisGrid(categories, opts, config, context) {
if (opts.yAxis.disableGrid === true) {
return;
}
let spacingValid = opts.height - opts.area[0] - opts.area[2];
let eachSpacing = spacingValid / config.yAxisSplit;
let startX = opts.area[3];
let xAxisPoints = opts.chartData.xAxisData.xAxisPoints,
xAxiseachSpacing = opts.chartData.xAxisData.eachSpacing;
let TotalWidth = xAxiseachSpacing * (xAxisPoints.length - 1);
let endX = startX + TotalWidth;
let points = [];
for (let i = 0; i < config.yAxisSplit + 1; i++) {
points.push(opts.height - opts.area[2] - eachSpacing * i);
}
context.save();
if (opts._scrollDistance_ && opts._scrollDistance_ !== 0) {
context.translate(opts._scrollDistance_, 0);
}
if (opts.yAxis.gridType == 'dash') {
context.setLineDash([opts.yAxis.dashLength, opts.yAxis.dashLength]);
}
context.setStrokeStyle(opts.yAxis.gridColor || "#cccccc");
context.setLineWidth(1 * opts.pixelRatio);
points.forEach(function(item, index) {
context.beginPath();
context.moveTo(startX, item);
context.lineTo(endX, item);
context.stroke();
});
context.setLineDash([]);
context.restore();
}
function drawYAxis(series, opts, config, context) {
if (opts.yAxis.disabled === true) {
return;
}
let rangesFormat = opts.chartData.yAxisData.rangesFormat;
var spacingValid = opts.height - opts.area[0] - opts.area[2];
var eachSpacing = Math.floor(spacingValid / config.yAxisSplit);
var startX = opts.area[3];
var endX = opts.width - opts.area[1];
var endY = opts.height - opts.area[2];
var fillEndY = endY + config.xAxisHeight;
if (opts.xAxis.scrollShow) {
fillEndY -= 3 * opts.pixelRatio;
}
// set YAxis background
context.beginPath();
context.setFillStyle(opts.background || '#ffffff');
if (opts._scrollDistance_ < 0) {
context.fillRect(0, 0, startX, fillEndY);
}
if(opts.enableScroll == true){
context.fillRect(endX, 0, opts.width, fillEndY);
}
context.closePath();
context.stroke();
var points = [];
for (var i = 0; i <= config.yAxisSplit; i++) {
points.push(opts.area[0] + eachSpacing * i);
}
var yAxisFontSize = opts.yAxis.fontSize || config.fontSize;
rangesFormat.forEach(function(item, index) {
var pos = points[index] ? points[index] : endY;
context.beginPath();
context.setFontSize(yAxisFontSize);
context.setFillStyle(opts.yAxis.fontColor || '#666666');
context.fillText(String(item), opts.area[3] - config.yAxisWidth, pos + yAxisFontSize / 2);
context.closePath();
context.stroke();
});
if (opts.yAxis.title) {
drawYAxisTitle(opts.yAxis.title, opts, config, context);
}
}
function drawLegend(series, opts, config, context, chartData) {
if (opts.legend.show === false) {
return;
}
let legendData = chartData.legendData;
let legendList = legendData.points;
let legendArea = legendData.area;
let padding = opts.legend.padding;
let fontSize = opts.legend.fontSize;
let shapeWidth = 15 * opts.pixelRatio;
let shapeRight = 5 * opts.pixelRatio;
let itemGap = opts.legend.itemGap;
let lineHeight = Math.max(opts.legend.lineHeight * opts.pixelRatio, fontSize);
//画背景及边框
context.beginPath();
context.setLineWidth(opts.legend.borderWidth);
context.setStrokeStyle(opts.legend.borderColor);
context.setFillStyle(opts.legend.backgroundColor);
context.moveTo(legendArea.start.x, legendArea.start.y);
context.rect(legendArea.start.x, legendArea.start.y, legendArea.width, legendArea.height);
context.closePath();
context.fill();
context.stroke();
legendList.forEach(function(itemList, listIndex) {
let width = 0;
let height = 0;
width = legendData.widthArr[listIndex];
height = legendData.heightArr[listIndex];
let startX = 0;
let startY = 0;
if (opts.legend.position == 'top' || opts.legend.position == 'bottom') {
startX = legendArea.start.x + (legendArea.width - width) / 2;
startY = legendArea.start.y + padding + listIndex * lineHeight;
} else {
if (listIndex == 0) {
width = 0;
} else {
width = legendData.widthArr[listIndex - 1];
}
startX = legendArea.start.x + padding + width;
startY = legendArea.start.y + padding + (legendArea.height - height) / 2;
}
context.setFontSize(config.fontSize);
for (let i = 0; i < itemList.length; i++) {
let item = itemList[i];
item.area = [0, 0, 0, 0];
item.area[0] = startX;
item.area[1] = startY;
item.area[3] = startY + lineHeight;
context.beginPath();
context.setLineWidth(1 * opts.pixelRatio);
context.setStrokeStyle(item.show ? item.color : opts.legend.hiddenColor);
context.setFillStyle(item.show ? item.color : opts.legend.hiddenColor);
switch (item.legendShape) {
case 'line':
context.moveTo(startX, startY + 0.5 * lineHeight - 2 * opts.pixelRatio);
context.fillRect(startX, startY + 0.5 * lineHeight - 2 * opts.pixelRatio, 15 * opts.pixelRatio, 4 * opts.pixelRatio);
break;
case 'triangle':
context.moveTo(startX + 7.5 * opts.pixelRatio, startY + 0.5 * lineHeight - 5 * opts.pixelRatio);
context.lineTo(startX + 2.5 * opts.pixelRatio, startY + 0.5 * lineHeight + 5 * opts.pixelRatio);
context.lineTo(startX + 12.5 * opts.pixelRatio, startY + 0.5 * lineHeight + 5 * opts.pixelRatio);
context.lineTo(startX + 7.5 * opts.pixelRatio, startY + 0.5 * lineHeight - 5 * opts.pixelRatio);
break;
case 'diamond':
context.moveTo(startX + 7.5 * opts.pixelRatio, startY + 0.5 * lineHeight - 5 * opts.pixelRatio);
context.lineTo(startX + 2.5 * opts.pixelRatio, startY + 0.5 * lineHeight);
context.lineTo(startX + 7.5 * opts.pixelRatio, startY + 0.5 * lineHeight + 5 * opts.pixelRatio);
context.lineTo(startX + 12.5 * opts.pixelRatio, startY + 0.5 * lineHeight);
context.lineTo(startX + 7.5 * opts.pixelRatio, startY + 0.5 * lineHeight - 5 * opts.pixelRatio);
break;
case 'circle':
context.moveTo(startX + 7.5 * opts.pixelRatio, startY + 0.5 * lineHeight);
context.arc(startX + 7.5 * opts.pixelRatio, startY + 0.5 * lineHeight, 5 * opts.pixelRatio, 0, 2 * Math.PI);
break;
case 'rect':
context.moveTo(startX, startY + 0.5 * lineHeight - 5 * opts.pixelRatio);
context.fillRect(startX, startY + 0.5 * lineHeight - 5 * opts.pixelRatio, 15 * opts.pixelRatio, 10 * opts.pixelRatio);
break;
default:
context.moveTo(startX, startY + 0.5 * lineHeight - 5 * opts.pixelRatio);
context.fillRect(startX, startY + 0.5 * lineHeight - 5 * opts.pixelRatio, 15 * opts.pixelRatio, 10 * opts.pixelRatio);
}
context.closePath();
context.fill();
context.stroke();
startX += shapeWidth + shapeRight;
let fontTrans = 0.5 * lineHeight + 0.5 * fontSize - 2;
context.beginPath();
context.setFontSize(fontSize);
context.setFillStyle(item.show ? opts.legend.fontColor : opts.legend.hiddenColor);
context.fillText(item.name, startX, startY + fontTrans);
context.closePath();
context.stroke();
if (opts.legend.position == 'top' || opts.legend.position == 'bottom') {
startX += measureText(item.name, fontSize) + itemGap;
item.area[2] = startX;
} else {
item.area[2] = startX + measureText(item.name, fontSize) + itemGap;;
startX -= shapeWidth + shapeRight;
startY += lineHeight;
}
}
});
}
function drawPieDataPoints(series, opts, config, context) {
var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
var pieOption = assign({}, {
activeOpacity: 0.5,
activeRadius: 10 * opts.pixelRatio,
offsetAngle: 0,
labelWidth: 15 * opts.pixelRatio,
ringWidth: 0,
border:false,
borderWidth:2,
borderColor:'#FFFFFF'
}, opts.extra.pie);
var centerPosition = {
x: opts.area[3] + (opts.width - opts.area[1] - opts.area[3]) / 2,
y: opts.area[0] + (opts.height - opts.area[0] - opts.area[2]) / 2
};
if (config.pieChartLinePadding == 0) {
config.pieChartLinePadding = pieOption.activeRadius;
}
var radius = Math.min((opts.width - opts.area[1] - opts.area[3]) / 2 - config.pieChartLinePadding - config.pieChartTextPadding - config._pieTextMaxLength_, (opts.height - opts.area[0] - opts.area[2]) / 2 - config.pieChartLinePadding - config.pieChartTextPadding);
series = getPieDataPoints(series, radius, process);
var activeRadius = pieOption.activeRadius;
series = series.map(function(eachSeries) {
eachSeries._start_ += (pieOption.offsetAngle) * Math.PI / 180;
return eachSeries;
});
series.forEach(function(eachSeries, seriesIndex) {
if (opts.tooltip) {
if (opts.tooltip.index == seriesIndex) {
context.beginPath();
context.setFillStyle(hexToRgb(eachSeries.color, opts.extra.pie.activeOpacity || 0.5));
context.moveTo(centerPosition.x, centerPosition.y);
context.arc(centerPosition.x, centerPosition.y, eachSeries._radius_ + activeRadius, eachSeries._start_,
eachSeries._start_ + 2 *
eachSeries._proportion_ * Math.PI);
context.closePath();
context.fill();
}
}
context.beginPath();
context.setLineWidth(pieOption.borderWidth * opts.pixelRatio);
context.lineJoin = "round";
context.setStrokeStyle(pieOption.borderColor);
context.setFillStyle(eachSeries.color);
context.moveTo(centerPosition.x, centerPosition.y);
context.arc(centerPosition.x, centerPosition.y, eachSeries._radius_, eachSeries._start_, eachSeries._start_ + 2 * eachSeries._proportion_ * Math.PI);
context.closePath();
context.fill();
if (pieOption.border == true) {
context.stroke();
}
});
if (opts.type === 'ring') {
var innerPieWidth = radius * 0.6;
if (typeof opts.extra.pie.ringWidth === 'number' && opts.extra.pie.ringWidth > 0) {
innerPieWidth = Math.max(0, radius - opts.extra.pie.ringWidth);
}
context.beginPath();
context.setFillStyle(opts.background || '#ffffff');
context.moveTo(centerPosition.x, centerPosition.y);
context.arc(centerPosition.x, centerPosition.y, innerPieWidth, 0, 2 * Math.PI);
context.closePath();
context.fill();
}
if (opts.dataLabel !== false && process === 1) {
var valid = false;
for (var i = 0, len = series.length; i < len; i++) {
if (series[i].data > 0) {
valid = true;
break;
}
}
if (valid) {
drawPieText(series, opts, config, context, radius, centerPosition);
}
}
if (process === 1 && opts.type === 'ring') {
drawRingTitle(opts, config, context, centerPosition);
}
return {
center: centerPosition,
radius: radius,
series: series
};
}
function drawRoseDataPoints(series, opts, config, context) {
var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
var roseOption = assign({}, {
type: 'area',
activeOpacity: 0.5,
activeRadius: 10 * opts.pixelRatio,
offsetAngle: 0,
labelWidth: 15 * opts.pixelRatio,
border:false,
borderWidth:2,
borderColor:'#FFFFFF'
}, opts.extra.rose);
if (config.pieChartLinePadding == 0) {
config.pieChartLinePadding = roseOption.activeRadius;
}
var centerPosition = {
x: opts.area[3] + (opts.width - opts.area[1] - opts.area[3]) / 2,
y: opts.area[0] + (opts.height - opts.area[0] - opts.area[2]) / 2
};
var radius = Math.min((opts.width - opts.area[1] - opts.area[3]) / 2 - config.pieChartLinePadding - config.pieChartTextPadding - config._pieTextMaxLength_, (opts.height - opts.area[0] - opts.area[2]) / 2 - config.pieChartLinePadding - config.pieChartTextPadding);
var minRadius = roseOption.minRadius || radius * 0.5;
series = getRoseDataPoints(series, roseOption.type, minRadius, radius, process);
var activeRadius = roseOption.activeRadius;
series = series.map(function(eachSeries) {
eachSeries._start_ += (roseOption.offsetAngle || 0) * Math.PI / 180;
return eachSeries;
});
series.forEach(function(eachSeries, seriesIndex) {
if (opts.tooltip) {
if (opts.tooltip.index == seriesIndex) {
context.beginPath();
context.setFillStyle(hexToRgb(eachSeries.color, roseOption.activeOpacity || 0.5));
context.moveTo(centerPosition.x, centerPosition.y);
context.arc(centerPosition.x, centerPosition.y, activeRadius + eachSeries._radius_, eachSeries._start_,
eachSeries._start_ + 2 * eachSeries._rose_proportion_ * Math.PI);
context.closePath();
context.fill();
}
}
context.beginPath();
context.setLineWidth(roseOption.borderWidth * opts.pixelRatio);
context.lineJoin = "round";
context.setStrokeStyle(roseOption.borderColor);
context.setFillStyle(eachSeries.color);
context.moveTo(centerPosition.x, centerPosition.y);
context.arc(centerPosition.x, centerPosition.y, eachSeries._radius_, eachSeries._start_, eachSeries._start_ + 2 *
eachSeries._rose_proportion_ * Math.PI);
context.closePath();
context.fill();
if (roseOption.border == true) {
context.stroke();
}
});
if (opts.dataLabel !== false && process === 1) {
var valid = false;
for (var i = 0, len = series.length; i < len; i++) {
if (series[i].data > 0) {
valid = true;
break;
}
}
if (valid) {
drawPieText(series, opts, config, context, radius, centerPosition);
}
}
return {
center: centerPosition,
radius: radius,
series: series
};
}
function drawArcbarDataPoints(series, opts, config, context) {
var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
var arcbarOption = assign({}, {
startAngle: 0.75,
endAngle: 0.25,
type: 'default',
width: 12 * opts.pixelRatio
}, opts.extra.arcbar);
series = getArcbarDataPoints(series, arcbarOption, process);
var centerPosition = {
x: opts.width / 2,
y: opts.height / 2
};
var radius = Math.min(centerPosition.x, centerPosition.y);
radius -= 5 * opts.pixelRatio;
radius -= arcbarOption.width / 2;
//背景颜色
context.setLineWidth(arcbarOption.width);
context.setStrokeStyle(arcbarOption.backgroundColor || '#E9E9E9');
context.setLineCap('round');
context.beginPath();
if (arcbarOption.type == 'default') {
context.arc(centerPosition.x, centerPosition.y, radius, arcbarOption.startAngle * Math.PI, arcbarOption.endAngle *
Math.PI, false);
} else {
context.arc(centerPosition.x, centerPosition.y, radius, 0, 2 * Math.PI, false);
}
context.stroke();
for (let i = 0; i < series.length; i++) {
let eachSeries = series[i];
context.setLineWidth(arcbarOption.width);
context.setStrokeStyle(eachSeries.color);
context.setLineCap('round');
context.beginPath();
context.arc(centerPosition.x, centerPosition.y, radius, arcbarOption.startAngle * Math.PI, eachSeries._proportion_ *
Math.PI, false);
context.stroke();
}
drawRingTitle(opts, config, context, centerPosition);
return {
center: centerPosition,
radius: radius,
series: series
};
}
function drawGaugeDataPoints(categories, series, opts, config, context) {
var process = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 1;
var gaugeOption = assign({}, {
startAngle: 0.75,
endAngle: 0.25,
width: 15,
splitLine: {
fixRadius: 0,
splitNumber: 10,
width: 15,
color: '#FFFFFF',
childNumber: 5,
childWidth: 5
},
pointer: {
width: 15,
color: 'auto'
}
}, opts.extra.gauge);
if (gaugeOption.oldAngle == undefined) {
gaugeOption.oldAngle = gaugeOption.startAngle;
}
if (gaugeOption.oldData == undefined) {
gaugeOption.oldData = 0;
}
categories = getGaugeAxisPoints(categories, gaugeOption.startAngle, gaugeOption.endAngle);
var centerPosition = {
x: opts.width / 2,
y: opts.height / 2
};
var radius = Math.min(centerPosition.x, centerPosition.y);
radius -= 5 * opts.pixelRatio;
radius -= gaugeOption.width / 2;
var innerRadius = radius - gaugeOption.width;
//画背景
context.setLineWidth(gaugeOption.width);
context.setLineCap('butt');
for (let i = 0; i < categories.length; i++) {
let eachCategories = categories[i];
context.beginPath();
context.setStrokeStyle(eachCategories.color);
context.arc(centerPosition.x, centerPosition.y, radius, eachCategories._startAngle_ * Math.PI, eachCategories._endAngle_ *
Math.PI, false);
context.stroke();
}
context.save();
//画刻度线
let totalAngle = gaugeOption.startAngle - gaugeOption.endAngle + 1;
let splitAngle = totalAngle / gaugeOption.splitLine.splitNumber;
let childAngle = totalAngle / gaugeOption.splitLine.splitNumber / gaugeOption.splitLine.childNumber;
let startX = -radius - gaugeOption.width * 0.5 - gaugeOption.splitLine.fixRadius;
let endX = -radius - gaugeOption.width * 0.5 - gaugeOption.splitLine.fixRadius + gaugeOption.splitLine.width;
let childendX = -radius - gaugeOption.width * 0.5 - gaugeOption.splitLine.fixRadius + gaugeOption.splitLine.childWidth;
context.translate(centerPosition.x, centerPosition.y);
context.rotate((gaugeOption.startAngle - 1) * Math.PI);
for (let i = 0; i < gaugeOption.splitLine.splitNumber + 1; i++) {
context.beginPath();
context.setStrokeStyle(gaugeOption.splitLine.color);
context.setLineWidth(2 * opts.pixelRatio);
context.moveTo(startX, 0);
context.lineTo(endX, 0);
context.stroke();
context.rotate(splitAngle * Math.PI);
}
context.restore();
context.save();
context.translate(centerPosition.x, centerPosition.y);
context.rotate((gaugeOption.startAngle - 1) * Math.PI);
for (let i = 0; i < gaugeOption.splitLine.splitNumber * gaugeOption.splitLine.childNumber + 1; i++) {
context.beginPath();
context.setStrokeStyle(gaugeOption.splitLine.color);
context.setLineWidth(1 * opts.pixelRatio);
context.moveTo(startX, 0);
context.lineTo(childendX, 0);
context.stroke();
context.rotate(childAngle * Math.PI);
}
context.restore();
//画指针
series = getGaugeDataPoints(series, categories, gaugeOption, process);
for (let i = 0; i < series.length; i++) {
let eachSeries = series[i];
context.save();
context.translate(centerPosition.x, centerPosition.y);
context.rotate((eachSeries._proportion_ - 1) * Math.PI);
context.beginPath();
context.setFillStyle(eachSeries.color);
context.moveTo(gaugeOption.pointer.width, 0);
context.lineTo(0, -gaugeOption.pointer.width / 2);
context.lineTo(-innerRadius, 0);
context.lineTo(0, gaugeOption.pointer.width / 2);
context.lineTo(gaugeOption.pointer.width, 0);
context.closePath();
context.fill();
context.beginPath();
context.setFillStyle('#FFFFFF');
context.arc(0, 0, gaugeOption.pointer.width / 6, 0, 2 * Math.PI, false);
context.fill();
context.restore();
}
if (opts.dataLabel !== false) {
drawGaugeLabel(gaugeOption, radius, centerPosition, opts, config, context);
}
drawRingTitle(opts, config, context, centerPosition);
if (process === 1 && opts.type === 'gauge') {
opts.extra.gauge.oldAngle = series[0]._proportion_;
opts.extra.gauge.oldData = series[0].data;
}
return {
center: centerPosition,
radius: radius,
innerRadius: innerRadius,
categories: categories,
totalAngle: totalAngle
};
}
function drawRadarDataPoints(series, opts, config, context) {
var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
var radarOption = assign({},{
gridColor: '#cccccc',
labelColor: '#666666',
opacity: 0.2
},opts.extra.radar);
var coordinateAngle = getRadarCoordinateSeries(opts.categories.length);
var centerPosition = {
x: opts.area[3] + (opts.width - opts.area[1] - opts.area[3]) / 2,
y: opts.area[0] + (opts.height - opts.area[0] - opts.area[2]) / 2
};
var radius = Math.min(centerPosition.x - (getMaxTextListLength(opts.categories) + config.radarLabelTextMargin),
centerPosition.y - config.radarLabelTextMargin);
//TODO逻辑不对
radius -= opts.padding[1];
// draw grid
context.beginPath();
context.setLineWidth(1 * opts.pixelRatio);
context.setStrokeStyle(radarOption.gridColor);
coordinateAngle.forEach(function(angle) {
var pos = convertCoordinateOrigin(radius * Math.cos(angle), radius * Math.sin(angle), centerPosition);
context.moveTo(centerPosition.x, centerPosition.y);
context.lineTo(pos.x, pos.y);
});
context.stroke();
context.closePath();
// draw split line grid
var _loop = function _loop(i) {
var startPos = {};
context.beginPath();
context.setLineWidth(1 * opts.pixelRatio);
context.setStrokeStyle(radarOption.gridColor);
coordinateAngle.forEach(function(angle, index) {
var pos = convertCoordinateOrigin(radius / config.radarGridCount * i * Math.cos(angle), radius / config.radarGridCount *
i * Math.sin(angle), centerPosition);
if (index === 0) {
startPos = pos;
context.moveTo(pos.x, pos.y);
} else {
context.lineTo(pos.x, pos.y);
}
});
context.lineTo(startPos.x, startPos.y);
context.stroke();
context.closePath();
};
for (var i = 1; i <= config.radarGridCount; i++) {
_loop(i);
}
var radarDataPoints = getRadarDataPoints(coordinateAngle, centerPosition, radius, series, opts, process);
radarDataPoints.forEach(function(eachSeries, seriesIndex) {
// 绘制区域数据
context.beginPath();
context.setFillStyle(hexToRgb(eachSeries.color, radarOption.opacity));
eachSeries.data.forEach(function(item, index) {
if (index === 0) {
context.moveTo(item.position.x, item.position.y);
} else {
context.lineTo(item.position.x, item.position.y);
}
});
context.closePath();
context.fill();
if (opts.dataPointShape !== false) {
var shape = config.dataPointShape[seriesIndex % config.dataPointShape.length];
var points = eachSeries.data.map(function(item) {
return item.position;
});
drawPointShape(points, eachSeries.color, shape, context, opts);
}
});
// draw label text
drawRadarLabel(coordinateAngle, radius, centerPosition, opts, config, context);
return {
center: centerPosition,
radius: radius,
angleList: coordinateAngle
};
}
function normalInt(min, max, iter) {
iter = iter==0?1:iter;
var arr = [];
for (var i = 0; i < iter; i++) {
arr[i] = Math.random();
};
return Math.floor(arr.reduce(function(i,j){return i+j})/iter*(max-min))+min;
};
function collisionNew(area,points,width,height){
var isIn=false;
for(let i=0;i<points.length;i++){
if(points[i].area){
if(area[3]<points[i].area[1]||area[0]>points[i].area[2]||area[1]>points[i].area[3]||area[2]<points[i].area[0]){
if(area[0]<0 || area[1]<0 || area[2]>width || area[3]>height){
isIn=true;
break;
}else{
isIn=false;
}
}else{
isIn=true;
break;
}
}
}
return isIn;
};
function getBoundingBox(data) {
var bounds = {}, coords;
bounds.xMin = 180;
bounds.xMax = 0;
bounds.yMin = 90;
bounds.yMax = 0
for (var i = 0; i < data.length; i++) {
var coorda = data[i].geometry.coordinates
for (var k = 0; k < coorda.length; k++) {
coords = coorda[k];
if (coords.length == 1) {
coords = coords[0]
}
for (var j = 0; j < coords.length; j++) {
var longitude = coords[j][0];
var latitude = coords[j][1];
var point = {
x: longitude,
y: latitude
}
bounds.xMin = bounds.xMin < point.x ? bounds.xMin : point.x;
bounds.xMax = bounds.xMax > point.x ? bounds.xMax : point.x;
bounds.yMin = bounds.yMin < point.y ? bounds.yMin : point.y;
bounds.yMax = bounds.yMax > point.y ? bounds.yMax : point.y;
}
}
}
return bounds;
}
function coordinateToPoint(latitude, longitude,bounds,scale,xoffset,yoffset) {
return {
x: (longitude - bounds.xMin) * scale+xoffset,
y: (bounds.yMax - latitude) * scale+yoffset
};
}
function pointToCoordinate(pointY, pointX,bounds,scale,xoffset,yoffset) {
return {
x: (pointX-xoffset)/scale+bounds.xMin,
y: bounds.yMax - (pointY-yoffset)/scale
};
}
function isRayIntersectsSegment(poi,s_poi,e_poi){
if (s_poi[1]==e_poi[1]){return false;}
if (s_poi[1]>poi[1] && e_poi[1]>poi[1]){return false;}
if (s_poi[1]<poi[1] && e_poi[1]<poi[1]){return false;}
if (s_poi[1]==poi[1] && e_poi[1]>poi[1]){return false;}
if (e_poi[1]==poi[1] && s_poi[1]>poi[1]){return false;}
if (s_poi[0]<poi[0] && e_poi[1]<poi[1]){return false;}
let xseg=e_poi[0]-(e_poi[0]-s_poi[0])*(e_poi[1]-poi[1])/(e_poi[1]-s_poi[1]);
if (xseg<poi[0]){
return false;
}else{
return true;
}
}
function isPoiWithinPoly(poi,poly){
let sinsc=0;
for (let i=0;i<poly.length;i++){
let epoly=poly[i][0];
if (poly.length == 1) {
epoly = poly[i][0]
}
for(let j=0;j<epoly.length-1;j++){
let s_poi=epoly[j];
let e_poi=epoly[j+1];
if (isRayIntersectsSegment(poi,s_poi,e_poi)){
sinsc+=1;
}
}
}
if(sinsc%2==1){
return true;
}else{
return false;
}
}
function drawMapDataPoints(series, opts, config, context) {
var mapOption=assign({},{
border:true,
borderWidth:1,
borderColor:'#666666',
fillOpacity:0.6,
activeBorderColor:'#f04864',
activeFillColor:'#facc14',
activeFillOpacity:1
},opts.extra.map);
var coords, point;
var data = series;
var bounds= getBoundingBox(data);
var xScale = opts.width / Math.abs(bounds.xMax - bounds.xMin);
var yScale = opts.height / Math.abs(bounds.yMax - bounds.yMin);
var scale = xScale < yScale ? xScale : yScale;
var xoffset=opts.width/2-Math.abs(bounds.xMax - bounds.xMin)/2*scale;
var yoffset=opts.height/2-Math.abs(bounds.yMax - bounds.yMin)/2*scale;
context.beginPath();
context.clearRect(0, 0, opts.width, opts.height);
context.setFillStyle(opts.background||'#FFFFFF');
context.rect(0,0,opts.width,opts.height);
context.fill();
for (var i = 0; i < data.length; i++) {
context.beginPath();
context.setLineWidth(mapOption.borderWidth * opts.pixelRatio);
context.setStrokeStyle(mapOption.borderColor);
context.setFillStyle(hexToRgb(series[i].color, mapOption.fillOpacity));
if (opts.tooltip) {
if (opts.tooltip.index == i ) {
context.setStrokeStyle(mapOption.activeBorderColor);
context.setFillStyle(hexToRgb(mapOption.activeFillColor, mapOption.activeFillOpacity));
}
}
var coorda = data[i].geometry.coordinates
for (var k = 0; k < coorda.length; k++) {
coords = coorda[k];
if (coords.length == 1) {
coords = coords[0]
}
for (var j = 0; j < coords.length; j++) {
point = coordinateToPoint(coords[j][1], coords[j][0],bounds,scale,xoffset,yoffset)
if (j === 0) {
context.beginPath();
context.moveTo(point.x, point.y);
} else {
context.lineTo(point.x, point.y);
}
}
context.fill();
if(mapOption.border == true){
context.stroke();
}
}
if(opts.dataLabel == true){
var centerPoint = data[i].properties.centroid;
if(centerPoint){
point = coordinateToPoint(centerPoint[1], centerPoint[0],bounds,scale,xoffset,yoffset);
let fontSize=data[i].textSize||config.fontSize;
let text=data[i].properties.name;
context.beginPath();
context.setFontSize(fontSize)
context.setFillStyle(data[i].textColor||'#666666')
context.fillText(text, point.x-measureText(text,fontSize)/2, point.y+fontSize/2);
context.closePath();
context.stroke();
}
}
}
opts.chartData.mapData={
bounds:bounds,
scale:scale,
xoffset:xoffset,
yoffset:yoffset
}
drawToolTipBridge(opts, config, context,1);
context.draw();
}
function getWordCloudPoint(opts,type){
let points = opts.series.sort(function(a,b){return parseInt(b.textSize)-parseInt(a.textSize);});
switch (type) {
case 'normal':
for (let i = 0; i < points.length; i++) {
let text = points[i].name;
let tHeight = points[i].textSize;
let tWidth = measureText(text,tHeight);
let x,y;
let area;
let breaknum=0;
while(true) {
breaknum++;
x = normalInt(-opts.width/2, opts.width/2,5) - tWidth/2;
y = normalInt(-opts.height/2, opts.height/2,5) + tHeight/2;
area=[x-5+opts.width/2,y-5-tHeight+opts.height/2,x+tWidth+5+opts.width/2,y+5+opts.height/2];
let isCollision = collisionNew(area,points,opts.width,opts.height);
if (!isCollision) break;
if (breaknum==1000){
area=[-100,-100,-100,-100];
break;
}
};
points[i].area=area;
}
break;
case 'vertical':
function Spin(){
//获取均匀随机值是否旋转旋转的概率为1-0.5
if (Math.random()>0.7) {
return true;
}else {return false};
};
for (let i = 0; i < points.length; i++) {
let text = points[i].name;
let tHeight = points[i].textSize;
let tWidth = measureText(text,tHeight);
let isSpin = Spin();
let x,y,area,areav;
let breaknum=0;
while(true) {
breaknum++;
let isCollision;
if (isSpin) {
x = normalInt(-opts.width/2, opts.width/2,5) - tWidth/2;
y = normalInt(-opts.height/2, opts.height/2,5)+tHeight/2;
area=[y-5-tWidth+opts.width/2,(-x-5+opts.height/2),y+5+opts.width/2,(-x+tHeight+5+opts.height/2)];
areav=[opts.width-(opts.width/2-opts.height/2)-(-x+tHeight+5+opts.height/2)-5,(opts.height/2-opts.width/2)+(y-5-tWidth+opts.width/2)-5,opts.width-(opts.width/2-opts.height/2)-(-x+tHeight+5+opts.height/2)+tHeight,(opts.height/2-opts.width/2)+(y-5-tWidth+opts.width/2)+tWidth+5];
isCollision = collisionNew(areav,points,opts.height,opts.width);
}else{
x = normalInt(-opts.width/2, opts.width/2,5) - tWidth/2;
y = normalInt(-opts.height/2, opts.height/2,5)+tHeight/2;
area=[x-5+opts.width/2,y-5-tHeight+opts.height/2,x+tWidth+5+opts.width/2,y+5+opts.height/2];
isCollision = collisionNew(area,points,opts.width,opts.height);
}
if (!isCollision) break;
if (breaknum==1000){
area=[-1000,-1000,-1000,-1000];
break;
}
};
if (isSpin) {
points[i].area=areav;
points[i].areav=area;
}else{
points[i].area=area;
}
points[i].rotate=isSpin;
};
break;
}
return points;
}
function drawWordCloudDataPoints(series, opts, config, context) {
let process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
let wordOption = assign({},{
type: 'normal',
autoColors: true
},opts.extra.word);
context.beginPath();
context.setFillStyle(opts.background||'#FFFFFF');
context.rect(0,0,opts.width,opts.height);
context.fill();
context.save();
let points = opts.chartData.wordCloudData;
context.translate(opts.width/2,opts.height/2);
for(let i=0;i<points.length;i++){
context.save();
if(points[i].rotate){
context.rotate(90 * Math.PI / 180);
}
let text = points[i].name;
let tHeight = points[i].textSize;
let tWidth = measureText(text,tHeight);
context.beginPath();
context.setStrokeStyle(points[i].color);
context.setFillStyle(points[i].color);
context.setFontSize(tHeight);
if(points[i].rotate){
if(points[i].areav[0]>0){
if (opts.tooltip) {
if (opts.tooltip.index == i) {
context.strokeText(text,(points[i].areav[0]+5-opts.width/2)*process-tWidth*(1-process)/2,(points[i].areav[1]+5+tHeight-opts.height/2)*process);
}else{
context.fillText(text,(points[i].areav[0]+5-opts.width/2)*process-tWidth*(1-process)/2,(points[i].areav[1]+5+tHeight-opts.height/2)*process);
}
}else{
context.fillText(text,(points[i].areav[0]+5-opts.width/2)*process-tWidth*(1-process)/2,(points[i].areav[1]+5+tHeight-opts.height/2)*process);
}
}
}else{
if(points[i].area[0]>0){
if (opts.tooltip) {
if (opts.tooltip.index == i) {
context.strokeText(text,(points[i].area[0]+5-opts.width/2)*process-tWidth*(1-process)/2,(points[i].area[1]+5+tHeight-opts.height/2)*process);
}else{
context.fillText(text,(points[i].area[0]+5-opts.width/2)*process-tWidth*(1-process)/2,(points[i].area[1]+5+tHeight-opts.height/2)*process);
}
}else{
context.fillText(text,(points[i].area[0]+5-opts.width/2)*process-tWidth*(1-process)/2,(points[i].area[1]+5+tHeight-opts.height/2)*process);
}
}
}
context.stroke();
context.restore();
}
context.restore();
}
function drawFunnelDataPoints(series, opts, config, context) {
let process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
let funnelOption = assign({},{
activeWidth:10,
activeOpacity:0.3,
border:false,
borderWidth:2,
borderColor:'#FFFFFF',
fillOpacity:1,
labelAlign:'right'
},opts.extra.funnel);
let eachSpacing = (opts.height - opts.area[0] - opts.area[2])/series.length;
let centerPosition = {
x: opts.area[3] + (opts.width - opts.area[1] - opts.area[3]) / 2,
y: opts.height-opts.area[2]
};
let activeWidth = funnelOption.activeWidth;
let radius = Math.min((opts.width - opts.area[1] - opts.area[3]) / 2 - activeWidth, (opts.height - opts.area[0] - opts.area[2]) / 2 - activeWidth);
series = getFunnelDataPoints(series, radius, process);
context.save();
context.translate(centerPosition.x,centerPosition.y);
for(let i=0;i<series.length;i++){
if(i==0){
if (opts.tooltip) {
if (opts.tooltip.index == i) {
context.beginPath();
context.setFillStyle(hexToRgb(series[i].color, funnelOption.activeOpacity));
context.moveTo(-activeWidth, 0);
context.lineTo(-series[i].radius-activeWidth, -eachSpacing);
context.lineTo(series[i].radius+activeWidth, -eachSpacing);
context.lineTo(activeWidth, 0);
context.lineTo(-activeWidth, 0);
context.closePath();
context.fill();
}
}
series[i].funnelArea=[centerPosition.x-series[i].radius,centerPosition.y-eachSpacing,centerPosition.x+series[i].radius,centerPosition.y];
context.beginPath();
context.setLineWidth(funnelOption.borderWidth * opts.pixelRatio);
context.setStrokeStyle(funnelOption.borderColor);
context.setFillStyle(hexToRgb(series[i].color, funnelOption.fillOpacity));
context.moveTo(0, 0);
context.lineTo(-series[i].radius, -eachSpacing);
context.lineTo(series[i].radius, -eachSpacing);
context.lineTo(0, 0);
context.closePath();
context.fill();
if(funnelOption.border == true){
context.stroke();
}
}else{
if (opts.tooltip) {
if (opts.tooltip.index == i) {
context.beginPath();
context.setFillStyle(hexToRgb(series[i].color, funnelOption.activeOpacity));
context.moveTo(0, 0);
context.lineTo(-series[i-1].radius-activeWidth, 0);
context.lineTo(-series[i].radius-activeWidth, -eachSpacing);
context.lineTo(series[i].radius+activeWidth, -eachSpacing);
context.lineTo(series[i-1].radius+activeWidth, 0);
context.lineTo(0, 0);
context.closePath();
context.fill();
context.closePath();
context.fill();
}
}
series[i].funnelArea=[centerPosition.x-series[i].radius,centerPosition.y-eachSpacing*(i+1),centerPosition.x+series[i].radius,centerPosition.y-eachSpacing*i];
context.beginPath();
context.setLineWidth(funnelOption.borderWidth * opts.pixelRatio);
context.setStrokeStyle(funnelOption.borderColor);
context.setFillStyle(hexToRgb(series[i].color, funnelOption.fillOpacity));
context.moveTo(0, 0);
context.lineTo(-series[i-1].radius, 0);
context.lineTo(-series[i].radius, -eachSpacing);
context.lineTo(series[i].radius, -eachSpacing);
context.lineTo(series[i-1].radius, 0);
context.lineTo(0, 0);
context.closePath();
context.fill();
if(funnelOption.border == true){
context.stroke();
}
}
context.translate(0,-eachSpacing)
}
context.restore();
if (opts.dataLabel !== false && process === 1) {
drawFunnelText(series, opts, context, eachSpacing, funnelOption.labelAlign, activeWidth, centerPosition);
}
return {
center: centerPosition,
radius: radius,
series: series
};
}
function drawFunnelText(series, opts, context, eachSpacing, labelAlign,activeWidth, centerPosition){
for(let i=0;i<series.length;i++){
let item = series[i];
let startX,endX,startY,fontSize;
let text = item.format ? item.format(+item._proportion_.toFixed(2)) : util.toFixed(item._proportion_ * 100) +'%';
if(labelAlign == 'right'){
if(i==0){
startX=(item.funnelArea[2]+centerPosition.x)/2;
}else{
startX=(item.funnelArea[2]+series[i-1].funnelArea[2])/2;
}
endX=startX+activeWidth*2;
startY=item.funnelArea[1]+eachSpacing/2;
fontSize = item.textSize || opts.fontSize;
context.setLineWidth(1 * opts.pixelRatio);
context.setStrokeStyle(item.color);
context.setFillStyle(item.color);
context.beginPath();
context.moveTo(startX,startY );
context.lineTo(endX,startY);
context.stroke();
context.closePath();
context.beginPath();
context.moveTo(endX, startY);
context.arc(endX, startY, 2, 0, 2 * Math.PI);
context.closePath();
context.fill();
context.beginPath();
context.setFontSize(fontSize);
context.setFillStyle(item.textColor || '#666666');
context.fillText(text, endX+5, startY + fontSize/2 -2);
context.closePath();
context.stroke();
context.closePath();
}else{
if(i==0){
startX=(item.funnelArea[0]+centerPosition.x)/2;
}else{
startX=(item.funnelArea[0]+series[i-1].funnelArea[0])/2;
}
endX=startX-activeWidth*2;
startY=item.funnelArea[1]+eachSpacing/2;
fontSize = item.textSize || opts.fontSize;
context.setLineWidth(1 * opts.pixelRatio);
context.setStrokeStyle(item.color);
context.setFillStyle(item.color);
context.beginPath();
context.moveTo(startX,startY );
context.lineTo(endX,startY);
context.stroke();
context.closePath();
context.beginPath();
context.moveTo(endX, startY);
context.arc(endX, startY, 2, 0, 2 * Math.PI);
context.closePath();
context.fill();
context.beginPath();
context.setFontSize(fontSize);
context.setFillStyle(item.textColor || '#666666');
context.fillText(text, endX-5-measureText(text), startY + fontSize/2 -2);
context.closePath();
context.stroke();
context.closePath();
}
}
}
function drawCanvas(opts, context) {
context.draw();
}
var Timing = {
easeIn: function easeIn(pos) {
return Math.pow(pos, 3);
},
easeOut: function easeOut(pos) {
return Math.pow(pos - 1, 3) + 1;
},
easeInOut: function easeInOut(pos) {
if ((pos /= 0.5) < 1) {
return 0.5 * Math.pow(pos, 3);
} else {
return 0.5 * (Math.pow(pos - 2, 3) + 2);
}
},
linear: function linear(pos) {
return pos;
}
};
function Animation(opts) {
this.isStop = false;
opts.duration = typeof opts.duration === 'undefined' ? 1000 : opts.duration;
opts.timing = opts.timing || 'linear';
var delay = 17;
function createAnimationFrame() {
if (typeof setTimeout !== 'undefined') {
return function(step, delay) {
setTimeout(function() {
var timeStamp = +new Date();
step(timeStamp);
}, delay);
};
} else if (typeof requestAnimationFrame !== 'undefined') {
return requestAnimationFrame;
} else {
return function(step) {
step(null);
};
}
};
var animationFrame = createAnimationFrame();
var startTimeStamp = null;
var _step = function step(timestamp) {
if (timestamp === null || this.isStop === true) {
opts.onProcess && opts.onProcess(1);
opts.onAnimationFinish && opts.onAnimationFinish();
return;
}
if (startTimeStamp === null) {
startTimeStamp = timestamp;
}
if (timestamp - startTimeStamp < opts.duration) {
var process = (timestamp - startTimeStamp) / opts.duration;
var timingFunction = Timing[opts.timing];
process = timingFunction(process);
opts.onProcess && opts.onProcess(process);
animationFrame(_step, delay);
} else {
opts.onProcess && opts.onProcess(1);
opts.onAnimationFinish && opts.onAnimationFinish();
}
};
_step = _step.bind(this);
animationFrame(_step, delay);
}
// stop animation immediately
// and tigger onAnimationFinish
Animation.prototype.stop = function() {
this.isStop = true;
};
function drawCharts(type, opts, config, context) {
var _this = this;
var series = opts.series;
var categories = opts.categories;
series = fillSeries(series, opts, config);
var duration = opts.animation ? opts.duration : 0;
this.animationInstance && this.animationInstance.stop();
var seriesMA = null;
if (type == 'candle') {
let average = assign({}, opts.extra.candle.average);
if (average.show) {
seriesMA = calCandleMA(average.day, average.name, average.color, series[0].data);
seriesMA = fillSeries(seriesMA, opts, config);
opts.seriesMA = seriesMA;
} else if (opts.seriesMA) {
seriesMA = opts.seriesMA = fillSeries(opts.seriesMA, opts, config);
} else {
seriesMA = series;
}
} else {
seriesMA = series;
}
/* 过滤掉show=false的series */
opts._series_ = series = filterSeries(series);
//重新计算图表区域
opts.area = new Array(4);
//复位绘图区域
for (let j = 0; j < 4; j++) {
opts.area[j] = opts.padding[j];
}
//通过计算三大区域图例、X轴、Y轴的大小确定绘图区域
var _calLegendData = calLegendData(seriesMA, opts, config, opts.chartData),
legendHeight = _calLegendData.area.wholeHeight,
legendWidth = _calLegendData.area.wholeWidth;
//TODO废弃config.legendHeight参数
config.legendHeight = legendHeight;
switch (opts.legend.position) {
case 'top':
opts.area[0] += legendHeight;
break;
case 'bottom':
opts.area[2] += legendHeight;
break;
case 'left':
opts.area[3] += legendWidth;
break;
case 'right':
opts.area[1] += legendWidth;
break;
}
let _calYAxisData = {},yAxisWidth = 0;
if (opts.type === 'line' || opts.type === 'column' || opts.type === 'area' || opts.type === 'mix' || opts.type === 'candle') {
_calYAxisData = calYAxisData(series, opts, config);
yAxisWidth = _calYAxisData.yAxisWidth;
config.yAxisWidth = yAxisWidth;
opts.area[3] += yAxisWidth;
} else {
config.yAxisWidth = yAxisWidth;
}
opts.chartData.yAxisData = _calYAxisData;
if (opts.categories && opts.categories.length) {
opts.chartData.xAxisData = getXAxisPoints(opts.categories, opts, config);
let _calCategoriesData = calCategoriesData(opts.categories, opts, config, opts.chartData.xAxisData.eachSpacing),
xAxisHeight = _calCategoriesData.xAxisHeight,
angle = _calCategoriesData.angle;
config.xAxisHeight = xAxisHeight;
config._xAxisTextAngle_ = angle;
opts.area[2] += xAxisHeight;
opts.chartData.categoriesData = _calCategoriesData;
}
//计算右对齐偏移距离
if (opts.enableScroll && opts.xAxis.scrollAlign == 'right' && opts._scrollDistance_ === undefined) {
let offsetLeft = 0,
xAxisPoints = opts.chartData.xAxisData.xAxisPoints,
startX = opts.chartData.xAxisData.startX,
endX = opts.chartData.xAxisData.endX,
eachSpacing = opts.chartData.xAxisData.eachSpacing;
let totalWidth = eachSpacing * (xAxisPoints.length - 1);
let screenWidth = endX - startX;
offsetLeft = screenWidth - totalWidth;
_this.scrollOption = {
currentOffset: offsetLeft,
startTouchX: offsetLeft,
distance: 0,
lastMoveTime: 0
};
opts._scrollDistance_ = offsetLeft;
}
if (type === 'pie' || type === 'ring' || type === 'rose') {
config._pieTextMaxLength_ = opts.dataLabel === false ? 0 : getPieTextMaxLength(seriesMA);
}
switch (type) {
case 'word':
let wordOption = assign({},{
type: 'normal',
autoColors: true
},opts.extra.word);
if(opts.updateData==true || opts.updateData==undefined){
opts.chartData.wordCloudData=getWordCloudPoint(opts,wordOption.type);
}
this.animationInstance = new Animation({
timing: 'easeInOut',
duration: duration,
onProcess: function(process) {
context.clearRect(0, 0, opts.width, opts.height);
if (opts.rotate) {
contextRotate(context, opts);
}
drawWordCloudDataPoints(series, opts, config, context,process);
drawCanvas(opts, context);
},
onAnimationFinish: function onAnimationFinish() {
_this.event.trigger('renderComplete');
}
});
break;
case 'map':
context.clearRect(0, 0, opts.width, opts.height);
drawMapDataPoints(series, opts, config, context);
break;
case 'funnel':
this.animationInstance = new Animation({
timing: 'easeInOut',
duration: duration,
onProcess: function(process) {
context.clearRect(0, 0, opts.width, opts.height);
if (opts.rotate) {
contextRotate(context, opts);
}
opts.chartData.funnelData = drawFunnelDataPoints(series, opts, config, context, process);
drawLegend(opts.series, opts, config, context, opts.chartData);
drawToolTipBridge(opts, config, context, process);
drawCanvas(opts, context);
},
onAnimationFinish: function onAnimationFinish() {
_this.event.trigger('renderComplete');
}
});
break;
case 'line':
this.animationInstance = new Animation({
timing: 'easeIn',
duration: duration,
onProcess: function onProcess(process) {
context.clearRect(0, 0, opts.width, opts.height);
if (opts.rotate) {
contextRotate(context, opts);
}
drawYAxisGrid(categories, opts, config, context);
drawXAxis(categories, opts, config, context);
var _drawLineDataPoints = drawLineDataPoints(series, opts, config, context, process),
xAxisPoints = _drawLineDataPoints.xAxisPoints,
calPoints = _drawLineDataPoints.calPoints,
eachSpacing = _drawLineDataPoints.eachSpacing,
minRange = _drawLineDataPoints.minRange,
maxRange = _drawLineDataPoints.maxRange;
opts.chartData.xAxisPoints = xAxisPoints;
opts.chartData.calPoints = calPoints;
opts.chartData.eachSpacing = eachSpacing;
drawYAxis(series, opts, config, context);
if (opts.enableMarkLine !== false && process === 1) {
drawMarkLine(minRange, maxRange, opts, config, context);
}
drawLegend(opts.series, opts, config, context, opts.chartData);
drawToolTipBridge(opts, config, context, process, eachSpacing, xAxisPoints);
drawCanvas(opts, context);
},
onAnimationFinish: function onAnimationFinish() {
_this.event.trigger('renderComplete');
}
});
break;
case 'mix':
this.animationInstance = new Animation({
timing: 'easeIn',
duration: duration,
onProcess: function onProcess(process) {
context.clearRect(0, 0, opts.width, opts.height);
if (opts.rotate) {
contextRotate(context, opts);
}
drawYAxisGrid(categories, opts, config, context);
drawXAxis(categories, opts, config, context);
var _drawMixDataPoints = drawMixDataPoints(series, opts, config, context, process),
xAxisPoints = _drawMixDataPoints.xAxisPoints,
calPoints = _drawMixDataPoints.calPoints,
eachSpacing = _drawMixDataPoints.eachSpacing,
minRange = _drawMixDataPoints.minRange,
maxRange = _drawMixDataPoints.maxRange;
opts.chartData.xAxisPoints = xAxisPoints;
opts.chartData.calPoints = calPoints;
opts.chartData.eachSpacing = eachSpacing;
drawYAxis(series, opts, config, context);
if (opts.enableMarkLine !== false && process === 1) {
drawMarkLine(minRange, maxRange, opts, config, context);
}
drawLegend(opts.series, opts, config, context, opts.chartData);
drawToolTipBridge(opts, config, context, process, eachSpacing, xAxisPoints);
drawCanvas(opts, context);
},
onAnimationFinish: function onAnimationFinish() {
_this.event.trigger('renderComplete');
}
});
break;
case 'column':
this.animationInstance = new Animation({
timing: 'easeIn',
duration: duration,
onProcess: function onProcess(process) {
context.clearRect(0, 0, opts.width, opts.height);
if (opts.rotate) {
contextRotate(context, opts);
}
drawYAxisGrid(categories, opts, config, context);
drawXAxis(categories, opts, config, context);
var _drawColumnDataPoints = drawColumnDataPoints(series, opts, config, context, process),
xAxisPoints = _drawColumnDataPoints.xAxisPoints,
calPoints = _drawColumnDataPoints.calPoints,
eachSpacing = _drawColumnDataPoints.eachSpacing,
minRange = _drawColumnDataPoints.minRange,
maxRange = _drawColumnDataPoints.maxRange;
opts.chartData.xAxisPoints = xAxisPoints;
opts.chartData.calPoints = calPoints;
opts.chartData.eachSpacing = eachSpacing;
drawYAxis(series, opts, config, context);
if (opts.enableMarkLine !== false && process === 1) {
drawMarkLine(minRange, maxRange, opts, config, context);
}
drawLegend(opts.series, opts, config, context, opts.chartData);
drawToolTipBridge(opts, config, context, process, eachSpacing, xAxisPoints);
drawCanvas(opts, context);
},
onAnimationFinish: function onAnimationFinish() {
_this.event.trigger('renderComplete');
}
});
break;
case 'area':
this.animationInstance = new Animation({
timing: 'easeIn',
duration: duration,
onProcess: function onProcess(process) {
context.clearRect(0, 0, opts.width, opts.height);
if (opts.rotate) {
contextRotate(context, opts);
}
drawYAxisGrid(categories, opts, config, context);
drawXAxis(categories, opts, config, context);
var _drawAreaDataPoints = drawAreaDataPoints(series, opts, config, context, process),
xAxisPoints = _drawAreaDataPoints.xAxisPoints,
calPoints = _drawAreaDataPoints.calPoints,
eachSpacing = _drawAreaDataPoints.eachSpacing,
minRange = _drawAreaDataPoints.minRange,
maxRange = _drawAreaDataPoints.maxRange;
opts.chartData.xAxisPoints = xAxisPoints;
opts.chartData.calPoints = calPoints;
opts.chartData.eachSpacing = eachSpacing;
drawYAxis(series, opts, config, context);
if (opts.enableMarkLine !== false && process === 1) {
drawMarkLine(minRange, maxRange, opts, config, context);
}
drawLegend(opts.series, opts, config, context, opts.chartData);
drawToolTipBridge(opts, config, context, process, eachSpacing, xAxisPoints);
drawCanvas(opts, context);
},
onAnimationFinish: function onAnimationFinish() {
_this.event.trigger('renderComplete');
}
});
break;
case 'ring':
case 'pie':
this.animationInstance = new Animation({
timing: 'easeInOut',
duration: duration,
onProcess: function onProcess(process) {
context.clearRect(0, 0, opts.width, opts.height);
if (opts.rotate) {
contextRotate(context, opts);
}
opts.chartData.pieData = drawPieDataPoints(series, opts, config, context, process);
drawLegend(opts.series, opts, config, context, opts.chartData);
drawToolTipBridge(opts, config, context, process);
drawCanvas(opts, context);
},
onAnimationFinish: function onAnimationFinish() {
_this.event.trigger('renderComplete');
}
});
break;
case 'rose':
this.animationInstance = new Animation({
timing: 'easeInOut',
duration: duration,
onProcess: function onProcess(process) {
context.clearRect(0, 0, opts.width, opts.height);
if (opts.rotate) {
contextRotate(context, opts);
}
opts.chartData.pieData = drawRoseDataPoints(series, opts, config, context, process);
drawLegend(opts.series, opts, config, context, opts.chartData);
drawToolTipBridge(opts, config, context, process);
drawCanvas(opts, context);
},
onAnimationFinish: function onAnimationFinish() {
_this.event.trigger('renderComplete');
}
});
break;
case 'radar':
this.animationInstance = new Animation({
timing: 'easeInOut',
duration: duration,
onProcess: function onProcess(process) {
context.clearRect(0, 0, opts.width, opts.height);
if (opts.rotate) {
contextRotate(context, opts);
}
opts.chartData.radarData = drawRadarDataPoints(series, opts, config, context, process);
drawLegend(opts.series, opts, config, context, opts.chartData);
drawToolTipBridge(opts, config, context, process);
drawCanvas(opts, context);
},
onAnimationFinish: function onAnimationFinish() {
_this.event.trigger('renderComplete');
}
});
break;
case 'arcbar':
this.animationInstance = new Animation({
timing: 'easeInOut',
duration: duration,
onProcess: function onProcess(process) {
context.clearRect(0, 0, opts.width, opts.height);
if (opts.rotate) {
contextRotate(context, opts);
}
opts.chartData.arcbarData = drawArcbarDataPoints(series, opts, config, context, process);
drawCanvas(opts, context);
},
onAnimationFinish: function onAnimationFinish() {
_this.event.trigger('renderComplete');
}
});
break;
case 'gauge':
this.animationInstance = new Animation({
timing: 'easeInOut',
duration: duration,
onProcess: function onProcess(process) {
context.clearRect(0, 0, opts.width, opts.height);
if (opts.rotate) {
contextRotate(context, opts);
}
opts.chartData.gaugeData = drawGaugeDataPoints(categories, series, opts, config, context, process);
drawCanvas(opts, context);
},
onAnimationFinish: function onAnimationFinish() {
_this.event.trigger('renderComplete');
}
});
break;
case 'candle':
this.animationInstance = new Animation({
timing: 'easeIn',
duration: duration,
onProcess: function onProcess(process) {
context.clearRect(0, 0, opts.width, opts.height);
if (opts.rotate) {
contextRotate(context, opts);
}
drawYAxisGrid(categories, opts, config, context);
drawXAxis(categories, opts, config, context);
var _drawCandleDataPoints = drawCandleDataPoints(series, seriesMA, opts, config, context, process),
xAxisPoints = _drawCandleDataPoints.xAxisPoints,
calPoints = _drawCandleDataPoints.calPoints,
eachSpacing = _drawCandleDataPoints.eachSpacing,
minRange = _drawCandleDataPoints.minRange,
maxRange = _drawCandleDataPoints.maxRange;
opts.chartData.xAxisPoints = xAxisPoints;
opts.chartData.calPoints = calPoints;
opts.chartData.eachSpacing = eachSpacing;
drawYAxis(series, opts, config, context);
if (opts.enableMarkLine !== false && process === 1) {
drawMarkLine(minRange, maxRange, opts, config, context);
}
if (seriesMA) {
drawLegend(seriesMA, opts, config, context, opts.chartData);
} else {
drawLegend(opts.series, opts, config, context, opts.chartData);
}
drawToolTipBridge(opts, config, context, process, eachSpacing, xAxisPoints);
drawCanvas(opts, context);
},
onAnimationFinish: function onAnimationFinish() {
_this.event.trigger('renderComplete');
}
});
break;
}
}
// simple event implement
function Event() {
this.events = {};
}
Event.prototype.addEventListener = function(type, listener) {
this.events[type] = this.events[type] || [];
this.events[type].push(listener);
};
Event.prototype.trigger = function() {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
var type = args[0];
var params = args.slice(1);
if (!!this.events[type]) {
this.events[type].forEach(function(listener) {
try {
listener.apply(null, params);
} catch (e) {
console.error(e);
}
});
}
};
var Charts = function Charts(opts) {
opts.pixelRatio = opts.pixelRatio ? opts.pixelRatio : 1;
opts.fontSize = opts.fontSize ? opts.fontSize * opts.pixelRatio : 13 * opts.pixelRatio;
opts.title = assign({}, opts.title);
opts.subtitle = assign({}, opts.subtitle);
opts.duration = opts.duration ? opts.duration : 1000;
opts.yAxis = assign({}, {
gridType: 'solid',
dashLength: 4 * opts.pixelRatio
}, opts.yAxis);
opts.xAxis = assign({}, {
rotateLabel: false,
type: 'calibration',
gridType: 'solid',
dashLength: 4 * opts.pixelRatio,
scrollAlign: 'left',
boundaryGap:'center'
}, opts.xAxis);
opts.legend = assign({}, {
show: true,
position: 'bottom',
float: 'center',
backgroundColor: 'rgba(0,0,0,0)',
borderColor: 'rgba(0,0,0,0)',
borderWidth: 0,
padding: 5,
margin: 5,
itemGap: 10,
fontSize: opts.fontSize,
lineHeight: opts.fontSize,
fontColor: '#333333',
format: {},
hiddenColor: '#CECECE'
}, opts.legend);
opts.legend.borderWidth = opts.legend.borderWidth * opts.pixelRatio;
opts.legend.itemGap = opts.legend.itemGap * opts.pixelRatio;
opts.legend.padding = opts.legend.padding * opts.pixelRatio;
opts.legend.margin = opts.legend.margin * opts.pixelRatio;
opts.extra = assign({}, opts.extra);
opts.rotate = opts.rotate ? true : false;
opts.animation = opts.animation ? true : false;
let config$$1 = JSON.parse(JSON.stringify(config));
config$$1.colors = opts.colors ? opts.colors : config$$1.colors;
config$$1.yAxisTitleWidth = opts.yAxis.disabled !== true && opts.yAxis.title ? config$$1.yAxisTitleWidth : 0;
if (opts.type == 'pie' || opts.type == 'ring') {
config$$1.pieChartLinePadding = opts.dataLabel === false ? 0 : opts.extra.pie.labelWidth * opts.pixelRatio || config$$1.pieChartLinePadding * opts.pixelRatio;
}
if (opts.type == 'rose') {
config$$1.pieChartLinePadding = opts.dataLabel === false ? 0 : opts.extra.rose.labelWidth * opts.pixelRatio || config$$1.pieChartLinePadding * opts.pixelRatio;
}
config$$1.pieChartTextPadding = opts.dataLabel === false ? 0 : config$$1.pieChartTextPadding * opts.pixelRatio;
config$$1.yAxisSplit = opts.yAxis.splitNumber ? opts.yAxis.splitNumber : config.yAxisSplit;
//屏幕旋转
config$$1.rotate = opts.rotate;
if (opts.rotate) {
let tempWidth = opts.width;
let tempHeight = opts.height;
opts.width = tempHeight;
opts.height = tempWidth;
}
//适配高分屏
opts.padding = opts.padding ? opts.padding : config$$1.padding;
for (let i = 0; i < 4; i++) {
opts.padding[i] *= opts.pixelRatio;
}
config$$1.yAxisWidth = config.yAxisWidth * opts.pixelRatio;
config$$1.xAxisHeight = config.xAxisHeight * opts.pixelRatio;
if (opts.enableScroll && opts.xAxis.scrollShow) {
config$$1.xAxisHeight += 6 * opts.pixelRatio;
}
config$$1.xAxisLineHeight = config.xAxisLineHeight * opts.pixelRatio;
config$$1.legendHeight = config.legendHeight * opts.pixelRatio;
config$$1.fontSize = opts.fontSize;
config$$1.titleFontSize = config.titleFontSize * opts.pixelRatio;
config$$1.subtitleFontSize = config.subtitleFontSize * opts.pixelRatio;
config$$1.toolTipPadding = config.toolTipPadding * opts.pixelRatio;
config$$1.toolTipLineHeight = config.toolTipLineHeight * opts.pixelRatio;
config$$1.columePadding = config.columePadding * opts.pixelRatio;
opts.$this = opts.$this ? opts.$this : this;
this.context = uni.createCanvasContext(opts.canvasId, opts.$this);
/* 兼容原生H5
this.context = document.getElementById(opts.canvasId).getContext("2d");
this.context.setStrokeStyle = function(e){ return this.strokeStyle=e; }
this.context.setLineWidth = function(e){ return this.lineWidth=e; }
this.context.setLineCap = function(e){ return this.lineCap=e; }
this.context.setFontSize = function(e){ return this.font=e+"px sans-serif"; }
this.context.setFillStyle = function(e){ return this.fillStyle=e; }
this.context.draw = function(){ }
*/
opts.chartData = {};
this.event = new Event();
this.scrollOption = {
currentOffset: 0,
startTouchX: 0,
distance: 0,
lastMoveTime: 0
};
this.opts = opts;
this.config = config$$1;
drawCharts.call(this, opts.type, opts, config$$1, this.context);
};
Charts.prototype.updateData = function() {
let data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
this.opts = assign({}, this.opts, data);
this.opts.updateData = true;
let scrollPosition = data.scrollPosition || 'current';
switch (scrollPosition) {
case 'current':
this.opts._scrollDistance_ = this.scrollOption.currentOffset;
break;
case 'left':
this.opts._scrollDistance_ = 0;
this.scrollOption = {
currentOffset: 0,
startTouchX: 0,
distance: 0,
lastMoveTime: 0
};
break;
case 'right':
let _calYAxisData = calYAxisData(this.opts.series, this.opts, this.config),
yAxisWidth = _calYAxisData.yAxisWidth;
this.config.yAxisWidth = yAxisWidth;
let offsetLeft = 0;
let _getXAxisPoints0 = getXAxisPoints(this.opts.categories, this.opts, this.config),
xAxisPoints = _getXAxisPoints0.xAxisPoints,
startX = _getXAxisPoints0.startX,
endX = _getXAxisPoints0.endX,
eachSpacing = _getXAxisPoints0.eachSpacing;
let totalWidth = eachSpacing * (xAxisPoints.length - 1);
let screenWidth = endX - startX;
offsetLeft = screenWidth - totalWidth;
this.scrollOption = {
currentOffset: offsetLeft,
startTouchX: offsetLeft,
distance: 0,
lastMoveTime: 0
};
this.opts._scrollDistance_ = offsetLeft;
break;
}
drawCharts.call(this, this.opts.type, this.opts, this.config, this.context);
};
Charts.prototype.zoom = function() {
var val = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.opts.xAxis.itemCount;
if (this.opts.enableScroll !== true) {
console.log('请启用滚动条后使用!')
return;
}
//当前屏幕中间点
let centerPoint = Math.round(Math.abs(this.scrollOption.currentOffset) / this.opts.chartData.eachSpacing) + Math.round(
this.opts.xAxis.itemCount / 2);
this.opts.animation = false;
this.opts.xAxis.itemCount = val.itemCount;
//重新计算x轴偏移距离
let _calYAxisData = calYAxisData(this.opts.series, this.opts, this.config),
yAxisWidth = _calYAxisData.yAxisWidth;
this.config.yAxisWidth = yAxisWidth;
let offsetLeft = 0;
let _getXAxisPoints0 = getXAxisPoints(this.opts.categories, this.opts, this.config),
xAxisPoints = _getXAxisPoints0.xAxisPoints,
startX = _getXAxisPoints0.startX,
endX = _getXAxisPoints0.endX,
eachSpacing = _getXAxisPoints0.eachSpacing;
let centerLeft = eachSpacing * centerPoint;
let screenWidth = endX - startX;
let MaxLeft = screenWidth - eachSpacing * (xAxisPoints.length - 1);
offsetLeft = screenWidth / 2 - centerLeft;
if (offsetLeft > 0) {
offsetLeft = 0;
}
if (offsetLeft < MaxLeft) {
offsetLeft = MaxLeft;
}
this.scrollOption = {
currentOffset: offsetLeft,
startTouchX: offsetLeft,
distance: 0,
lastMoveTime: 0
};
this.opts._scrollDistance_ = offsetLeft;
drawCharts.call(this, this.opts.type, this.opts, this.config, this.context);
};
Charts.prototype.stopAnimation = function() {
this.animationInstance && this.animationInstance.stop();
};
Charts.prototype.addEventListener = function(type, listener) {
this.event.addEventListener(type, listener);
};
Charts.prototype.getCurrentDataIndex = function(e) {
var touches = null;
if (e.changedTouches) {
touches = e.changedTouches[0];
} else {
touches = e.mp.changedTouches[0];
}
if (touches) {
let _touches$ = getTouches(touches, this.opts, e);
if (this.opts.type === 'pie' || this.opts.type === 'ring' || this.opts.type === 'rose') {
return findPieChartCurrentIndex({
x: _touches$.x,
y: _touches$.y
}, this.opts.chartData.pieData);
} else if (this.opts.type === 'radar') {
return findRadarChartCurrentIndex({
x: _touches$.x,
y: _touches$.y
}, this.opts.chartData.radarData, this.opts.categories.length);
} else if (this.opts.type === 'funnel') {
return findFunnelChartCurrentIndex({
x: _touches$.x,
y: _touches$.y
}, this.opts.chartData.funnelData);
} else if (this.opts.type === 'map') {
return findMapChartCurrentIndex({
x: _touches$.x,
y: _touches$.y
}, this.opts);
}else if (this.opts.type === 'word') {
return findWordChartCurrentIndex({
x: _touches$.x,
y: _touches$.y
}, this.opts.chartData.wordCloudData);
} else {
return findCurrentIndex({
x: _touches$.x,
y: _touches$.y
}, this.opts.chartData.xAxisPoints, this.opts, this.config, Math.abs(this.scrollOption.currentOffset));
}
}
return -1;
};
Charts.prototype.getLegendDataIndex = function(e) {
var touches = null;
if (e.changedTouches) {
touches = e.changedTouches[0];
} else {
touches = e.mp.changedTouches[0];
}
if (touches) {
let _touches$ = getTouches(touches, this.opts, e);
return findLegendIndex({
x: _touches$.x,
y: _touches$.y
}, this.opts.chartData.legendData);
}
return -1;
};
Charts.prototype.touchLegend = function(e) {
var option = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var touches = null;
if (e.changedTouches) {
touches = e.changedTouches[0];
} else {
touches = e.mp.changedTouches[0];
}
if (touches) {
var _touches$ = getTouches(touches, this.opts, e);
var index = this.getLegendDataIndex(e);
if (index >= 0) {
this.opts.series[index].show = !this.opts.series[index].show;
this.opts.animation = option.animation ? true : false;
drawCharts.call(this, this.opts.type, this.opts, this.config, this.context);
}
}
};
Charts.prototype.showToolTip = function(e) {
var option = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var touches = null;
if (e.changedTouches) {
touches = e.changedTouches[0];
} else {
touches = e.mp.changedTouches[0];
}
if (!touches) {
console.log("touchError");
}
var _touches$ = getTouches(touches, this.opts, e);
var currentOffset = this.scrollOption.currentOffset;
var opts = assign({}, this.opts, {
_scrollDistance_: currentOffset,
animation: false
});
if (this.opts.type === 'line' || this.opts.type === 'area' || this.opts.type === 'column') {
var index = this.getCurrentDataIndex(e);
if (index > -1) {
var seriesData = getSeriesDataItem(this.opts.series, index);
if (seriesData.length !== 0) {
var _getToolTipData = getToolTipData(seriesData, this.opts.chartData.calPoints, index, this.opts.categories,option),
textList = _getToolTipData.textList,
offset = _getToolTipData.offset;
offset.y = _touches$.y;
opts.tooltip = {
textList: textList,
offset: offset,
option: option,
index: index
};
}
}
drawCharts.call(this, opts.type, opts, this.config, this.context);
}
if (this.opts.type === 'mix') {
var index = this.getCurrentDataIndex(e);
if (index > -1) {
var currentOffset = this.scrollOption.currentOffset;
var opts = assign({}, this.opts, {
_scrollDistance_: currentOffset,
animation: false
});
var seriesData = getSeriesDataItem(this.opts.series, index);
if (seriesData.length !== 0) {
var _getMixToolTipData = getMixToolTipData(seriesData, this.opts.chartData.calPoints, index, this.opts.categories,option),
textList = _getMixToolTipData.textList,
offset = _getMixToolTipData.offset;
offset.y = _touches$.y;
opts.tooltip = {
textList: textList,
offset: offset,
option: option,
index: index
};
}
}
drawCharts.call(this, opts.type, opts, this.config, this.context);
}
if (this.opts.type === 'candle') {
var index = this.getCurrentDataIndex(e);
if (index > -1) {
var currentOffset = this.scrollOption.currentOffset;
var opts = assign({}, this.opts, {
_scrollDistance_: currentOffset,
animation: false
});
var seriesData = getSeriesDataItem(this.opts.series, index);
if (seriesData.length !== 0) {
var _getToolTipData = getCandleToolTipData(this.opts.series[0].data, seriesData, this.opts.chartData.calPoints,
index, this.opts.categories, this.opts.extra.candle, option),
textList = _getToolTipData.textList,
offset = _getToolTipData.offset;
offset.y = _touches$.y;
opts.tooltip = {
textList: textList,
offset: offset,
option: option,
index: index
};
}
}
drawCharts.call(this, opts.type, opts, this.config, this.context);
}
if (this.opts.type === 'pie' || this.opts.type === 'ring' || this.opts.type === 'rose'||this.opts.type === 'funnel' ) {
var index = this.getCurrentDataIndex(e);
if (index > -1) {
var currentOffset = this.scrollOption.currentOffset;
var opts = assign({}, this.opts, {
_scrollDistance_: currentOffset,
animation: false
});
var seriesData = this.opts._series_[index];
var textList = [{
text: option.format ? option.format(seriesData) : seriesData.name + ': ' + seriesData.data,
color: seriesData.color
}];
var offset = {
x: _touches$.x,
y: _touches$.y
};
opts.tooltip = {
textList: textList,
offset: offset,
option: option,
index: index
};
}
drawCharts.call(this, opts.type, opts, this.config, this.context);
}
if (this.opts.type === 'map'||this.opts.type === 'word') {
var index = this.getCurrentDataIndex(e);
if (index > -1) {
var currentOffset = this.scrollOption.currentOffset;
var opts = assign({}, this.opts, {
_scrollDistance_: currentOffset,
animation: false
});
var seriesData = this.opts._series_[index];
var textList = [{
text: option.format ? option.format(seriesData) : seriesData.properties.name ,
color: seriesData.color
}];
var offset = {
x: _touches$.x,
y: _touches$.y
};
opts.tooltip = {
textList: textList,
offset: offset,
option: option,
index: index
};
}
opts.updateData = false;
drawCharts.call(this, opts.type, opts, this.config, this.context);
}
if (this.opts.type === 'radar') {
var index = this.getCurrentDataIndex(e);
if (index > -1) {
var currentOffset = this.scrollOption.currentOffset;
var opts = assign({}, this.opts, {
_scrollDistance_: currentOffset,
animation: false
});
var seriesData = getSeriesDataItem(this.opts.series, index);
if (seriesData.length !== 0) {
var textList = seriesData.map(function(item) {
return {
text: option.format ? option.format(item) : item.name + ': ' + item.data,
color: item.color
};
});
var offset = {
x: _touches$.x,
y: _touches$.y
};
opts.tooltip = {
textList: textList,
offset: offset,
option: option,
index: index
};
}
}
drawCharts.call(this, opts.type, opts, this.config, this.context);
}
};
Charts.prototype.translate = function(distance) {
this.scrollOption = {
currentOffset: distance,
startTouchX: distance,
distance: 0,
lastMoveTime: 0
};
let opts = assign({}, this.opts, {
_scrollDistance_: distance,
animation: false
});
drawCharts.call(this, this.opts.type, opts, this.config, this.context);
};
Charts.prototype.scrollStart = function(e) {
var touches = null;
if (e.changedTouches) {
touches = e.changedTouches[0];
} else {
touches = e.mp.changedTouches[0];
}
var _touches$ = getTouches(touches, this.opts, e);
if (touches && this.opts.enableScroll === true) {
this.scrollOption.startTouchX = _touches$.x;
}
};
Charts.prototype.scroll = function(e) {
if (this.scrollOption.lastMoveTime === 0) {
this.scrollOption.lastMoveTime = Date.now();
}
let Limit = this.opts.extra.touchMoveLimit || 20;
let currMoveTime = Date.now();
let duration = currMoveTime - this.scrollOption.lastMoveTime;
if (duration < Math.floor(1000 / Limit)) return;
this.scrollOption.lastMoveTime = currMoveTime;
var touches = null;
if (e.changedTouches) {
touches = e.changedTouches[0];
} else {
touches = e.mp.changedTouches[0];
}
if (touches && this.opts.enableScroll === true) {
var _touches$ = getTouches(touches, this.opts, e);
var _distance;
_distance = _touches$.x - this.scrollOption.startTouchX;
var currentOffset = this.scrollOption.currentOffset;
var validDistance = calValidDistance(currentOffset + _distance, this.opts.chartData, this.config, this.opts);
this.scrollOption.distance = _distance = validDistance - currentOffset;
var opts = assign({}, this.opts, {
_scrollDistance_: currentOffset + _distance,
animation: false
});
drawCharts.call(this, opts.type, opts, this.config, this.context);
return currentOffset + _distance;
}
};
Charts.prototype.scrollEnd = function(e) {
if (this.opts.enableScroll === true) {
var _scrollOption = this.scrollOption,
currentOffset = _scrollOption.currentOffset,
distance = _scrollOption.distance;
this.scrollOption.currentOffset = currentOffset + distance;
this.scrollOption.distance = 0;
}
};
if (typeof module === "object" && typeof module.exports === "object") {
module.exports = Charts;
//export default Charts;//建议使用nodejs的module导出方式如报错请使用export方式导出
}