From 8ad0557d71534030b60475580bacce44dcb45a92 Mon Sep 17 00:00:00 2001 From: gsd Date: Thu, 4 Jun 2026 12:10:10 +0300 Subject: [PATCH] v2 --- chartGenerator.js | 203 +++++++++++++++++++++++++++++++--------------- 1 file changed, 138 insertions(+), 65 deletions(-) diff --git a/chartGenerator.js b/chartGenerator.js index 0559e26..487216c 100644 --- a/chartGenerator.js +++ b/chartGenerator.js @@ -1,10 +1,9 @@ -const { createCanvas, registerFont } = require('canvas'); +const { createCanvas } = require('canvas'); const Chart = require('chart.js/auto'); -const {testData} = require("./trash/testData"); -const {formatSeconds} = require("./utils"); +const { testData } = require("./trash/testData"); +const { formatSeconds } = require("./utils"); const moment = require('moment'); -const fs = require("fs"); function prepareData(data) { const result = []; @@ -15,7 +14,7 @@ function prepareData(data) { return container[mapName]; } return 0; - } + }; const grabMapName = (container) => { if (!container) return ""; @@ -23,103 +22,178 @@ function prepareData(data) { return mapName; } return ""; - } - //console.log(data) + }; + 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]) + 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 generateChartData(d) { - const maxGametime = d.sort((a, b) => a.gametime - b.gametime).at(d.length - 1)["gametime"] - console.log(maxGametime) +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'; + } - let gametimeDelimiter = 1; - for (const delimiter of [60, 3600, 3600 * 24]) { - if (maxGametime > delimiter) - gametimeDelimiter = delimiter; + if (dx > 0) { + return 'left'; } - console.log(gametimeDelimiter) + 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 + 50); + 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) => b.gametime - a.gametime).forEach( - (val) => { - chartData.labels.push(`${val['server_name']}\n${val['lastplay_moment']}\nНаиграно: ${formatSeconds(val['gametime'])}`) - data.push(val['gametime'] / gametimeDelimiter) - } - ) + }; + + 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: data, - }) - - const chartConfig = { + 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, - - borderColor: '#bd3b3b', - //backgroundColor: '#FFFFFF', layout: { - padding: 20 + padding: 110 }, - plugins: { legend: { - position: 'top', display: false }, title: { - display: false, - text: 'Chart.js Polar Area Chart With Centered Point Labels' + display: false } }, scales: { r: { + beginAtZero: true, grid: { - color: '#ef9849' + color: '#6d6d6d' }, angleLines: { - color: '#f08149' + color: '#6d6d6d' }, - pointLabels: { - display: true, - //centerPointLabels: true, - padding: 44, - font: { - size: 16 - }, - backdropColor: "#395c78", - backdropPadding: "1", - color: "#f5e7de" + ticks: { + display: false }, - + pointLabels: { + display: false, + } } } - }, + } }; - return chartConfig; } function createChartOnData(testData) { - -// Create a canvas and chart - const width = process.env.W || 900; - const height = process.env.H || 700; + const width = Number(process.env.W) || 900; + const height = Number(process.env.H) || 700; const canvas = createCanvas(width, height); const ctx = canvas.getContext('2d'); @@ -129,10 +203,10 @@ function createChartOnData(testData) { new Chart(ctx, chartConfig); - return canvas.createPNGStream(); + return canvas.createPNGStream(); } -module.exports = {createChartOnData} +module.exports = { createChartOnData }; function test() { const stream = createChartOnData(testData); @@ -143,5 +217,4 @@ function test() { out.on('finish', () => console.log('The chart image was saved.')); } -//test() - +test()