2016-08-10 62 views
1

我正在嘗試創建一個也可以放大的平移圖像查看器。如果縮放因子或圖像大小使圖像不再覆蓋整個畫布,我希望畫布區域不包含用指定背景顏色繪製的圖像。畫布上的圖像在平移時留下平鋪痕跡

我目前的實現允許縮放和平移,但會產生不希望的效果,即在平移操作期間圖像留下平鋪痕跡(很像當您贏得遊戲時在Windows紙牌中顯示的紙牌)。如何清理畫布,使圖像不會留下痕跡,並且我的背景矩形可以在畫布中正確呈現?

重新創建不需要的效果設置放大到某個級別,在該級別您將看到深灰色背景顯示,然後用鼠標平移圖像(鼠標向下並拖動)。

下面添加的代碼片段和Plnkr鏈接爲那些誰想要在那裏搞砸。 http://plnkr.co/edit/Cl4T4d13AgPpaDFzhsq1

<!DOCTYPE html> 
 
<html> 
 

 
    <head> 
 
    <style> 
 
     canvas{ 
 
     border:solid 5px #333; 
 
     } 
 
    </style> 
 
    
 
    </head> 
 

 
    <body> 
 

 
\t <button onclick="changeScale(0.10)">+</button> 
 
\t <button onclick="changeScale(-0.10)">-</button> 
 
\t 
 
    <div id="container"> 
 
     <canvas width="700" height="500" id ="canvas1"></canvas> 
 
    </div> 
 
    
 
    <script> 
 
    
 
     var canvas = document.getElementById('canvas1'); 
 
     var context = canvas.getContext("2d"); 
 
     var imageDimensions ={width:0,height:0}; 
 
     var photo = new Image(); 
 
     var isDown = false; 
 
     var startCoords = []; 
 
     var last = [0, 0]; 
 
     var windowWidth = canvas.width; 
 
     var windowHeight = canvas.height; 
 

 
     var scale=1; 
 
     
 
     photo.addEventListener('load', eventPhotoLoaded , false); 
 
     photo.src = "http://www.html5rocks.com/static/images/cors_server_flowchart.png"; 
 
     
 
     function eventPhotoLoaded(e) { 
 
     imageDimensions.width = photo.width; 
 
     imageDimensions.height = photo.height; 
 
     drawScreen(); 
 
     } 
 
     
 
     function changeScale(delta){ 
 
     scale += delta; 
 
     drawScreen(); 
 
     } 
 
     
 
     function drawScreen(){ 
 
     context.fillRect(0,0, windowWidth, windowHeight); 
 
     context.fillStyle="#333333"; 
 
     context.drawImage(photo,0,0,imageDimensions.width*scale,imageDimensions.height*scale); 
 
     } 
 

 
     canvas.onmousedown = function(e) { 
 
      isDown = true; 
 
     
 
      startCoords = [ 
 
       e.offsetX - last[0], 
 
       e.offsetY - last[1] 
 
     ]; 
 
     }; 
 
     
 
     canvas.onmouseup = function(e) { 
 
      isDown = false; 
 
      
 
      last = [ 
 
       e.offsetX - startCoords[0], // set last coordinates 
 
       e.offsetY - startCoords[1] 
 
      ]; 
 
     }; 
 
     
 
     canvas.onmousemove = function(e) 
 
     { 
 
      if(!isDown) return; 
 
      
 
      var x = e.offsetX; 
 
      var y = e.offsetY; 
 
      
 
      context.setTransform(1, 0, 0, 1, 
 
          x - startCoords[0], y - startCoords[1]); 
 
      drawScreen(); 
 
     } 
 
     
 

 
    </script> 
 
    </body> 
 

 
</html>

+1

也許像'context.save(); context.fillRect(0,0,windowWidth,windowHeight); context.restore();'因爲setTransform也改變了你填充的矩形。 – Quarter2Twelve

+0

你是對的。如果你想發表評論作爲答案,我會很樂意接受它。 – mccainz

回答

1

漂亮的代碼;)

,因爲你畫的圖像縮放到畫布上的每個時間的同時拖動drawScreen()函數被調用其本身上您看到您的演示「平鋪」的效果。你可以通過兩個簡單的步驟來解決這個問題。

首先,你需要明確調用之間的畫布drawScreen()和第二,你需要使用畫布context.save()context.restore()方法來清潔重置畫布變換調用之間矩陣drawScreen()

給定您的代碼爲:

創建一個清除畫布的函數。例如

function clearCanvas() { 
     context.clearRect(0, 0, canvas.width, canvas.height); 
    } 

canavs.onmousemove()功能,呼叫clearCanvas()和之前調用context.save()重新定義變換矩陣...

canvas.onmousemove = function(e) { 
     if(!isDown) return; 

     var x = e.offsetX; 
     var y = e.offsetY; 

     /* !!! */ 
     clearCanvas(); 
     context.save(); 

     context.setTransform(
     1, 0, 0, 1, 
     x - startCoords[0], y - startCoords[1] 
    ); 

     drawScreen(); 
    } 

...然後在有條件的drawScreen()末尾調用context.restore() ...

