Les subtilités de l'affichage en HTML

Bonsoir à toutes et à tous,
Toujours dans mon projet de routeur photovoltaïque, J'ai cette fois des problèmes de dimension d'affichage.

Avant que j'aborde un troisième affichage en mode bubble, J'avais deux affichages :

      <canvas id="myChart1" width="800" height="300" style="display: none"></canvas>
      <canvas id="myChart2" width="800" height="300" style="display: none"></canvas>

et quand j'affichais l'un ou l'autre, chacun penait la largeur de l'écran.

Maintenant, avec mes trois affichages, j'ai écrit ce qui suit :

      <div style="display: flex; justify-content: space-between;">
        <canvas id="myChart0" width="250" height="250" style="display: none"></canvas>
        <canvas id="myChart1" width="550" height="250" style="display: none"></canvas>
      </div>  
      <canvas id="myChart2" width="800" height="300" style="display: none"></canvas>

Quand je demande l'affichage de myChart0 et myChart1, ces deux affichages prennent une place pratiquement égale à deux fois la largeur de la fenêtre.
Si je diminue la taille de la fenêtre, mes deux affichages se rétrécissent et quand je remets la fenêtre à sa dimension originale, les deux affichages re-grandissent un peu mais cette fois prennent environ 1.2 fois la largeur de la fenêtre.

J'ai modifié (diminué) les valeurs de width et de height proportionnellement, mais le problème reste le même.

Une idée ?

Cordialement.

Pierre.

la largeur du conteneur

avec display: flex; n’est pas contrainte par la taille de la fenêtre mais par les canvas qui prennent plus de place que prévu sans doute.

Esssayez avec flex-wrap et des pourcentages

<div style="display: flex; justify-content: space-between; flex-wrap: wrap;">
  <canvas id="myChart0" width="45%" height="250" style="display: none"></canvas>
  <canvas id="myChart1" width="45%" height="250" style="display: none"></canvas>
</div>  
<canvas id="myChart2" width="100%" height="300" style="display: none"></canvas>

Malheureusement, ça ne fonctionne pas. Par ailleurs, flex-wrap: wrap; me place les deux graphiques l'un au dessus- de l'autre.

  • Avec flex-wrap: wrap;, l'affichage s'étend sur une largeur d'écran et 16 hauteurs d'écran !!
  • Sans flex-wrap: wrap;, l'affichage s'étend sur deux largeurs d'écran et 8 hauteurs d'écran !!

J'ai aussi l'impression que mélanger des valeurs en pixel et d'autres en % ne fait pas bon ménage.

J'ai essayé des dizaines de configurations sans aboutir à quelque chose de cohérent.

J'ai posé la question à "Perplexity" qui m'a donné un code qui semble aller dans le bon sens. Pour autant, avec les valeurs qu'il donne, je couvre encore deux écrans de large. Si je réduit à 50 % la largeur du container, mes deux graphiques tiennent sur un seul écran.
Après, quelque soient les valeurs que je donne aux largeurs de chacun des deux graphiques ou, que je supprime la partie concernant chaque graphique dans la partie css, ils prennent chacun la moitié de l'écran. Je me demande s'il n'y a pas une erreur de syntaxe dans la partie css ?

Cordialement.

Pierre.

Je pense que c'est chartjs qui change la taille de tes canvas en fonction de la taille du DIV qui le contient.
Je penses que de mettre chaque canvas d'un DIV dédié, devrait solutionner ton problème ?

A près les divers essais infructueux que j'ai faits, c'est peut-être la solution.

Mais concrètement, comment fais-tu pour que ces deux DIV se placent l'un à côté de l'autre ?

Cordialement.

Pierre.

J'ai trouvé un tuto qui n'a pas l'air mal, je vais essayer de me débrouiller...

Cordialement.
Pierre.

Moi j'ai essayé le code suivant et cela me paraissait correspondre à ce que tu voulais.

<div style="display: flex;">
            <div style="width: 45%;">
                <canvas id="myChart1" style="background-color: aqua;"></canvas>
            </div>
            <div style="width: 45%;">
                <canvas id="myChart2" style="background-color: beige;"></canvas>
            </div>
        </div>

OK oui sans doute parce que les dimensions font que ça ne rentre pas dans la fenêtre

Si d'abord, je regarde l'affichage myChart2 qui est seul, lorsque je réduit la largeur de la fenêtre, les dimensions de l'affichage suivent ce rétrécissement proportionnellement en horizontal et vertical. Si je ré-agrandis la fenêtre, l'affichage suit cet agrandissement proportionnellement en vertical et en horizontal pour revenir à ses dimensions d'origine.

Avec ce que tu me proposes, lors du rétrécissement, tout se passe bien, mais lors du ré-agrandissement, l'affichage reste à sa dimension réduite.

Par ailleurs, ce que je souhaite, est d'avoir l'affichage de gauche à 25 % et celui de droite à 75 %. Dans ces conditions, quelque soient les valeurs que je donne à height, ça ne change rien à l'affichage ; l'affichage à 25 % en width reste toujours inférieur en height par rapport à celui à 75% en width. Bref, un comportement pas très orthodoxe.

En suivant le tuto dont j'ai parlé précédemment, j'arrive à ce fichier html :


    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>ESP32 Routeur photovoltaïque</title>
      <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.js"></script>
