简体   繁体   中英

Move cropped image in HTML5 canvas

How can I move the cropped image with the crop down by 100px and left by 50px inside the canvas? Included jsfiddle link.

Javascript

// Grab the Canvas and Drawing Context
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');



// Create an image element
var img = document.createElement('IMG');

// When the image is loaded, draw it
img.onload = function () {

// Save the state, so we can undo the clipping
ctx.save();


// Create a shape, of some sort
ctx.beginPath();
ctx.moveTo(10, 10);
ctx.lineTo(100, 30);
ctx.lineTo(180, 10);
ctx.lineTo(200, 60);
ctx.arcTo(180, 70, 120, 0, 10);
ctx.lineTo(200, 180);
ctx.lineTo(100, 150);
ctx.lineTo(70, 180);
ctx.lineTo(20, 130);
ctx.lineTo(50, 70);
ctx.closePath();
// Clip to the current path
ctx.clip();


ctx.drawImage(img, 0, 0);

// Undo the clipping
ctx.restore();
}

// Specify the src to load the image
img.src = "http://i.imgur.com/gwlPu.jpg";

html

<canvas id="c" width="400" height="400"></canvas>

jsFiddle http://jsfiddle.net/dDUC3/3805/

You should create the shape at your desired location in the first place, that would be the appropriate solution.

