【问题标题】:Fabric JS: Performance of very large images (20mb+)Fabric JS:超大图像的性能(20mb+)
【发布时间】:2019-04-15 11:11:24
【问题描述】:

我正在使用 Fabric JS 来处理非常大的图像(20mb+)。我发现与使用标准 Canvas API 相比,Fabric 在处理画布中的大图像时要慢得多。

下面的代码 sn-p 有两个输入按钮,一个用于使用标准 Canvas API 将图像添加到画布,另一个使用 Fabric JS。每个方法还将使用toDataUrl() 将画布转换为数据 url。每个方法还记录 3 次:开始时间、img.onload 函数完成的时间和toDataUrl() 完成的时间。

下表比较了我针对不同图像大小测试的导入和导出时间: import times for 500kb to 50mb photos

这是一个图表,显示了 Fabric 导入 + 导出时间与 Canvas API 的性能:graph

问题:

  • 为什么在画布上导入和导出大图像时,Fabric 的性能比 Canvas API 慢得多?
  • 有没有办法在使用大图像时提高 Fabric 性能?
  • 我的测试用例是否准确地代表了 Fabric 的性能?

// Standard Import
function handleFiles(e) {
  var t0 = performance.now();
  console.log('Standard Import')
  console.log('Start Time: ', Math.round(t0/1000))  
  var promise = new Promise(function(resolve) {
    var URL = window.webkitURL || window.URL;
    var ctx = document.getElementById('canvas').getContext('2d');
    canvas = document.getElementById('canvas');
    var url = URL.createObjectURL(e.target.files[0]);
    var img = new Image();
    img.onload = function() {
        canvas.width = img.width;
        canvas.height = img.height;
        ctx.drawImage(img, 0, 0);
        resolve("done")
    };
    img.src = url;  
  });

  promise.then(function(result) {
    var t1 = performance.now()
    console.log('Done img.onload() Elapsed Time: ', Math.round(t1 - t0)/1000);
    var dataURL = canvas.toDataURL('image/png')
    return    
  }).then(function(result){
    t2 = performance.now()
    console.log('Done canvas.ToDataURL() Elapsed Time: ', Math.round(t2 - t0)/1000)
    return
  });
}

// Fabric Import
function handleFilesFabric(e) {
  var t0 = performance.now();
  console.log('Fabric Import')
  console.log('Start Time: ', Math.round(t0/1000))
  var promise = new Promise(function(resolve) {   
    var canvas = new fabric.Canvas('canvas');
    var reader = new FileReader();
    reader.onload = function (event){
      var imgObj = new Image();
      imgObj.src = event.target.result;
      imgObj.onload = function () {
        var image = new fabric.Image(imgObj);
        canvas.setHeight(imgObj.height);
        canvas.setWidth(imgObj.width);
        canvas.add(image);
        canvas.renderAll();
        canvas.forEachObject(function(object){ 
          object.selectable = false; 
        });
        resolve("done")
      }
    }
    reader.readAsDataURL(e.target.files[0]);
  });
  
  promise.then(function(result) {
    var t1 = performance.now()
    console.log('Done img.onload() Elapsed Time: ', Math.round(t1 - t0)/1000);
    var dataURL = canvas.toDataURL('image/png')
    return
  }).then(function(result){
    t2 = performance.now()
    console.log('Done canvas.ToDataURL() Elapsed Time: ', Math.round(t2 - t0)/1000)
    return
  });
}

window.onload = function() {
  // Standard Import 
  var input = document.getElementById('input');
  input.addEventListener('change', handleFiles, false);
  // Fabric Import
  var input2 = document.getElementById('input2');
  input2.addEventListener('change', handleFilesFabric, false);  
};
canvas {
  border: 2px solid;
}
<!DOCTYPE html>
<html lang="en" >
<head>
  <meta charset="UTF-8">
  <title>Upload & Display Image w/ Canvas</title>
  <link rel="stylesheet" href="css/style.css">
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.3/fabric.min.js"></script>
</head>
<body>
    <h1> Upload & Display Image</h1>
    <canvas width="400" height="400" id="canvas"></canvas>
    <br>
    <b>Standard Add Image</b><br>
    <input type="file" id="input"/>
    <br>
    <b>Fabric Add Image</b><br>
    <input type="file" id="input2"/>
    <script src="js/index.js"></script>