<!--      <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> -->
      <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.min.js"></script>
      <style>
        .cadreflex{
          display: flex;
		  height: 500px;
          background-color: #0AD;
          border: 2px solid blue;
          box-sizing: border-box;
		  margin: 20px 20px;
        }
		.cadre{
		  flex-direction: row;
		}
		.boite0{
		  flex-basis: 25%;
		  height: 50%;
		  background-color: #0DA;
		  padding: 10px 0px;
		  border: 2px solid green;
		  box-sizing: border-box;
		  margin: 5px;
		}
		.boite1{
		  flex-basis: 75%;
		  height: 50%;
		  background-color: #0DA;
		  padding: 10px 0px;
		  border: 2px solid green;
		  box-sizing: border-box;
		  margin: 5px;
		}

      </style>

    </head>
    <body onload="init();">
      <h1 style="text-align: center;">ESP32 Routeur photovoltaïque</h1>
      <div style="text-align: center;">
        <button onclick="updateChart(1);" class="button button-data">Forme Courant &amp; Tension</button> 
        <button onclick="updateChart(2);" class="button button-data">Historique Puissance</button>
      </div>
      <div id = "txt1">
        <p>Date et Heure : <span id="DetH">0.00</span></p>
        <p>Puissance active (W) : <span id="PA">0.00</span></p>
      </div>

      <div class="cadreflex cadre">
		<div class="boite0">0</div>
		<div class="boite1">1</div>
	  </div>      

    </body>
    </html>

Si on l'exécute en tant que fichier html, on voit que si on raccourcit/agrandit la fenêtre, les containers (cadreflex, cadre, boite0 et boite1) suivent le mouvement ; ce que je souhaite.

Par contre, si au sein des containers intérieurs(boite0 et boite1), je place mes canvas, leurs affichages ne veulent plus suivre ... Voilà le code qui est une reprise de mon projet agrémenté de cette modification. C'est encore un fichier html qu'on peut lancer tout seul ...


    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>ESP32 Routeur photovoltaïque</title>
      <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.js"></script>
<!--      <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> -->
      <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.min.js"></script>
	  
	  <style>
        .cadreflex{
          display: flex;
		  height: 500px;
          background-color: #0AD;
          border: 2px solid blue;
          box-sizing: border-box;
		  margin: 20px 20px;
        }
		.cadre{
		  flex-direction: row;
		}
		.boite0{
		  flex-basis: 25%;
		  height: 50%;
		  background-color: #0DA;
		  padding: 10px 0px;
		  border: 2px solid green;
		  box-sizing: border-box;
		  margin: 5px;
		}
		.boite1{
		  flex-basis: 75%;
		  height: 50%;
		  background-color: #0DA;
		  padding: 10px 0px;
		  border: 2px solid green;
		  box-sizing: border-box;
		  margin: 5px;
		}
      </style>

    </head>
    <body onload="init();">
      <h1 style="text-align: center;">ESP32 Routeur photovoltaïque</h1>
      <div style="text-align: center;">
