2016-08-10 1 views
1

확대가 가능한 이미지 뷰어를 만들려고합니다. 확대/축소 비율이나 이미지 크기가 이미지가 더 이상 전체 캔버스에 칠해지지 않을 정도로 크면 특정 배경색으로 칠해진 이미지가 포함되지 않은 캔버스 영역을 갖기를 원합니다.캔버스의 이미지가 팬될 때 바둑판 모양의 흔적을 남깁니다.

내 현재 구현은 확대/축소 및 패닝을 허용하지만 이미지가 팬 조작 중에 바둑판 모양의 흔적을 남기는 원치 않는 효과가 있습니다 (게임에서 승리 할 때 Windows Solitaire의 카드와 유사). 이미지가 흔적을 남기지 않고 배경 사각형이 내 캔버스에서 제대로 렌더링되도록 캔바스를 정리하려면 어떻게합니까?

원하지 않는 효과 설정 배율을 어두운 회색 배경으로 표시되는 수준으로 다시 설정 한 다음 마우스로 이미지를 패닝 (마우스를 아래로 드래그)하십시오.

아래에 코드 스 니펫이 추가되었으며 거기에 대해 궁금한 사람들은 Plnkr 링크가 있습니다.

<!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()에 통화 사이의 캔버스를 삭제해야하고 둘째, 당신은 깔끔하게 drawScreen()에 호출 사이의 매트릭스 변환 캔버스 를 다시 캔버스를 context.save()context.restore() 방법을 사용해야합니다. 코드 감안할 때

은 그대로 약자

캔버스을 취소하는 기능을 만듭니다. 예 : canavs.onmousemove() 함수에서

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

...

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() 호출 ... clearCanvas() 전화 및 변환 행렬을 재정의
전에 context.save() 호출
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로 스타일을 지정할 수 있습니다. 이는 저사양의 기기에서 성능을 향상시킬 수 있습니다. https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/save

  • Canvas.context.restore : https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/restore
  • 관련 항목

  • +0

    멋진 답변. 나는 너를이 주제에 대해 깊은 우물이라고 말할 수있다. – mccainz

    +1

    비효율적 인 저장/복원 단계를 사용해야하는 이유는 변환을 기본값으로 재설정하는 것뿐입니다. 그리고 왜'if (context.save) context.restore()'??? save는 함수이며 항상 true로 평가됩니다. 불필요한 복잡성 만 추가했습니다. 또한 마우스 이벤트에 RAF를 추가하는 것은 마우스 이동이 프레임 동안 여러 번 발사 될 수 있기 때문에 좋은 생각이 아닙니다. 마우스 이벤트에서 렌더링을 분리하고 새로 고침 당 한 번만 렌더링해야합니다. 다른 모든 것은 처리되지 않으므로 낭비입니다. – Blindman67

    +0

    @ Blindman67 "save는 함수이며 항상 true로 평가됩니다"- 예, 그다지 쓸모가 없지만'drawScreen()'이 onmousemove' 이벤트 사이클 외부에서 호출되기 때문에 어떤 조건을 적용해야합니다. 예를 들어'if (isDown) context.restore()'? 나는 RAF를 draw 함수에 대한 호출을 조절하는 유용한 방법이라고 생각합니다. 아마도 당신은 아래에서 더 자세한 설명을 줄 수 있습니다. ;) –

    3

    http://plnkr.co/edit/Cl4T4d13AgPpaDFzhsq1

    당신은 변환을 다시 설정해야합니다.

    캔버스를 지우기 직전에 context.setTransform(1,0,0,1,0,0);을 추가하면 문제가 해결됩니다. 현재 변환을 기본값으로 설정합니다. 그런 다음 이미지를 그리기 위해 이미지 변환을 설정합니다.

    업데이트 : 마우스 또는 터치 이벤트와 같은 사용자 입력과 상호 작용할 때 렌더링과는 별도로 처리해야합니다. 렌더링은 프레임 당 한 번만 실행되며 이전 새로 고침 간격 동안 발생한 모든 마우스 변경 사항을 시각적으로 변경합니다. 필요하지 않은 경우 렌더링이 수행되지 않습니다.

    필요하지 않은 경우 저장 및 복원을 사용하지 마십시오. 당신이 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을 설정해야합니다, 최적화 할 수 있습니다.

    관련 문제