Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
178 views
in Technique[技术] by (71.8m points)

javascript - scale canvas with imageData inside

I have a canvas with imageData inside. I would like to learn how to zoom canvas (the fastest way)

Right now my code looks like this

// get canvas element
var canvas = document.getElementById('canvas');
var canvasWidth  = 1000;
var canvasHeight = 1000;
var ctx = canvas.getContext('2d');


var imageData = ctx.getImageData(0, 0, window.innerWidth, window.innerHeight);

var buf = new ArrayBuffer(imageData.data.length);
var buf8 = new Uint8ClampedArray(buf);
var data = new Uint32Array(buf);

// little noise 
for (var y = 0; y < canvasHeight; ++y) {
    for (var x = 0; x < canvasWidth; ++x) {
        var value = x * y & 0xff;

        data[y * canvasWidth + x] =
            (255 << 24) |    // alpha
            (Math.floor(Math.random() * 256) << 24) |    // blue
            (Math.floor(Math.random() * 256) << 12) |    // green
             Math.floor(Math.random() * 256) << 6;            // red
    }
}

imageData.data.set(buf8);


// lets start  zooming

let WINDOW_WIDTH = window.innerWidth;
let WINDOW_HEIGHT = window.innerHeight;


// max and minimum level of zooming
let MAX_ZOOM = 5;
let MIN_ZOOM = 0.5;
let SCROLL_SENSITIVITY = 0.0005;
let cameraZoom = 1;


function adjustZoom(zoomAmount) {
    if (zoomAmount) 
    {
        cameraZoom += zoomAmount;
    }
        
    cameraZoom = Math.min( cameraZoom, MAX_ZOOM ); 
    cameraZoom = Math.max( cameraZoom, MIN_ZOOM );
}


function draw() {

      canvas.width = WINDOW_WIDTH;
    canvas.height = WINDOW_HEIGHT;
    // set new center 
    ctx.translate( WINDOW_WIDTH / 2, WINDOW_HEIGHT / 2 );

    // scalling 
    ctx.scale(cameraZoom, cameraZoom);
    
    // clear canvas
    ctx.clearRect(0,0, WINDOW_WIDTH, WINDOW_HEIGHT);

    // draw pixels
    ctx.putImageData(imageData, 0, 0);

  // draw rectangle
    ctx.fillStyle = "#FF0000";
    ctx.fillRect( 100, 100, 100, 100 );

    requestAnimationFrame( draw );
}

// wheel listener
canvas.addEventListener( 'wheel', (e) => adjustZoom(e.deltaY*SCROLL_SENSITIVITY));


draw();
canvas {
    position: absolute;
  top: 0; 
  left: 0;
  width: 100%; 
  height: 100%;
  border: 1px solid black;
  image-rendering: pixelated;
}
<canvas id="canvas" width="1000px" height="1000px"></canvas>
question from:https://stackoverflow.com/questions/65946550/scale-canvas-with-imagedata-inside

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

putImageData (and getImageData) methods are not affected by the current transformation of the context.

To do what you want the best is to create a second canvas, the size of your ImageData only to render the ImageData, and then to drawImage that second canvas onto the main one:

// get canvas element
var canvas = document.getElementById('canvas');
var canvasWidth  = 500;
var canvasHeight = 500;
var ctx = canvas.getContext('2d');
// create a second canvas, only for the ImageData
var imageDataCanvas = document.createElement('canvas');
// <note>
//   if you don't read the data, don't call getImageData
//   simply create a new blank ImageData
// </note>
var imageData = ctx.createImageData(canvasWidth, canvasHeight);
// set the canvas size to the ImageData's size
imageDataCanvas.width = imageData.width;
imageDataCanvas.height = imageData.height;
var imageDataCtx = imageDataCanvas.getContext("2d");

// <note>
//   don't assign a second buffer
//   eats memory for nothing and .set is not that fast
//   instead work directly with the one from the ImageData 
// </note>
var buf = imageData.data.buffer;
var data = new Uint32Array(buf);

// made a function so it changes every frame
function makeNoise() {
  // little noise 
  for (var y = 0; y < canvasHeight; ++y) {
      for (var x = 0; x < canvasWidth; ++x) {
          var value = x * y & 0xff;

          data[y * canvasWidth + x] =
              (255 << 24) |    // alpha
              (Math.floor(Math.random() * 256) << 24) |    // blue
              (Math.floor(Math.random() * 256) << 12) |    // green
               Math.floor(Math.random() * 256) << 6;            // red
      }
  }
}
// no need to set anything anymore, we did modify the ArrayBuffer directly


// lets start  zooming

// max and minimum level of zooming
let MAX_ZOOM = 5;
let MIN_ZOOM = 0.5;
let SCROLL_SENSITIVITY = 0.0005;
let cameraZoom = 1;


function adjustZoom(zoomAmount) {
    if (zoomAmount) 
    {
        cameraZoom += zoomAmount;
    }
        
    cameraZoom = Math.min( cameraZoom, MAX_ZOOM ); 
    cameraZoom = Math.max( cameraZoom, MIN_ZOOM );
}


function draw() {

    // update the pixels
    makeNoise();
    // draw the pixels on the second canvas
    // <note>
    //   no need to clear this context
    //   putImageData takes care of it for us
    // </note>
    imageDataCtx.putImageData(imageData, 0, 0);
    // <note>
    //   don't set the width or height of your canvas in the rendering loop
    //   resizing the context will reset all the properties of the context to their default
    //   which is already slow enough
    //   but it will even reassign a new drawing-buffer
    //   meaning GC will have to kick-in to get back the previous buffer
    //   (at least in Chromium browsers)
    // </note>

    // reset to identity matrix
    ctx.setTransform(1, 0, 0, 1, 0, 0);
    ctx.clearRect(0, 0, canvasWidth, canvasHeight)
    
    // set scaling origin
    ctx.translate( canvasWidth / 2, canvasHeight / 2 );

    // scaling 
    ctx.scale(cameraZoom, cameraZoom);

    // move back for drawing
    ctx.translate( -canvasWidth / 2, -canvasHeight / 2 );

    // <note> clear only at identity matrix </note>

    // draw pixels
    // avoid blur
    ctx.imageSmoothingEnabled = false;
    ctx.drawImage(imageDataCanvas, 0, 0);

  // draw rectangle
    ctx.fillStyle = "#FF0000";
    ctx.fillRect( 100, 100, 100, 100 );

    requestAnimationFrame( draw );
}

// wheel listener
canvas.addEventListener( 'wheel', (e) => {
  e.preventDefault(); // prevent scroll
  adjustZoom(e.deltaY*SCROLL_SENSITIVITY);
});

draw();
canvas {
  position: absolute;
  top: 0; 
  left: 0;
  image-rendering: pixelated;
}
<canvas id="canvas" width="500" height="500"></canvas>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...