Html canvas 元素在 drag/drop 之后将宽度和高度重置为零

Html canvas element resets width and height to zero after drag/drop

我正在开发可自定义的仪表板,用户可以在其中(除其他功能外)拖动仪表板图块(div 元素)并将这些图块重新放置在仪表板中的任何位置。

HTML结构

html 结构类似于下面的代码片段

<div class="dashboard">
    <div class="tile"><canvas/></div>
    <div class="tile"><canvas/></div>
    <div class="tile empty"></div>
</div>

预期行为

想法是 .dashboard 可以包含多个 .tiles,每个 .tile 包含一个报告(在 canvas 元素上绘制的 graph/chart) .其中一些 .tiles 可以是 .empty,如不包含任何报告。然后可以将 .tile 拖放到 .empty 方块中。

因此,div.tile.empty 用作“拖放区”,而 div.tile 将是可拖动元素。下面添加了一个 fiddler 片段,作为一个简单但功能齐全的示例。

使用的库

问题

一切似乎都运行良好,只是在删除 .tile 之后 canvas 将其 width/height 重置为零!!!而且我还没有找到一种方法可以将其重置为 drag/drop 事件之前的原始尺寸。即使我手动设置 width/height,canvas.

上也不会绘制任何内容

问题

有什么方法可以保留(或恢复)canvas 的 width/height,同时在 drag/dropping 后重新绘制图形?

我尝试使用 chartjsupdaterenderresize 函数无济于事。 这些函数的文档可以在下面的 link(版本 3.5.0)...

中找到

https://www.chartjs.org/docs/3.5.0/developers/api.html

代码示例

这是一个示例代码片段,您可以在其中重现上述问题。这些按钮是我在 drag/dropping.

之后尝试 update/resize/re-render 图表

var $sourceTile = null;
var charts = [];

$(() => {
  $(".buttons-container a").on("click", btnClickHandler);

  renderChart("canvas1", 'doughnut');
  renderChart("canvas2", "line");

  attachDropHandlers();
});

attachDropHandlers = () => {
  $(".tile").off("dragstart").off("dragover").off("drop");
  $(".tile .report").on("dragstart", dragStartHandler);
  $(".tile.empty").on("dragover", dragOverHandler);
  $(".tile.empty").on("drop", dropHandler);
}

dragStartHandler = (e) => {
  const $target = $(e.target);
  const $report = $target.is(".report") ? $target : $target.parents(".report");
  $sourceTile = $report.parents(".tile");

  e.originalEvent.dataTransfer.setData("application/dashboard", $report[0].id);
  e.originalEvent.dataTransfer.effectAllowed = "move";
  e.originalEvent.dataTransfer.dropEffect = "move";
}

dragOverHandler = (e) => {
  e.preventDefault();
  e.originalEvent.dataTransfer.dropEffect = "move";
}

dropHandler = (e) => {
  e.preventDefault();
  const id = e.originalEvent.dataTransfer.getData("application/dashboard");
  if (id) {
    $("#" + id).appendTo(".tile.empty");
    $(".tile.empty").removeClass("empty");

    if ($sourceTile) {
      $sourceTile.addClass("empty");
      attachDropHandlers();
    }
  }
}

renderChart = (canvasId, type) => {
  const labels = ["Red", "Green", "Blue"];
  const data = [30, 25, 42];
  const colors = ['rgba(255, 99, 132, 1)', 'rgba(54, 162, 235, 1)', 'rgba(255, 206, 86, 1)'];
  const canvas = document.getElementById(canvasId);
  const ctx = canvas.getContext('2d');
  const chart = new Chart(ctx, {
    type: type,
    data: {
      labels: labels,
      datasets: [{
        data: data,
        backgroundColor: colors,
        borderColor: colors,
        borderWidth: 1
      }]
    },
    options: {
      responsive: true,
      maintainAspectRatio: true,
      aspectRatio: 1,
      plugins: {
        legend: {
          display: false
        },
        htmlLegend: {
          tile: this.$tile,
          maxItems: 8
        }
      }
    }
  });

  chart.update();
  charts.push(chart);
}

btnClickHandler = (e) => {
  const button = e.target.id;
  switch (button) {
    case "btn1":
      charts.forEach((chart) => chart.update());
      break;
    case "btn2":
      charts.forEach((chart) => chart.update('resize'));
      break;
    case "btn3":
      charts.forEach((chart) => chart.render());
      break;
    case "btn4":
      charts.forEach((chart) => chart.resize());
      break;
    case "btn5":
      charts.forEach((chart) => chart.resize(120, 120));
      break;
  }
}
html,
body {
  background-color: #eee;
}

h3 {
  margin: 0;
  padding: 10px;
}

.dashboard {}

.dashboard .tile {
  display: inline-block;
  vertical-align: top;
  margin: 5px;
  height: 250px;
  width: 250px;
}

.tile.empty {
  border: 2px dashed #ccc;
}

.report {
  height: 250px;
  width: 250px;
  background-color: #fff;
  border-radius: 3px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .18);
}

.buttons-container {
  display: flex;
  justify-content: space-between;
  margin: 20px 0;
}

.buttons-container a {
  background-color: #673AB7;
  color: #EDE7F6;
  cursor: pointer;
  padding: 10px 15px;
  border-radius: 3px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .18);
}

