2016-08-10 6 views
1

拡大可能な画像ビューアを作成しようとしています。ズームファクタまたは画像サイズが、画像がキャンバス全体に塗りつぶされないようなものである場合、指定された背景色で塗りつぶされた画像を含まないキャンバスの領域を持つことを望みます。キャンバス内の画像がパンするとタイル状の軌跡が残る

現在の実装では、ズーム操作とパン操作が可能ですが、パン操作中に画像がタイル張りの軌跡を残すという望ましくない効果があります(ゲームに勝利したときのソリティソリティアのカードとよく似ています)。画像が軌跡を残さず、背景の矩形がキャンバスに正しくレンダリングされるようにキャンバスをクリーンアップするにはどうすればよいですか?

暗い灰色の背景が表示されるレベルに、不要なエフェクト設定倍率を再作成し、マウスで画像をパンします(マウスを下にドラッグします)。

コードスニペットを下に追加し、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()関数が呼び出されるたびにキャンバスにスケーリングされた画像をペイントしているので、あなたはあなたのデモンストレーションで「タイル張り」効果を見ている

。簡単な2つの手順でこれを修正できます。

まず、あなたはdrawScreen()への呼び出しの間にキャンバスをクリアする必要があると第二、あなたはきれいにキャンバスdrawScreen()への呼び出しの間で行列を変換リセットするためにキャンバスcontext.save()context.restore()メソッドを使用する必要があります。であるとして、あなたのコードを考える

スタンド:

は、キャンバスをクリアする関数を作成します。例えば

canavs.onmousemove()機能で
function clearCanvas() { 
     context.clearRect(0, 0, canvas.width, canvas.height); 
    } 

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イメージを再スケーリングする前にclearCanvas()に電話して、.fillRect()drawScreen())ではなく、キャンバスの背景をCSSでスタイル設定することができます。これは、低スペックのデバイスでパフォーマンスを向上させる可能性があります。参照

以下Blindman67からのコメントに照らして編集

また

+0

素敵な答え。私はあなたがこの主題の深い井戸であることを伝えることができます。 – mccainz

+1

高価なセーブ/リストア・ステップを使用するのは、トランスフォームをデフォルトにリセットするだけで済む理由です。そしてなぜ 'if(context.save)context.restore()' ??? saveは関数であり、常にtrueと評価されます。不要な複雑さだけが追加されました。また、マウスの動きにRAFを追加することは、マウスの動きがフレーム中に何度も発射することができるので、良い考えではありません。レンダリングをマウスイベントから切り離し、1回のリフレッシュにつき1回だけレンダリングする必要があります。それ以外の場合、それは見えないほど処理の無駄です。 – Blindman67

+0

@ Blindman67 "saveは関数であり、常にtrueと評価されます" - はい、それは不合理ですが、 'drawScreen()'が 'onmousemove'イベントサイクルの外で呼び出されるため、 isDown)context.restore() '?私はRAFを描画関数の呼び出しを抑制するための有益な方法と考えています - おそらく、あなたは以下でより詳細な説明を与えることができます。 ;) –

3

キャンバスを消去する直前にcontext.setTransform(1,0,0,1,0,0);を追加すると、問題が解決されます。現在の変換をデフォルト値に設定します。次に、イメージはイメージの変換を描画セットにします。

更新: マウスやタッチイベントなどのユーザー入力と対話するときは、レンダリングとは独立して処理する必要があります。レンダリングはフレームごとに1回だけ実行され、前のリフレッシュ間隔で発生したマウスの変更を視覚的に変更します。不要な場合はレンダリングが行われません。

必要がない場合は、保存と復元を使用しないでください。あなたがcontext.fillRectを呼び出す前に、変換行列を保存する

 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.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を設定する必要があります。

関連する問題