画布保存和恢复

时间:2015-02-26 17:14:22

标签: javascript html5 canvas

我有一个画布,我需要在添加每一点内容后更改高度 - 这是我获得动态画布高度的唯一方法。我尝试使用saverestore,因此我不会丢失所有样式,设置等。

我无法让saverestore工作。我一定做错了,或者这是错误的做法?

function DrawLeftText(text) {
var canvas = document.getElementById('canvasPaper');

 if (canvas.getContext) {
    var context = canvas.getContext('2d');

    context.textAlign = 'left';
    context.font = 'normal 20px Monkton';
    context.fillText(text, leftPosition, cursor);
    context.textAlign = 'start';

    context.save();


 }
}

function restoreSettings(){
  var canvas = document.getElementById('canvasPaper');
   if (canvas.getContext) {
    var context = canvas.getContext('2d');
     context.restore();
  }  
}


function onDrawReceipt() {
    var canvas = document.getElementById('canvasPaper');

    if (canvas.getContext) {
        var context = canvas.getContext('2d');

    context.textBaseline = 'top';

        lineSpace      =  20;
        leftPosition   =  0;
    // centerPosition =  canvas.width       / 2;
        centerPosition = (canvas.width + 26) / 2;
     // rightPosition  =  canvas.width;
        rightPosition  = (canvas.width - 0);

//      cursor = 0;
        cursor = 80;

    context.fillRect(25, cursor - 2, 526, 2); cursor += lineSpace;
    context.fillRect(25, cursor - 2, 526, 2); cursor += lineSpace;
    DrawCenterText(company['name']); cursor += lineSpace;
    DrawCenterText(company['address']['address_line_1']); cursor += lineSpace;
    DrawCenterText(company['address']['city'].toUpperCase()); cursor += lineSpace;
    DrawCenterText(company['address']['postcode']); cursor += lineSpace;

    context.clearRect(0, 0, canvas.width, canvas.height += 20);

    **//increasing the height and then trying to restore**

    restoreSettings();

  }
}

//////额外的尝试。如果没有运气,我也尝试了以下方法。 //////尽管在添加内容之前设置了高度,但没有出现任何内容?

var header = function headerSection(){

      var height = cursor;
      canvas.height += height;

      context.textBaseline = 'top';
      lineSpace      =  20;
      leftPosition   =  0;
      centerPosition = (canvas.width + 26) / 2;
      rightPosition  = (canvas.width - 0);

      console.log(height);
      console.log(context);

      context.fillRect(25, cursor - 2, 526, 2); cursor += lineSpace;
      context.fillRect(25, cursor - 2, 526, 2); cursor += lineSpace;
      DrawCenterText(company['name']); cursor += lineSpace;
      DrawCenterText(company['address']['address_line_1']); cursor += lineSpace;
      DrawCenterText(company['address']['city'].toUpperCase()); cursor += lineSpace;
      DrawCenterText(company['address']['postcode']); cursor += lineSpace;


      console.log(canvas.height);

      return;

    }

    header()

2 个答案:

答案 0 :(得分:1)

调整canvas元素的大小将始终将其上下文重置为默认样式状态。 (.save & .restore不会让上下文样式在调整大小时存活下来)