function drawScreen() { 
    context.fillRect(0,0, windowWidth, windowHeight); 
    context.fillStyle="#333333"; 
    context.drawImage(photo,0,0,imageDimensions.width*scale,imageDimensions.height*scale); 
    /* !!! */ 
    if (isDown) context.restore(); 
} 

此外,y ou可能希望在重新縮放圖像之前調用clearCanvas(),並且可以使用CSS而不是.fillRect()(在drawScreen()中)對畫布背景進行樣式設置 - 這可以在低規格設備上提高性能。

編輯中的評論光從Blindman67下面

參見

+0

精彩的回答。我可以告訴你這個問題很深入。 – mccainz

+1

爲什麼在需要的時候使用昂貴的保存/恢復步驟將變換重置爲默認值。爲什麼'if(context.save)context.restore()'???保存是一個功能,並將始終評估爲真實。你只增加了不必要的複雜性。在鼠標事件中添加RAF並不是一個好主意,因爲鼠標移動可以在一幀中多次觸發。應該從鼠標事件中去除渲染,並且每刷新一次只渲染一次,這是浪費處理,因爲它永遠不會被看到。 – Blindman67

+0

@ Blindman67「save是一個函數,並且總是評估爲真」 - 是的,這是sl,的,但是一些條件需要適用,因爲'drawScreen()'也被稱爲'onmousemove'事件週期之外 - 例如'if isDown)context.restore()'?.我認爲RAF是一個有用的方法來限制對draw函數的調用 - 也許你可以在下面給出更詳細的解釋。 ;) –

3

您需要重置變換。

在清除畫布之前添加context.setTransform(1,0,0,1,0,0);即可解決問題。它將當前轉換設置爲默認值。然後爲圖像繪製圖像的變換。

更新: 與用戶輸入(如鼠標或觸摸事件)交互時,應該獨立於渲染處理。渲染將每幀觸發一次,並對上一次刷新間隔期間發生的任何鼠標變化進行可視化更改。如果不需要,則不進行渲染。

如果你不需要,不要使用保存和恢復。

 var canvas = document.getElementById('canvas1'); 
 
     var ctx = canvas.getContext("2d"); 
 
     var photo = new Image(); 
 
     var mouse = {} 
 
     mouse.lastY = mouse.lastX = mouse.y = mouse.x = 0; 
 
     mouse.down = false; 
 
     var changed = true; 
 
     var scale = 1; 
 
     var imageX = 0; 
 
     var imageY = 0; 
 
     photo.src = "http://www.html5rocks.com/static/images/cors_server_flowchart.png"; 
 
     function changeScale(delta){ 
 
     scale += delta; 
 
     changed = true; 
 
     } 
 
     // Turns mouse button of when moving out to prevent mouse button locking if you have other mouse event handlers. 
 
     function mouseEvents(event){ // do it all in one function 
 
      if(event.type === "mouseup" || event.type === "mouseout"){ 
 
      mouse.down = false; 
 
      changed = true; 
 
      }else 
 
      if(event.type === "mousedown"){ 
 
      mouse.down = true;    
 
      } 
 
      mouse.x = event.offsetX; 
 
      mouse.y = event.offsetY; 
 
      if(mouse.down) { 
 
      changed = true; 
 
      } 
 
     } 
 
     canvas.addEventListener("mousemove",mouseEvents); 
 
     canvas.addEventListener("mouseup",mouseEvents); 
 
     canvas.addEventListener("mouseout",mouseEvents); 
 
     canvas.addEventListener("mousedown",mouseEvents); 
 

 
     function update(){ 
 
     requestAnimationFrame(update); 
 
     if(photo.complete && changed){ 
 
      ctx.setTransform(1,0,0,1,0,0); 
 
      ctx.fillStyle="#333"; 
 
      ctx.fillRect(0,0, canvas.width, canvas.height); 
 
      if(mouse.down){ 
 
      imageX += mouse.x - mouse.lastX; 
 
      imageY += mouse.y - mouse.lastY; 
 
      } 
 
      ctx.setTransform(scale, 0, 0, scale, imageX,imageY); 
 
      ctx.drawImage(photo,0,0); 
 
      changed = false; 
 
     } 
 
     mouse.lastX = mouse.x 
 
     mouse.lastY = mouse.y     
 
     } 
 
     requestAnimationFrame(update);
canvas{ 
 
    border:solid 5px #333; 
 
    }
<button onclick="changeScale(0.10)">+</button><button onclick="changeScale(-0.10)">-</button> 
 
<canvas width="700" height="500" id ="canvas1"></canvas>

+0

很好地工作,並解決了我在使用縮放功能以及簡單地保存和恢復狀態時遇到的問題。 – mccainz

1

呼叫context.save給你打電話context.fillRect之前保存的變換矩陣。 然後,無論何時您需要繪製圖像,請致電context.restore恢復矩陣。

例如:

function drawScreen(){ 
    context.save(); 
    context.fillStyle="#333333"; 
    context.fillRect(0,0, windowWidth, windowHeight); 
    context.restore(); 
    context.drawImage(photo,0,0,imageDimensions.width*scale,imageDimensions.height*scale); 
    } 

此外,爲了進一步優化,你只需要設置fillStyle一次,直到您更改畫布大小。