UNPKG

@qiun/ucharts

Version:

【原生uCharts】跨平台图表库,全端全平台支持的图表库,开箱即用。支持PC、H5、微信小程序、支付宝小程序、百度小程序、头条小程序、飞书小程序、QQ小程序、360小程序、快手小程序、钉钉小程序、淘宝小程序、京东小程序、Vue、Taro等更多支持canvas的框架,体积小巧、调用简单方便、性能及体验极佳。

1,523 lines (1,461 loc) 304 kB
/* * uCharts (R) * 高性能跨平台图表库,支持H5、APP、小程序(微信/支付宝/百度/头条/QQ/360/快手)、Vue、Taro等支持canvas的框架平台 * Copyright (C) 2018-2022 QIUN (R) 秋云 https://www.ucharts.cn All rights reserved. * Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) * 复制使用请保留本段注释,感谢支持开源! * * uCharts (R) 官方网站 * https://www.uCharts.cn * * 开源地址: * https://gitee.com/uCharts/uCharts * * uni-app插件市场地址: * http://ext.dcloud.net.cn/plugin?id=271 * */ 'use strict'; var config = { version: 'v2.5.0-20230101', yAxisWidth: 15, xAxisHeight: 22, padding: [10, 10, 10, 10], rotate: false, fontSize: 13, fontColor: '#666666', dataPointShape: ['circle', 'circle', 'circle', 'circle'], color: ['#1890FF', '#91CB74', '#FAC858', '#EE6666', '#73C0DE', '#3CA272', '#FC8452', '#9A60B4', '#ea7ccc'], linearColor: ['#0EE2F8', '#2BDCA8', '#FA7D8D', '#EB88E2', '#2AE3A0', '#0EE2F8', '#EB88E2', '#6773E3', '#F78A85'], pieChartLinePadding: 15, pieChartTextPadding: 5, titleFontSize: 20, subtitleFontSize: 15, radarLabelTextMargin: 13, }; var assign = function(target, ...varArgs) { if (target == null) { throw new TypeError('[uCharts] Cannot convert undefined or null to object'); } if (!varArgs || varArgs.length <= 0) { return target; } // 深度合并对象 function deepAssign(obj1, obj2) { for (let key in obj2) { obj1[key] = obj1[key] && obj1[key].toString() === "[object Object]" ? deepAssign(obj1[key], obj2[key]) : obj1[key] = obj2[key]; } return obj1; } varArgs.forEach(val => { target = deepAssign(target, val); }); return target; }; 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] series数据需为Number格式'); } 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') { if (num == num + 1) { //修复数据值过大num++无效的bug by 向日葵 @xrk_jy break; } 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(self, distance, chartData, config, opts) { var dataChartAreaWidth = opts.width - opts.area[1] - opts.area[3]; var dataChartWidth = chartData.eachSpacing * (opts.chartData.xAxisData.xAxisPoints.length - 1); if(opts.type == 'mount' && opts.extra && opts.extra.mount && opts.extra.mount.widthRatio && opts.extra.mount.widthRatio > 1){ if(opts.extra.mount.widthRatio>2) opts.extra.mount.widthRatio = 2 dataChartWidth += (opts.extra.mount.widthRatio - 1)*chartData.eachSpacing; } var validDistance = distance; if (distance >= 0) { validDistance = 0; self.uevent.trigger('scrollLeft'); self.scrollOption.position = 'left' opts.xAxis.scrollPosition = 'left'; } else if (Math.abs(distance) >= dataChartWidth - dataChartAreaWidth) { validDistance = dataChartAreaWidth - dataChartWidth; self.uevent.trigger('scrollRight'); self.scrollOption.position = 'right' opts.xAxis.scrollPosition = 'right'; } else { self.scrollOption.position = distance opts.xAxis.scrollPosition = distance; } 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 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; } } function isNotMiddlePointX(points, i) { if (points[i - 1] && points[i + 1]) { return points[i].x >= Math.max(points[i - 1].x, points[i + 1].x) || points[i].x <= Math.min(points[i - 1].x, points[i + 1].x); } 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; } if (isNotMiddlePointX(points, i + 1)) { pBx = points[i + 1].x; } if (isNotMiddlePointX(points, i)) { pAx = points[i].x; } if (pAy >= Math.max(points[i].y, points[i + 1].y) || pAy <= Math.min(points[i].y, points[i + 1].y)) { pAy = points[i].y; } if (pBy >= Math.max(points[i].y, points[i + 1].y) || pBy <= Math.min(points[i].y, points[i + 1].y)) { pBy = points[i + 1].y; } if (pAx >= Math.max(points[i].x, points[i + 1].x) || pAx <= Math.min(points[i].x, points[i + 1].x)) { pAx = points[i].x; } if (pBx >= Math.max(points[i].x, points[i + 1].x) || pBx <= Math.min(points[i].x, points[i + 1].x)) { pBx = points[i + 1].x; } 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 fixPieSeries(series, opts, config){ let pieSeriesArr = []; if(series.length>0 && series[0].data.constructor.toString().indexOf('Array') > -1){ opts._pieSeries_ = series; let oldseries = series[0].data; for (var i = 0; i < oldseries.length; i++) { oldseries[i].formatter = series[0].formatter; oldseries[i].data = oldseries[i].value; pieSeriesArr.push(oldseries[i]); } opts.series = pieSeriesArr; }else{ pieSeriesArr = series; } return pieSeriesArr; } function fillSeries(series, opts, config) { var index = 0; for (var i = 0; i < series.length; i++) { let item = series[i]; if (!item.color) { item.color = config.color[index]; index = (index + 1) % config.color.length; } if (!item.linearIndex) { item.linearIndex = i; } if (!item.index) { item.index = 0; } 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': case 'bar': item.legendShape = "rect"; break; case 'area': case 'mount': item.legendShape = "triangle"; break; default: item.legendShape = "circle"; } } } return series; } function fillCustomColor(linearType, customColor, series, config) { var newcolor = customColor || []; if (linearType == 'custom' && newcolor.length == 0 ) { newcolor = config.linearColor; } if (linearType == 'custom' && newcolor.length < series.length) { let chazhi = series.length - newcolor.length; for (var i = 0; i < chazhi; i++) { newcolor.push(config.linearColor[(i + 1) % config.linearColor.length]); } } return newcolor; } 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, fontSize, context) { var width = 0; text = String(text); // #ifdef MP-ALIPAY || MP-BAIDU || APP-NVUE context = false; // #endif if (context !== false && context !== undefined && context.setFontSize && context.measureText) { context.setFontSize(fontSize); return context.measureText(text).width; } else { var text = text.split(''); 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 (/:/.test(item)) { width += 2.5; } 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.pix; x = (touches.pageY - e.currentTarget.offsetTop - (opts.height / opts.pix / 2) * (opts.pix - 1)) * opts.pix; } else { x = touches.clientX * opts.pix; y = (touches.pageY - e.currentTarget.offsetTop - (opts.height / opts.pix / 2) * (opts.pix - 1)) * opts.pix; } } else { if (opts.rotate) { y = opts.height - touches.x * opts.pix; x = touches.y * opts.pix; } else { x = touches.x * opts.pix; y = touches.y * opts.pix; } } return { x: x, y: y } } function getSeriesDataItem(series, index, group) { var data = []; var newSeries = []; var indexIsArr = index.constructor.toString().indexOf('Array') > -1; if(indexIsArr){ let tempSeries = filterSeries(series); for (var i = 0; i < group.length; i++) { newSeries.push(tempSeries[group[i]]); } }else{ newSeries = series; }; for (let i = 0; i < newSeries.length; i++) { let item = newSeries[i]; let tmpindex = -1; if(indexIsArr){ tmpindex = index[i]; }else{ tmpindex = index; } if (item.data[tmpindex] !== null && typeof item.data[tmpindex] !== '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.legendShape = item.legendShape; seriesItem.name = item.name; seriesItem.show = item.show; seriesItem.data = item.formatter ? item.formatter(item.data[tmpindex]) : item.data[tmpindex]; data.push(seriesItem); } } return data; } function getMaxTextListLength(list, fontSize, context) { var lengthList = list.map(function(item) { return measureText(item, fontSize, context); }); 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, opts, index, group, categories) { var option = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {}; var calPoints = opts.chartData.calPoints?opts.chartData.calPoints:[]; let points = {}; if(group.length > 0){ let filterPoints = []; for (let i = 0; i < group.length; i++) { filterPoints.push(calPoints[group[i]]) } points = filterPoints[0][index[0]]; }else{ for (let i = 0; i < calPoints.length; i++) { if(calPoints[i][index]){ points = calPoints[i][index]; break; } } }; var textList = seriesData.map(function(item) { let titleText = null; if (opts.categories && opts.categories.length>0) { titleText = categories[index]; }; return { text: option.formatter ? option.formatter(item, titleText, index, opts) : item.name + ': ' + item.data, color: item.color, legendShape: opts.extra.tooltip.legendShape == 'auto'? item.legendShape : opts.extra.tooltip.legendShape }; }); var offset = { x: Math.round(points.x), y: Math.round(points.y) }; return { textList: textList, offset: offset }; } function getMixToolTipData(seriesData, opts, index, categories) { var option = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; var points = opts.chartData.xAxisPoints[index] + opts.chartData.eachSpacing / 2; var textList = seriesData.map(function(item) { return { text: option.formatter ? option.formatter(item, categories[index], index, opts) : item.name + ': ' + item.data, color: item.color, disableLegend: item.disableLegend ? true : false, legendShape: opts.extra.tooltip.legendShape == 'auto'? item.legendShape : opts.extra.tooltip.legendShape }; }); textList = textList.filter(function(item) { if (item.disableLegend !== true) { return item; } }); var offset = { x: Math.round(points), y: 0 }; return { textList: textList, offset: offset }; } function getCandleToolTipData(series, seriesData, opts, index, categories, extra) { var option = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : {}; var calPoints = opts.chartData.calPoints; let upColor = extra.color.upFill; let downColor = extra.color.downFill; //颜色顺序为开盘,收盘,最低,最高 let color = [upColor, upColor, downColor, upColor]; var textList = []; seriesData.map(function(item) { if (index == 0) { if (item.data[1] - item.data[0] < 0) { color[1] = downColor; } else { color[1] = upColor; } } 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], legendShape: opts.extra.tooltip.legendShape == 'auto'? item.legendShape : opts.extra.tooltip.legendShape }; let text2 = { text: '收盘:' + item.data[1], color: color[1], legendShape: opts.extra.tooltip.legendShape == 'auto'? item.legendShape : opts.extra.tooltip.legendShape }; let text3 = { text: '最低:' + item.data[2], color: color[2], legendShape: opts.extra.tooltip.legendShape == 'auto'? item.legendShape : opts.extra.tooltip.legendShape }; let text4 = { text: '最高:' + item.data[3], color: color[3], legendShape: opts.extra.tooltip.legendShape == 'auto'? item.legendShape : opts.extra.tooltip.legendShape }; 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, calPoints, opts, config) { var offset = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0; var current={ index:-1, group:[] }; var spacing = opts.chartData.eachSpacing / 2; let xAxisPoints = []; if (calPoints && calPoints.length > 0) { if (!opts.categories) { spacing = 0; }else{ for (let i = 1; i < opts.chartData.xAxisPoints.length; i++) { xAxisPoints.push(opts.chartData.xAxisPoints[i] - spacing); } if ((opts.type == 'line' || opts.type == 'area') && opts.xAxis.boundaryGap == 'justify') { xAxisPoints = opts.chartData.xAxisPoints; } } if (isInExactChartArea(currentPoints, opts, config)) { if (!opts.categories) { let timePoints = Array(calPoints.length); for (let i = 0; i < calPoints.length; i++) { timePoints[i] = Array(calPoints[i].length) for (let j = 0; j < calPoints[i].length; j++) { timePoints[i][j] = (Math.abs(calPoints[i][j].x - currentPoints.x)); } }; let pointValue = Array(timePoints.length); let pointIndex = Array(timePoints.length); for (let i = 0; i < timePoints.length; i++) { pointValue[i] = Math.min.apply(null, timePoints[i]); pointIndex[i] = timePoints[i].indexOf(pointValue[i]); } let minValue = Math.min.apply(null, pointValue); current.index = []; for (let i = 0; i < pointValue.length; i++) { if(pointValue[i] == minValue){ current.group.push(i); current.index.push(pointIndex[i]); } }; }else{ xAxisPoints.forEach(function(item, index) { if (currentPoints.x + offset + spacing > item) { current.index = index; } }); } } } return current; } function findBarChartCurrentIndex(currentPoints, calPoints, opts, config) { var offset = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0; var current={ index:-1, group:[] }; var spacing = opts.chartData.eachSpacing / 2; let yAxisPoints = opts.chartData.yAxisPoints; if (calPoints && calPoints.length > 0) { if (isInExactChartArea(currentPoints, opts, config)) { yAxisPoints.forEach(function(item, index) { if (currentPoints.y + offset + spacing > item) { current.index = index; } }); } } return current; } function findLegendIndex(currentPoints, legendData, opts) { let currentIndex = -1; let gap = 0; 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 (area && currentPoints.x > area[0] - gap && currentPoints.x < area[2] + gap && currentPoints.y > area[1] - gap && currentPoints.y < area[3] + gap) { 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, opts.chartData.mapData.mercator)) { currentIndex = i; break; } } return currentIndex; } function findRoseChartCurrentIndex(currentPoints, pieData, opts) { var currentIndex = -1; var series = getRoseDataPoints(opts._series_, opts.extra.rose.type, pieData.radius, pieData.radius); if (pieData && pieData.center && isInExactPieChartArea(currentPoints, pieData.center, pieData.radius)) { var angle = Math.atan2(pieData.center.y - currentPoints.y, currentPoints.x - pieData.center.x); angle = -angle; if(opts.extra.rose && opts.extra.rose.offsetAngle){ angle = angle - opts.extra.rose.offsetAngle * Math.PI / 180; } for (var i = 0, len = series.length; i < len; i++) { if (isInAngleRange(angle, series[i]._start_, series[i]._start_ + series[i]._rose_proportion_ * 2 * Math.PI)) { currentIndex = i; break; } } } return currentIndex; } function findPieChartCurrentIndex(currentPoints, pieData, opts) { var currentIndex = -1; var series = getPieDataPoints(pieData.series); if (pieData && pieData.center && isInExactPieChartArea(currentPoints, pieData.center, pieData.radius)) { var angle = Math.atan2(pieData.center.y - currentPoints.y, currentPoints.x - pieData.center.x); angle = -angle; if(opts.extra.pie && opts.extra.pie.offsetAngle){ angle = angle - opts.extra.pie.offsetAngle * Math.PI / 180; } if(opts.extra.ring && opts.extra.ring.offsetAngle){ angle = angle - opts.extra.ring.offsetAngle * Math.PI / 180; } for (var i = 0, len = series.length; i < len; i++) { if (isInAngleRange(angle, series[i]._start_, series[i]._start_ + series[i]._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,eachSeries) { var newPoints = []; var items = []; points.forEach(function(item, index) { if(eachSeries.connectNulls){ if (item !== null) { items.push(item); } }else{ 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, context) { 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 * opts.pix; let margin = opts.legend.margin * opts.pix; let fontSize = opts.legend.fontSize ? opts.legend.fontSize * opts.pix : config.fontSize; let shapeWidth = 15 * opts.pix; let shapeRight = 5 * opts.pix; let lineHeight = Math.max(opts.legend.lineHeight * opts.pix, 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]; const legendText = item.legendText ? item.legendText : item.name; let itemWidth = shapeWidth + shapeRight + measureText(legendText || 'undefined', fontSize, context) + opts.legend.itemGap * opts.pix; if (widthCount + itemWidth > opts.width - opts.area[1] - opts.area[3]) { legendList.push(currentRow); widthCountArr.push(widthCount - opts.legend.itemGap * opts.pix); widthCount = itemWidth; currentRow = [item]; } else { widthCount += itemWidth; currentRow.push(item); } } if (currentRow.length) { legendList.push(currentRow); widthCountArr.push(widthCount - opts.legend.itemGap * opts.pix); legendData.widthArr = widthCountArr; let legendWidth = Math.max.apply(null, widthCountArr); switch (opts.legend.float) { case 'left': legendData.area.start.x = opts.area[3]; legendData.area.end.x = opts.area[3] + legendWidth + 2 * padding; break; case 'right': legendData.area.start.x = opts.width - opts.area[1] - legendWidth - 2 * padding; legendData.area.end.x = opts.width - opts.area[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.area[0] - opts.area[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.area[0] + margin; legendData.area.end.y = opts.area[0] + margin + legendData.area.height; break; case 'bottom': legendData.area.start.y = opts.height - opts.area[2] - margin - legendData.area.height; legendData.area.end.y = opts.height - opts.area[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, context) + opts.legend.itemGap * opts.pix; 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 * opts.pix + 2 * padding; legendData.area.wholeWidth = legendData.area.width + padding; } } switch (opts.legend.position) { case 'top': legendData.area.start.y = opts.area[0] + margin; legendData.area.end.y = opts.area[0] + margin + legendData.area.height; break; case 'bottom': legendData.area.start.y = opts.height - opts.area[2] - legendData.area.height - margin; legendData.area.end.y = opts.height - opts.area[2] - margin; break; case 'left': legendData.area.start.x = opts.area[3]; legendData.area.end.x = opts.area[3] + legendData.area.width; break; case 'right': legendData.area.start.x = opts.width - opts.area[1] - legendData.area.width; legendData.area.end.x = opts.width - opts.area[1]; break; } chartData.legendData = legendData; return legendData; } function calCategoriesData(categories, opts, config, eachSpacing, context) { var result = { angle: 0, xAxisHeight: opts.xAxis.lineHeight * opts.pix + opts.xAxis.marginTop * opts.pix }; var fontSize = opts.xAxis.fontSize * opts.pix; var categoriesTextLenth = categories.map(function(item,index) { var xitem = opts.xAxis.formatter ? opts.xAxis.formatter(item,index,opts) : item; return measureText(String(xitem), fontSize, context); }); var maxTextLength = Math.max.apply(this, categoriesTextLenth); if (opts.xAxis.rotateLabel == true) { result.angle = opts.xAxis.rotateAngle * Math.PI / 180; let tempHeight = opts.xAxis.marginTop * opts.pix * 2 + Math.abs(maxTextLength * Math.sin(result.angle)) tempHeight = tempHeight < fontSize + opts.xAxis.marginTop * opts.pix * 2 ? tempHeight + opts.xAxis.marginTop * opts.pix * 2 : tempHeight; result.xAxisHeight = tempHeight; } if (opts.enableScroll && opts.xAxis.scrollShow) { result.xAxisHeight += 6 * opts.pix; } if (opts.xAxis.disabled){ result.xAxisHeight = 0; } return result; } function getXAxisTextList(series, opts, config, stack) { var index = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : -1; 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 (item.constructor.toString().indexOf('Array') > -1) { return item !== null; } else { return item.value !== null; } } else { return item !== null; } }); data.map(function(item) { if (typeof item === 'object') { if (item.constructor.toString().indexOf('Array') > -1) { if (opts.type == 'candle') { item.map(function(subitem) { sorted.push(subitem); }) } else { sorted.push(item[0]); } } 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); } //为了兼容v1.9.0之前的项目 if (index > -1) { if (typeof opts.xAxis.data[index].min === 'number') { minData = Math.min(opts.xAxis.data[index].min, minData); } if (typeof opts.xAxis.data[index].max === 'number') { maxData = Math.max(opts.xAxis.data[index].max, maxData); } } else { if (typeof opts.xAxis.min === 'number') { minData = Math.min(opts.xAxis.min, minData); } if (typeof opts.xAxis.max === 'number') { maxData = Math.max(opts.xAxis.max, maxData); } } if (minData === maxData) { var rangeSpan = maxData || 10; maxData += rangeSpan; } //var dataRange = getDataRange(minData, maxData); var minRange = minData; var maxRange = maxData; var range = []; var eachRange = (maxRange - minRange) / opts.xAxis.splitNumber; for (var i = 0; i <= opts.xAxis.splitNumber; i++) { range.push(minRange + eachRange * i); } return range; } function calXAxisData(series, opts, config, context) { //堆叠图重算Y轴 var columnstyle = assign({}, { type: "" }, opts.extra.bar); var result = { angle: 0, xAxisHeight: opts.xAxis.lineHeight * opts.pix + opts.xAxis.marginTop * opts.pix }; result.ranges = getXAxisTextList(series, opts, config, columnstyle.type); result.rangesFormat = result.ranges.map(function(item) { //item = opts.xAxis.formatter ? opts.xAxis.formatter(item) : util.toFixed(item, 2); item = util.toFixed(item, 2); return item; }); var xAxisScaleValues = result.ranges.map(function(item) { // 如果刻度值是浮点数,则保留两位小数 item = util.toFixed(item, 2); // 若有自定义格式则调用自定义的格式化函数 //item = opts.xAxis.formatter ? opts.xAxis.formatter(Number(item)) : item; return item; }); result = Object.assign(result, getXAxisPoints(xAxisScaleValues, opts, config)); // 计算X轴刻度的属性譬如每个刻度的间隔,刻度的起始点\结束点以及总长 var eachSpacing = result.eachSpacing; var textLength = xAxisScaleValues.map(function(item) { return measureText(item, opts.xAxis.fontSize * opts.pix, context); }); if (opts.xAxis.disabled === true) { result.xAxisHeight = 0; } 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.legendShape = each.legendShape; listItem.pointShape = each.pointShape; listItem.data = []; each.data.forEach(function(item, index) { let tmp = {}; tmp.angle = angleList[index]; tmp.proportion = item / maxData; tmp.value = item; 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, option, eachSpacing) { var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1; for (let i = 0; i < series.length; i++) { if(option.type == 'funnel'){ series[i].radius = series[i].data / series[0].data * radius * process; }else{ series[i].radius = (eachSpacing * (series.length - i)) / (eachSpacing * series.length) * radius * process; } series[i]._proportion_ = series[i].data / series[0].data; } // if(option.type !== 'pyramid'){ // series.reverse(); // } return series; } 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) { item._proportion_ = 1 / series.length * process; item._rose_proportion_ = 1 / series.length * process; } else { item._proportion_ = item.data / count * process; if(type == 'area'){ item._rose_proportion_ = 1 / series.length * process; }else{ item._rose_proportion_ = item.data / count * process; } } item._radius_ = minRadius + radiusLength * ((item.data - minData) / (maxData - minData)) || radius; } 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 == 'circle') { totalAngle = 2; } else { if(arcbarOption.direction == 'ccw'){ if (arcbarOption.startAngle < arcbarOption.endAngle) { totalAngle = 2 + arcbarOption.startAngle - arcbarOption.endAngle; } else { totalAngle = arcbarOption.startAngle - arcbarOption.endAngle; } }else{ if (arcbarOption.endAngle < arcbarOption.startAngle) { totalAngle = 2 + arcbarOption.endAngle - arcbarOption.startAngle; } else { totalAngle = arcbarOption.startAngle - arcbarOption.endAngle; } } } item._proportion_ = totalAngle * item.data * process + arcbarOption.startAngle; if(arcbarOption.direction == 'ccw'){ item._proportion_ = arcbarOption.startAngle - totalAngle * item.data * process ; } if (item._proportion_ >= 2) { item._proportion_ = item._proportion_ % 2; } } return series; } function getGaugeArcbarDataPoints(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 == 'circle') { totalAngle = 2; } else { if (arcbarOption.endAngle < arcbarOption.startAngle) { totalAngle = 2 + arcbarOption.endAngle - arcbarOption.startAngle; } else { totalAngle = arcbarOption.startAngle - arcbarOption.endAngle; } } 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; if (endAngle < startAngle) { totalAngle = 2 + endAngle - startAngle; } else { totalAngle = startAngle - endAngle; } 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; if (gaugeOption.endAngle < gaugeOption.startAngle) { totalAngle = 2 + gaugeOption.endAngle - gaugeOption.startAngle; } else { totalAngle = gaugeOption.startAngle - gaugeOption.endAngle; } 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, config, context, opts) { series = getPieDataPoints(series); let maxLength = 0; for (let i = 0; i < series.length; i++) { let item = series[i]; let text = item.formatter ? item.formatter(+item._proportion_.toFixed(2)) : util.toFixed(item._proportion_ * 100) + '%'; maxLength = Math.max(maxLength, measureText(text, item.textSize * opts.pix || config.fontSize, context)); } return maxLength; } function fixColumeData(points, eachSpacing, columnLen, index, config, opts) { return points.map(function(item) { if (item === null) { return null; } var seriesGap = 0; var categoryGap = 0; if (opts.type == 'mix') { seriesGap = opts.extra.mix.column.seriesGap * opts.pix || 0; categoryGap = opts.extra.mix.column.categoryGap * opts.pix || 0; } else { seriesGap = opts.extra.column.seriesGap * opts.pix || 0; categoryGap = opts.extra.column.categoryGap * opts.pix || 0; } seriesGap = Math.min(seriesGap, eachSpacing / columnLen) categoryGap = Math.min(categoryGap, eachSpacing / columnLen) item.width = Math.ceil((eachSpacing - 2 * categoryGap - seriesGap * (columnLen - 1)) / columnLen); if (opts.extra.mix && opts.extra.mix.column.width && +opts.extra.mix.column.width > 0) { item.width = Math.min(item.width, +opts.extra.mix.column.width * opts.pix); } if (opts.extra.column && opts.extra.column.width && +opts.extra.column.width > 0) { item.width = Math.min(item.width, +opts.extra.column.width * opts.pix); } if (item.width <= 0) { item.width = 1; } item.x += (index + 0.5 - columnLen / 2) * (item.width + seriesGap); return item; }); } function fixBarData(points, eachSpacing, columnLen, index, config, opts) { return points.map(function(item) { if (item === null) { return null; } var seriesGap = 0; var categoryGap = 0; seriesGap = opts.extra.bar.seriesGap * opts.pix || 0; categoryGap = opts.extra.bar.categoryGap * opts.pix || 0; seriesGap = Math.min(seriesGap, eachSpacing / columnLen) categoryGap = Math.min(categoryGap, eachSpacing / columnLen) item.width = Math.ceil((eachSpacing - 2 * categoryGap - seriesGap * (columnLen - 1)) / columnLen); if (opts.extra.bar && opts.extra.bar.width && +opts.extra.bar.width > 0) { item.width = Math.min(item.width, +opts.extra.b