Search code examples
javascriptjquerycanvasfabricjs

Saving Div as JPG Saves but Some Contents Empty/Blank


I have a little app (all code follows) which should replicate c0 to the other canvases and allow for printing; the latter is the only piece of this not working at the moment. What I mean is, it saves an image, but the transferred canvas appear blank when I open it on my computer.

Why is that? Thanks in advance.

var canvas = [],
  image;
var mainCanvas;
mainCanvas = new fabric.Canvas('c0');
for (i = 1; i <= 3; i++) {
  canvas[i] = new fabric.StaticCanvas('sc' + i);
}

function addText() {
  var text = new fabric.IText('Type here...', {
    fontSize: 27,
    top: 10,
    left: 10,
  });
  mainCanvas.add(text);
}

var rect = new fabric.Rect({
  fill: '#ff0000',
  width: 100,
  height: 100,
  id: 1
});
var circle = new fabric.Circle({
  fill: '#ffff00',
  radius: 50,
  left: 150,
  top: 150,
  originX: 'center',
  originY: 'center',
  id: 2
});

mainCanvas.on('object:added', onModified);
mainCanvas.on('object:modified', onModified);
mainCanvas.on('object:scaling', onModified);
mainCanvas.on('object:moving', onModified);
mainCanvas.add(rect, circle);

function onModified(option) {
  var ob = option.target;
  var index = mainCanvas.getObjects().indexOf(ob);
  ob.clone(function(obj) {
    for (i = 1; i <= 3; i++) {
      canvas[i].insertAt(obj, index, true);
    }
  });
};

$('#update').click(function() {
  updateCanvas();
});

function updateCanvas() {
  var json = JSON.stringify(mainCanvas);
  for (i = 1; i <= 3; i++) {
    canvas[i].loadFromJSON(json);
  }
}
// Toggling Images
function replaceImage(imgUrl) {
  if (!isImageLoaded) return; //return if initial image not loaded
  image.setSrc(imgUrl, function() {
    mainCanvas.renderAll();
    updateCanvas();
  });
}

// Default (Blank)
fabric.Image.fromURL('https://i.imgur.com/SamdNdX.png', function(img) {
  isImageLoaded = true;
  image = img.set({
    selectable: false,
    evented: false,
  });
  mainCanvas.add(image);
  mainCanvas.sendToBack(image);
  updateCanvas();
});

$('#save').click(function() {
  html2canvas($('#imagesave'), {
    onrendered: function(canvas) {
      var a = document.createElement('a');
      // toDataURL defaults to png, so we need to request a jpeg, then convert for file download.
      a.href = canvas.toDataURL("image/jpeg").replace("image/jpeg", "image/octet-stream");
      a.download = 'myfile.jpg';
      a.click();
    }
  });
});
html * {
  margin: 0px;
  padding: 0px;
}

body {
  margin: 0px;
  padding: 0px;
}

canvas {
  margin: 0px;
  display: block;
  padding: 0;
}

td,
tr {
  margin: 0;
  padding: 0;
  border: 0;
  outline: 0;
  vertical-align: baseline;
}

#imagesave {
  background-color: white;
  height: 637.5px;
  width: 825px;
  padding-left: 75px;
  border: 1px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.22/fabric.js"></script>

<button onclick="addText();" class="dropdown-item">Add Text</button><button id="save">Save</button>
<button onclick="replaceImage('https://i.imgur.com/SamdNdX.png')">Blank</button>
<button onclick="replaceImage('https://i.imgur.com/TIINd6E.png')">Hands Pic</button>

<div id="imagesave">

  <table>
    <tr>
      <td>
        <canvas id="c0" width="187.5" height="636"></canvas>
      </td>
      <td>
        <canvas id="sc1" width="187.5" height="636"></canvas>
      </td>
      <td>
        <canvas id="sc2" width="187.5" height="636"></canvas>
      </td>
      <td>
        <canvas id="sc3" width="187.5" height="636"></canvas>
      </td>
    </tr>

  </table>

</div>


