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

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()