You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
221 lines
5.8 KiB
221 lines
5.8 KiB
const { createCanvas } = require('canvas');
|
|
const Chart = require('chart.js/auto');
|
|
|
|
const { testData } = require("./trash/testData");
|
|
const { formatSeconds } = require("./utils");
|
|
const moment = require('moment');
|
|
|
|
function prepareData(data) {
|
|
const result = [];
|
|
|
|
const grabFirstValue = (container) => {
|
|
if (!container) return 0;
|
|
for (const mapName of Object.keys(container)) {
|
|
return container[mapName];
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
const grabMapName = (container) => {
|
|
if (!container) return "";
|
|
for (const mapName of Object.keys(container)) {
|
|
return mapName;
|
|
}
|
|
return "";
|
|
};
|
|
|
|
for (const srvId of Object.keys(data.lastplay)) {
|
|
const obj = {};
|
|
obj.srv_id = srvId;
|
|
obj.server_name = data.servers[srvId].name;
|
|
obj.lastplay = grabFirstValue(data.lastplay[srvId]);
|
|
obj.lastplay_moment = moment.unix(obj.lastplay).format("DD-MM-YYYY HH:mm");
|
|
obj.gametime = grabFirstValue(data.gametime[srvId]);
|
|
obj.map_name = grabMapName(data.lastplay[srvId]);
|
|
result.push(obj);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
function getTextAlignByPoint(p, r) {
|
|
const dx = p.x - r.xCenter;
|
|
const dy = p.y - r.yCenter;
|
|
|
|
if (Math.abs(dx) < Math.abs(dy) * 0.1) {
|
|
return 'center';
|
|
}
|
|
|
|
if (dx > 0) {
|
|
return 'left';
|
|
}
|
|
|
|
return 'right';
|
|
}
|
|
|
|
function getAlignedRectX(textAlign, x, width, padX) {
|
|
if (textAlign === 'left') {
|
|
return x - padX;
|
|
}
|
|
if (textAlign === 'right') {
|
|
return x - width - padX;
|
|
}
|
|
return x - width / 2 - padX;
|
|
}
|
|
|
|
const customLabelsPlugin = {
|
|
id: 'customLabelsPlugin',
|
|
afterDraw(chart) {
|
|
const { ctx, scales: { r } } = chart;
|
|
const labels = chart.data.labels;
|
|
|
|
ctx.save();
|
|
|
|
labels.forEach((label, i) => {
|
|
const p = r.getPointPosition(i, r.drawingArea + 40);
|
|
const align = getTextAlignByPoint(p, r);
|
|
|
|
const lines = Array.isArray(label) ? label : [label];
|
|
const [mapName, serverName, playTime] = lines;
|
|
|
|
ctx.textAlign = align;
|
|
ctx.textBaseline = 'middle';
|
|
|
|
// 1 строка
|
|
ctx.font = 'bold 16px Arial';
|
|
let w = ctx.measureText(mapName).width;
|
|
let rx = getAlignedRectX(align, p.x, w, 6);
|
|
|
|
ctx.fillStyle = '#395c78';
|
|
ctx.fillRect(rx, p.y - 28, w + 12, 22);
|
|
ctx.fillStyle = '#ffffff';
|
|
ctx.fillText(mapName, p.x, p.y - 17);
|
|
|
|
// 2 строка
|
|
ctx.font = '14px Arial';
|
|
w = ctx.measureText(serverName).width;
|
|
rx = getAlignedRectX(align, p.x, w, 6);
|
|
|
|
ctx.fillStyle = '#0e0d0d';
|
|
ctx.fillText(serverName, p.x, p.y + 4);
|
|
|
|
// 3 строка
|
|
ctx.font = '14px Arial';
|
|
ctx.fillStyle = '#0e0d0d';
|
|
ctx.fillText(playTime, p.x, p.y + 24);
|
|
});
|
|
|
|
ctx.restore();
|
|
}
|
|
};
|
|
|
|
function generateChartData(d) {
|
|
const chartData = {
|
|
labels: [],
|
|
datasets: []
|
|
};
|
|
|
|
const data = [];
|
|
|
|
d
|
|
.sort((a, b) => a.server_name.localeCompare(b.server_name, 'ru'))
|
|
.forEach((val) => {
|
|
chartData.labels.push(
|
|
[
|
|
`${val.server_name}`,
|
|
`${val.lastplay_moment}`,
|
|
`Наиграно: ${formatSeconds(val.gametime)}`
|
|
]
|
|
);
|
|
data.push(Math.pow(val.gametime || 0, 0.3));
|
|
});
|
|
|
|
chartData.datasets.push({
|
|
label: "Наиграно времени",
|
|
data,
|
|
borderColor: '#ff853e',
|
|
backgroundColor: '#ff853e94',
|
|
pointBackgroundColor: '#ff853e',
|
|
pointBorderColor: '#ff853e'
|
|
});
|
|
|
|
return {
|
|
type: 'radar',
|
|
data: chartData,
|
|
plugins: [
|
|
{
|
|
id: 'customCanvasBackgroundColor',
|
|
beforeDraw: (chart) => {
|
|
const { ctx, width, height } = chart;
|
|
ctx.save();
|
|
ctx.globalCompositeOperation = 'destination-over';
|
|
ctx.fillStyle = '#f7f3f1';
|
|
ctx.fillRect(0, 0, width, height);
|
|
ctx.restore();
|
|
}
|
|
},
|
|
customLabelsPlugin
|
|
],
|
|
options: {
|
|
responsive: false,
|
|
maintainAspectRatio: false,
|
|
layout: {
|
|
padding: 130
|
|
},
|
|
plugins: {
|
|
legend: {
|
|
display: false
|
|
},
|
|
title: {
|
|
display: false
|
|
}
|
|
},
|
|
scales: {
|
|
r: {
|
|
beginAtZero: true,
|
|
grid: {
|
|
color: '#6d6d6d'
|
|
},
|
|
angleLines: {
|
|
color: '#6d6d6d'
|
|
},
|
|
ticks: {
|
|
display: false
|
|
},
|
|
pointLabels: {
|
|
display: false,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
function createChartOnData(testData) {
|
|
const width = Number(process.env.W) || 900;
|
|
const height = Number(process.env.H) || 700;
|
|
|
|
const canvas = createCanvas(width, height);
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
const d = prepareData(testData);
|
|
const chartConfig = generateChartData(d);
|
|
|
|
new Chart(ctx, chartConfig);
|
|
|
|
return canvas.createPNGStream();
|
|
}
|
|
|
|
module.exports = { createChartOnData };
|
|
|
|
function test() {
|
|
const stream = createChartOnData(testData);
|
|
// Save the chart image as a file
|
|
const fs = require('fs');
|
|
const out = fs.createWriteStream('chart.png');
|
|
stream.pipe(out);
|
|
out.on('finish', () => console.log('The chart image was saved.'));
|
|
}
|
|
|
|
if (process.env.TEST)
|
|
test()
|
|
|