UNPKG

datavizstocklib

Version:

测试一下呗

556 lines (509 loc) 19.6 kB
import * as d3 from "d3"; import $ from "jquery"; import infoPanel from "./infoPanel"; import vizlog from "./vizlog"; /** * H 水平 开发ing */ let config = { name: "cashChart", svg: {}, screenWidth: document.documentElement.clientWidth, //屏幕宽度 screenHeight: document.documentElement.clientHeight, //屏幕高度 /** * 当前页面宽度 */ comWidth: "", comHeight: "", //当前页面高度 widthHeightRate: 0.75, // 宽高度比率 /** * 数据项,二维数组 0期初 8期末 */ resData: [], unitnumber: 100000000, dataMaxMin: [], xScale: {}, fillopacity: 1, margin: [60, 60, 20, 20], //上,右,下,左 colors: [["#D64D4A", "#EA625D"], ["#F08860", "#FAA573"], ["#49ADA2", "#3BA1A6"]], jbs: [], timer: {}, click: {}, /** * 控件上下间距 */ baseH: 7, /** * 控件左右间距 */ ws: 3, /** * 控件高度 */ comH: 100, ylength: 0, //y 轴宽度 referenceAxis: { x: 0, y: 0 }, //参考 X坐标 referValue: 0,//参考 Value 内部使用 axisfontsize: [13, 13],//坐标轴字号 先x,再y fontsize: 14, //字号 fontfamilyBase: "SourceHanSansK-Regular-plotly,Helvetica Neue,PingFang SC,Microsoft YaHei,Helvetica,Hiragino Sans GB,Arial,sans-serif", fontfamilyPost: '', //字体 Bold Medium rectTextColor: "#FFFFFF",//文字颜色 textanchor: 'center', el: "el", ticks: 5,//刻度个数 minshowtext: 30,//最小展示文字宽度 minRectWidth: 2, //色块最小宽度 shadow: true, linecolor: "#B5B5E6", column: 8, shadowid: "" } export class cashH { constructor() { let newconfig = JSON.parse(JSON.stringify(config)); Object.assign(this, newconfig) } init(obj) { Object.assign(this, obj); this.unitChange(); this.svg = d3.select("#" + this.el).append("svg"); this.show(); this.shadowid = infoPanel.randomWord(8); } show() { if (this.dataMaxMin.length == 0) { this.dataMaxMin = this.maxNumberOfData(); } else { this.dataMaxMin['max'] = this.dataMaxMin['max'] / this.unitnumber; this.dataMaxMin['min'] = this.dataMaxMin['min'] / this.unitnumber; } this.createAxis(); vizlog.i("创建期初现金"); this.start(0); this.income(1); // 经营添加 this.cost(2); // 经营减少 this.income(3); // 投资增加 this.cost(4); // this.income(5); this.cost(6); vizlog.i("创建期末 "); this.end(7); infoPanel.initInfoPanel(this.svg, this.el, this.fontfamilyBase); infoPanel.black(); } /** * 坐标系 */ createAxis() { let that = this; this.comWidth = $("#" + this.el).width(); this.comHeight = $("#" + this.el).height() //this.comWidth * this.widthHeightRate; this.svg.attr("width", this.comWidth).attr("height", this.comHeight); //this.svg.attr("width", this.comWidth).attr("height", this.comHeight + this.margin[2]); const chartWidth = this.comWidth - this.margin[1] - this.margin[3], chartHeight = this.comHeight - this.margin[0] - this.margin[2]; let chart = this.svg .append("g").attr("id", "idg") .attr("transform", `translate(${this.margin[1]}, ${this.margin[0]})`); //创建y轴比例尺 this.yScale = d3 .scaleLinear() .range([chartHeight, 0]) // 界面像素范围 .domain([-0.25, this.column]); //数据范围 // 生成y轴 //chart.append("g").attr("id", "yyyy" + this.el).call(d3.axisLeft(this.yScale)); //this.dataMaxMin["max"] = 1100;//测试使用 //创建x轴比例尺 this.xScale = d3 .scaleLinear() .range([0, chartWidth]) // 界面像素范围 .domain([this.dataMaxMin["min"], this.dataMaxMin["max"]]); //数据范围 //生成x轴 chart .append("g") .attr("id", "xxxx" + this.el) .attr('transform', 'translate(0,' + this.yScale(-0.25) + ' )') .style('font-size', this.axisfontsize[1]) .style("font-family", this.fontfamilyBase) .call(d3.axisBottom(this.xScale).ticks(that.ticks)); // 删除Y轴上的多余线 let linenode = d3.select("#xxxx" + this.el).selectAll("line"); linenode.each(function (d, i) { d3.select(this).remove(); }) let m = d3.select("#xxxx" + this.el).select("path").attr("d"); m = 'M0,0V0.5H' + m.substring(m.indexOf("H") + 1, m.lastIndexOf("V")); d3.select("#xxxx" + this.el).select("path").attr("d", m); //绘制平行的坐标系轴线,其实是将刻度线反向延长 let t = d3.axisBottom() .scale(this.xScale) .tickSize(-chartHeight, 0, 0) .tickFormat("") .ticks(that.ticks); chart.append("g") .attr("class", "grid") .attr("id", "yxx" + this.el) .attr('transform', `translate(0, ${chartHeight})`) .call(t) .style("color", "#DCDCDC"); d3.select("#yxx" + this.el) .select("path") .remove(); vizlog.i("原点位置:" + this.xScale(0) + " : " + this.yScale(0)); this.comH = this.yScale(0) - this.yScale(1) - this.baseH; // y轴加粗 let x1 = this.xScale(0) + this.margin[1]; let y1 = this.margin[0]; let x2 = x1; let y2 = chartHeight + this.margin[0]; infoPanel.addLineForDataviz(this.svg, x1, y1, x2, y2, 1, "black"); } /** * 期初情况 */ start(col) { let that = this; let t = that.resData[0]; let x = that.xScale(0) + that.margin[1]; this.column--; let y = that.yScale(this.column) - that.comH; let w = 0; this.column--; for (var i = 0; i < t.length; i++) { let p = t[i]; //当 y 为负时,矩形块顶到上边距了 w = that.xScale(p.value) - that.xScale(0); if (w < 0) { w = Math.abs(w); x = x - w; } w = (w < that.minRectWidth) ? that.minRectWidth : w; var _name = infoPanel.randomWord(8); infoPanel.gradient(that.svg, _name, that.colors[0][0], that.colors[0][1]); //向下画线 infoPanel.addLineForDataviz(this.svg, x + w + 1, y + that.margin[0] + that.comH - 1, x + w + 1, this.yScale(this.column) + that.margin[0] - that.comH + 1, 2, that.linecolor); that.addRect(p.name, p.value, x, y, w + 2, that.comH, _name, col); x = x + w; that.referValue = p.value; } that.referenceAxis.x = x; if (w < that.minshowtext) { let text = t[0].name; let ty = that.yScale(7) + that.margin[0] - that.comH / 2; that.renderText(infoPanel.randomWord(8), x + 10, ty, 30, that.comH, text, "start", "middle", that.fontsize, this.fontfamilyPost, "#DF5A55"); } that.referenceAxis.y = that.yScale(this.column) - that.comH;; } //增加的 income(index) { let that = this; let t = that.resData[index]; if (t.length == 0) { return; } let x = that.referenceAxis.x; let color = d3.scaleLinear() .domain([that.dataMaxMin.min, that.dataMaxMin.max]) .range(that.colors[1]); that.column--; for (var i = 0; i < t.length; i++) { let p = t[i]; let w = that.calNumberWidth(p.value); if (w < that.minRectWidth) { w = that.minRectWidth; } var start = color(that.referValue); that.referValue = parseFloat(p.value) + parseFloat(that.referValue); var end = color(that.referValue); var _name = infoPanel.randomWord(8); infoPanel.gradient(that.svg, _name, start, end); if (i == (t.length - 1)) { //向下画线 let y = that.referenceAxis.y; let y1 = y + that.margin[0] + that.comH - 2, y2 = this.yScale(that.column) + that.margin[0] - that.comH + 4, c = that.linecolor;//color(that.referValue); infoPanel.addLineForDataviz(this.svg, x + w - 1, y1, x + w - 1, y2, 2, c); } that.addRect(p.name, p.value, x, that.referenceAxis.y, w, that.comH, _name, index); x = x + w + that.ws; } that.referenceAxis.x = x - that.ws; that.referenceAxis.y = that.yScale(this.column) - that.comH; } /** * 减少的 */ cost(index) { let that = this; let t = that.resData[index]; if (t.length == 0) { return; } var x = that.referenceAxis.x; let y = that.referenceAxis.y; var color = d3.scaleLinear() .domain([that.dataMaxMin.min, that.dataMaxMin.max]) .range(that.colors[2]); that.column--; for (var i = 0; i < t.length; i++) { let p = t[i]; let w = that.calNumberWidth(p.value); if (w < that.minRectWidth) { w = that.minRectWidth; } x = x - w; var _name = infoPanel.randomWord(8); var start = color(that.referValue); that.referValue = parseFloat(that.referValue) - parseFloat(p.value); var end = color(that.referValue); infoPanel.gradient(that.svg, _name, end, start); if (index < 6 && i == (t.length - 1)) { //向下画线 let y1 = y + that.margin[0] + that.comH - 2, y2 = this.yScale(that.column) + that.margin[0] - that.comH + 1, c = that.linecolor;//color(that.referValue); infoPanel.addLineForDataviz(this.svg, x + 1, y1, x + 1, y2, 2, c); } that.addRect(p.name, p.value, x, y, w, that.comH, _name, index); //c++; x = x - that.ws; } that.referenceAxis.x = x + that.ws; that.referenceAxis.y = that.yScale(this.column) - that.comH; } /** * 期末表 */ end(col) { //判断数据在 x 轴上方还是下方 let that = this; let p = that.resData[8][0]; let w = that.calNumberWidth(p.value); let x = that.xScale(0) + that.margin[1]; let y = that.yScale(0) - that.comH; var _name = infoPanel.randomWord(8); infoPanel.gradient(that.svg, _name, that.colors[0][0], that.colors[0][1]); if (w < 1) { w = 1; } that.addRect(p.name, p.value, x, y, w, that.comH, _name, col); let mx = x + w + 10; let my = y + that.margin[0] + that.comH / 2; if (w < that.minshowtext) { let text = p.name; that.renderText(infoPanel.randomWord(8), mx, my, 100, 100, text, "start", "middle", that.fontsize, this.fontfamilyPost, "#DF5A55"); } } /** * 渲染色块 * @param {String} id 控件id * @param {number} v 控件值 * @param {number} x 矩形X坐标 * @param {number} y 矩形Y坐标 * @param {number} w 宽度 * @param {number} h 高度 * @param {String} c 色系索引 * @param {String} col */ addRect(id, v, x, y, w, h, c, col) { if ((h <= 0) || (w <= 0)) { vizlog.e("注意有负值 => id:" + id + " " + v + " x:" + x + " y:" + y + " w:" + w + " h:" + h + " c:" + c); return; } let that = this; let rectId = infoPanel.randomWord(8); let circle = this.comWidth >= 500 ? 3 : 1.5; let rect = that.svg .append("rect") .attr("id", rectId + "_" + that.el) .attr("memo", id) .attr("vt", id) .attr("v", v) .attr("col", col) .attr("fill-opacity", that.fillopacity) .attr("fill", 'url(#' + c + ')') .attr("x", x) .attr("y", function () { let t = y + that.margin[0]; return t; }) .attr("rx", circle) .attr("ry", circle) .attr("width", w) .attr("height", function () { return h; }) .on('click', function () { let _that = d3.select(this); _that.attr('transform', 'translate(2,2)'); d3.timeout(function () { _that.attr('transform', 'translate(0,0)') }, 300); var col = _that.attr("col"); var thisId = _that.attr("id"); var thisValue = _that.attr("v"); var thisXY = d3.mouse(this); col = parseInt(col) + 1; if (typeof that.click === "function") { that.click(thisId, thisValue, thisXY, col, d3.event); } }) .on("mouseover", function () { var thisId = d3.select(this).attr("id"); var thisValue = d3.select(this).attr("v"); var thisXY = d3.mouse(this); var t = d3.select(this).node().parentNode.parentNode.id; infoPanel.showInfoPanel(thisId, thisValue, thisXY, t); }) .on("mouseout", function () { var thisId = d3.select(this).attr("id"); var t = d3.select(this).node().parentNode.parentNode.id; infoPanel.hideInfoPanel(thisId, t); }) .on("mousemove", function () { var thisId = d3.select(this).attr("id"); var thisValue = d3.select(this).attr("v"); var thisXY = d3.mouse(this); var t = d3.select(this).node().parentNode.parentNode.id; infoPanel.moveInfoPanel(thisId, thisValue, thisXY, t); }); if (that.shadow) { rect.style("filter", "url(#drop-shadow)"); } that.addText(w, h, id, v, x, y, 'lefttop'); } /** * 添加文本 * @param {Number} w 宽度 * @param {Number} h 高度 * @param {String} t 文字 * @param {Nubmer} v 数值 * @param {Nubmer} x x 轴 * @param {Nubmer} y y 轴 * @param {*} pos */ addText(w, h, t, v, x, y, pos) { let tmpmargin = 5; let that = this; if (w < that.minshowtext) { return; } let mx = x + tmpmargin; let my = y + this.margin[0] + h / 2; let id = infoPanel.randomWord(8); let p = "start"; that.renderText(id, mx, my, w, h, t, p, "middle", that.fontsize, this.fontfamilyPost, that.rectTextColor); let textWidth = d3.select("#" + id).node().getComputedTextLength(); let pertTextWidth = textWidth / t.length; vizlog.i(t + " : " + textWidth + " => " + pertTextWidth); if (this.comWidth < 500) {//只显示一行,如果显示不下 就加省略号 if (textWidth > (w - tmpmargin * 2)) { d3.select("#" + id).remove(); let nt = t.substring(0, parseInt((w - tmpmargin * 2) / pertTextWidth) - 1) + "..."; that.renderText(id, mx, my, w, h, nt, p, "middle", that.fontsize, this.fontfamilyPost, that.rectTextColor); } } else { if (textWidth > (w - tmpmargin * 2)) { d3.select("#" + id).remove(); let nt = t.substring(0, parseInt((w - tmpmargin * 2) / pertTextWidth) - 1) + "..."; that.renderText(id, mx, my, w, h, nt, p, "middle", that.fontsize, this.fontfamilyPost, that.rectTextColor); } } } /** * 文字渲染 * @param {String} id 编号 * @param {number} x X 坐标 * @param {number} y Y 坐标 * @param {number} w 宽度 * @param {number} h 高度 * @param {String} text 文本 * @param {String} textanchor 文本水平位置 start|middle|end * @param {String} baseline 文本垂直位置 start|middle|end * @param {number} fontsize 字号 * @param {String} fontfamily 字体 * @param {String} textcolor 颜色 */ renderText(id, x, y, w, h, text, textanchor, baseline, fontsize, fontfamily, textColor) { this.svg .append("text") .attr("id", id) .text(text) .attr("x", x) .attr("y", y) .attr("style", "text-anchor: " + textanchor) .attr("dominant-baseline", baseline) .attr("width", w) .attr("height", h) .attr("font-size", fontsize) .attr('font-weight', 700) .attr("font-family", fontfamily) .attr("fill", textColor) .attr("pointer-events", "none"); } /** * 获取最大数和最小数 */ maxNumberOfData() { let t = this.resData; let max = -99999999; let min = 0; let tmp = 0; this.column = 8; for (var i = 0; i < t.length; i++) { var _t = t[i]; if (_t.length == 0) { this.column--; } for (var p in _t) { if (i == 7) { continue; } if (i == 2 || i == 4 || i == 6) { tmp = tmp - parseFloat(_t[p].value); } else { tmp = tmp + parseFloat(_t[p].value); } min = min < tmp ? min : tmp; max = max > tmp ? max : tmp; } } vizlog.i("max:" + max + " min:" + min); return { max: max, min: min }; } /** * 单位转为亿元 */ unitChange() { let that = this; for (var i = that.resData.length - 1; i >= 0; i--) { for (var k = that.resData[i].length - 1; k >= 0; k--) { let t = that.resData[i][k]; t.value = (t.doubleValue / that.unitnumber).toFixed(2); if (t.value == '0.00') { // that.resData[i].splice(k, 1); that.resData[i][k].value = t.doubleValue / that.unitnumber; } } } } /** * 计算数据对应 Y 轴高度 */ calNumberWidth(numberValue) { return this.xScale(numberValue) - this.xScale(0); } /** * 页面大小切换 */ resize() { let that = this; clearTimeout(that.timer); that.timer = setTimeout(function () { that.svg.selectAll("*").remove(); that.show(); }, 200); } } export default cashH;