.buttons-container a:hover {
  background-color: #7E57C2;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.6.0/dist/chart.min.js"></script>
<div class="dashboard">
  <div class="tile">
    <div id="report1" class="report" draggable="true">
      <h3>
        Report 1
      </h3>
      <div style="padding:10px;height:180px;width:180px">
        <canvas id="canvas1"></canvas>
      </div>
    </div>

  </div>
  <div class="tile">
    <div id="report2" class="report" draggable="true">
      <h3>
        Report 2
      </h3>
      <div style="padding:10px;height:180px;width:180px">
        <canvas id="canvas2"></canvas>
      </div>
    </div>

  </div>
  <div class="tile empty">

  </div>
</div>

<div class="buttons-container">
  <a id="btn1">update()</a>
  <a id="btn2">update('resize')</a>
  <a id="btn3">render()</a>
  <a id="btn4">resize()</a>
  <a id="btn5">resize(120,120)</a>
</div>

这是一个Chart.js issue of version 3.6.0 and fixed in version 3.6.1。示例如下:

var $sourceTile = null;
var charts = [];

$(() => {
  $(".buttons-container a").on("click", btnClickHandler);

  renderChart("canvas1", 'doughnut');
  renderChart("canvas2", "line");

  attachDropHandlers();
});

attachDropHandlers = () => {
  $(".tile").off("dragstart").off("dragover").off("drop");
  $(".tile .report").on("dragstart", dragStartHandler);
  $(".tile.empty").on("dragover", dragOverHandler);
  $(".tile.empty").on("drop", dropHandler);
}

dragStartHandler = (e) => {
  const $target = $(e.target);
  const $report = $target.is(".report") ? $target : $target.parents(".report");
  $sourceTile = $report.parents(".tile");

  e.originalEvent.dataTransfer.setData("application/dashboard", $report[0].id);
  e.originalEvent.dataTransfer.effectAllowed = "move";
  e.originalEvent.dataTransfer.dropEffect = "move";
}

dragOverHandler = (e) => {
  e.preventDefault();
  e.originalEvent.dataTransfer.dropEffect = "move";
}

dropHandler = (e) => {
  e.preventDefault();
  const id = e.originalEvent.dataTransfer.getData("application/dashboard");
  if (id) {
    $("#" + id).appendTo(".tile.empty");
    $(".tile.empty").removeClass("empty");

    if ($sourceTile) {
      $sourceTile.addClass("empty");
      attachDropHandlers();
    }
  }
}

renderChart = (canvasId, type) => {
  const labels = ["Red", "Green", "Blue"];
  const data = [30, 25, 42];
  const colors = ['rgba(255, 99, 132, 1)', 'rgba(54, 162, 235, 1)', 'rgba(255, 206, 86, 1)'];
  const canvas = document.getElementById(canvasId);
  const ctx = canvas.getContext('2d');
  const chart = new Chart(ctx, {
    type: type,
    data: {
      labels: labels,
      datasets: [{
        data: data,
        backgroundColor: colors,
        borderColor: colors,
        borderWidth: 1
      }]
    },
    options: {
      responsive: true,
      maintainAspectRatio: true,
      aspectRatio: 1,
      plugins: {
        legend: {
          display: false
        },
        htmlLegend: {
          tile: this.$tile,
          maxItems: 8
        }
      }
    }
  });

  chart.update();
  charts.push(chart);
}

btnClickHandler = (e) => {
  const button = e.target.id;
  switch (button) {
    case "btn1":
      charts.forEach((chart) => chart.update());
      break;
    case "btn2":
      charts.forEach((chart) => chart.update('resize'));
      break;
    case "btn3":
      charts.forEach((chart) => chart.render());
      break;
    case "btn4":
      charts.forEach((chart) => chart.resize());
      break;
    case "btn5":
      charts.forEach((chart) => chart.resize(120, 120));
      break;
  }
}
html,
body {
  background-color: #eee;
}

h3 {
  margin: 0;
  padding: 10px;
}

.dashboard {}

.dashboard .tile {
  display: inline-block;
  vertical-align: top;
  margin: 5px;
  height: 250px;
  width: 250px;
}

.tile.empty {
  border: 2px dashed #ccc;
}

.report {
  height: 250px;
  width: 250px;
  background-color: #fff;
  border-radius: 3px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .18);
}

.buttons-container {
  display: flex;
  justify-content: space-between;
  margin: 20px 0;
}

.buttons-container a {
  background-color: #673AB7;
  color: #EDE7F6;
  cursor: pointer;
  padding: 10px 15px;
  border-radius: 3px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .18);
}

.buttons-container a:hover {
  background-color: #7E57C2;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.6.1/dist/chart.min.js"></script>
<div class="dashboard">
  <div class="tile">
    <div id="report1" class="report" draggable="true">
      <h3>
        Report 1
      </h3>
      <div style="padding:10px;height:180px;width:180px">
        <canvas id="canvas1"></canvas>
      </div>
    </div>

  </div>
  <div class="tile">
    <div id="report2" class="report" draggable="true">
      <h3>
        Report 2
      </h3>
      <div style="padding:10px;height:180px;width:180px">
        <canvas id="canvas2"></canvas>
      </div>
    </div>

  </div>
  <div class="tile empty">

  </div>
</div>

<div class="buttons-container">
  <a id="btn1">update()</a>
  <a id="btn2">update('resize')</a>
  <a id="btn3">render()</a>
  <a id="btn4">resize()</a>
  <a id="btn5">resize(120,120)</a>
</div>