datavizstocklib
Version:
测试一下呗
642 lines (582 loc) • 22.9 kB
JavaScript
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;