<!--        <button id="maj-DT">MaJ Date & Heure</button> -->
        <button onclick="updateChart(1);" class="button button-data">Forme Courant &amp; Tension</button> 
        <button onclick="updateChart(2);" class="button button-data">Historique Puissance</button>
        <a href="download-data"><button class="button button-data">Télécharger les données</button></a>
        <button onclick="chgtHistos();" class="button button-data">Téléchargement historiques</button>
      </div>
      <div id = "txt1">
        <p>Date et Heure : <span id="DetH">0.00</span></p>
        <p>Puissance active (W) : <span id="PA">0.00</span></p>
      </div>
      <div id = "txt2">
        <p>Puissance max : <input type="number" id="pMaxMod" value="1000" min="0"> <button onclick="ajusterPmax()">Ajuster</button></p>
      </div>
	  
      <div class="cadreflex cadre">
		<div class="boite0"><canvas id="myChart0"  width="100%" height="100%" style="display: none"></canvas></div>
		<div class="boite1"><canvas id="myChart1"  width="100%" height="100%" style="display: none"></canvas></div>
	  </div>      
      <canvas id="myChart2" width="100%" height="35%" style="display: none"></canvas>

      <script>
        let txt1 = document.getElementById("txt1");
        let txt2 = document.getElementById("txt2");

        const interval = 2000;
        let intervalId = -1;
        let pAjustee = 1000;  // Limite par défaut du nombre de points affichés

        function init() {
          txt1.style.display = "none";
          txt2.style.display = "none";
        }
        async function chgtHistos() {
          for (let i = 0; i < 7; i++) {
            fetch('/quelFichier?val=' + i)
              .then(response => {
                const contentDisposition = response.headers.get('Content-Disposition');
                const defaultFileName = `fichier_${i}.txt`; // Nom par défaut si le serveur n'en fournit pas
                let fileName = defaultFileName;
                if (contentDisposition && contentDisposition.includes('filename=')) {
                  const match = contentDisposition.match(/filename="?([^"]+)"?/);
                  if (match) {
                    fileName = match[1];
                  }
                }
                return response.blob().then(blob => ({ blob, fileName }));
              })
              .then(({ blob, fileName }) => {
                const url = window.URL.createObjectURL(blob);
                const a = document.createElement('a');
                a.style.display = 'none';
                a.href = url;
                a.download = fileName;
                document.body.appendChild(a);
                a.click();
                window.URL.revokeObjectURL(url);
              })
              .catch(error => console.error('Erreur lors du téléchargement:', error));
          }
        }

        function ajusterPmax() {
          const newPmax = document.getElementById('pMaxMod').value;
          const parsedPmax = parseInt(newPmax, 10);  // On force la base 10 pour la conversion
          if (!isNaN(parsedPmax) && parsedPmax > 0) {
            pAjustee = parsedPmax;
            chart2.options.scales.y0.max = pAjustee;
            chart2.options.scales.y2.max = pAjustee/230;
            chart2.update();
            fetch('/modifVal?val='+pAjustee);
          }
        }
        const ctx0 = document.getElementById('myChart0').getContext('2d');
        const chart0 = new Chart(ctx0, {
          type: 'bubble',
          data: {
            datasets: [{
              label: 'Cos Phi',
              backgroundColor: 'rgb(255, 99, 132)'
            }]
          },
          options: {
            scales: {
              x: {
                min: -1,
                max: 1,
                display: true,
                position: 'center',
                title : {
                  display: true,
                  text: 'Re',
                }
              },
              y: {
                min: -1,
                max: 1,
                display: true,
                position: 'center',
                title : {
                  display: true,
                  text: 'Im',
                }
              }
            }
          }
        });

        const ctx1 = document.getElementById('myChart1').getContext('2d');
        const chart1 = new Chart(ctx1, {
          type: 'line',
          data: {
            labels: [],
            datasets: [{
              label: "Tension",
              yAxisID: 'y0',
              data: [],
              borderColor: 'rgb(75, 192, 192)',
              tension: 0.3 // Filtrage du signal ; doit être compris entre 0 (pas de filtrage) et 1 (filtrage max)
            },
            {
              label: "Courant",
              yAxisID: 'y1',
              data: [],
              borderColor: 'rgb(192, 192, 75)',
              tension: 0.3
            }]
          },
          options: {
            scales: {
              x: {
                display: true,
                title : {
                  display: true,
                  text: 'Echantillons',
                }
              },
              y0: {
                display: true,
                position: 'left',
                title : {
                  display: true,
                  text: 'Tension (V)',
                }
              },
              y1: {
                display: true,
                position: 'left',
                title : {
                  display: true,
                  text: 'Courant (A)',
                }
              }
            }
          }
        });

        const ctx2 = document.getElementById('myChart2').getContext('2d');
        const chart2 = new Chart(ctx2, {
          type: 'line',
          data: {
            labels: [],
            datasets: [{
              pointRadius: 0,
              label: "Historique Puissance",
              yAxisID: 'y0',
              data: [],
              borderColor: 'blue',
              tension: 0.3 // Filtrage du signal ; doit être compris entre 0 (pas de filtrage) et 1 (filtrage max)
            },
            {
              pointRadius: 0,
              label: "Historique Tension",
              yAxisID: 'y1',
              data: [],
              borderColor: 'green',
              tension: 0.3
            },
            {
              pointRadius: 0,
              label: "Historique Courant",
              yAxisID: 'y2',
              data: [],
              borderColor: 'red',
              tension: 0.3
            }]
          },
          options: {
            plugins: {
              zoom: {
                zoom: {
                  drag: { // En sélectionnant une zone autour de la valeur à zoommer
                    enabled: true,
                  },
                  wheel: { // Avec la molette de la souris
                    enabled: true,
                  },
                  pinch: { // En pincement avec deux doigts sur un écran tactile
                    enabled: true,
                  },
                  mode: 'x',
                  onZoom: ({ brouette }) => console.log('Debut Zoom'),
                  onZoomComplete: ({ brouette }) => console.log('Fin Zoom'),
                },
                pan: {
                  enabled: true,
                  mode: 'x',
                  modifierKey: 'ctrl',
                  onPan: ({ brouette }) => console.log('Debut Pan'),
                  onPanStart: ({ brouette }) => console.log('Lancement Pan'),
                  onPanComplete: ({ brouette }) => console.log('Fin Pan'),
                },
              },
            },
            scales: {
              x: {
                display: true,
                title : {
                  display: true,
                  text: 'Date et heure',
                }
              },
              y0: {
                min : 0,
                max : pAjustee,
                display: true,
                position: 'right',
                title : {
                  display: true,
                  text: 'Puissance (W)',
                }
              },
              y1: {
                min : 0,
                max : 300,
                display: true,
                position: 'left',
                title : {
                  display: true,
                  text: 'Tension (V)',
                }
              },
              y2: {
                min : 0,
                max : 20,
                display: true,
                position: 'left',
                title : {
                  display: true,
                  text: 'Courant (A)',
                }
              }
            }
          }
        });

        function updateChart(type) {
          if (type == 1) {
            fetch('/maj-DT')
              .then(response => response.text())
              .then(data => {
                document.getElementById('DetH').textContent = data;
              });

            fetch('/pActive')
              .then(response => response.text())
              .then(data => {
                document.getElementById('PA').textContent = data;

                chart0.data.datasets[0].data = [{ x: 0.6, y: 0.3, r: 40 }];

                chart0.update();
              });

            if (intervalId == -1) {
              intervalId = setInterval(updateChart, interval, 1);
            }
            fetch('/formeUI')
            .then(response => response.text())
            .then(data => {
              const rows = data.split('\n');
              const labels = [];
              const values0 = [];
              const values1 = [];
              let indx = 0;
              rows.forEach(row => {
                const [value0, value1] = row.split(',');
                labels.push(indx++);
                values0.push(parseFloat(value0));
                values1.push(parseFloat(value1));
              });             
              chart1.data.labels = labels;
              chart1.data.datasets[0].data = values0;
              chart1.data.datasets[1].data = values1;
              chart1.update();
            });
            txt1.style.display = 'block';
            txt2.style.display = 'none';
            document.getElementById('myChart0').style.display = 'block';
            document.getElementById('myChart1').style.display = 'block';
            document.getElementById('myChart2').style.display = 'none';
          } else {
            if (intervalId != -1) {
              clearInterval(intervalId);
            }
            intervalId = -1;
            fetch('/histoUIP')
            .then(response => response.text())
            .then(data => {
              const rows = data.split('\n');
              const labels = [];
              const values0 = [];
              const values1 = [];
              const values2 = [];
              let indx = 0;
              rows.forEach(row => {
              const [label, value0, value1, value2] = row.split(',');
              labels.push(label);
                values0.push(parseFloat(value0));
                values1.push(parseFloat(value1));
                values2.push(parseFloat(value2));
              });   
              chart2.resetZoom();          
              chart2.data.labels = labels;
              chart2.data.datasets[0].data = values0;
              chart2.data.datasets[1].data = values1;
              chart2.data.datasets[2].data = values2;
              chart2.update();
            });
            txt1.style.display = 'none';
            txt2.style.display = 'block';
            document.getElementById('myChart0').style.display = 'none';
            document.getElementById('myChart1').style.display = 'none';
            document.getElementById('myChart2').style.display = 'block';
          }
        }

      </script>
    </body>
    </html>

