UNPKG

@qn-pandora/pandora-visualization

Version:

Pandora 通用可视化库

368 lines (367 loc) 16.2 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.HoneycombChart = void 0; var d3 = __importStar(require("d3")); var d3Hexbin = __importStar(require("d3-hexbin")); var lodash_1 = require("lodash"); var util_1 = require("./util"); var HoneycombChart = /** @class */ (function () { function HoneycombChart(dom, addTooltip, removeTooltip, onMouseDown) { var _this = this; this._svg = null; this._bodyG = null; // option this._margin = { top: 0, left: 0, right: 0, bottom: 0 }; this.honeycomb = { maxRadius: 100, minRadius: 10, distance: 0.06 // 百分数 }; this.group = { honeycombNum: 6, lineGroupNum: 3, titleHeight: 20, showTitle: true }; this.series = []; this.formatPrecent = function (value, base) { if (lodash_1.isString(value)) { return (base * parseInt(value.slice(0, -1))) / 100; } return value; }; this.margin = function () { var _a = _this._margin, top = _a.top, left = _a.left, right = _a.right, bottom = _a.bottom; return { top: _this.formatPrecent(top, _this._height), left: _this.formatPrecent(left, _this._width), right: _this.formatPrecent(right, _this._width), bottom: _this.formatPrecent(bottom, _this._height) }; }; this.renderSvg = function () { _this._svg = d3.select(_this._dom).select('svg'); if (_this._svg.empty()) { _this._svg = d3 .select(_this._dom) .append('svg') .attr('width', _this._width) .attr('height', _this._height); } else { _this._svg.attr('width', _this._width).attr('height', _this._height); } _this._svg .append('defs') .append('style') .text('.active-group path{opacity: 0.5;}.active-group .active {opacity: 1;stroke: #000000;stroke-width: 3px;}'); }; this.partX = function () { var radius = _this.radius(); return (Math.sqrt(3) * radius) / 2; }; this.getBodyXY = function () { var _a = _this.margin(), left = _a.left, top = _a.top; var x = left; var y = top; if (_this.series.length === 1) { var _b = _this.pointLayout(), rows = _b.rows, cols = _b.cols; var radius = _this.radius(); var partX = _this.partX(); // 一行有多少个蜂窝 var relhoneyNumOnLine = cols; // const relhoneyNumOnLine = // count > this.group.honeycombNum ? this.group.honeycombNum : count var width = partX * (relhoneyNumOnLine * 2 + 1); x = (_this.quadrantWidth() - width) / 2 + left; var honeyHeight = 1.88 * radius; var height = honeyHeight + (honeyHeight / 4) * 3 * (rows - 1); y = (_this.quadrantHeight() - height) / 2; } return { x: x, y: y }; }; this.getRealSvgHeight = function () { var radius = _this.radius(); var honeyHeight = 1.88 * radius; var _a = _this.margin(), top = _a.top, bottom = _a.bottom; var rows = _this.pointLayout().rows; // 只有一个分组的时候 if (_this.series.length === 1) { var height = honeyHeight + (honeyHeight / 4) * 3 * (rows - 1); if (height > _this.quadrantHeight()) { return top + bottom + height; } return _this._height; } var newSeries = lodash_1.clone(_this.series); var result = []; while (newSeries.length > 0) { var deleteSeries = newSeries.splice(0, _this.group.lineGroupNum); var maxPoints = lodash_1.max(deleteSeries.map(function (line) { return line.data.length; })); var rows_1 = Math.ceil(maxPoints / _this.group.honeycombNum); var height = honeyHeight + (honeyHeight / 4) * 3 * (rows_1 - 1); result.push(height + _this.realTitleHeight() + 2); } return result.reduce(function (sum, b) { return sum + b; }, 0); }; this.renderBody = function () { var _a; var _b = _this.getBodyXY(), x = _b.x, y = _b.y; if (!_this._bodyG) { _this._bodyG = _this._svg.append('g') .attr('class', 'body') .attr('transform', "translate(" + x + ", " + y + ")") .attr('clip-path', 'url(#body-clip)'); } else { // 更新需要清除旧的图形 _this._bodyG.selectAll('g').remove(); _this._bodyG .attr('transform', "translate(" + x + ", " + y + ")") .attr('clip-path', 'url(#body-clip)'); } if (_this.series.length === 0) { return; } var height = _this.getRealSvgHeight(); if (height > _this._height) { (_a = _this._svg) === null || _a === void 0 ? void 0 : _a.attr('height', height); } _this.renderGroup(); }; // 根据数据获取每个分组宽度和高度 this.groups = function () { // 分组数目 var groups = _this.series.length; // 行数 var lines = 1; if (groups > _this.group.lineGroupNum) { lines = Math.ceil(groups / _this.group.lineGroupNum); } // 计算每一行高度 var lineHeight = lines === 1 ? _this.quadrantHeight() : Math.floor(_this.quadrantHeight() / lines); // 列数 var columns = groups; if (lines > 1) { columns = _this.group.lineGroupNum; } // 计算每一组的宽度 var columnWidth = Math.floor(_this.quadrantWidth() / columns); return { lineHeight: lineHeight, columnWidth: columnWidth, lines: lines, columns: columns }; }; this.pointLayout = function () { var maxPoints = lodash_1.max(_this.series.map(function (line) { return line.data.length; })); if (_this.series.length === 1) { return util_1.getColRows(_this.quadrantWidth(), _this.quadrantHeight(), maxPoints); } return { cols: _this.group.honeycombNum, rows: Math.ceil(maxPoints / _this.group.honeycombNum) }; }; // 根据数据获取半径 this.radius = function () { var _a = _this.groups(), lineHeight = _a.lineHeight, columnWidth = _a.columnWidth; // 点数量最后的分组对应的点数 var maxPoints = lodash_1.max(_this.series.map(function (line) { return line.data.length; })); if (!maxPoints) { return _this.honeycomb.maxRadius; } var _b = _this.pointLayout(), rows = _b.rows, cols = _b.cols; // 根据行高计算六边形高度 var hexgonHeight = Math.floor((lineHeight - _this.realTitleHeight() - 2) / rows); // 根据列宽计算六边形宽度 var hexgonWidth = Math.floor(columnWidth / (maxPoints > cols ? cols : maxPoints)); // 计算每一个六边形直径 var hexgonDiameter = Math.min(hexgonHeight, hexgonWidth); var HoneycombRadius = Math.floor(hexgonDiameter / 2 / Math.sign(Math.PI / 3)); return HoneycombRadius > _this.honeycomb.maxRadius ? _this.honeycomb.maxRadius : HoneycombRadius < _this.honeycomb.minRadius ? _this.honeycomb.minRadius : HoneycombRadius; }; // 根据数据及半径求每一行的实际高度 this.lineHeight = function () { var _a = _this.groups(), lines = _a.lines, columns = _a.columns; var radius = _this.radius(); return lodash_1.range(lines).map(function (_, line) { var maxHeight = lodash_1.max(lodash_1.range(columns).map(function (_, column) { var points = (lodash_1.get(_this.series, [ line * _this.group.lineGroupNum + column, 'data' ]) || []).length; var pointLines = Math.ceil(points / _this.group.honeycombNum); return ((radius * 3) / 2) * (pointLines + 0.5); })) || 0; return maxHeight + _this.realTitleHeight(); }); }; this.renderGroup = function () { var _a = _this.groups(), columnWidth = _a.columnWidth, columns = _a.columns; var lineHeights = _this.lineHeight(); var groupG = _this._bodyG.selectAll('.group') .data(_this.series) .enter() .append('g') .attr('class', function (_, index) { return "group-" + index; }) .attr('transform', function (_, index) { var row = Math.floor(index / columns); var column = index % columns; return "translate(" + columnWidth * column + ", " + lodash_1.sum(lineHeights.slice(0, row)) + ")"; }); if (_this.group.showTitle) { groupG .append('foreignObject') .attr('x', '0') .attr('y', '0') .attr('width', columnWidth + "px") .attr('height', _this.group.titleHeight + "px") .attr('transform', function () { //距离底部有一点的边距 return "translate(0, -20)"; }) .append('xhtml:div') .text(function (data) { return "" + (data.name + " (" + data.data.length + ")"); }) .attr('style', 'color: #666;overflow: hidden;text-overflow: ellipsis;white-space:nowrap;'); } _this.renderHoneycomb(groupG); }; this.renderHoneycomb = function (groupG) { var radius = _this.radius(); var hexbin = d3Hexbin.hexbin().radius(radius); var partX = _this.partX(); var partY = radius * 1.5; var cols = _this.pointLayout().cols; groupG.each(function (group, index) { var data = group.data; var points = data.map(function (_, index) { // 计算当前节点处于第几行 var lineNum = Math.floor(index / cols); // 计算当前节点处于第几列 var columnNum = Math.floor(index % cols); // 判断当前节点是否为奇数行 var isEventLine = lineNum % 2 === 0; var x = isEventLine ? partX * (columnNum * 2 + 1) : partX * (columnNum + 1) * 2; var y = 1.5 * radius + partY * lineNum; return [x, y]; }); var that = _this; d3.select(groupG.nodes()[index]) .selectAll(".path-" + index) .data(hexbin(points)) .enter() .append('path') .attr('class', ".path-" + index) .attr('transform', function (d) { // 2, 图形与title 之前有一点的间距 return "translate(" + d.x + ", " + (d.y - radius / 2 + 2) + ")"; }) .attr('fill', function (_, index) { return data[index].color; }) .attr('d', hexbin.hexagon(radius - radius * _this.honeycomb.distance)) // 由于事件通过this传递dom,不能使用箭头函数 .on('mousedown', function (_, index) { var d = data[index]; that.mousedown(this, d); }) .on('mousemove', function (_, index) { var d = data[index]; that.mousemove(this, d); }) .on('mouseup', function () { that.mouseup(this); }) .on('mouseout', function () { that.mouseout(this); }); }); }; this.mousedown = function (el, data) { var _a; (_a = _this.onMouseDown) === null || _a === void 0 ? void 0 : _a.call(_this, data); _this.mousemove(el, data); }; this.mouseup = function (el) { _this.mouseout(el); }; this.mousemove = function (el, data) { d3.select(el).attr('class', 'active'); d3.select(el.parentElement).attr('class', 'active-group'); if (_this.addTooltip) { _this.addTooltip({ x: d3.event.pageX + 10, y: d3.event.pageY + 10 }, data); } }; this.mouseout = function (el) { d3.select(el).attr('class', ''); d3.select(el.parentElement).attr('class', ''); if (_this.removeTooltip) { _this.removeTooltip(); } }; this._dom = dom; var clientWidth = dom.clientWidth, clientHeight = dom.clientHeight; this._width = clientWidth; this._height = clientHeight; this.addTooltip = addTooltip; this.removeTooltip = removeTooltip; this.onMouseDown = onMouseDown; } HoneycombChart.prototype.quadrantHeight = function () { if (this._height === 0) return 0; var _a = this.margin(), top = _a.top, bottom = _a.bottom; return this._height - top - bottom; }; HoneycombChart.prototype.quadrantWidth = function () { if (this._width === 0) return 0; var _a = this.margin(), left = _a.left, right = _a.right; return this._width - left - right; }; HoneycombChart.prototype.realTitleHeight = function () { return this.group.showTitle ? this.group.titleHeight : 0; }; HoneycombChart.prototype.render = function (option) { var margin = option.margin, series = option.series, honeycomb = option.honeycomb, groups = option.groups; this._margin = margin; this.honeycomb = honeycomb; this.group = groups; this.series = series; this.renderSvg(); this.renderBody(); }; HoneycombChart.prototype.dispose = function () { if (this._svg) { this._svg.remove(); } }; return HoneycombChart; }()); exports.HoneycombChart = HoneycombChart;