UNPKG

@rdkmaster/jigsaw-labs

Version:

Jigsaw, the next generation component set for RDK

335 lines (286 loc) 12 kB
import {CommonUtils} from "../../core/utils/common-utils"; export class ChartIconPie { delimiter?: string = null; fill?: string[] | ((...any) => string) = ["#ff9900", "#fff4dd", "#ffd592"]; height?: number = null; radius?: number = 8; width?: number = null; } export class ChartIconDonut { delimiter?: string = null; fill?: string[] = ["#ff9900", "#fff4dd", "#ffd592"]; height?: number = null; innerRadius?: number = null; radius?: number = 8; width?: number = null; } export class ChartIconLine { delimiter?: string = ","; fill?: string = "#c6d9fd"; height?: number = 16; max?: number = null; min?: number = 0; stroke?: string = "#4d89f9"; strokeWidth?: number = 1; width?: number = 32; } export class ChartIconBar { delimiter?: string = ","; fill?: string[] = ["#4d89f9"]; height?: number = 16; max?: number = null; min?: number = 0; padding?: number = 0.1; width?: number = 32; } export class ChartIconCustomPieLegend { /** * orient只有'top'和'right'两个值 * - 如果是'right',图例的默认宽度是100,用户也可以自定义 * - 如果是'top',图例的高度是自动算出来的,所以height属性不需要配置,width也不用配置 */ orient: string; data: string[]; width: number; height: number; marginLeft: number; } export class ChartIconCustomPie { delimiter?: string = null; fill?: string[] | ((...any) => string) = ["#ff9900", "#fff4dd", "#ffd592"]; height?: number = null; radius?: number = 8; width?: number = null; legend?: ChartIconCustomPieLegend; series?: any; after?: Function; link?: Function | string; title: string[]; // 当没有title,默认使用legend.data context?: object; } export enum ChartType { pie, donut, line, bar, customPie } export class ChartIconFactory { public static create(selector: string, chartType: ChartType, options: ChartIconPie | ChartIconDonut | ChartIconLine | ChartIconBar | ChartIconCustomPie) { $(selector).peity(this._chartTypeMap.get(chartType), options); } private static _chartTypeMap = new Map([ [ChartType.pie, 'pie'], [ChartType.donut, 'donut'], [ChartType.line, 'line'], [ChartType.bar, 'bar'], [ChartType.customPie, 'customPie'] ]); public static registerCustomPie() { $.fn.peity.register('customPie', { fill: ['#ff9900', '#fff4dd', '#ffc66e'], radius: 8, legend: { orient: 'top', width: 100, height: 100, data: [] } }, function (opts) { if (!opts.delimiter) { let delimiter = this.$el.text().match(/[^0-9\.]/); opts.delimiter = delimiter ? delimiter[0] : "," } let values = $.map(this.values(), function (n) { return n > 0 ? n : 0 }); if (opts.delimiter == "/") { let v1 = values[0]; let v2 = values[1]; values = [v1, Math.max(0, v2 - v1)] } let i = 0; let length = values.length; let sum = 0; for (; i < length; i++) { sum += values[i] } if (!sum) { length = 2; sum = 1; values = [0, 1]; } let diameter = opts.radius * 2; let legendWidth = 0, legendHeight = 0, pieOffsetX = 0, pieOffsetY = 0; if (opts.legend.orient == 'top') { legendHeight = 14 * length + 5; pieOffsetY = legendHeight / 2; } else if (opts.legend.orient == 'right') { legendWidth = opts.legend.width + 20; pieOffsetX = legendWidth / 2; } let $svg = this.prepare( (opts.width || diameter) + legendWidth, (opts.height || diameter) + legendHeight ); let width = $svg.width(), height = $svg.height(), cx = width / 2 - pieOffsetX, cy = height / 2 + pieOffsetY; if (this.$el.text().replace(/\s+/g, '') === '') { // 没有数据 $svg.remove(); this.$svg = null; if (!this.$box) { this.$box = $('<div class="peity-no-data"></div>'); } this.$el.hide().after(this.$box); this.$box.empty().append(`<image width="${width}" height="${height}" src="${CommonUtils.noDataImageSrc}">`); return; } else { if (this.$box) { this.$box.remove(); this.$box = null; } } let radius = Math.min(cx, cy), innerRadius = opts.innerRadius; if (this.type == 'donut' && !innerRadius) { innerRadius = radius * 0.5 } let pi = Math.PI; let fill = this.fill(); let scale = this.scale = function (value, radius) { let radians = value / sum * pi * 2 - pi / 2; return [ radius * Math.cos(radians) + cx + '', radius * Math.sin(radians) + cy + '' ] }; let cumulative = 0; for (let i = 0; i < length; i++) { let value = values[i] , portion = value / sum , $node; if (portion == 0) continue; if (portion == 1) { if (innerRadius) { let x2 = cx - 0.01 , y1 = cy - radius , y2 = cy - innerRadius; $node = this.svgElement('path', { d: [ 'M', cx, y1, 'A', radius, radius, 0, 1, 1, x2, y1, 'L', x2, y2, 'A', innerRadius, innerRadius, 0, 1, 0, cx, y2 ].join(' ') }) } else { $node = this.svgElement('circle', { cx: cx, cy: cy, r: radius }) } } else { let cumulativePlusValue = cumulative + value; let d = ['M'].concat( scale(cumulative, radius), 'A', radius + '', radius + '', 0 + '', (portion > 0.5 ? 1 : 0) + '', 1 + '', scale(cumulativePlusValue, radius), 'L' ); if (innerRadius) { d = d.concat( scale(cumulativePlusValue, innerRadius), 'A', innerRadius, innerRadius, 0 + '', (portion > 0.5 ? 1 : 0) + '', 0 + '', scale(cumulative, innerRadius) ) } else { d.push(cx + '', cy + '') } cumulative += value; $node = this.svgElement('path', { d: d.join(" ") }) } $node.attr('fill', fill.call(this, value, i, values)); if (opts.title && !(opts.title instanceof Array)) { throw('customPie\'s title must be type of array'); } if (opts.legend && !(opts.legend.data instanceof Array)) { throw('customPie\'s legend data must be type of array'); } // 饼图链接 // 图形的title如果没有,使用图例的data let $title = this.svgElement('title', {}) .text(opts.title ? opts.title[i] : opts.legend.data[i]); let $link = this.svgElement('a', {'href': 'javascript:;'}) .append($title) .append($node); if (opts.link instanceof Function) { if (opts.context) { $link.bind('click', () => { opts.link.call(opts.context, opts.series, i); }); } else { $link.bind('click', () => { opts.link(opts.series, i); }); } } else if (typeof opts.link === 'string') { $link.attr('href', opts.link); } // 绘制图例 let $legendTitle = this.svgElement('title', {}) .text(opts.legend.data[i]); let $legend = this.svgElement('g', {x: '0', y: i * 10}); $legend.append($legendTitle); let legendPositionX = 0; if(opts.legend.orient == 'right'){ if(Number.isNaN(Number(opts.legend.marginLeft))){ opts.legend.marginLeft = 20; } legendPositionX = diameter + Number(opts.legend.marginLeft); } let $rect = this.svgElement('rect', { x: legendPositionX, y: 1 + i * 14, width: 10, height: 10, 'fill': fill.call(this, value, i, values) }); let $text = this.svgElement('text', { x: 12 + legendPositionX, y: 10 + i * 14, 'font-size': 12 }).text(opts.legend.data[i]); $legend.append($rect).append($text); // 等待text渲染 setTimeout(() => { const rangeWidth = (opts.legend.orient == 'right' ? opts.legend.width : width) - 12; if ($text.width() > rangeWidth) { // 加入省略号 let $ellipsis = this.svgElement('text', { x: width - 9, y: 9 + i * 14, 'font-size': 12 }).text('...'); let $ellipsisBg = this.svgElement('rect', { x: width - 10, y: i * 14 - 1, width: 10, height: 16, fill: '#fff' }); $legend.append($ellipsisBg).append($ellipsis); } }); $svg.append($link); $svg.append($legend); } } ) } }