</body>
</html>

【问题讨论】:

    标签: javascript canvas fabricjs


    【解决方案1】:

    为了更公平,我稍微重构了代码: 加载图像的方式相同,两个事件在两个不同的画布上依次运行。

    同时删除对本次测试没有意义的额外 fabricJS 功能。我认为对于更大的图像,现在差异应该更低,您可以在 sn-p 中尝试使用大图像吗?

    顺便说一句,您无法将文件中的 URL.createObjectUrl 与 dataUrl 上的文件阅读器进行比较。只是不公平。 createObjectUrl 在内存中创建对您上传文件的引用。

    ReadAsDataUrl 读取文件,在 base64 中编码,创建一个字符串对象,然后浏览器必须再次读取该字符串,从 base64 解码。

    不同之处还可能在于fabricJS 使用drawImage 和9 args 绘制图像,而您使用的是3 args 版本。

    // Standard Import
    fabric.Object.prototype.objectCaching = false;
    function handleFiles(e) {
      var t0 = performance.now();
    
      var promise = new Promise(function(resolve) {
        var URL = window.webkitURL || window.URL;
        var ctx = document.getElementById('canvas').getContext('2d');
        canvas = document.getElementById('canvas');
        var url = URL.createObjectURL(e.target.files[0]);
        var img = new Image();
        img.onload = function() {
          console.log('Standard Import')
      console.log('Start Time: ', Math.round(t0/1000))  
            canvas.width = img.width;
            canvas.height = img.height;
            ctx.drawImage(img, 0, 0);
            resolve("done")
        };
        img.src = url;  
      });
    
      promise.then(function(result) {
        var t1 = performance.now()
        console.log('Done img.onload() Elapsed Time: ', Math.round(t1 - t0)/1000);
        var dataURL = canvas.toDataURL('image/png')
        return    
      }).then(function(result){
        t2 = performance.now()
        console.log('Done canvas.ToDataURL() Elapsed Time: ', Math.round(t2 - t0)/1000)
        return
      });
    }
    
    // Fabric Import
    function handleFilesFabric(e) {
      var t0 = performance.now();
      var canvas = new fabric.StaticCanvas('canvas2', {enableRetinaScaling: false, renderOnAddRemove: false });
      var promise = new Promise(function(resolve) {   
        
        var reader = new FileReader();
        var URL = window.webkitURL || window.URL;
        var url = URL.createObjectURL(e.target.files[0]);
          var imgObj = new Image();
          
          imgObj.onload = function () {
            console.log('Fabric Import')
      console.log('Start Time: ', Math.round(t0/1000))
    
            var image = new fabric.Image(imgObj);
            canvas.setDimensions({ width: imgObj.width, height: imgObj.height});
            canvas.add(image);
            resolve("done")
          }
          imgObj.src = url;
      });
      
      promise.then(function(result) {
        var t1 = performance.now()
        console.log('Done img.onload() Elapsed Time: ', Math.round(t1 - t0)/1000);
        canvas.renderAll();
        var dataURL = canvas.lowerCanvasEl.toDataURL('image/png')
        return
      }).then(function(result){
        t2 = performance.now()
        console.log('Done canvas.ToDataURL() Elapsed Time: ', Math.round(t2 - t0)/1000)
        return
      });
    }
    
    window.onload = function() {
      // Standard Import 
      var input = document.getElementById('input');
      input.addEventListener('change', handleFiles, false);
      input.addEventListener('change', handleFilesFabric, false);
    };
    canvas {
      border: 2px solid;
    }
    <!DOCTYPE html>
    <html lang="en" >
    <head>
      <meta charset="UTF-8">
      <title>Upload & Display Image w/ Canvas</title>
      <link rel="stylesheet" href="css/style.css">
      <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.3/fabric.min.js"></script>
    </head>
    <body>
        <h1> Upload & Display Image</h1>
        <canvas width="400" height="400" id="canvas"></canvas>
        <canvas width="400" height="400" id="canvas2"></canvas>
        <br>
        <b>Standard Add Image</b><br>
        <input type="file" id="input"/>
        <br>
    </body>
    </html>

    【讨论】:

      猜你喜欢
      • 2013-01-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-17
      相关资源
      最近更新 更多