多次复制画布:克隆画布还是复制图像数据?

时间:2015-05-08 05:09:39

标签: javascript html html5 canvas cloning

我的一个界面元素正在使用HTML5 <canvas>元素和相关的JavaScript API进行渲染。此元素在整个应用程序的同一屏幕和多个屏幕上的多个位置使用。在所需的任何地方展示这种效果的最有效方法是什么?

我的第一个想法是绘制一个主画布,然后我将其克隆并插入页面中所需的位置。主画布可能类似于:

var master = $('<canvas>').attr({
      width: 100,
      height: 100
    }),
    c = master[0],
    ctx = c.getContext("2d");

    ctx.fillStyle = "#FF0000";
    ctx.fillRect(0, 0, 150, 75);

我想说我想在这些div容器中复制画布:

<div class="square-container" id="square_header"></div>
...
<div class="square-container" id="square_dataTable"></div>
...
<div class="square-container" id="square_gallery"></div>
....

当页面加载时,我这样做是为了在每个容器中插入一个重复的canvas元素:

$(document).ready(function() {
    $('.square-container').each(function() {
        master.clone().appendTo($(this));
    });
}); 

在画布上呈现的内容将比本示例中使用的简单方块更复杂,但仍然只是一个静态图像。但是,有可能每页都会有数十个不同的图像被克隆数十次。

我想到的另一种方法是使用toDataURL()方法创建图像,并将其设置为适当的图像&#39;来源:

var master = $('<canvas>').attr({
    width: 100,
    height: 100
}),
    c = master[0],
    ctx = c.getContext("2d");

    ctx.fillStyle = "#FF0000";
    ctx.fillRect(0,0,150,75);

var square = c.toDataURL('image/png'); 

我会在必要时添加图片标签:

<img src="" id="square_header" class="square" alt="" />
...
<img src="" id="square_dataTable1" class="square" alt="" />
...
<img src="" id="square_gallery" class="square" alt="" />
....

然后将所有SRC设置为新创建的图像:

$(document).ready(function() {
    $('img.square').attr('src', square);
});

对我来说,它几乎看起来像六个,另外六个。但是我想知道一种方式是否被认为比另一方更好?如果在<canvas>上呈现的内容更复杂,那么一种方式会比另一种更有效吗?

同样的精神,当我需要在后续页面上使用该元素时,最好是在每个页面上执行所有javascript(来自上述最佳解决方案),或者保存CANVAS_ELEMENT.toDataURL()的值在一个cookie中然后在后续页面上使用它会更有效吗?

1 个答案:

答案 0 :(得分:6)

克隆画布将复制其尺寸和样式,但不会复制其图像数据。您可以通过在上下文中调用drawImage来复制图像数据。要将originalCanvas的内容绘制到duplicateCanvas,请写:

duplicateCanvas.getContext('2d').drawImage(originalCanvas, 0, 0);

作为演示,以下代码段会生成四个画布:

  • 原画布上画有小场景

  • 仅通过调用cloneNode

  • 制作的副本
  • 通过致电cloneNodedrawImage

  • 制作的副本
  • 通过创建新图像并将其源设置为数据URI而创建的副本

&#13;
&#13;
function message(s) {
  document.getElementById('message').innerHTML += s + '<br />';
}

function timeIt(action, description, initializer) {
  var totalTime = 0,
      initializer = initializer || function () {};
  initializer();
  var startTime = performance.now();
  action();
  var elapsed = performance.now() - startTime;
  message('<span class="time"><span class="number">' +
      Math.round(elapsed * 1000) + ' &mu;s</span></span> ' + description);
}

function makeCanvas() {
  var canvas = document.createElement('canvas'),
      context = canvas.getContext('2d');
  canvas.width = 100;
  canvas.height = 100;
  timeIt(function () {
    context.fillStyle = '#a63d3d';
    context.fillRect(10, 10, 80, 40);   // Paint a small scene.
    context.fillStyle = '#3b618c';
    context.beginPath();
    context.arc(60, 60, 25, 0, 2*Math.PI);
    context.closePath();
    context.fill();
  }, '(millionths of a second) to draw original scene', function () {
    context.clearRect(0, 0, canvas.width, canvas.height);
  });
  return canvas;
}

// copyCanvas returns a canvas containing the same image as the given canvas.
function copyCanvas(original) {
  var copy;
  timeIt(function () {
    copy = original.cloneNode();  // Copy the canvas dimensions.
    copy.getContext('2d').drawImage(original, 0, 0);  // Copy the image.
  }, 'to copy canvas with cloneNode and drawImage');
  return copy;
}

// imageFromStorage extracts the image data from a canvas, stores the image data
// in a browser session, then retrieves the image data from the session and
// makes a new image element out of it. We measure the total time to retrieve
// the data and make the image.
function imageFromStorage(original) {
  var image,
      dataURI = original.toDataURL();
  timeIt(function () {
    image = document.createElement('img');
    image.src = dataURI;
  }, 'to make image from a dataURI');
  return image;
}

function pageLoad() {
  var target = document.getElementById('canvases'),
      containers = {},  // We'll put the canvases inside divs.
      names = ['original', 'cloneNode', 'drawImage', 'dataURI'];
  for (var i = 0; i < names.length; ++i) {
    var name = names[i],  // Use the name as an ID and a visible header.
        container = document.createElement('div'),
        header = document.createElement('div');
    container.className = 'container';
    header.className = 'header';
    header.innerHTML = container.id = name;
    container.appendChild(header);
    target.appendChild(container);
    containers[name] = container;  // The canvas container is ready.
  }
  var canvas = makeCanvas();
  containers.original.appendChild(canvas);  // Original canvas.
  containers.cloneNode.appendChild(canvas.cloneNode());  // cloneNode
  containers.drawImage.appendChild(copyCanvas(canvas));  // cloneNode + drawImage
  containers.dataURI.appendChild(imageFromStorage(canvas));  // localStorage
}

pageLoad();
&#13;
body {
  font-family: sans-serif;
}
.header {
  font-size: 18px;
}
.container {
  margin: 10px;
  display: inline-block;
}
canvas, img {
  border: 1px solid #eee;
}
#message {
  color: #666;
  font-size: 16px;
  line-height: 28px;
}
#message .time {
  display: inline-block;
  text-align: right;
  width: 100px;
}
#message .number {
  font-weight: bold;
  padding: 1px 3px;
  color: #222;
  background: #efedd4;
}
&#13;
<div id="canvases"></div>

<div id="message"></div>
&#13;
&#13;
&#13;

如果您致电toDataURL将图片数据复制到字符串中以便在其他页面中使用,请不要将字符串放入Cookie中。 Cookie旨在存储少量数据。而是使用HTML5 Web Storage API将图像数据存储在浏览器中。或者,如果图像在用户会话之间没有变化,您可以将其渲染为服务器上的PNG图像,并使用Cache-Control标头鼓励浏览器cache the image file for fast retrieval

当涉及到客户端图像渲染的性能时,重新绘制场景可能比将字符串化图像数据绘制到画布上更快。解码字符串并绘制像素是一项相对昂贵的操作。要了解在每个页面上重绘场景是否有意义,您可以使用performance.now计划绘图操作,如代码段所示。