Solution

  • Your fabric canvases are tainted by the images you draw on it; html2canvas will thus not draw these since they would also taint the resulting canvas.

    Since your images are served with the proper headers, the solution is to request them with the crossOrigin flag.

    You'd do it both in the initial fabric.Image call

    fabric.Image.fromURL( url, callback, { crossOrigin: true } )
    

    and in the replaceImage function

    image.setSrc( imgUrl, callback, { crossOrigin: true } );
    

    var canvas = [],
      image;
    var mainCanvas;
    mainCanvas = new fabric.Canvas('c0');
    for (i = 1; i <= 3; i++) {
      canvas[i] = new fabric.StaticCanvas('sc' + i);
    }
    
    function addText() {
      var text = new fabric.IText('Type here...', {
        fontSize: 27,
        top: 10,
        left: 10,
      });
      mainCanvas.add(text);
    }
    
    var rect = new fabric.Rect({
      fill: '#ff0000',
      width: 100,
      height: 100,
      id: 1
    });
    var circle = new fabric.Circle({
      fill: '#ffff00',
      radius: 50,
      left: 150,
      top: 150,
      originX: 'center',
      originY: 'center',
      id: 2
    });
    
    mainCanvas.on('object:added', onModified);
    mainCanvas.on('object:modified', onModified);
    mainCanvas.on('object:scaling', onModified);
    mainCanvas.on('object:moving', onModified);
    mainCanvas.add(rect, circle);
    
    function onModified(option) {
      var ob = option.target;
      var index = mainCanvas.getObjects().indexOf(ob);
      ob.clone(function(obj) {
        for (i = 1; i <= 3; i++) {
          canvas[i].insertAt(obj, index, true);
        }
      });
    };
    
    $('#update').click(function() {
      updateCanvas();
    });
    
    function updateCanvas() {
      var json = JSON.stringify(mainCanvas);
      for (i = 1; i <= 3; i++) {
        canvas[i].loadFromJSON(json);
      }
    }
    // Toggling Images
    function replaceImage(imgUrl) {
      if (!isImageLoaded) return; //return if initial image not loaded
      image.setSrc(imgUrl, function() {
        mainCanvas.renderAll();
        updateCanvas();
      },{ crossOrigin: 'anonymous' } );
    }
    
    // Default (Blank)
    fabric.Image.fromURL('https://i.imgur.com/SamdNdX.png', function(img) {
      isImageLoaded = true;
      image = img.set({
        selectable: false,
        evented: false,
      });
      mainCanvas.add(image);
      mainCanvas.sendToBack(image);
      updateCanvas();
    },{ crossOrigin: 'anonymous' });
    
    $('#save').click(function() {
      html2canvas($('#imagesave'), {
        onrendered: function(canvas) {
          var a = document.createElement('a');
          // toDataURL defaults to png, so we need to request a jpeg, then convert for file download.
          a.href = canvas.toDataURL("image/jpeg").replace("image/jpeg", "image/octet-stream");
          a.download = 'myfile.jpg';
          a.click();
        }
      });
    });
    html * {
      margin: 0px;
      padding: 0px;
      background: white;
    }
    
    body {
      margin: 0px;
      padding: 0px;
    }
    
    canvas {
      margin: 0px;
      display: block;
      padding: 0;
    }
    
    td,
    tr {
      margin: 0;
      padding: 0;
      border: 0;
      outline: 0;
      vertical-align: baseline;
    }
    
    #imagesave {
      background-color: white;
      height: 637.5px;
      width: 825px;
      padding-left: 75px;
      border: 1px solid black;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.22/fabric.js"></script>
    
    <button onclick="addText();" class="dropdown-item">Add Text</button><button id="save">Save</button>
    <button onclick="replaceImage('https://i.imgur.com/SamdNdX.png')">Blank</button>
    <button onclick="replaceImage('https://i.imgur.com/TIINd6E.png')">Hands Pic</button>
    
    <div id="imagesave">
    
      <table>
        <tr>
          <td>
            <canvas id="c0" width="187.5" height="636"></canvas>
          </td>
          <td>
            <canvas id="sc1" width="187.5" height="636"></canvas>
          </td>
          <td>
            <canvas id="sc2" width="187.5" height="636"></canvas>
          </td>
          <td>
            <canvas id="sc3" width="187.5" height="636"></canvas>
          </td>
        </tr>
    
      </table>
    
    </div>

    Also note that your version of html2canvas is quite outdated, you may want to use one the latest releases where a lot of bugs have been fixed (not this issue though).