Cordialement.

Pierre.

En se basant sur cette proposition, je n'arrive pas à faire en sorte que les deux graphiques aient la même hauteur, mais que celui de gauche ne représente que 25 % en horizontal et celui de droite, le reste soit 75 %.

Voici le code html complet représentant mon application avec ta structure :


    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>ESP32 Routeur photovoltaïque</title>
      <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.js"></script>
<!--      <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> -->
      <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.min.js"></script>
	  

    </head>
    <body onload="init();">
      <h1 style="text-align: center;">ESP32 Routeur photovoltaïque</h1>
      <div style="text-align: center;">
<!--        <button id="maj-DT">MaJ Date & Heure</button> -->
        <button onclick="updateChart(1);" class="button button-data">Forme Courant &amp; Tension</button> 
        <button onclick="updateChart(2);" class="button button-data">Historique Puissance</button>
        <a href="download-data"><button class="button button-data">Télécharger les données</button></a>
        <button onclick="chgtHistos();" class="button button-data">Téléchargement historiques</button>
      </div>
      <div id = "txt1">
        <p>Date et Heure : <span id="DetH">0.00</span></p>
        <p>Puissance active (W) : <span id="PA">0.00</span></p>
      </div>
      <div id = "txt2">
        <p>Puissance max : <input type="number" id="pMaxMod" value="1000" min="0"> <button onclick="ajusterPmax()">Ajuster</button></p>
      </div>
	  
	  <div style="display: flex;">
        <div style="width: 45%;">
          <canvas id="myChart0" style="background-color: aqua;"></canvas>
        </div>
        <div style="width: 45%;">
          <canvas id="myChart1" style="background-color: beige;"></canvas>
        </div>
      </div>
	  <canvas id="myChart2" width="100%" height="35%" style="display: none"></canvas>

      <script>
        let txt1 = document.getElementById("txt1");
        let txt2 = document.getElementById("txt2");

        const interval = 2000;
        let intervalId = -1;
        let pAjustee = 1000;  // Limite par défaut du nombre de points affichés

        function init() {
          txt1.style.display = "none";
          txt2.style.display = "none";
		  
		  
		  
        }
        async function chgtHistos() {
          for (let i = 0; i < 7; i++) {
            fetch('/quelFichier?val=' + i)
              .then(response => {
                const contentDisposition = response.headers.get('Content-Disposition');
                const defaultFileName = `fichier_${i}.txt`; // Nom par défaut si le serveur n'en fournit pas
                let fileName = defaultFileName;
                if (contentDisposition && contentDisposition.includes('filename=')) {
                  const match = contentDisposition.match(/filename="?([^"]+)"?/);
                  if (match) {
                    fileName = match[1];
                  }
                }
                return response.blob().then(blob => ({ blob, fileName }));
              })
              .then(({ blob, fileName }) => {
                const url = window.URL.createObjectURL(blob);
                const a = document.createElement('a');
                a.style.display = 'none';
                a.href = url;
                a.download = fileName;
                document.body.appendChild(a);
                a.click();
                window.URL.revokeObjectURL(url);
              })
              .catch(error => console.error('Erreur lors du téléchargement:', error));
          }
        }

        function ajusterPmax() {
          const newPmax = document.getElementById('pMaxMod').value;
          const parsedPmax = parseInt(newPmax, 10);  // On force la base 10 pour la conversion
          if (!isNaN(parsedPmax) && parsedPmax > 0) {
            pAjustee = parsedPmax;
            chart2.options.scales.y0.max = pAjustee;
            chart2.options.scales.y2.max = pAjustee/230;
            chart2.update();
            fetch('/modifVal?val='+pAjustee);
          }
        }
        const ctx0 = document.getElementById('myChart0').getContext('2d');
        const chart0 = new Chart(ctx0, {
          type: 'bubble',
          data: {
            datasets: [{
              label: 'Cos Phi',
              backgroundColor: 'rgb(255, 99, 132)'
            }]
          },
          options: {
            scales: {
              x: {
                min: -1,
                max: 1,
                display: true,
                position: 'center',
                title : {
                  display: true,
                  text: 'Re',
                }
              },
              y: {
                min: -1,
                max: 1,
                display: true,
                position: 'center',
                title : {
                  display: true,
                  text: 'Im',
                }
              }
            }
          }
        });

        const ctx1 = document.getElementById('myChart1').getContext('2d');
        const chart1 = new Chart(ctx1, {
          type: 'line',
          data: {
            labels: [],
            datasets: [{
              label: "Tension",
              yAxisID: 'y0',
              data: [],
              borderColor: 'rgb(75, 192, 192)',
              tension: 0.3 // Filtrage du signal ; doit être compris entre 0 (pas de filtrage) et 1 (filtrage max)
            },
            {
              label: "Courant",
              yAxisID: 'y1',
              data: [],
              borderColor: 'rgb(192, 192, 75)',
              tension: 0.3
            }]
          },
          options: {
            scales: {
              x: {
                display: true,
                title : {
                  display: true,
                  text: 'Echantillons',
                }
              },
              y0: {
                display: true,
                position: 'left',
                title : {
                  display: true,
                  text: 'Tension (V)',
                }
              },
              y1: {
                display: true,
                position: 'left',
                title : {
                  display: true,
                  text: 'Courant (A)',
                }
              }
            }
          }
        });

        const ctx2 = document.getElementById('myChart2').getContext('2d');
        const chart2 = new Chart(ctx2, {
          type: 'line',
          data: {
            labels: [],
            datasets: [{
              pointRadius: 0,
              label: "Historique Puissance",
              yAxisID: 'y0',
              data: [],
              borderColor: 'blue',
              tension: 0.3 // Filtrage du signal ; doit être compris entre 0 (pas de filtrage) et 1 (filtrage max)
            },
            {
              pointRadius: 0,
              label: "Historique Tension",
              yAxisID: 'y1',
              data: [],
              borderColor: 'green',
              tension: 0.3
            },
            {
              pointRadius: 0,
              label: "Historique Courant",
              yAxisID: 'y2',
              data: [],
              borderColor: 'red',
              tension: 0.3
            }]
          },
          options: {
            plugins: {
              zoom: {
                zoom: {
                  drag: { // En sélectionnant une zone autour de la valeur à zoommer
                    enabled: true,
                  },
                  wheel: { // Avec la molette de la souris
                    enabled: true,
                  },
                  pinch: { // En pincement avec deux doigts sur un écran tactile
                    enabled: true,
                  },
                  mode: 'x',
                  onZoom: ({ brouette }) => console.log('Debut Zoom'),
                  onZoomComplete: ({ brouette }) => console.log('Fin Zoom'),
                },
                pan: {
                  enabled: true,
                  mode: 'x',
                  modifierKey: 'ctrl',
                  onPan: ({ brouette }) => console.log('Debut Pan'),
                  onPanStart: ({ brouette }) => console.log('Lancement Pan'),
                  onPanComplete: ({ brouette }) => console.log('Fin Pan'),
                },
              },
            },
            scales: {
              x: {
                display: true,
                title : {
                  display: true,
                  text: 'Date et heure',
                }
              },
              y0: {
                min : 0,
                max : pAjustee,
                display: true,
                position: 'right',
                title : {
                  display: true,
                  text: 'Puissance (W)',
                }
              },
              y1: {
                min : 0,
                max : 300,
                display: true,
                position: 'left',
                title : {
                  display: true,
                  text: 'Tension (V)',
                }
              },
              y2: {
                min : 0,
                max : 20,
                display: true,
                position: 'left',
                title : {
                  display: true,
                  text: 'Courant (A)',
                }
              }
            }
          }
        });

        function updateChart(type) {
          if (type == 1) {
            fetch('/maj-DT')
              .then(response => response.text())
              .then(data => {
                document.getElementById('DetH').textContent = data;
              });

            fetch('/pActive')
              .then(response => response.text())
              .then(data => {
                document.getElementById('PA').textContent = data;

                chart0.data.datasets[0].data = [{ x: 0.6, y: 0.3, r: 40 }];

                chart0.update();
              });

            if (intervalId == -1) {
              intervalId = setInterval(updateChart, interval, 1);
            }
            fetch('/formeUI')
            .then(response => response.text())
            .then(data => {
              const rows = data.split('\n');
              const labels = [];
              const values0 = [];
              const values1 = [];
              let indx = 0;
              rows.forEach(row => {
                const [value0, value1] = row.split(',');
                labels.push(indx++);
                values0.push(parseFloat(value0));
                values1.push(parseFloat(value1));
              });             
              chart1.data.labels = labels;
              chart1.data.datasets[0].data = values0;
              chart1.data.datasets[1].data = values1;
              chart1.update();
            });
            txt1.style.display = 'block';
            txt2.style.display = 'none';
            document.getElementById('myChart0').style.display = 'block';
            document.getElementById('myChart1').style.display = 'block';
            document.getElementById('myChart2').style.display = 'none';
          } else {
            if (intervalId != -1) {
              clearInterval(intervalId);
            }
            intervalId = -1;
            fetch('/histoUIP')
            .then(response => response.text())
            .then(data => {
              const rows = data.split('\n');
              const labels = [];
              const values0 = [];
              const values1 = [];
              const values2 = [];
              let indx = 0;
              rows.forEach(row => {
              const [label, value0, value1, value2] = row.split(',');
              labels.push(label);
                values0.push(parseFloat(value0));
                values1.push(parseFloat(value1));
                values2.push(parseFloat(value2));
              });   
              chart2.resetZoom();          
              chart2.data.labels = labels;
              chart2.data.datasets[0].data = values0;
              chart2.data.datasets[1].data = values1;
              chart2.data.datasets[2].data = values2;
              chart2.update();
            });
            txt1.style.display = 'none';
            txt2.style.display = 'block';
            document.getElementById('myChart0').style.display = 'none';
            document.getElementById('myChart1').style.display = 'none';
            document.getElementById('myChart2').style.display = 'block';
          }
        }

      </script>
    </body>
    </html>

