将fabricjs画布保存为PNG不起作用

时间:2017-12-01 19:55:07

标签: javascript html canvas fabricjs

我想弄清楚为什么这不会将画布保存为PNG。

我包含了很多代码,因为我确信它会干扰自身,但不知道如何。我已经使用类似的应用程序进行了修补,并且没有遇到过这个问题:请注意当你选择Save it时如何炸毁画布但是没有做到应该将画布保存为PNG到底应该做什么你的电脑。

我得到的错误是:

Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
at n.__toDataURL (https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.20/fabric.min.js:1:111706)
at n.__toDataURLWithMultiplier (https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.20/fabric.min.js:1:111496)
at n.toDataURL (https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.20/fabric.min.js:1:111060)
at downloadFabric (file://portrait.js:207:21)
at HTMLButtonElement.onclick (file://index.html:62:114)

这是我的代码:



var canvas = new fabric.Canvas('c', {
  preserveObjectStacking: true
});
canvas.setHeight(412);
canvas.setWidth(637);
var oImg, oImg2, isImageLoaded;


// oImgObj bread and butter, kudos @grunt
function replaceImage(oImgObj, imgUrl) {
  if (!isImageLoaded) return; //return if initial image not loaded
  var imgElem = oImgObj._element; //reference to actual image element
  imgElem.src = imgUrl; //set image source
  imgElem.onload = () => canvas.renderAll(); //render on image load
}

// initialize default frame (light brown wood oval)
fabric.Image.fromURL('https://i.imgur.com/DrzSWSa.png', function(img) {
  isImageLoaded = true;
  oImg = img.set({
    selectable: false,
    evented: false,
  }).scale(0.5);
  canvas.add(oImg).renderAll();
  canvas.sendToBack(oImg);
});

// add photo (link)
$(function() {
  $("#upload_link").on('click', function(e) {
    e.preventDefault();
    $("#file:hidden").trigger('click');
  });
});

// add photo
document.getElementById('file').addEventListener("change", function(e) {
  var file = e.target.files[0];
  var reader = new FileReader();
  reader.onload = function(f) {
    var data = f.target.result;
    fabric.Image.fromURL(data, function(img) {
      var oImg = img.set({
        left: 400,
        top: 102,
        centeredScaling: true,
        lockUniScaling: true,
        cornerStyle: 'circle',
        transparentCorners: false,
      }).scale(.8);
      canvas.add(oImg);
      canvas.setActiveObject(oImg);
      var image = canvas.getActiveObject();
      image.moveTo(-1);
      canvas.discardActiveObject();
      canvas.renderAll();
      canvas.sendToBack(oImg);
    });
  };
  reader.readAsDataURL(file);
});

// Some Text
canvas.add(new fabric.IText('Some Text', {
  left: 475,
  top: 25,
  fontSize: 27,
  hasBorders: true,
  hasControls: false,
  selectable: true,
  lockRotation: true,
  lockMovementX: true,
  lockMovementY: true,
  align: 'mid',
  originX: 'center',
  originY: 'center',
  centeredScaling: true,
}));

// Some Text
canvas.add(new fabric.IText('Some Text', {
  left: 475,
  top: 60,
  fontSize: 27,
  hasBorders: true,
  hasControls: false,
  selectable: true,
  lockRotation: true,
  lockMovementX: true,
  lockMovementY: true,
  align: 'mid',
  originX: 'center',
  originY: 'center',
  centeredScaling: true,
}));

// Text Style Options
var underline = document.getElementById('btn-underline');
var bold = document.getElementById('btn-bold');
var italic = document.getElementById('btn-italic');

underline.addEventListener('click', function() {
  dtEditText('underline');
});
bold.addEventListener('click', function() {
  dtEditText('bold');
});
italic.addEventListener('click', function() {
  dtEditText('italic');
});

// Font Styling
function dtEditText(action) {
  var a = action;
  var o = canvas.getActiveObject();
  var t;
  // If object selected, what type?
  if (o) {
    t = o.get('type');
  }
  if (o && t === 'i-text') {
    switch (a) {
      case 'bold':
        var isBold = dtGetStyle(o, 'fontWeight') === 'bold';
        dtSetStyle(o, 'fontWeight', isBold ? '' : 'bold');
        break;

      case 'italic':
        var isItalic = dtGetStyle(o, 'fontStyle') === 'italic';
        dtSetStyle(o, 'fontStyle', isItalic ? '' : 'italic');
        break;

      case 'underline':
        var isUnderline = dtGetStyle(o, 'textDecoration') === 'underline';
        dtSetStyle(o, 'textDecoration', isUnderline ? '' : 'underline');
        break;
        canvas.renderAll();
    }
  }
}
// Get the style
function dtGetStyle(object, styleName) {
  return object[styleName];
}
// Set the style
function dtSetStyle(object, styleName, value) {
  object[styleName] = value;
  object.set({
    dirty: true
  });
  canvas.renderAll();
}

// Switching Fonts
document.getElementById("cinzel").addEventListener("click", function(e) {
  canvas.getActiveObject().set("fontFamily", "Cinzel");
  canvas.renderAll();
});

document
  .getElementById("cinzelDecorative")
  .addEventListener("click", function(e) {
    canvas.getActiveObject().set("fontFamily", "Cinzel Decorative");
    canvas.renderAll();
  });

document
  .getElementById("monsieurladoulaise")
  .addEventListener("click", function(e) {
    canvas.getActiveObject().set("fontFamily", "Monsieur La Doulaise");
    canvas.renderAll();
  });
document.getElementById("opensans").addEventListener("click", function(e) {
  canvas.getActiveObject().set("fontFamily", "Open Sans");
  canvas.renderAll();
});

document.getElementById("montserrat").addEventListener("click", function(e) {
  canvas.getActiveObject().set("fontFamily", "Montserrat");
  canvas.renderAll();
});

document.getElementById("times").addEventListener("click", function(e) {
  canvas.getActiveObject().set("fontFamily", "Times New Roman");
  canvas.renderAll();
});

// Centered Line
var line = new fabric.Line([canvas.width / 2, 0, canvas.width / 2, canvas.height], {
  strokeWidth: 1,
  stroke: '#dddddd',
  selectable: false,
});
canvas.add(line);

// Save
function download(url, name) {
  // make the link. set the href and download. emulate dom click
  $('<a>').attr({
    href: url,
    download: name
  })[0].click();
}

function downloadFabric(canvas, name) {
  //  convert the canvas to a data url and download it.
  download(canvas.toDataURL({
    multiplier: 4
  }), name + '.png');
}

// Print
function printCanvas() {
  var dataUrl = document.getElementById('c').toDataURL( /* data multiplier?*/ ); //attempt to save base64 string to server using this var  
  var windowContent = '<!DOCTYPE html>';
  windowContent += '<html>'
  windowContent += '<head><title>Print canvas</title></head>';
  windowContent += '<body>'
  windowContent += '<img src="' + dataUrl + '" onload=window.print();window.close();>';
  windowContent += '</body>';
  windowContent += '</html>';
  var printWin = window.open('', '', 'width=340,height=260');
  printWin.document.open();
  printWin.document.write(windowContent);
}
&#13;
canvas {
  border: 1px solid #dddddd;
  margin-top: 5px;
}

a:visited {
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
  cursor: pointer;
}

a.dropdown-item {
  cursor: pointer;
}

.btn {
  margin-top: 10px;
  cursor: pointer;
}

#upload_link {
  text-decoration: none;
}

#file {
  display: none;
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.20/fabric.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css">
<link href="https://fonts.googleapis.com/css?family=Cinzel|Cinzel+Decorative|Monsieur+La+Doulaise|Montserrat|Open+Sans" rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>


<div class="container">
  <div class="row">
    <div class="col-md-2">

      <button type="file" class="btn btn-dark btn-sm" id="upload_link">New Photo</button>

      <input type="file" id="file" />
      <br>
      <div class="btn-group">
        <button class="btn btn-default btn-sm dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Themes</button>
        <div class="dropdown-menu">
          <h6 class="dropdown-header">Frame</h6>
          <a onclick="replaceImage(oImg, 'images/frames/LightBrownWoodOval.png')" class="dropdown-item">Oval Light Brown</a>
          <a onclick="replaceImage(oImg, 'images/frames/MidToneWoodFrameOval.png')" class="dropdown-item">Oval Mid Tone Wood</a>
          <a onclick="replaceImage(oImg, 'images/frames/SilverFrameOval.png')" class="dropdown-item">Oval Silver</a>
          <a onclick="replaceImage(oImg, 'images/frames/DistressedWhiteFrameRec.png')" class="dropdown-item">Rectangle Distressed White</a>
          <a onclick="replaceImage(oImg, 'images/frames/GoldScrollFrameRec.png')" class="dropdown-item">Rectangle Gold Scroll</a>
          <a onclick="replaceImage(oImg, 'images/frames/MidtoneWoodFrameRec.png')" class="dropdown-item">Rectangle Mid Tone Wood</a>
          <a onclick="replaceImage(oImg, 'images/frames/SilverFrameRec.png')" class="dropdown-item">Rectangle Silver</a>
        </div>
      </div>
      <br>
      <div class="btn-group">
        <button class="btn btn-default btn-sm dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Text Options</button>
        <div class="dropdown-menu">
          <h6 class="dropdown-header">Styles</h6>
          <a class="dropdown-item" id="btn-bold" style="font-weight: bold;">Bold</a>
          <a class="dropdown-item" id="btn-italic" style="text-decoration: italic;">
            <em>Italic</em>
          </a>
          <a class="dropdown-item" id="btn-underline" style="text-decoration: underline;">Underline</a>
          <div class="dropdown-divider"></div>
          <h6 class="dropdown-header">Fonts</h6>
          <a class="dropdown-item" id="cinzel" style="font-family:cinzel;">Cinzel</a>
          <a class="dropdown-item" id="cinzelDecorative" style="font-family:cinzel decorative;">Cinzel Decorative</a>
          <a class="dropdown-item" id="monsieurladoulaise" style="font-family:Monsieur La Doulaise;">Monsieur La Doulaise</a>
          <a class="dropdown-item" id="montserrat" style="font-family:montserrat;">Montserrat</a>
          <a class="dropdown-item" id="opensans" style="font-family:Open Sans;">Open Sans</a>
          <a class="dropdown-item" id="times" style="font-family:Times New Roman;">Times</a>
        </div>
      </div>

      <br>
      <button onclick="downloadFabric(canvas,'portrait')" type="button" class="btn btn-sm" title="Save as PNG">Save</button>
      <br>
      <button onclick="printCanvas()" type="button" class="btn btn-default btn-sm">Print</button>

      <br>
      <button onclick="window.location.reload(true)" type="button" class="btn btn-danger btn-sm">Restart</button>
    </div>
    <div class="col-md-10">
      <canvas id="c"></canvas>
    </div>
  </div>
</div>



<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js"></script>
&#13;
&#13;
&#13;

同样,印刷现在也不起作用。

3 个答案:

答案 0 :(得分:4)

使用toDataURL方法:

const dataURL = canvas.toDataURL({
     width: canvas.width,
     height: canvas.height,
     left: 0,
     top: 0,
     format: 'png',
});
const link = document.createElement('a');
link.download = 'image.png';
link.href = dataURL;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);

