2013-10-02 14 views
8

Tôi đang cố gắng vẽ một hình ảnh 300dpi vào đối tượng canvas nhưng trong Chrome nó hiển thị ở chất lượng rất kém. Khi tôi sử dụng mã dưới đây, nó không cải thiện nhưng đó là vì devicePixelRatio giống với backingStoreRatio (cả hai đều là 1).Cách vẽ ở độ phân giải cao đến canvas trên Chrome? Và tại sao nếu devicePixelRatio === webkitBackingStorePixelRatio làm rộng gấp 2 lần để cải thiện độ phân giải?

sau đó tôi đã cố gắng để buộc một số thay đổi tỷ lệ và thấy như sau:

  • nếu tôi thay đổi ratio2 và buộc mã mở rộng quy mô để chạy, sau đó nó lôi kéo vào khung ở độ phân giải tốt hơn.
  • Nếu tôi thay đổi ratio bất cứ điều gì lớn hơn 2 (ví dụ 3, 4, 5, 6, vv) sau đó nó có độ phân giải nghèo!

Điều này được thực hiện trên máy tính để bàn.

Làm cách nào để đảm bảo canvas vẽ với độ phân giải cao?

(Mã số từ: http://www.html5rocks.com/en/tutorials/canvas/hidpi/)

/** 
* Writes an image into a canvas taking into 
* account the backing store pixel ratio and 
* the device pixel ratio. 
* 
* @author Paul Lewis 
* @param {Object} opts The params for drawing an image to the canvas 
*/ 
function drawImage(opts) { 

    if(!opts.canvas) { 
     throw("A canvas is required"); 
    } 
    if(!opts.image) { 
     throw("Image is required"); 
    } 

    // get the canvas and context 
    var canvas = opts.canvas, 
    context = canvas.getContext('2d'), 
    image = opts.image, 

    // now default all the dimension info 
    srcx = opts.srcx || 0, 
    srcy = opts.srcy || 0, 
    srcw = opts.srcw || image.naturalWidth, 
    srch = opts.srch || image.naturalHeight, 
    desx = opts.desx || srcx, 
    desy = opts.desy || srcy, 
    desw = opts.desw || srcw, 
    desh = opts.desh || srch, 
    auto = opts.auto, 

    // finally query the various pixel ratios 
    devicePixelRatio = window.devicePixelRatio || 1, 
    backingStoreRatio = context.webkitBackingStorePixelRatio || 
    context.mozBackingStorePixelRatio || 
    context.msBackingStorePixelRatio || 
    context.oBackingStorePixelRatio || 
    context.backingStorePixelRatio || 1,  
    ratio = devicePixelRatio/backingStoreRatio; 

    // ensure we have a value set for auto. 
    // If auto is set to false then we 
    // will simply not upscale the canvas 
    // and the default behaviour will be maintained 
    if (typeof auto === 'undefined') { 
     auto = true; 
    } 

    // upscale the canvas if the two ratios don't match 
    if (auto && devicePixelRatio !== backingStoreRatio) { 

     var oldWidth = canvas.width; 
     var oldHeight = canvas.height; 

     canvas.width = oldWidth * ratio; 
     canvas.height = oldHeight * ratio; 

     canvas.style.width = oldWidth + 'px'; 
     canvas.style.height = oldHeight + 'px'; 

     // now scale the context to counter 
     // the fact that we've manually scaled 
     // our canvas element 
     context.scale(ratio, ratio); 

    } 

    context.drawImage(pic, srcx, srcy, srcw, srch, desx, desy, desw, desh); 
} 

Making kết quả chỉ là những thay đổi dưới đây trong hình ảnh vải có độ phân giải cao (tại sao?):

//WE FORCE RATIO TO BE 2 
    ratio = 2; 

    //WE FORCE IT TO UPSCALE (event though they're equal) 
    if (auto && devicePixelRatio === backingStoreRatio) { 

Nếu chúng ta thay đổi trên là một tỷ lệ 3, nó không còn có độ phân giải cao nữa!

EDIT: Một thêm quan sát - ngay cả với tỷ lệ 2x, trong khi nó là độ phân giải đáng chú ý hơn, nó vẫn không sắc nét như hiển thị hình ảnh trong thẻ img)

+0

Xem này: http://stackoverflow.com/a/15666143/3655 – Aardvark

+0

@Aardvark Như tôi đã đề cập trong bài viết của tôi , Tôi đã thử mã từ bài viết đó (HTML5Rocks) mà là những gì mà bài tham khảo, và nó không hoạt động bởi vì nó luôn luôn cho thấy tỷ lệ là '1', mà không có gì. –

+0

Bạn đã nhầm lẫn với imageSmoothingEnabled chưa? – Aardvark

Trả lời

3

Một mẹo nhỏ bạn có thể sử dụng là thực sự đặt chiều rộng và chiều cao của canvas thành chiều cao và chiều rộng của ảnh, sau đó sử dụng css để thay đổi kích thước hình ảnh. Dưới đây là một ví dụ về mã sudo.

canvasElement.width = imgWidth; 
canvasElement.height = imgHeight; 
canvasElement.getContext("2d").drawImage(ARGS); 

Sau đó, bạn có thể sử dụng cài đặt chiều cao & chiều cao hoặc chuyển đổi 3d.

canvasElement.style.transform = "scale3d(0.5,0.5,0)"; 

hoặc

canvasElement.style.width = newWidth; 
canvasElement.style.height = newWidth * imgHeight/imgWidth; 

tôi khuyên bạn sử dụng 3d transform vì khi bạn sử dụng 3d biến đổi dữ liệu hình ảnh của nguyên tố này được sao chép vào GPU, do đó bạn không thực sự phải lo lắng về bất kỳ chất lượng suy thoái.

Tôi hy vọng điều này sẽ hữu ích!

9

Bài viết HTML5Rocks liên kết từ các câu hỏi làm cho mọi thứ khó khăn hơn họ cần phải được, nhưng nó làm cho các sai lầm cơ bản giống như các nguồn lực khác tôi đã thấy (1, 2, 3, 4).Các tham chiếu đó cung cấp một số thay đổi về công thức này:

var rect = canvas.getBoundingClientRect(); 
canvas.width = Math.round (devicePixelRatio * rect.width); // WRONG! 

Công thức sai. Một công thức tốt hơn là

var rect = canvas.getBoundingClientRect(); 
canvas.width = Math.round (devicePixelRatio * rect.right) 
      - Math.round (devicePixelRatio * rect.left); 

Vấn đề là, nó không có ý nghĩa với quy mô chiều rộng hoặc chiều cao (ví dụ: , sự khác biệt của hai vị trí) bởi devicePixelRatio. Bạn chỉ nên quy mô một vị trí tuyệt đối. Tôi không thể tìm thấy một tài liệu tham khảo cho điểm chính xác này nhưng tôi nghĩ rằng nó là hiển nhiên, một khi bạn nhận được nó.

Đề xuất.

Không có cách nào để tính chiều rộng và chiều cao vật lý của hình chữ nhật (theo pixel thiết bị) từ chiều rộng và chiều cao CSS của nó (trong pixel độc lập của thiết bị).

Bằng chứng.

Giả sử bạn có hai yếu tố có hình chữ nhật bounding bằng pixel thiết bị độc lập được

{ left: 0, top: 10, right: 8, bottom: 20, width: 8, height: 10 }, 
{ left: 1, top: 20, right: 9, bottom: 30, width: 8, height: 10 }. 

Bây giờ giả sử devicePixeRatio là 1,4 các yếu tố sẽ bao gồm những hình chữ nhật thiết bị-pixel:

{ left: 0, top: 14, right: 11, bottom: 28, width: 11, height: 14 }, 
{ left: 1, top: 28, right: 13, bottom: 42, width: 12, height: 14 }, 

nơi trái, trên cùng, bên phải và dưới cùng đã được nhân với devicePixelRatio và được làm tròn tới số nguyên gần nhất (sử dụng Math.round()).

Bạn sẽ nhận thấy rằng hai hình chữ nhật có cùng chiều rộng trong pixel không phụ thuộc thiết bị nhưng có độ rộng khác nhau trong pixel của thiết bị. ▯

Thử nghiệm.

Đây là mẫu mã để thử nghiệm. Tải nó trong một trình duyệt, sau đó phóng to và thu nhỏ bằng chuột. Canvas cuối cùng phải luôn có đường nét sắc nét. Ba thiết bị còn lại sẽ bị mờ ở một số độ phân giải.

Được thử nghiệm trên máy tính để bàn Firefox, IE, Edge, Chrome và Android Chrome và Firefox. (Lưu ý, đây không hoạt động trên JSfiddle vì getBoundingClientRect trả về giá trị không chính xác ở đó.)

<!DOCTYPE html> 
<html> 
    <head> 
    <script> 
     function resize() { 
     var canvases = document.getElementsByTagName("canvas"); 
     var i, j; 
     for (i = 0; i != canvases.length; ++ i) { 
      var canvas = canvases[i]; 
      var method = canvas.getAttribute("method"); 
      var dipRect = canvas.getBoundingClientRect(); 
      var context = canvas.getContext("2d"); 
      switch (method) { 
      case "0": 
       // Incorrect: 
       canvas.width = devicePixelRatio * dipRect.width; 
       canvas.height = devicePixelRatio * dipRect.height; 
       break; 

      case "1": 
       // Incorrect: 
       canvas.width = Math.round(devicePixelRatio * dipRect.width); 
       canvas.height = Math.round(devicePixelRatio * dipRect.height); 
       break; 

      case "2": 
       // Incorrect: 
       canvas.width = Math.floor(devicePixelRatio * dipRect.width); 
       canvas.height = Math.floor(devicePixelRatio * dipRect.height); 
       break; 

      case "3": 
       // Correct: 
       canvas.width = Math.round(devicePixelRatio * dipRect.right) 
       - Math.round(devicePixelRatio * dipRect.left); 
       canvas.height = Math.round(devicePixelRatio * dipRect.bottom) 
       - Math.round(devicePixelRatio * dipRect.top); 
       break; 
      } 
      console.log("method " + method 
      + ", devicePixelRatio " + devicePixelRatio 
      + ", client rect (DI px) (" + dipRect.left + ", " + dipRect.top + ")" 
      + ", " + dipRect.width + " x " + dipRect.height 
      + ", canvas width, height (logical px) " + canvas.width + ", " + canvas.height); 

      context.clearRect(0, 0, canvas.width, canvas.height); 
      context.fillStyle = "cyan"; 
      context.fillRect(0, 0, canvas.width, canvas.height); 
      context.fillStyle = "black"; 
      for (j = 0; j != Math.floor (canvas.width/2); ++ j) { 
      context.fillRect(2 * j, 0, 1, canvas.height); 
      } 
     } 
     }; 
     addEventListener("DOMContentLoaded", resize); 
     addEventListener("resize", resize); 
    </script> 
    </head> 
    <body> 
    <canvas method="0" style="position: absolute; left: 1px; top: 10px; width: 8px; height: 10px"></canvas> 
    <canvas method="1" style="position: absolute; left: 1px; top: 25px; width: 8px; height: 10px"></canvas> 
    <canvas method="2" style="position: absolute; left: 1px; top: 40px; width: 8px; height: 10px"></canvas> 
    <canvas method="3" style="position: absolute; left: 1px; top: 55px; width: 8px; height: 10px"></canvas> 
    </body> 
</html> 
Các vấn đề liên quan