我如何知道 HTML5 Canvas 何时完成绘图?

Posted

技术标签:

【中文标题】我如何知道 HTML5 Canvas 何时完成绘图?【英文标题】:How can I know when HTML5 Canvas finished drawing? 【发布时间】:2015-03-11 03:08:21 【问题描述】:

我是 javascript 的初学者。 我正在尝试在客户端调整图像大小,然后再将它们上传到我的服务器进行存储 我找到了一种使用 html5 Canvas 的方法:http://hacks.mozilla.org/2011/01/how-to-develop-a-html5-image-uploader/

我的问题:一直上传空白图片。我认为空白图像的原因是当我从画布上抓取图像时画布没有完成绘制。 当我在从画布上抓取图像之前设置 3 秒 windows.timeout 时,一切正常。 有比超时更可靠的方法吗?或者有没有其他方法可以进行客户端图像大小调整?

我进行客户端图像大小调整的原因是因为我正在与上传不必要的大文件的人打交道,我认为进行客户端图像大小调整是最有效的。

感谢您的任何提示/答案!

//resize image
Resizeimage.resize(file);

//***if i wrap this part with timeout it works
var canvas = document.getElementById('canvas');
var data = canvas.toDataURL(file.type);

// convert base64/URLEncoded data component to raw binary data held in a string
var bytestring;
if (data.split(',')[0].indexOf('base64') >= 0)
  bytestring = atob(data.split(',')[1]);
else
  bytestring = escape(data.split(',')[1]);

//get imagetype
var mimeString = data.split(',')[0].split(':')[1].split(';')[0];

// write the bytes of the string to a typed array
var ia = new Uint8Array(bytestring.length);
for (var i = 0; i < bytestring.length; i++) 
  ia[i] = bytestring.charCodeAt(i);


// create a blob from array
var blob = new Blob([ia], 
  type: mimeString
);

//upload files
$scope.upload = $upload.upload(
  url: 'articles/imageUpload',
  method: 'POST',
  file: blob
    //update progress bar
).progress(function(event) 
  $scope.uploadInProgress = true;
  $scope.uploadProgress = Math.floor(event.loaded / event.total * 100);
  //file upload success
).success(function(data, status, headers, config) 
  $scope.uploadInProgress = false;
  $scope.uploadedImage = JSON.parse(data);
  $scope.isDisabled = false;
  //file upload fail
).error(function(err) 
  $scope.uploadInProgress = false;
  console.log('Error uploading file: ' + err.message || err);
);
//***

angular.module('articles').factory('Resizeimage', [
  function() 

    var imgSelectorStr = null;

    // Resizeimage service logic
    function render(src) 
      var image = new Image();

      image.onload = function() 
        var canvas = document.getElementById('canvas');
        
        //check the greater out of width and height
        if (image.height > image.width) 
          image.width *= 945 / image.height;
          image.height = 945;
         else 
          image.height *= 945 / image.width;
          image.width = 945;
        

        //draw (&resize) image to canvas
        var ctx = canvas.getContext('2d');
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        canvas.width = image.width;
        canvas.height = image.height;
        ctx.drawImage(image, 0, 0, image.width, image.height);

      ;
      image.src = src;
    

    function loadImage(src) 
      // Create fileReader and run the result through the render
      var reader = new FileReader();

      reader.onload = function(e) 
        render(e.target.result);
      ;
      reader.readAsDataURL(src);
    

    // Public API
    return 
      resize: function(src) 
        loadImage(src);
        return true;
      
    ;
  
]);

【问题讨论】:

很好的解释,但如果你也能发布一些代码,那就太好了 @Breakpoint 谢谢,我已经插入了我的代码。顺便说一句,我正在尝试使用 MeanJS。所以我的代码是客户端的angularJS 【参考方案1】:

你的问题很简单。

只要您不附加到任何事件,JavaScript 就会一个接一个地运行语句(您需要这样做,因为您希望在抓取图像时执行所有语句)。

如果您将代码附加到一个事件(例如image.onload),您的这部分代码将等待该事件,而您的其余代码将继续异步。

所以你是对的 - 当你试图抓住它时,图像没有被绘制出来。

通过超时(最低效/不安全)解决它的不好风格更好地解决这个问题,方法是将你的图像抓取代码包装在一个函数中,并在你的图像调整大小完成后运行这个函数。这就像您用来同步异步事件的回调。

示例

/* setTimeout will behave like an event */
setTimeout(
  function()
    $('console').append("<div>SetTimeout</div>");
     SynchCodeAfterTimeout();
  , 1000);

AsynchCodeAfterTimeout();

function AsynchCodeAfterTimeout()
    $('console').append("<div>AsynchCodeAfterTimeout</div>");


function SynchCodeAfterTimeout()
    $('console').append("<div>SynchCodeAfterTimeout</div>");
console
  font-family: Consolas, monaco, monospace;
<script type="text/javascript" src="https://code.jquery.com/jquery-latest.min.js"></script>

<console></console>

代码示例

render();

// Resizeimage service logic
function render() 
  var image = new Image();

  image.onload = function() 
    var canvas = document.getElementById('canvas');

    //check the greater out of width and height
    if (image.height > image.width) 
      image.width *= 945 / image.height;
      image.height = 945;
     else 
      image.height *= 945 / image.width;
      image.width = 945;
    

    //draw (&resize) image to canvas
    var ctx = canvas.getContext('2d');
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    canvas.width = image.width;
    canvas.height = image.height;
    ctx.drawImage(image, 0, 0, image.width, image.height);
    /*timeout not needed its just there so the browser renders the canvas contents so you can see them when the alert pops up*/
    setTimeout(function() 
      alert('yey all the drawing is done (this is synchronous)');
    );
  ;
  image.src = 'https://loremflickr.com/320/240';
&lt;canvas id="canvas"&gt;&lt;/canvas&gt;

【讨论】:

所以我在正确的方向上使绘图同步。很抱歉问这个问题,但我如何让它同步呢?我用谷歌搜索并读到我可以通过触发事件或使用延迟对象来做到这一点。但是他们不需要检查画布何时准备就绪吗? 正如我在回答中指出的那样 - 将所有图像抓取代码包装在一个函数中,该函数可以从调整大小代码的最后一部分访问,并像我所说的 SynchCodeAfterTimeout() 一样调用它。 据我所知,无需测试,您应该在function render(src) 中调用.onload 末尾的代码 非常感谢您的详尽解释和示例!你是最厉害的!所以画布实际上是阻塞的..但是 onload 使整个代码块异步...现在更有意义:) 伙计,你帮了我很多忙!【参考方案2】:
<script type="text/javascript">
     window.onload = function()
         //Whatever you want to do when it has finished loading here
     
</script>

只有在没有脚本标签的情况下,您才能将这段代码放在 HTML 中或 JavaScript 文件中的某个位置;)

希望这对你有用:D

【讨论】:

如果目标是绘制图像并将文本添加到画布并且要写入的数据来自 api,则此方法将不起作用。

以上是关于我如何知道 HTML5 Canvas 何时完成绘图?的主要内容,如果未能解决你的问题,请参考以下文章

如何撤消 HTML5 Canvas 绘图应用程序的橡皮擦操作

canvas 2D绘图

如何在 HTML5 画布上绘图

使用鼠标在 HTML5 Canvas 上绘图

HTML5 Canvas 性能:加载图像与绘图

html5之canvas初解