它将在文档中创建一个不可见的链接,然后单击它以触发下载。

答案 1 :(得分:1)

使用下面的代码

  var dataURL = canvas.toDataURL({
                    format: "png",
                    left: 0,
                    top: 0,
                    width: canvas.width ,
                    height: canvas.height ,
                });

答案 2 :(得分:1)

如果您收到此错误,则无需检查代码:

Uncaught DOMException: Failed to execute 'toDataURL' on 

'HTMLCanvasElement': Tainted canvases may not be exported.
at n.__toDataURL (https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.20/fabric.min.js:1:111706)
at n.__toDataURLWithMultiplier (https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.20/fabric.min.js:1:111496)
at n.toDataURL (https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.20/fabric.min.js:1:111060)
at downloadFabric (file://portrait.js:207:21)
at HTMLButtonElement.onclick (file://index.html:62:114)

你的画布被污染了。 这意味着您加载了一些图像而未指定crossOrigin属性。

Fabricjs在回调后确实支持fromOrl的crossOrigin选项作为第三个参数(请查看此处的文档http://fabricjs.com/docs/fabric.Image.html#.fromURL

在函数replaceImage中,在将.src属性设置为图像之前,你必须自己处理它。

    // oImgObj bread and butter, kudos @grunt
function replaceImage(oImgObj, imgUrl) {
  if (!isImageLoaded) return; //return if initial image not loaded
  var imgElem = oImgObj._element; //reference to actual image element
  imgElem.src = imgUrl; //set image source
  imgElem.onload = () => canvas.renderAll(); //render on image load
}

// initialize default frame (light brown wood oval)
fabric.Image.fromURL('https://i.imgur.com/DrzSWSa.png', function(img) {
  isImageLoaded = true;
  oImg = img.set({
    selectable: false,
    evented: false,
  }).scale(0.5);
  canvas.add(oImg).renderAll();
  canvas.sendToBack(oImg);
});