UNPKG

datavizstocklib

Version:

测试一下呗

642 lines (582 loc) 22.9 kB
import * as d3 from "d3"; import $ from "jquery"; import infoPanel from "./infoPanel"; import vizlog from "./vizlog"; let config = { name: "valueperChart", svg: {}, screenWidth: document.documentElement.clientWidth, //屏幕宽度 screenHeight: document.documentElement.clientHeight, //屏幕高度 comWidth: "", //当前页面宽度 comHeight: "", //当前页面高度 widthHeightRate: 0.5, // 宽高度比率 fillopacity: 0.65, /** * 0 正常情况, 1 正常情况下,但权益有负值,2资不抵债 */ chartType: 0, perfitSourceData: [], resData: [], dataMaxMin: [], yScale: {}, xScale: {}, margin: [60, 60, 60, 60], //上,右,下,左 colors: ['#cc3edd', '#396ae9'], colorname: [], ws: 0, // ws white space 空白 baseW: 30, //控件左右间距 comW: 100, //控件宽度 xlength: 0, //X 轴宽度 referenceYaxis: 0, //参考 Y坐标 fontsize: 14, //字号 fontfamilyBase: "SourceHanSansK-Regular-plotly,Helvetica Neue,PingFang SC,Microsoft YaHei,Helvetica,Hiragino Sans GB,Arial,sans-serif", fontfamilyPost: '', //字体 Bold Medium rectTextColor: "#185FC6", //文字颜色 responsedatamin: 0, // 数据中的最小值 el: '_dataviz', pos: [],//各点位置 timer: {}, click: {}, mdpos: [], mpos: [], legend: true, shadow: true, shadowid: "", } export class Valueper { constructor() { let newconfig = JSON.parse(JSON.stringify(config)); Object.assign(this, newconfig) } init(obj) { Object.assign(this, obj); this.svg = d3.select("#" + this.el).append("svg"); if (this.dataMaxMin.length == 0) { this.dataMaxMin = this.maxNumberOfData(); } this.getComw(); //初始化宽度值 this.show(); this.fix(); } show() { this.shadowid = infoPanel.randomWord(8); this.createAxis(); this.createDiamond(); infoPanel.initInfoPanel(this.svg, this.el, this.fontfamilyBase); infoPanel.black(this.shadowid); this.createline() if (this.legend) { this.addLegend() } } createAxis() { let that = this; this.comWidth = $("#" + this.el).width(); this.comHeight = $("#" + this.el).height() const chartWidth = this.comWidth - this.margin[1] - this.margin[3], chartHeight = this.comHeight - this.margin[0] - this.margin[2]; let chart = this.svg.attr("width", this.comWidth).attr("height", this.comHeight) .append("g").attr("id", "idg") .attr("transform", `translate(${this.margin[1]}, ${this.margin[0]})`); this.svg = chart; //创建x轴比例尺 this.xScale = d3.scaleLinear().range([0, chartWidth]); // 界面像素范围 this.xScale.domain([-0.5, 7.5]); //数据范围 //创建y轴比例尺 this.yScale = d3.scaleLinear().range([chartHeight - 40, 0]); // 界面像素范围 this.yScale.domain([this.dataMaxMin.min, this.dataMaxMin.max * 1.5]); //数据范围 //绘制平行的坐标系轴线,其实是将刻度线反向延长 let t = d3 .axisBottom() .scale(this.xScale) .tickSize(-chartHeight, 0, 0) .tickFormat(""); let tttt = chartHeight;//+ 200; chart .append("g") .attr("class", "grid") .attr("id", "yxx" + this.el) .style('stroke', function (d) { '#A3A3A3' }) // .style('stroke-dasharray', function (d) { // return '4'; // }) // .attr('transform', `translate(0, ${chartHeight})`) .attr('transform', 'translate(0,' + tttt + ' )') .call(t) .style("color", "#A3A3A3"); this.getPos(); if (that.resData.md.length > 0) { this.getMdPos(); } this.createPolygon(); if (that.resData.md.length > 0) { this.createMdLine(); } //生成y轴 chart.append("g").attr("id", "yyyy" + this.el) // .attr('transform', `translate(0, ${chartHeight})`) .call(d3.axisLeft(this.yScale)); //生成x轴 chart .append("g") .attr("id", "xxxx" + this.el) .attr('transform', 'translate(0,' + this.yScale(0) + ' )') .call(d3.axisBottom(this.xScale)); let textnodex = d3.select("#xxxx" + this.el) .selectAll("text"); textnodex.each(function (d, i) { let textnode = d3.select(this); let showtext = that.resData.data[i].name; showtext = showtext.replace(/每股/g, ""); if (showtext.length > 6) { let strs = []; let t = showtext; for (; ;) { if (t.length <= 6) { strs.push(t); break; } else { let s = t.substring(0, 6); strs.push(s); t = t.substring(6); } } textnode.text(""); textnode.selectAll("tspan").data(strs).enter() .append("tspan").text(function (d) { return d; }) .attr("dy", "1.2em").attr("x", '0') .attr("font-size", that.fontsize); textnode.attr("transform", "rotate(35) translate(0,10)") .style("text-anchor", "start"); } else { textnode.text(showtext) .attr("transform", "rotate(35) translate(0,20)") .style("text-anchor", "start") .attr("font-size", that.fontsize); } }); // 删除x轴上的多余线 let linenode = d3.select("#xxxx" + this.el).selectAll("line"); linenode.each(function (d, i) { d3.select(this).remove(); }) //删除 y 轴上的多余线 linenode = d3.select("#yyyy" + this.el).selectAll("line"); linenode.each(function (d, i) { d3.select(this).remove(); }) linenode = d3.select("#yyyy" + this.el).selectAll("text"); linenode.each(function (d, i) { d3.select(this).remove(); }) d3.select("#yyyy" + this.el).select("path").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).style("stroke", "#4f4f4f"); d3.select("#yxx" + this.el) .select("path") .remove(); vizlog.i("原点位置:" + this.yScale(0) + " : " + this.xScale(0)); this.ylength = document.getElementById("yxx" + this.el).getBBox().height; this.comH = (this.ylength - (7 * this.baseH)) / 9; this.svg.append("text") .text("0") .attr("transform", "translate(" + (this.xScale(0) - 5) + "," + (this.yScale(0) + 15) + ")") .attr("style", "text-anchor: end") .attr("dominant-baseline", "start") .attr("font-size", this.fontsize) .attr("font-family", "SourceHanSansK-Regular-plotly") .attr("fill", "#595757") .attr("pointer-events", "none"); } createPolygon() { let that = this; var area = d3.line() .x(function (d) { return that.xScale(d.x); }) .y(function (d) { return that.yScale(d.y); }) // .y0(y(0)); let linePos = []; for (var i = 0; i < this.pos.length; i++) { linePos.push(this.pos[i]); } linePos.push(this.pos[0]); var _name = "cname" + parseInt(Math.random() * 10000); infoPanel.gradient(this.svg, _name, this.colors[0], this.colors[1], 1); this.svg.append('svg:path') .attr('d', area(linePos)) .attr('stroke-width', 2) // .attr("stroke", 'url(#' + _name + ')') .attr('fill', 'none'); _name = "cname" + parseInt(Math.random() * 10000); infoPanel.gradient(this.svg, _name, this.colors[0], this.colors[1], 1); let polygon = this.svg.selectAll("polygon") .data([that.pos]) .enter().append("polygon") // .attr("fill", 'url(#' + _name + ')') .attr("fill", '#6088D8') .attr("fill-opacity", this.fillopacity) .attr("points", function (d) { return d.map(function (d) { return [that.xScale(d.x), that.yScale(d.y)].join(","); }).join(" "); }); if (this.shadow == true) { polygon.style("filter", "url(#" + that.shadowid + ")"); } } /** * 非 x 轴上点创建 菱形 */ createDiamond() { let that = this; var symbol = d3.symbol().size([100]); var _name = "cname" + parseInt(Math.random() * 10000); infoPanel.gradient(this.svg, _name, "#e520c0", "#ff5824", 1); let shapes = [d3.symbolCircle, d3.symbolCross, d3.symbolDiamond, d3.symbolSquare, d3.symbolStar, d3.symbolTriangle, d3.symbolWye]; let addPercent = true; for (let i = 0; i < this.pos.length; i++) { let id = infoPanel.randomWord(8); if (this.pos[i].y != 0) { let t = this.calPos(this.pos[i]); let v = this.pos[i].v.toFixed(2) + "元"; let k = 0; if (i > 7) { k = 15 - i; } else { k = i; } let showtext = that.resData.data[k].name; showtext = showtext.replace(/每股/g, ""); this.svg .append("path") .attr("id", id) .attr("memo", showtext) .attr("v", v) .attr("d", symbol.type(shapes[0])) .attr("transform", "translate(" + t.x + "," + t.y + ") rotate(45)") .attr("stroke", "#6088D8") .attr('stroke-width', 2) .attr("fill-opacity", 1) .style("fill", "#FFFFFF") .on('click', function () { let _that = d3.select(this); var thisId = _that.attr("id"); var thisValue = _that.attr("v"); let t = d3.select(this).node().parentNode; var thisXY = d3.mouse(t); d3.event.stopPropagation(); }) .on("mouseover", function () { let node = d3.select(this); var thisId = node.attr("id"); var thisValue = node.attr("v"); let t = d3.select(this).node().parentNode; var thisXY = d3.mouse(t); infoPanel.showInfoPanel2(thisId, thisValue, thisXY, that.el, thisValue, 'valueper'); }) .on("mouseout", function () { var thisId = d3.select(this).attr("id"); infoPanel.hideInfoPanel(thisId, that.el); }) .on("mousemove", function () { var thisId = d3.select(this).attr("id"); var thisValue = d3.select(this).attr("v"); let t = d3.select(this).node().parentNode; var thisXY = d3.mouse(t); infoPanel.moveInfoPanel2(thisId, thisValue, thisXY, that.el, thisValue, 'valueper'); }); this.addText(t.x, t.y, this.pos[i].v, addPercent); addPercent = false; } } } /** * 中位值 */ createMdLine() { let that = this; var line = d3.line() .x(function (d) { return that.xScale(d.x); }) .y(function (d) { return that.yScale(d.y); }) let linePos = []; for (var i = 0; i < this.mdpos.length; i++) { linePos.push(this.mdpos[i]); } this.svg.append('path') .attr('d', line(linePos)) .attr('stroke', "#808080") .attr('opacity', 1) .attr('stroke-width', '1.5') .attr('stroke-dasharray', '4,4') .attr('fill', 'none') } calPos(pos) { let x = this.xScale(pos.x); let y = this.yScale(pos.y); return { x: x, y: y }; } /** * w 宽度 h高度 t 文本内容 x y */ addText(x, y, t, addPercent) { let id = "AAA" + parseInt(Math.random() * 100000); let p = "middle"; let showvalue = t.toFixed(2); let baseline = "end"; if (t < 0) { baseline = "start"; y = y + 20; } else { y = y - 15; } if (showvalue == '-0.00') showvalue = '0.00' this.svg .append("text") .text(showvalue) .attr("id", id) .attr("transform", "translate(" + x + "," + y + ")") .attr("style", "text-anchor: " + p) .attr("dominant-baseline", baseline) .attr("font-size", this.fontsize + 2) .attr("font-weight", 900) .attr("font-family", this.fontfamilyPost) .attr("fill", this.rectTextColor) .attr("pointer-events", "none"); } /** * 交互区域 */ createline() { let that = this; var symbol = d3.symbol().size([100]); let shapes = [d3.symbolCircle, d3.symbolCross, d3.symbolDiamond, d3.symbolSquare, d3.symbolStar, d3.symbolTriangle, d3.symbolWye]; for (let i = 0; i < this.pos.length; i++) { if (this.pos[i].y != 0) { let po = this.calPos(this.pos[i]); let v = this.pos[i].v.toFixed(2) + "元"; let v2 = "" if (this.mpos.length > 0) { v2 = this.mpos[i].v.toFixed(2) + "元" } let k = 0; if (i > 7) { k = 15 - i; } else { k = i; } let showtext = that.resData.data[k].name; showtext = showtext.replace(/每股/g, ""); let g = this.svg.append("g") .attr("id", infoPanel.randomWord(8) + "_" + "line") .attr("v", v) .attr("v2", v2) .attr("memo", showtext) .attr("opacity", 0.0001) .on("mouseover", function () { let node = d3.select(this); var thisId = node.attr("id"); var thisValue = node.attr("v"); var mdValue = node.attr("v2"); node.attr("opacity", '1') let t = d3.select(this).node().parentNode; var thisXY = d3.mouse(t); infoPanel.showInfoPanel2(thisId, thisValue, thisXY, that.el, thisValue, mdValue, that.resData.industry, 'valueper'); }) .on("mouseout", function () { let node = d3.select(this); node.attr("opacity", 0.0001) var thisId = d3.select(this).attr("id"); infoPanel.hideInfoPanel(thisId, that.el); }) .on("mousemove", function () { let node = d3.select(this); node.attr("opacity", '1') var thisId = d3.select(this).attr("id"); var thisValue = d3.select(this).attr("v"); var mdValue = node.attr("v2"); let t = d3.select(this).node().parentNode; var thisXY = d3.mouse(t); infoPanel.moveInfoPanel2(thisId, thisValue, thisXY, that.el, thisValue, mdValue, that.resData.industry, 'valueper'); }) .on('click', function () { let _that = d3.select(this); var thisId = _that.attr("id"); var thisValue = _that.attr("v"); var thisXY = d3.mouse(this); if (typeof that.click === "function") { that.click(thisId, thisValue, thisXY, d3.event); } // d3.event.stopPropagation(); }) g.append("line") .attr("fill", 'none') .attr("opacity", '1') .attr("fill-opacity", '1') .attr("stroke-width", (this.comWidth - this.margin[1] - this.margin[3]) / ((this.pos.length) / 2)) .attr("stroke", "rgba(0, 0, 0, 0)") .attr("x1", po.x) .attr("x2", po.x) .attr("y1", 0) .attr("y2", this.comHeight) g.append("line") .attr("fill", 'none') .attr("opacity", '1') .attr("fill-opacity", '1') .attr("stroke-width", '1') .attr("stroke", "rgba(0, 0, 0, 0.3)") .attr("x1", po.x) .attr("x2", po.x) .attr("y1", 0) .attr("y2", this.comHeight) g.append("path") .attr("d", symbol.type(shapes[0])) .attr("transform", "translate(" + po.x + "," + po.y + ") rotate(45)") .attr('stroke', '#FFFFFF') .attr('stroke-width', 2) .attr("fill-opacity", 1) .style("fill", "#6088D8") } } } /** * 修复样式 */ fix() { let that = this; d3.select("#xxxx" + this.el).selectAll("text").each(function (d, i) { let t = "translate( -20, " + (that.yScale(that.dataMaxMin.min) - that.yScale(0) + 15) + ") rotate(35) "; d3.select(this).attr("transform", t) .style("text-anchor", "start") .attr("font-size", that.fontsize);; }) } /** * 控件默认宽度 */ getComw() { return 100; } newPos(x, y, v) { return { 'x': x, 'y': y, v: v }; } /** * 计算各顶点数据 */ getPos() { let that = this; let poly = []; let a = new Array(16); // 所有y 零点 for (var i = 0; i < 8; i++) { let tv = parseFloat(that.resData.data[i].value); if (tv > 0) { a[i] = that.newPos(i, tv, tv); a[15 - i] = that.newPos(i, 0, tv); } else { a[i] = that.newPos(i, 0, tv); a[15 - i] = that.newPos(i, tv, tv); } } that.pos = a; } addLegend() { this.svg.append('rect') .attr('width', '15') .attr('height', '3') .attr('x', '60') .attr('y', '-26') .attr('fill', '#6088D8') this.svg .append("text") .text("每股价值") .attr("font-size", 12) .attr("font-family", "Arial") .attr("fill", "#000000") .attr("width", 0) .attr("height", 0) .attr("x", 78) .attr("y", -20) .attr("display", "block"); this.svg.append('rect') .attr('width', '15') .attr('height', '3') .attr('x', '140') .attr('y', '-26') .attr('fill', '#808080') this.svg .append("text") .text("中位值") .attr("font-size", 12) .attr("font-family", "Arial") .attr("fill", "#000000") .attr("width", 0) .attr("height", 0) .attr("x", 158) .attr("y", -20) .attr("display", "block"); } /** * 计算中位值各顶点数据 */ getMdPos() { let that = this; let a = new Array(16); // 所有y 零点 for (var i = 0; i < 8; i++) { let tv = parseFloat(that.resData.md[i].value); if (tv > 0) { a[i] = that.newPos(i, tv, tv); a[15 - i] = that.newPos(i, 0, tv); } else { a[i] = that.newPos(i, 0, tv); a[15 - i] = that.newPos(i, tv, tv); } } that.mpos = a; that.mdpos = a.slice(0, 8); } getMa(value) { value = parseFloat(value); var p = Math.floor(Math.log(value) / Math.LN10); var n = value * Math.pow(10, -p); return n; } //获取最大数和最小数 maxNumberOfData() { let max = -99999999; let min = 0; for (let i = 0; i < this.resData.data.length; i++) { let t = parseFloat(this.resData.data[i].value); max = max < t ? t : max; min = min > t ? t : min; } for (let i = 0; i < this.resData.md.length; i++) { let t = parseFloat(this.resData.md[i].value); max = max < t ? t : max; min = min > t ? t : min; } return { max: max, min: min }; } //页面大小切换 resize() { let that = this; clearTimeout(that.timer); that.timer = setTimeout(function () { that.svg.selectAll("*").remove(); that.show(); }, 200); } }; export default Valueper;