2011-01-23 26 views
69

Hãy xem xét những điều sau đây (mong manh) mã JavaScript:Nên đặt hình ảnh src thành URL dữ liệu có sẵn ngay lập tức không?

var img = new Image; 
img.src = "data:image/png;base64,..."; // Assume valid data 

// Danger(?) Attempting to use image immediately after setting src 
console.log(img.width, img.height); 
someCanvasContext.drawImage(img, 0, 0); 

// Danger(?) Setting onload after setting src 
img.onload = function(){ console.log('I ran!'); }; 

Các Câu hỏi (s)

  • chiều rộng và chiều cao hình ảnh nên được đúng ngay lập tức?
  • Nếu canvas vẽ hoạt động ngay lập tức?
  • Nếu cuộc gọi lại onload (được đặt sau khi đã thay đổi src) chưa?

Các thử nghiệm thực nghiệm
Tôi tạo ra một simple test page với mã tương tự. Trong Safari thử nghiệm đầu tiên của tôi, cả hai bằng cách mở trang HTML cục bộ (file:/// url) và tải nó từ máy chủ của tôi, cho thấy mọi thứ hoạt động: chiều cao và chiều rộng là chính xác, canvas vẽ hoạt động, các onload cũng cháy.

Trong Firefox v3.6 (OS X), tải trang sau khi khởi chạy trình duyệt cho thấy chiều cao/chiều rộng không chính xác ngay sau khi cài đặt, drawImage() không thành công. (Tuy nhiên, trình xử lý onload không kích hoạt.) Tải lại trang, hiển thị chiều rộng/chiều cao chính xác ngay sau khi cài đặt và drawImage() hoạt động. Có vẻ như Firefox lưu trữ nội dung của URL dữ liệu dưới dạng hình ảnh và có sẵn ngay khi được sử dụng trong cùng một phiên.

Trong Chrome v8 (OS X), tôi thấy kết quả tương tự như trong Firefox: hình ảnh không có sẵn ngay lập tức, nhưng mất một thời gian để tải 'không đồng bộ' một cách không đồng bộ từ URL dữ liệu.

Ngoài bằng chứng thực nghiệm về những trình duyệt nào ở trên hoặc không hoạt động, tôi thực sự yêu các liên kết đến thông số kỹ thuật về cách hoạt động này. Cho đến nay, Google-fu của tôi vẫn chưa thực hiện được nhiệm vụ.

Playing Nó Safe
Đối với những người không hiểu tại sao ở trên có thể là nguy hiểm, hãy biết rằng bạn nên sử dụng hình ảnh như thế này để được an toàn:

// First create/find the image 
var img = new Image; 

// Then, set the onload handler 
img.onload = function(){ 
    // Use the for-sure-loaded img here 
}; 

// THEN, set the src 
img.src = '...'; 
+1

Bạn chắc chắn cần đặt 'onload' trước khi đặt' src'. Nhưng nếu sự kiện tải đôi khi xảy ra trong chòm sao hiện tại, thì việc đặt URI "dữ liệu" thực sự là không đồng bộ - điều này sẽ làm tôi ngạc nhiên. Hấp dẫn! –

+0

Không an toàn hơn để giả định rằng không, để bơm thời gian chờ và tiếp tục sau khi trình duyệt có cơ hội vẽ lại chính nó? –

+0

@mP Vâng, chắc chắn là; Tôi đã chỉnh sửa câu hỏi của mình để làm rõ điều này trong 5 phút trước khi bình luận của bạn :) Tuy nhiên, điều này không làm mất hiệu lực câu hỏi. Giả sử rằng hành vi trong Chrome và FF được cho phép - không phải là lỗi - thì thực tế là nó rất quan trọng, để chơi nó an toàn. – Phrogz

Trả lời

51

Vì chưa có bất kỳ thông số kỹ thuật nào về cách này được cho là để hoạt động, chúng tôi sẽ phải hài lòng với cách hoạt động . Sau đây là kết quả kiểm tra của tôi.

 
      | is image data usable right | does onload callback set after src 
      |  after setting src?  | is changed still get invoked? 