Merci de ton aide.

Pierre.

Partant sur ces bases, je suis arrivé à ce qui suit et qui semble satisfaisant :

      <div style="display: flex;">
        <div style="width: 25%;">
          <canvas id="myChart0"  width="100%" height="100%" style="display: none"></canvas>
        </div>
        <div style="width: 73%;">
          <canvas id="myChart1"  width="100%" height="40%" style="display: none"></canvas>
        </div>
      </div>

Les dimensions dans div style semblent donner le rapport de grandeur des deux graphiques dans la fenêtre et les dimensions dans les canvas semblent correspondre au rapport largeur sur hauteur des graphiques.

Pour autant, il reste un petit bémol : lorsque je réduis la fenêtre, les deux graphiques rétrécissent proportionnellement mais lorsque je la ré-agrandis, les deux graphiques restent à la dimension mini qu'elles ont acquise. Il faut que je repasse par l'affichage du troisième graphique et que je revienne sur les deux concernés pour qu'ils reprennent la bonne forme.

J'ai vu qu'il y avait des paramètres flex-grow et flex-shrink, mais je ne sais pas si cela va résoudre le problème ni comment les introduire dans les styles.

Cordialement.

Pierre.

Le soucis est que charjt, modifie les modifications demandé par le HTML.
essaye ça dans les options de tes graphes.