However, at this stage, you can use getImageData() and putImageData() methods to accomplish the movements ...

 // Grab the Canvas and Drawing Context var canvas = document.getElementById('c'); var ctx = canvas.getContext('2d'); // Create an image element var img = document.createElement('IMG'); // When the image is loaded, draw it img.onload = function() { // Save the state, so we can undo the clipping ctx.save(); // Create a shape, of some sort ctx.beginPath(); ctx.moveTo(10, 10); ctx.lineTo(100, 30); ctx.lineTo(180, 10); ctx.lineTo(200, 60); ctx.arcTo(180, 70, 120, 0, 10); ctx.lineTo(200, 180); ctx.lineTo(100, 150); ctx.lineTo(70, 180); ctx.lineTo(20, 130); ctx.lineTo(50, 70); ctx.closePath(); // Clip to the current path ctx.clip(); ctx.drawImage(img, 0, 0); // Undo the clipping ctx.restore(); move(50, 100); //move left: 50px, down: 100px } // Set cross origin for the image, as it's not hosted on local server img.crossOrigin = 'anonymous'; // Specify the src to load the image img.src = "http://i.imgur.com/gwlPu.jpg"; function move(left, down) { var croppedImage = ctx.getImageData(0, 0, 200, 200); ctx.clearRect(0, 0, canvas.width, canvas.height); //clear canvas ctx.putImageData(croppedImage, 0 + left, 0 + down); } 
 canvas { background: #CEF; } 
 <canvas id="c" width="400" height="400"></canvas> 

A safe canvas cut and paste

The given answer will fail in many situations due to cross origin security violations associated with ctx.getImageData and images outside your domain.

Direct copy & paste

If you are moving the image as a square region or so that it does not overlap with the original you can copy directly from within the canvas.

ctx.drawImage(ctx.canvas, 0, 0, 200, 200, 100, 100, 200, 200);

Direct cut & paste

As you have some overlap that will need to be cleared you can copy in two parts

// move bottom half
ctx.drawImage(ctx.canvas, 0, 100, 200, 100, 100, 200, 200, 100); 
// clear bottom half
ctx.clearRect(0, 100, 200, 100);
// move top half
ctx.drawImage(ctx.canvas, 0, 0, 200, 100, 100, 100, 200, 100); 
// lear top half
ctx.clearRect(0, 0, 200, 100);

But that is still a little problematic.

General cut and paste

The Ideal solution is to create a temporary canvas to hold the image so that it can be moved. It will not be effected by overlap, nor will it fail if the canvas is tainted by cross origin data.

function cutFromCanvas(ctx, x, y, w, h){
    const cut = document.createElement("canvas");
    cut.width = w;
    cut.height = h;
    cut.getContext("2d").drawImage(ctx.canvas, -x, -y, w, h, 0, 0, w, h);
    ctx.clearRect(x, y, w, h);
    return cut;
}

Then to past at the new location just draw the image

  ctx.drawImage(cutFromCanvas(ctx, 0, 0 200, 200), 100, 100);

As said by @gaand, the best is to move your clipping-shape where it should be. This can be done without modifying your path declaration, with setTransform .

 var canvas = document.getElementById('c'); var ctx = canvas.getContext('2d'); var img = document.createElement('IMG'); img.onload = function() { // move everything ctx.setTransform(1, 0, 0, 1, 100, 100); ctx.save(); ctx.beginPath(); ctx.moveTo(10, 10); ctx.lineTo(100, 30); ctx.lineTo(180, 10); ctx.lineTo(200, 60); ctx.arcTo(180, 70, 120, 0, 10); ctx.lineTo(200, 180); ctx.lineTo(100, 150); ctx.lineTo(70, 180); ctx.lineTo(20, 130); ctx.lineTo(50, 70); ctx.closePath(); ctx.clip(); ctx.drawImage(img, 0, 0); // to restore the original matrix ctx.setTransform(1, 0, 0, 1, 100, 100); // but it's also restored here anyway... ctx.restore(); }; img.src = "http://i.imgur.com/gwlPu.jpg"; 
 canvas { background: lightblue } 
 <canvas id="c" width="500" height="300"></canvas> 

But if you really need to move your drawing once it has been drawn, then the best is to use an offscreen canvas on which you will generate your clipped image, and finally draw this offscreen canvas where you want on the visible one :

 var canvas = document.getElementById('c'); var ctx = canvas.getContext('2d'); var img = document.createElement('IMG'); var clippedCanvas = document.createElement('canvas'); var clippedCtx = clippedCanvas.getContext('2d'); img.onload = function() { //prepare our offscreen canvas clippedCanvas.width = this.width; clippedCanvas.height = this.height; // generate the drawing on the offscreen ctx clippedCtx.save(); clippedCtx.beginPath(); clippedCtx.moveTo(10, 10); clippedCtx.lineTo(100, 30); clippedCtx.lineTo(180, 10); clippedCtx.lineTo(200, 60); clippedCtx.arcTo(180, 70, 120, 0, 10); clippedCtx.lineTo(200, 180); clippedCtx.lineTo(100, 150); clippedCtx.lineTo(70, 180); clippedCtx.lineTo(20, 130); clippedCtx.lineTo(50, 70); clippedCtx.closePath(); clippedCtx.clip(); clippedCtx.drawImage(img, 0, 0); // now we can our composed image anywhere in th visible canvas ctx.drawImage(clippedCanvas, 100,100); }; img.src = "http://i.imgur.com/gwlPu.jpg"; 
 canvas { background: lightblue } 
 <canvas id="c" width="500" height="300"></canvas> 

But for a one-shot, you may find it cumbersome to declare a new canvas element. So a third option is to make use of globalCompositeOperation 'copy' which allow us to draw a canvas over itself without keeping the previous state :

 var canvas = document.getElementById('c'); var ctx = canvas.getContext('2d'); var img = document.createElement('IMG'); img.onload = function() { ctx.save(); ctx.beginPath(); ctx.moveTo(10, 10); ctx.lineTo(100, 30); ctx.lineTo(180, 10); ctx.lineTo(200, 60); ctx.arcTo(180, 70, 120, 0, 10); ctx.lineTo(200, 180); ctx.lineTo(100, 150); ctx.lineTo(70, 180); ctx.lineTo(20, 130); ctx.lineTo(50, 70); ctx.closePath(); ctx.clip(); ctx.drawImage(img, 0, 0); ctx.restore(); // the magic ctx.globalCompositeOperation = 'copy'; // draw the canvas over itself, at 100 100 ctx.drawImage(canvas, 100,100); }; img.src = "http://i.imgur.com/gwlPu.jpg"; 
 canvas { background: lightblue } 
 <canvas id="c" width="500" height="300"></canvas> 

And now that we have discovered gCO, we can even remove this clipping operation :

 var canvas = document.getElementById('c'); var ctx = canvas.getContext('2d'); var img = document.createElement('IMG'); img.onload = function() { // move everything ctx.setTransform(1,0,0,1,100,100); ctx.beginPath(); ctx.moveTo(10, 10); ctx.lineTo(100, 30); ctx.lineTo(180, 10); ctx.lineTo(200, 60); ctx.arcTo(180, 70, 120, 0, 10); ctx.lineTo(200, 180); ctx.lineTo(100, 150); ctx.lineTo(70, 180); ctx.lineTo(20, 130); ctx.lineTo(50, 70); // we draw this shape ctx.fill(); // new pixels will be drawn only where they do overlap with existing ones ctx.globalCompositeOperation = 'source-in'; ctx.drawImage(img, 0, 0); // restore the matrix ctx.setTransform(1,0,0,1,0,0); }; img.src = "http://i.imgur.com/gwlPu.jpg"; 
 canvas { background: lightblue } 
 <canvas id="c" width="500" height="300"></canvas> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM