cloud-ui.vusion
Version:
Vusion Cloud UI
273 lines (248 loc) • 9.85 kB
JavaScript
const TICKES = [2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 15, 20, 30, 40, 50, 100, 200, 500, 1000, 1];
const FILTER = 360;
export default {
name: 'u-line-chart',
props: {
data: Array,
title: String,
caption: String,
series: Array,
border: Boolean,
legend: Boolean,
width: {
type: String,
default: '100%',
},
height: {
type: String,
default: '480px',
},
xAxis: Object,
yAxis: Object,
smooth: Boolean,
fill: Boolean,
titleAlign: { type: String, default: 'center' },
loading: { type: Boolean, default: false },
contentStyle: Object,
},
data() {
return {
width_: undefined,
height_: undefined,
xAxis_: {
data: [],
},
yAxis_: {
data: [],
},
percent_: undefined,
currentData: this.getCurrentData(),
};
},
created() {
this.draw();
window.addEventListener('resize', this._onResize, false);
},
watch: {
data(newValue) {
this.currentData = this.getCurrentData(newValue);
this.draw();
},
},
methods: {
_getSize() {
if (!this.$refs)
return;
this.width_ = this.$refs.grid && this.$refs.grid.offsetWidth;
this.height_ = this.$refs.grid && this.$refs.grid.offsetHeight;
},
/**
* @private
*/
_onResize() {
this._getSize();
this.draw();
},
getCurrentData(value) {
const data = value || this.data;
const length = data.length;
let currentData = [];
if (length > FILTER) {
const diff = Math.round(length / FILTER);
currentData = data.filter((item, index) => index % diff === 0);
} else
currentData = data;
return currentData;
},
draw() {
if (!this.currentData || !this.currentData.length)
return;
this._getSize();
//
// 确定横坐标
//
{
const xAxis_ = this.xAxis_;
xAxis_.count = this.xAxis.count || 12;
let pieceCounts = this.currentData.length - 1;
let tick = pieceCounts / xAxis_.count;
if (tick !== parseInt(tick)) {
tick = 1;
while (!(pieceCounts / tick <= xAxis_.count && pieceCounts % tick === 0)) {
for (let i = 0; i < TICKES.length; i++) {
tick = TICKES[i];
if (pieceCounts / tick <= xAxis_.count && pieceCounts % tick === 0)
break;
}
// 如果不能整除,则补充空数据
if (tick === 1) {
this.currentData.push({ hidden: true });
pieceCounts++;
} else
break;
}
}
xAxis_.tick = tick;
xAxis_.data = [];
this.currentData.forEach((item, index) =>
index % tick === 0 && xAxis_.data.push(item[this.xAxis.key]));
}
//
// 确定纵坐标
//
{
const yAxis_ = this.yAxis_;
// 如果没有设置最小值和最大值,则寻找
if (this.yAxis.min !== undefined)
yAxis_.min = this.yAxis.min;
else {
yAxis_.min = Math.min(...this.series.map((sery) =>
!sery.absent && Math.min(...this.currentData.map((item) =>
item[sery.key] !== undefined ? item[sery.key] : Infinity)
)
)); // 支持空数据
}
if (this.yAxis.max !== undefined)
yAxis_.max = this.yAxis.max;
else {
yAxis_.max = Math.max(...this.series.map((sery) =>
!sery.absent && Math.max(...this.currentData.map((item) =>
item[sery.key] !== undefined ? item[sery.key] : -Infinity)
)
)); // 支持空数据
}
if (yAxis_.min === yAxis_.max && yAxis_.min > 0)
yAxis_.min = 0;
else if (yAxis_.min === yAxis_.max && yAxis_.max < 0)
yAxis_.max = 0;
yAxis_.count = this.yAxis.count || 8;
const tick = this.roundToFirst((yAxis_.max - yAxis_.min) / yAxis_.count) || 1;
const fixedCount = this.getFixedCount(tick);
yAxis_.min = Math.floor(yAxis_.min / tick) * tick;
yAxis_.max = Math.ceil(yAxis_.max / tick) * tick;
// 如果最小值和最大值相等,则强行区分
if (yAxis_.min === yAxis_.max)
yAxis_.max = yAxis_.min + yAxis_.count;
yAxis_.data = [];
while (yAxis_.min + yAxis_.count * tick < yAxis_.max)
yAxis_.count++;
for (let i = 0; i <= yAxis_.count; i++) {
const value = yAxis_.min + i * tick;
yAxis_.data.push(value.toFixed(fixedCount)); // 防止+的时候出现无限小数的情况
}
const dataMax = Number(yAxis_.data[yAxis_.data.length - 1]);
yAxis_.max = Math.max(yAxis_.max, dataMax);
}
setTimeout(() => {
this._getSize();
});
},
getD(sery, type) {
if (!this.width_ || !this.height_ || !this.currentData || !this.xAxis_.data.length || !this.yAxis_.data.length)
return;
if (this.currentData.length <= 1) // 一个点无需绘制线条
return;
if (sery.absent)
return;
const width = this.width_;
const height = this.height_;
const delta = width / (this.currentData.length - 1) / 2;
const points = this.currentData.map((item, index) => {
if (isNaN(item[sery.key]))
return null;
else {
return [
width * index / (this.currentData.length - 1),
height * (1 - (item[sery.key] - this.yAxis_.min) / (this.yAxis_.max - this.yAxis_.min)),
];
}
});
points.push(null); // 起始点也可以看做间断结束,最后一个null看做间断开始
const cmds = [];
let discontinued = true;
for (let i = 0; i < points.length; i++) {
const point = points[i];
let cmd = '';
if (!point) {
if (!discontinued) {
discontinued = true;
if (type === 'area')
cmd = 'V' + height;
}
} else {
const pointStr = point.join(',');
if (discontinued) { // discontinue end
discontinued = false;
if (type !== 'area')
cmd = 'M ' + pointStr;
else {
const bottomPointStr = [point[0], height].join(',');
cmd = `M ${bottomPointStr} L ${pointStr}`;
}
const nextPoint = points[i + 1];
if (this.smooth && nextPoint) {
const helperPointStr = [point[0] + delta, point[1]].join(',');
const nextHelperPointStr = [nextPoint[0] - delta, nextPoint[1]].join(',');
cmd += ` C ${helperPointStr} ${nextHelperPointStr} ` + nextPoint.join(',');
i++;
}
} else {
if (!this.smooth)
cmd = 'L ' + pointStr;
else {
const helperPointStr = [point[0] - delta, point[1]].join(',');
cmd = `S ${helperPointStr} ${pointStr}`;
}
}
}
cmds.push(cmd);
}
return cmds.join(' ');
},
getTopOne(item) {
return Math.max(...this.series.map((sery) => !sery.absent && !sery.hidden && item[sery.key] ? item[sery.key] : 0));
},
format(value) {
return value;
},
roundToFirst(num) {
if (num >= 1) {
const power = Math.pow(10, String(Math.round(num)).length - 1);
return Math.round(num / power) * power;
} else if (num > 0)
return +num.toFixed(String(num).match(/^0\.0*/)[0].length - 1);
else // 不解决0或负数
return num;
},
getFixedCount(num) {
const m = String(num).match(/\.\d+/);
return m ? m[0].length - 1 : 0;
},
getPercent(item) {
return 100 * (this.yAxis_.max - this.getTopOne(item)) / (this.yAxis_.max - this.yAxis_.min);
},
},
destroyed() {
window.removeEventListener('resize', this._onResize, false);
},
};