responsive: true,
maintainAspectRatio: false,

Merci pour cette proposition, mais comment et où introduire ces paramètres dans ma description des graphiques dans la partie script ?

J'ai écrit ce qui suit :

        const ctx0 = document.getElementById('myChart0').getContext('2d');
        const chart0 = new Chart(ctx0, {
          responsive: true,
          maintainAspectRatio: false,
          type: 'bubble',
          data: {
            datasets: [{
              label: 'Cos Phi',
              backgroundColor: 'rgb(255, 0, 0)'
            }]
          },
          ....

Ce ne doit certainement pas être ça car les données ne s'affichent plus.

Cordialement.

Pierre

J'ai vu qu'en les mettant dans les options ça agissait. Mais comme je le souhaite :

responsive: true, est à vrai par défaut

maintainAspectRatio: false, Fait que aussi bien au rétrécissement qu'à l'agrandissement le comportement est le même. Mais alors, les graphiques sont complètement distordus.

Je vais essayer avec d'autres paramètres ...

Cordialement.

Pierre.

Oui désolé, je pensais te l'avoir indiqué.

Comme tu as changé complétement l'affichage, je n'ai pas reporté tout tes changements sur mon script Python, donc je n'ai plus le rafraichissement des données et l'action des boutons.

Voilà ce qu'il est devenu :

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>ESP32 Routeur photovoltaïque</title>
      <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.js"></script>
<!--      <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> -->
      <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.min.js"></script>

    </head>
    <body onload="init();">
      <h1 style="text-align: center;">ESP32 Routeur photovoltaïque</h1>
      <div style="text-align: center;">
<!--        <button id="maj-DT">MaJ Date & Heure</button> -->
        <button onclick="updateChart(1);" class="button button-data">Forme Courant &amp; Tension</button> 
        <button onclick="updateChart(2);" class="button button-data">Historique Puissance</button>
        <a href="download-data"><button class="button button-data">Télécharger les données</button></a>
        <button onclick="chgtHistos();" class="button button-data">Téléchargement historiques</button>
      </div>
      <div id = "txt1">
        <p>Date et Heure : <span id="DetH">0.00</span></p>
        <p>Puissance active (W) : <span id="PA">0.00</span></p>
      </div>
      <div id = "txt2">
        <p>Puissance max : <input type="number" id="pMaxMod" value="1000" min="0"> <button onclick="ajusterPmax()">Ajuster</button></p>
      </div>

      <div style="display: flex;">
        <div style="width: 25%;">
          <canvas id="myChart0"  width="100%" height="100%" style="display: none"></canvas>
        </div>
        <div style="width: 73%;">
          <canvas id="myChart1"  width="100%" height="40%" style="display: none"></canvas>
        </div>
      </div>
      <canvas id="myChart2" width="100%" height="35%" style="display: none"></canvas>

      <script>
        let txt1 = document.getElementById("txt1");
        let txt2 = document.getElementById("txt2");

        const interval = 2000;
        let intervalId = -1;
        let pAjustee = 1000;  // Limite par défaut du nombre de points affichés

        function init() {
          txt1.style.display = "none";
          txt2.style.display = "none";
        }

        async function chgtHistos() {
          for (let i = 0; i < 7; i++) {
            fetch('/quelFichier?val=' + i)
              .then(response => {
                const contentDisposition = response.headers.get('Content-Disposition');
                const defaultFileName = `fichier_${i}.txt`; // Nom par défaut si le serveur n'en fournit pas
                let fileName = defaultFileName;
                if (contentDisposition && contentDisposition.includes('filename=')) {
                  const match = contentDisposition.match(/filename="?([^"]+)"?/);
                  if (match) {
                    fileName = match[1];
                  }
                }
                return response.blob().then(blob => ({ blob, fileName }));
              })
              .then(({ blob, fileName }) => {
                const url = window.URL.createObjectURL(blob);
                const a = document.createElement('a');
                a.style.display = 'none';
                a.href = url;
                a.download = fileName;
                document.body.appendChild(a);
                a.click();
                window.URL.revokeObjectURL(url);
              })
              .catch(error => console.error('Erreur lors du téléchargement:', error));
          }
        }

        function ajusterPmax() {
          const newPmax = document.getElementById('pMaxMod').value;
          const parsedPmax = parseInt(newPmax, 10);  // On force la base 10 pour la conversion
          if (!isNaN(parsedPmax) && parsedPmax > 0) {
            pAjustee = parsedPmax;
            chart2.options.scales.y0.max = pAjustee;
            chart2.options.scales.y2.max = pAjustee/230;
            chart2.update();
            fetch('/modifVal?val='+pAjustee);
          }
        }
      
/*        document.getElementById('maj-DT').addEventListener('click', function() {
          fetch('/maj-DT')
            .then(response => response.text())
            .then(data => {
              document.getElementById('DetH').textContent = data;
            });
        });*/

        const ctx0 = document.getElementById('myChart0').getContext('2d');
        const chart0 = new Chart(ctx0, {
          type: 'bubble',
          data: {
            datasets: [{
              label: 'Cos Phi',
              backgroundColor: 'rgb(255, 0, 0)'
            }]
          },
          options: {
            scales: {
              x: {
                min: -1,
                max: 1,
                display: true,
                position: 'center',
                title : {
                  display: true,
                  text: 'Re',
                }
              },
              y: {
                min: -1,
                max: 1,
                display: true,
                position: 'center',
                title : {
                  display: true,
                  text: 'Im',
                }
              }
            }
          }
        });

        const ctx1 = document.getElementById('myChart1').getContext('2d');
        const chart1 = new Chart(ctx1, {
          type: 'line',
          data: {
            labels: [],
            datasets: [{
              label: "Tension",
              yAxisID: 'y0',
              data: [],
              borderColor: 'rgb(75, 192, 192)',
              tension: 0.3 // Filtrage du signal ; doit être compris entre 0 (pas de filtrage) et 1 (filtrage max)
            },
            {
              label: "Courant",
              yAxisID: 'y1',
              data: [],
              borderColor: 'rgb(192, 192, 75)',
              tension: 0.3
            }]
          },
          options: {
            scales: {
              x: {
                display: true,
                title : {
                  display: true,
                  text: 'Echantillons',
                }
              },
              y0: {
                display: true,
                position: 'left',
                title : {
                  display: true,
                  text: 'Tension (V)',
                }
              },
              y1: {
                display: true,
                position: 'left',
                title : {
                  display: true,
                  text: 'Courant (A)',
                }
              }
            }
          }
        });

        const ctx2 = document.getElementById('myChart2').getContext('2d');
        const chart2 = new Chart(ctx2, {
          type: 'line',
          data: {
            labels: [],
            datasets: [{
              pointRadius: 0,
              label: "Historique Puissance",
              yAxisID: 'y0',
              data: [],
              borderColor: 'blue',
              tension: 0.3 // Filtrage du signal ; doit être compris entre 0 (pas de filtrage) et 1 (filtrage max)
            },
            {
              pointRadius: 0,
              label: "Historique Tension",
              yAxisID: 'y1',
              data: [],
              borderColor: 'green',
              tension: 0.3
            },
            {
              pointRadius: 0,
              label: "Historique Courant",
              yAxisID: 'y2',
              data: [],
              borderColor: 'red',
              tension: 0.3
            }]
          },
          options: {
            plugins: {
              zoom: {
                zoom: {
                  drag: { // En sélectionnant une zone autour de la valeur à zoommer
                    enabled: true,
                  },
                  wheel: { // Avec la molette de la souris
                    enabled: true,
                  },
                  pinch: { // En pincement avec deux doigts sur un écran tactile
                    enabled: true,
                  },
                  mode: 'x',
                  onZoom: ({ brouette }) => console.log('Debut Zoom'),
                  onZoomComplete: ({ brouette }) => console.log('Fin Zoom'),
                },
                pan: {
                  enabled: true,
                  mode: 'x',
                  modifierKey: 'ctrl',
                  onPan: ({ brouette }) => console.log('Debut Pan'),
                  onPanStart: ({ brouette }) => console.log('Lancement Pan'),
                  onPanComplete: ({ brouette }) => console.log('Fin Pan'),
                },
              },
            },
            scales: {
              x: {
                display: true,
                title : {
                  display: true,
                  text: 'Date et heure',
                }
              },
              y0: {
                min : 0,
                max : pAjustee,
                display: true,
                position: 'right',
                title : {
                  display: true,
                  text: 'Puissance (W)',
                }
              },
              y1: {
                min : 0,
                max : 300,
                display: true,
                position: 'left',
                title : {
                  display: true,
                  text: 'Tension (V)',
                }
              },
              y2: {
                min : 0,
                max : 20,
                display: true,
                position: 'left',
                title : {
                  display: true,
                  text: 'Courant (A)',
                }
              }
            }
          }
        });

        function updateChart(type) {
          if (type == 1) {
            fetch('/maj-DT')
              .then(response => response.text())
              .then(data => {
                document.getElementById('DetH').textContent = data;
              });

            fetch('/pActive') // Affichage de la puissance active
              .then(response => response.text())
              .then(data => {
                document.getElementById('PA').textContent = data;
              });
 
            fetch('/infoUIP') // Affichage du cos Phi
              .then(response => response.text())
              .then(data => {
                const [P, U, I] = data.split(',');
                let Re = parseFloat(P)/(parseFloat(U)*parseFloat(I));
                let Im = Math.sqrt(1 - Math.pow(Re, 2));
                chart0.data.datasets[0].data = [{ x: Re, y: Im, r: 10}];
                if (Im > 0.98)
                  {chart0.data.datasets[0].backgroundColor = ['rgb(255, 0, 0)'];}
                else if (Im > 0.83)
                  {chart0.data.datasets[0].backgroundColor = ['rgb(236, 98, 0)'];}
                else if (Im > 0.55)
                  {chart0.data.datasets[0].backgroundColor = ['rgb(180, 180, 0)'];}
                else if (Im > 0.19)
                  {chart0.data.datasets[0].backgroundColor = ['rgb(98, 236, 0)'];}
                else
                  {chart0.data.datasets[0].backgroundColor = ['rgb(0, 255, 0)'];}
                chart0.update();
              });

            if (intervalId == -1) {
              intervalId = setInterval(updateChart, interval, 1);
            }
            fetch('/formeUI')
            .then(response => response.text())
            .then(data => {
              const rows = data.split('\n');
              const labels = [];
              const values0 = [];
              const values1 = [];
              let indx = 0;
              rows.forEach(row => {
                const [value0, value1] = row.split(',');
                labels.push(indx++);
                values0.push(parseFloat(value0));
                values1.push(parseFloat(value1));
              });             
              chart1.data.labels = labels;
              chart1.data.datasets[0].data = values0;
              chart1.data.datasets[1].data = values1;
              chart1.update();
            });
            txt1.style.display = 'block';
            txt2.style.display = 'none';
            document.getElementById('myChart0').style.display = 'block';
            document.getElementById('myChart1').style.display = 'block';
            document.getElementById('myChart2').style.display = 'none';
          } else {
            if (intervalId != -1) {
              clearInterval(intervalId);
            }
            intervalId = -1;
            fetch('/histoUIP')
            .then(response => response.text())
            .then(data => {
              const rows = data.split('\n');
              const labels = [];
              const values0 = [];
              const values1 = [];
              const values2 = [];
              let indx = 0;
              rows.forEach(row => {
                const [label, value0, value1, value2] = row.split(',');
//              const [value0, value1, value2] = row.split(',');
              labels.push(label);
//              labels.push(indx++);
                values0.push(parseFloat(value0));
                values1.push(parseFloat(value1));
                values2.push(parseFloat(value2));
              });   
              chart2.resetZoom();          
              chart2.data.labels = labels;
              chart2.data.datasets[0].data = values0;
              chart2.data.datasets[1].data = values1;
              chart2.data.datasets[2].data = values2;
              chart2.update();
            });
            txt1.style.display = 'none';
            txt2.style.display = 'block';
            document.getElementById('myChart0').style.display = 'none';
            document.getElementById('myChart1').style.display = 'none';
            document.getElementById('myChart2').style.display = 'block';
          }
        }

      </script>
    </body>
    </html>

J'ai essayé, respectivement dans les options de chart0 et chart1 :

			onResize: (chart0, size) => {
			  chart0.resize(window.innerWidth, size.width);
            },  
...
			onResize: (chart1, size) => {
			  chart1.resize(window.innerWidth, size.width);
            },  

Cette fois les comportements au rétrécissement et agrandissement sont identiques ... mais mes graphiques débordent de la fenêtre (environ 30 %) !

Cordialement.

Pierre.

Je suis désolé, mais je ne comprends pas ce que tu veux faire ?
pourquoi tu met des tailles proportionnel dans le canvas, alors qu'il est seul dans le DIV qui est lui même proportionnel à la taille proportionnel.
Tu veux les canvas 0 et 1 sur la même ligne est que 1 soit 3 fois plus large que 0 ?
et pour le 2 tu veux quoi ?

Voilà ce que ça donne en image :
Grande fenêtre :

Dans cet affichage, le graphique de gauche représente 25 % de la largeur de l'écran --> donné par

        <div style="width: 25%;">

et sa hauteur est égale à sa largeur --> données par

         <canvas id="myChart0"  width="100%" height="100%" style="display: none"></canvas>

Écran réduit : Les proportions sont gardées :

Je reviens à la grande fenêtre : Les proportions sont toujours gardées, mais l'affichage reste réduit. J'aurai aimé à ce qu'il revienne à ce qu'il était au départ.

Cordialement.

Pierre.

Tu as du coup mis :
responsive: true,
maintainAspectRatio: true,

Effectivement autant le resize fonctionne bien, mais la restauration en pleine écran ou fenêtrée ne semble pas bien fonctionné.

Il faudrait peut être imposer un resize des graphiques.

Edit:
j'ai essayé un truc comme ça:

const charts = [];
        window.addEventListener("resize", () => {
            charts.forEach(chart => {
            chart.resize();
            console.error("Resize");
          });
        });

avec charts.push(chart0); pour chaque graphe à redimensionner.

Ca semble améliorer, mais ce n'est pas encore parfait :frowning:

et où se place cette instruction ?

Cordialement.