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