------------+-----------------------------+------------------------------------- 
Safari 5 |    yes    |     yes     
------------+-----------------------------+------------------------------------- 
Chrome 8 | **no** (1st page load), but |     yes     
FireFox 3.6 | yes thereafter (cached) |          
------------+-----------------------------+------------------------------------- 
IE8   | yes (32kB data limit) |     yes   
------------+-----------------------------+------------------------------------- 
IE9b  |    yes    |     **no**    
------------+-----------------------------+------------------------------------- 

Nói tóm lại:

  • Bạn không thể giả định rằng các dữ liệu hình ảnh sẽ được quyền có sẵn sau khi cài đặt một dữ liệu-uri; bạn phải đợi sự kiện onload.
  • Do IE9, bạn không thể đặt trình xử lý onload sau khi đặt src và mong muốn nó được gọi.
  • Các đề xuất trong "Phát an toàn" (từ câu hỏi ở trên) là cách duy nhất để đảm bảo hành vi đúng.

Nếu bất kỳ ai có thể tìm thấy thông số kỹ thuật thảo luận về điều này, tôi sẽ vui lòng chấp nhận câu trả lời của họ thay thế.

+3

Cảm ơn bạn đã nghiên cứu ở đây. Tôi đã đấu tranh với khả năng buộc hành vi này cho một webapp ngoại tuyến, nơi tôi cần phải báo cáo bằng cách sử dụng canvas, vì vậy tôi đào sâu hơn một chút. Thông số WHATWG sống thực sự không gọi trường hợp được lưu trong bộ nhớ cache là đồng bộ và trường hợp không được lưu trong bộ nhớ cache là không đồng bộ. Kiểm tra bước 7 trong phần src của spec được tìm thấy ở đây: http://www.whatwg.org/specs/web-apps/current-work/multipage/embedded-content-1.html#attr-img-src. Nó xuất hiện nếu hình ảnh nằm trong "danh sách hình ảnh có sẵn" của Tài liệu, nó hoàn thành đồng bộ, hoặc đó là cách tôi đọc nó. –

+0

@Phrogz Bạn đã tiến hành kiểm tra ở giai đoạn hoặc tài liệu nào? Tôi đang quan sát Safari 5.1 để có hành vi Chrome, nhưng đó là trước sự kiện 'window.load'. Các thử nghiệm của bạn được thực hiện sau khi tải hay trước? Bạn có nhớ? – hexalys

6

Sau khi kiểm soát trở lại công cụ hiển thị của trình duyệt, báo cáo kiểm tra true trong FF 4b9 và Chromium 8.0.552.237. Tôi đã sửa đổi các bộ phận:

img.onload = function(){ // put this above img.src = … 
    document.getElementById('onload').innerHTML = 'yes'; 
}; 
img.src = img_src; 
… 
window.setTimeout(function() { // put this inside setTimeout 
    document.getElementById('wh').innerHTML = (img.height==img.width) && (img.height==128); 
}, 0); 

Cập nhật: Vâng, tôi hiểu 'câu trả lời' đây là giống như một bình luận, nhưng chỉ muốn chỉ nó ra. Và sau khi thử nghiệm tôi nhận được kết quả tái sản xuất:

  • mà không setTimeout, trong cả hai trình duyệt tôi nhận được false lần đầu tiên tôi mở trang và true chỉ sau khi chạm F5. Sau khi xóa rõ ràng bộ nhớ cache, hãy nói lại false sau khi tải lại.
  • với setTimeout kiểm tra chiều rộng/chiều cao luôn luôn chuyển sang true.

Trong cả hai trường hợp, hình ảnh không hiển thị lần đầu tiên, chỉ sau khi tải lại trang.

+0

Bạn đang nói với tôi rằng nếu không có những thay đổi này, bạn sẽ thấy 'thất bại' trong FF và Chromium? Câu hỏi của tôi không phải là làm thế nào để làm cho công việc này-rõ ràng thiết lập 'onload' trước' src' và chỉ tham chiếu hình ảnh trong gọi lại sẽ làm việc. Câu hỏi của tôi là liệu có tồn tại các thông số kỹ thuật mô tả cách thức này nên hoạt động hay không. – Phrogz

Các vấn đề liên quan