Hello guys i got this problem where im trying to put some labels or kind of annotations outside a canvas that i generate with ChartJS, but im new to using ChartJS and idk if im doing the right way to do this. After the graph is generated also gives you the option to download the canva to a pdf. Let me show you what the canva looks like pic 1. And pic 2 what i want to do.pic1. PIC2.
This is what my JS looks like:
function inicializarGrafica() {
let fecha1Input = document.getElementById("fecha1");
let enviarButton = document.getElementById("enviar");
let miGraficaCanvas = document.getElementById('miGrafica');
let miGrafica;
let totalRealElement = document.getElementById('totalReal');
let totalEsperadoElement = document.getElementById('totalEsperado');
let descargarButton = document.getElementById('descargarPdf');
let graficaContainer = document.getElementById('graficaContainer');
const grupos = {
"Pozos Carrizal": ["C1", "C3", "27SP", "28SP", "SG1", "SG2", "BURGOIN", "MAUTO"],
"Acueducto 1": ["8", "9", "10", "11", "12", "16", "30R", "31R", "H3"],
"Acueducto 2": ["18", "17", "15BIS", "17BIS"],
"Acueducto 3": ["20", "21", "22", "23", "24", "25", "26"],
"Acueducto 4": ["2R", "4R", "19R"],
"Cham. y Cent.": ["ARD1", "ARD2", "LC1", "29C"],
"Ciudad": ["1", "2", "4", "6", "7", "14"],
"Otros": ["PINO", "DUARTE"]
};
if (fecha1Input && enviarButton && miGraficaCanvas && totalRealElement && totalEsperadoElement && descargarButton) {
enviarButton.addEventListener("click", async function () {
var fechaActual = new Date().toISOString().split('T')[0];
const fecha1 = fecha1Input.value;
if (!fecha1) {
Swal.fire({
icon: 'error',
title: 'Error',
text: 'El campo de fecha está vacío, favor de ingresar una fecha.'
});
return;
}
if (fecha1 > fechaActual) {
Swal.fire({
icon: 'error',
title: 'Error',
text: 'La fecha ingresada no puede ser mayor a la fecha actual.'
});
return;
}
document.getElementById('fechaTotal').innerText = `Fecha: ${fecha1}`;
const url = "APIURL";
const datos = new URLSearchParams();
datos.append("fecha1", fecha1);
const opciones = {
method: "POST",
body: datos,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
};
try {
const response = await fetch(url, opciones);
if (!response.ok) {
throw new Error(`Error en la solicitud. Código de estado: ${response.status}`);
}
const data = await response.json();
let lbls = [];
let data_real = [];
let valor_esperado = [];
const pozosConAsterisco = ["2R", "4R", "4", "6", "16", "17BIS", "17", "14", "19R", "20", "22", "24", "25", "26", "27SP", "28SP", "2", "8", "9", "15BIS", "21", "DUARTE", "BURGOIN", "H3"];
// Mapear y ordenar los pozos en el orden de cada grupo
for (let grupo in grupos) {
const pozosGrupo = grupos[grupo];
pozosGrupo.forEach(pozo => {
const pozoData = data.find(d => d.nombre === pozo);
if (pozoData) {
let nombrePozo = pozoData.nombre;
if (pozosConAsterisco.includes(nombrePozo)) {
nombrePozo += " *"; // Añadir asterisco
}
lbls.push(nombrePozo);
data_real.push(pozoData.data);
valor_esperado.push(pozoData.esperado);
}
});
}
const totalReal = data_real.reduce((a, b) => a + Number(b), 0).toFixed(2);
const totalEsperado = valor_esperado.reduce((a, b) => a + Number(b), 0).toFixed(2);
totalRealElement.innerHTML = `Total producido: <span style="color:${totalReal < 1067 ? '#FFAA00' : 'green'};">${totalReal}</span>`;
totalEsperadoElement.innerHTML = `Total esperado: ${totalEsperado}`;
function obtenerHoraUltimaCerrada() {
let ahoraUTC = new Date();
let offsetHoras = 0;
let horaLocal = new Date(ahoraUTC.getTime() + (offsetHoras * 60 * 60 * 1000));
let hora = horaLocal.getHours();
let horaCerrada = Math.floor(hora);
horaCerrada = horaCerrada < 10 ? `0${horaCerrada}` : horaCerrada;
document.getElementById('ultimaActualizacion').innerHTML = `Actualización: ${horaCerrada}:00 horas`;
}
obtenerHoraUltimaCerrada();
if (miGrafica) {
miGrafica.destroy();
}
var datos_grafica = {
labels: lbls,
datasets: [
{
label: 'Producción',
data: data_real,
backgroundColor: 'rgba(75, 192, 192, 0.2)',
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 1
},
{
label: 'Valor esperado',
data: valor_esperado,
backgroundColor: 'rgba(150, 192, 152, 0.527)',
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 1
}
]
};
var config = {
type: 'bar',
data: datos_grafica,
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
ticks: {
maxRotation: 45,
minRotation: 45
}
},
y: {
beginAtZero: true
}
},
layout: {
padding: {
bottom: 50
}
},
plugins: {
tooltip: {
enabled: false
},
datalabels: {
display: true,
anchor: 'end',
align: 'end',
color: function(context) {
const datasetIndex = context.datasetIndex;
if (datasetIndex === 0) {
const valueReal = context.dataset.data[context.dataIndex];
const valueEsperado = valor_esperado[context.dataIndex];
const porcentaje = (valueReal / valueEsperado) * 100;
if (porcentaje < 70) {
return 'red';
} else if (porcentaje < 100) {
return '#FFAA00';
} else {
return 'green';
}
} else {
return 'black';
}
},
font: {
size: 14
},
formatter: function(value) {
return value;
},
offset: 5,
clip: false,
rotation: -90
}
},
devicePixelRatio: window.devicePixelRatio > 1 ? 2 : 1
},
plugins: [ChartDataLabels]
};
miGrafica = new Chart(miGraficaCanvas, config);
// once you generate the graph you can download
descargarButton.style.display = 'inline-block';
// download PDF
descargarButton.addEventListener('click', () => {
const fecha = fecha1Input.value;
const formattedFecha = fecha ? fecha.replace(/-/g, '') : 'fecha';
const container = graficaContainer; // Contenedor de la gráfica
html2canvas(container).then((canvasImage) => {
const { jsPDF } = window.jspdf;
const pdf = new jsPDF('l', 'mm', 'a4');
const imgData = canvasImage.toDataURL('image/png');
pdf.addImage(imgData, 'PNG', 10, 10, 280, 0);
const filename = `Reporte_produccion_${formattedFecha}.pdf`;
pdf.save(filename);
});
});
} catch (error) {
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Ocurrió un error al obtener los datos.'
});
}
});
}
}
This is the version of charJS that im using and annotation.
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
And here is where the graph is shown:
<body>
<script src="views/g_todos_dia.js" defer></script>
<div class="cont_grafica" id="graficaContainer">
<div class="cont_canvas">
<canvas id="miGrafica" height="auto"></canvas>
</div>
<!-- Aquí se muestran los totales -->
<div class="cont_totales" id="totales">
<div id="totalReal">Total producido: </div>
<div id="totalEsperado">Total esperado: </div>
<div id="ultimaActualizacion">Actualización: </div> <!-- Nuevo renglón -->
<div id="fechaTotal"></div> <!-- Este nuevo div mostrará la fecha -->
</div>
</div>
<div class="cont_inputs">
<label for="fecha1" id="lbl_fecha" style='font-size: 18px;'>Fecha: </label>
<input type="date" id="fecha1" style='font-size: 18px; height: auto;'>
</div>
<div class="cont_btn_enviar">
<input type="button" id="enviar" value="Enviar" style='font-size: 18px; padding: 3px 40px;'>
</div>
<div class="cont_btn_descargar">
<input type="button" id="descargarPdf" value="Descargar PDF" style='font-size: 18px; padding: 3px 40px;'>
</div>
</body>
I tried to use annotations but what it found in the forums of chartJS is that you cant put an annotation outside the graph so, im just wondering what should be the best option to do this labeling. I tried to group in javascript every data so that would be easier to group up once i knew how to get to label correctly.