处理画布内容更改的常见画布模式是:

  • 保存数据(例如您的company

  • 将上下文样式保存为javascript变量或将样式更改嵌入到函数中(例如,设置适当样式和重绘公司标题的专用函数)。

  • 调整(或清除)画布

  • 使用保存的数据和保存的样式/功能重绘所有内容。

示例代码:

点击“完整页面”查看完整收据

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;

var lineSpace=20;
var leftOfForm=25;
var topOfForm=80;
var testingItems=110;

var company={
  name:'MyCompany, Inc',
  address:{
    address_line_1: '123 Main Street',
    city:'Anytown, Anywhere',
    postcode:'12345'
  }
}

var lineItems=[];
addLineItem(testingItems,'Description of sku#'+testingItems,testingItems);

$('#test').click(function(){
  testingItems++;
  addLineItem(testingItems,'Description of sku#'+testingItems,testingItems);
  draw();
});

draw();

function addLineItem(sku,description,price){
  lineItems.push({
    sku:sku,
    description:description,
    price:price
  });
}

function draw(){
  // note: changing canvas.height auto-erases the content also
  canvas.height=topOfForm+(6+2+lineItems.length)*lineSpace;
  ctx.strokeRect(0,0,canvas.width,canvas.height);
  drawHeader(leftOfForm,topOfForm);
  for(var i=0;i<lineItems.length;i++){
    drawLineItem(lineItems[i],leftOfForm,topOfForm+(6+2+i)*lineSpace);
  }
}

function drawHeader(left,cursor){
  var cx=canvas.width/2;
  var line=function(linecount){ return(cursor+lineSpace*linecount); }

  ctx.save();

  ctx.beginPath();
  ctx.moveTo(left,line(0)-2);
  ctx.lineTo(526,line(0)-2);
  ctx.moveTo(left,line(1)-2);
  ctx.lineTo(526,line(1)-2);
  ctx.lineWidth=2;
  ctx.stroke();

  ctx.font='18px verdana';
  ctx.textAlign='center';
  ctx.textBaseline='top';
  ctx.fillText(company.name,cx,line(2));
  ctx.fillText(company.address.address_line_1,cx,line(3));
  ctx.fillText(company.address.city,cx,line(4));
  ctx.fillText(company.address.postcode,cx,line(5));

  ctx.restore();
}

function drawLineItem(item,left,cursor){

  ctx.save();

  ctx.font='14px verdana';
  ctx.textAlign='left';
  ctx.textBaseline='top';
  ctx.fillText(item.sku,left,cursor);
  ctx.fillText(item.description,left+40,cursor);
  ctx.textAlign='right';
  ctx.fillText(item.price,left+450,cursor);

  ctx.restore();
}
body{ background-color: ivory; padding:10px; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<button id=test>Add another line</button>
<br>
<canvas id="canvas" width=550 height=300></canvas>

答案 1 :(得分:1)

我会以更多的记忆为代价建议采用以下方法(如果你要处理非常大的画布,你当然必须考虑内存问题) -

  • 使用代表最大尺寸(或块尺寸 1
  • 的内部较大画布
  • 保留一个代表当前身高(非实际)的值
  • 在文档中以值
  • 中存储的高度创建可视画布
  • 对于每个绘制操作,绘制到内部画布
  • 完成后,使用当前高度值设置可视画布高度,将内部画布绘制为可视化

示例1

此示例使用此技术以随机颜色绘制文本。正如您所看到的,当我们更改视觉画布时,我们不需要重新绘制文本。大小,我们只是复制内部画布中的所有内容,其中还包含当前样式(字体,字体大小,填充样式)。

&#13;
&#13;
var vcanvas = document.querySelector("canvas"),
    vctx = vcanvas.getContext("2d"),
    canvas = document.createElement("canvas"),
    ctx = canvas.getContext("2d"),
    line = 25,
    maxHeight = 7 * line,
    currentHeight = line,
    chars = "abcdefghijklmnowxyzABCD#/&%/(#)=!LMNOPQRSTUVWXYZ".split(""),
    x = 0, y = 0, ch;

vcanvas.height = currentHeight;

// internal canvas setup
ctx.font = "20px sans-serif";
ctx.textBaseline = "top";

// draw someting to internal canvas:
(function loop() {
  ch = chars[(Math.random() * chars.length)|0];
  ctx.fillStyle = "hsl(" + (360*Math.random()) + ",75%,50%)";
  ctx.fillText(ch, x, y);
  x += ctx.measureText(ch).width;
  if (x > canvas.width) { 
    x = 0; y += line; currentHeight += line;
  }
  
  // copy to visual canvas:
  if (currentHeight < maxHeight) vcanvas.height = currentHeight;
  vctx.drawImage(canvas, 0, 0, canvas.width, currentHeight, 
                         0, 0, canvas.width, currentHeight);
  
  
  if (currentHeight < maxHeight) setTimeout(loop, 50);
})();
&#13;
body{background:#eee} canvas{background:#fff}
canvas{border:1px solid #000}
&#13;
<canvas></canvas>
&#13;
&#13;
&#13;

1)如果你正在处理&#34;无限&#34;您希望将进程拆分为块的高度。将内部画布设置为块大小,超出时使用新块大小放大内部画布 - 但是在这里您将不得不重新进行全部重绘并重新设置。

这导致选项2:后一种技术也可以直接与视觉画布一起使用,你可以使用CSS剪辑它,将它放在一个div元素中,你用高度和溢出来设置:隐藏。

示例2

&#13;
&#13;
var canvas = document.querySelector("canvas"),
    ctx = canvas.getContext("2d"),
    div = document.querySelector("div"),
    line = 25,
    maxHeight = 7 * line,
    currentHeight = line,
    chars = "abcdefghijklmnowxyzABCD#/&%/(#)=!LMNOPQRSTUVWXYZ".split(""),
    x = 0, y = 0, ch;

// set canvas to max height, div to clip height
canvas.height = maxHeight;
div.style.height = currentHeight + "px";

// canvas setup
ctx.font = "20px sans-serif";
ctx.textBaseline = "top";

// draw someting to the canvas:
(function loop() {
  ch = chars[(Math.random() * chars.length)|0];
  ctx.fillStyle = "hsl(" + (360*Math.random()) + ",75%,50%)";
  ctx.fillText(ch, x, y);
  x += ctx.measureText(ch).width;
  if (x > canvas.width) { 
    x = 0; y += line; currentHeight += line;
  }
  
  // copy to visual canvas:
  if (currentHeight < maxHeight) {
    div.style.height = currentHeight + "px";  // increase clipping height
    setTimeout(loop, 50)
  }
})();
&#13;
body{background:#eee} canvas{background:#fff}
div{border:1px solid #000; overflow:hidden; width:302px}
&#13;
<div><canvas></canvas></div>
&#13;
&#13;
&#13;