最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Rotate Existing Image on Canvas - Stack Overflow

programmeradmin1浏览0评论

I'm attempting to rotate an image that has already been drawn on a HTML canvas, like so:

var canvas = document.getElementById("editorCanvas");
var ctx = canvas.getContext("2d");

var canvasOffset = $("#editorCanvas").offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;

var startX;
var startY;
var isDown = false;


var pi2 = Math.PI * 2;
var resizerRadius = 4;
var rr = resizerRadius * resizerRadius;
var draggingResizer = {
    x: 0,
    y: 0
};
var imageX = 0;
var imageY;
var imageWidth, imageHeight, imageRight, imageBottom;
var draggingImage = false;
var startX;
var startY;



var img = new Image();
img.crossOrigin='anonymous';
img.onload = function () {

    var ratio = img.width / img.height;

    imageWidth = 71;
    imageHeight = imageWidth / ratio;
    imageY = (245-imageHeight)/2;
    if (imageHeight > 245) {
        imageHeight = 245;
        imageWidth = imageHeight * ratio;
        imageY = 0;
    }

    imageX = ((canvas.width-imageWidth)/2);
    imageY = ((canvas.height-imageHeight)/2);

    imageRight = imageX + imageWidth;
    imageBottom = imageY + imageHeight;

    draw(true, false);
}

function draw(withAnchors, withBorders) {

    // clear the canvas
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // draw the image
    ctx.drawImage(img, 0, 0, img.width, img.height, imageX, imageY, imageWidth, imageHeight);

    // optionally draw the draggable anchors
    if (withAnchors) {
        drawDragAnchor(imageX, imageY);
        drawDragAnchor(imageRight, imageY);
        drawDragAnchor(imageRight, imageBottom);
        drawDragAnchor(imageX, imageBottom);
    }

    // optionally draw the connecting anchor lines
    if (withBorders) {
        ctx.beginPath();
        ctx.moveTo(imageX, imageY);
        ctx.lineTo(imageRight, imageY);
        ctx.lineTo(imageRight, imageBottom);
        ctx.lineTo(imageX, imageBottom);
        ctx.closePath();
        ctx.stroke();
    }

}

function drawDragAnchor(x, y) {
    ctx.beginPath();
    ctx.arc(x, y, resizerRadius, 0, pi2, false);
    ctx.closePath();
    ctx.fill();
}

function anchorHitTest(x, y) {

    var dx, dy;

    // top-left
    dx = x - imageX;
    dy = y - imageY;
    if (dx * dx + dy * dy <= rr) {
        return (0);
    }
    // top-right
    dx = x - imageRight;
    dy = y - imageY;
    if (dx * dx + dy * dy <= rr) {
        return (1);
    }
    // bottom-right
    dx = x - imageRight;
    dy = y - imageBottom;
    if (dx * dx + dy * dy <= rr) {
        return (2);
    }
    // bottom-left
    dx = x - imageX;
    dy = y - imageBottom;
    if (dx * dx + dy * dy <= rr) {
        return (3);
    }
    return (-1);

}


function hitImage(x, y) {
    return (x > imageX && x < imageX + imageWidth && y > imageY && y < imageY + imageHeight);
}


function handleMouseDown(e) {
    startX = parseInt(e.clientX - offsetX);
    startY = parseInt(e.clientY - offsetY);
    draggingResizer = anchorHitTest(startX, startY);
    draggingImage = draggingResizer < 0 && hitImage(startX, startY);
}

function handleMouseUp(e) {
    draggingResizer = -1;
    draggingImage = false;
    draw(true, false);
}

function handleMouseOut(e) {
    handleMouseUp(e);
}

function handleMouseMove(e) {

    if (draggingResizer > -1) {

        mouseX = parseInt(e.clientX - offsetX);
        mouseY = parseInt(e.clientY - offsetY);

        // resize the image
        switch (draggingResizer) {
            case 0:
                //top-left
                imageX = mouseX;
                imageWidth = imageRight - mouseX;
                imageY = mouseY;
                imageHeight = imageBottom - mouseY;
                break;
            case 1:
                //top-right
                imageY = mouseY;
                imageWidth = mouseX - imageX;
                imageHeight = imageBottom - mouseY;
                break;
            case 2:
                //bottom-right
                imageWidth = mouseX - imageX;
                imageHeight = mouseY - imageY;
                break;
            case 3:
                //bottom-left
                imageX = mouseX;
                imageWidth = imageRight - mouseX;
                imageHeight = mouseY - imageY;
                break;
        }

        if(imageWidth<25){imageWidth=25;}
        if(imageHeight<25){imageHeight=25;}

        // set the image right and bottom
        imageRight = imageX + imageWidth;
        imageBottom = imageY + imageHeight;

        // redraw the image with resizing anchors
        draw(true, true);

    } else if (draggingImage) {

        imageClick = false;

        mouseX = parseInt(e.clientX - offsetX);
        mouseY = parseInt(e.clientY - offsetY);

        // move the image by the amount of the latest drag
        var dx = mouseX - startX;
        var dy = mouseY - startY;
        imageX += dx;
        imageY += dy;
        imageRight += dx;
        imageBottom += dy;
        // reset the startXY for next time
        startX = mouseX;
        startY = mouseY;

        // redraw the image with border
        draw(false, true);

    }


}


$("#editorCanvas").mousedown(function (e) {
    handleMouseDown(e);
});
$("#editorCanvas").mousemove(function (e) {
    handleMouseMove(e);
});
$("#editorCanvas").mouseup(function (e) {
    handleMouseUp(e);
});
$("#editorCanvas").mouseout(function (e) {
    handleMouseOut(e);
});

I was hoping it was this simple:

    function rotateImage() {
        var rotateCanvas = document.getElementById('editorCanvas');
        var rotateContext = canvas.getContext('2d');
        rotateContext.rotate(90 * Math.PI / 180);
    }

But I think the only way is through redrawing the image?

Is there any way of rotating an image on a canvas without redrawing it, if not how would I go about rotating an image on a canvas?

I'm attempting to rotate an image that has already been drawn on a HTML canvas, like so:

var canvas = document.getElementById("editorCanvas");
var ctx = canvas.getContext("2d");

var canvasOffset = $("#editorCanvas").offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;

var startX;
var startY;
var isDown = false;


var pi2 = Math.PI * 2;
var resizerRadius = 4;
var rr = resizerRadius * resizerRadius;
var draggingResizer = {
    x: 0,
    y: 0
};
var imageX = 0;
var imageY;
var imageWidth, imageHeight, imageRight, imageBottom;
var draggingImage = false;
var startX;
var startY;



var img = new Image();
img.crossOrigin='anonymous';
img.onload = function () {

    var ratio = img.width / img.height;

    imageWidth = 71;
    imageHeight = imageWidth / ratio;
    imageY = (245-imageHeight)/2;
    if (imageHeight > 245) {
        imageHeight = 245;
        imageWidth = imageHeight * ratio;
        imageY = 0;
    }

    imageX = ((canvas.width-imageWidth)/2);
    imageY = ((canvas.height-imageHeight)/2);

    imageRight = imageX + imageWidth;
    imageBottom = imageY + imageHeight;

    draw(true, false);
}

function draw(withAnchors, withBorders) {

    // clear the canvas
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // draw the image
    ctx.drawImage(img, 0, 0, img.width, img.height, imageX, imageY, imageWidth, imageHeight);

    // optionally draw the draggable anchors
    if (withAnchors) {
        drawDragAnchor(imageX, imageY);
        drawDragAnchor(imageRight, imageY);
        drawDragAnchor(imageRight, imageBottom);
        drawDragAnchor(imageX, imageBottom);
    }

    // optionally draw the connecting anchor lines
    if (withBorders) {
        ctx.beginPath();
        ctx.moveTo(imageX, imageY);
        ctx.lineTo(imageRight, imageY);
        ctx.lineTo(imageRight, imageBottom);
        ctx.lineTo(imageX, imageBottom);
        ctx.closePath();
        ctx.stroke();
    }

}

function drawDragAnchor(x, y) {
    ctx.beginPath();
    ctx.arc(x, y, resizerRadius, 0, pi2, false);
    ctx.closePath();
    ctx.fill();
}

function anchorHitTest(x, y) {

    var dx, dy;

    // top-left
    dx = x - imageX;
    dy = y - imageY;
    if (dx * dx + dy * dy <= rr) {
        return (0);
    }
    // top-right
    dx = x - imageRight;
    dy = y - imageY;
    if (dx * dx + dy * dy <= rr) {
        return (1);
    }
    // bottom-right
    dx = x - imageRight;
    dy = y - imageBottom;
    if (dx * dx + dy * dy <= rr) {
        return (2);
    }
    // bottom-left
    dx = x - imageX;
    dy = y - imageBottom;
    if (dx * dx + dy * dy <= rr) {
        return (3);
    }
    return (-1);

}


function hitImage(x, y) {
    return (x > imageX && x < imageX + imageWidth && y > imageY && y < imageY + imageHeight);
}


function handleMouseDown(e) {
    startX = parseInt(e.clientX - offsetX);
    startY = parseInt(e.clientY - offsetY);
    draggingResizer = anchorHitTest(startX, startY);
    draggingImage = draggingResizer < 0 && hitImage(startX, startY);
}

function handleMouseUp(e) {
    draggingResizer = -1;
    draggingImage = false;
    draw(true, false);
}

function handleMouseOut(e) {
    handleMouseUp(e);
}

function handleMouseMove(e) {

    if (draggingResizer > -1) {

        mouseX = parseInt(e.clientX - offsetX);
        mouseY = parseInt(e.clientY - offsetY);

        // resize the image
        switch (draggingResizer) {
            case 0:
                //top-left
                imageX = mouseX;
                imageWidth = imageRight - mouseX;
                imageY = mouseY;
                imageHeight = imageBottom - mouseY;
                break;
            case 1:
                //top-right
                imageY = mouseY;
                imageWidth = mouseX - imageX;
                imageHeight = imageBottom - mouseY;
                break;
            case 2:
                //bottom-right
                imageWidth = mouseX - imageX;
                imageHeight = mouseY - imageY;
                break;
            case 3:
                //bottom-left
                imageX = mouseX;
                imageWidth = imageRight - mouseX;
                imageHeight = mouseY - imageY;
                break;
        }

        if(imageWidth<25){imageWidth=25;}
        if(imageHeight<25){imageHeight=25;}

        // set the image right and bottom
        imageRight = imageX + imageWidth;
        imageBottom = imageY + imageHeight;

        // redraw the image with resizing anchors
        draw(true, true);

    } else if (draggingImage) {

        imageClick = false;

        mouseX = parseInt(e.clientX - offsetX);
        mouseY = parseInt(e.clientY - offsetY);

        // move the image by the amount of the latest drag
        var dx = mouseX - startX;
        var dy = mouseY - startY;
        imageX += dx;
        imageY += dy;
        imageRight += dx;
        imageBottom += dy;
        // reset the startXY for next time
        startX = mouseX;
        startY = mouseY;

        // redraw the image with border
        draw(false, true);

    }


}


$("#editorCanvas").mousedown(function (e) {
    handleMouseDown(e);
});
$("#editorCanvas").mousemove(function (e) {
    handleMouseMove(e);
});
$("#editorCanvas").mouseup(function (e) {
    handleMouseUp(e);
});
$("#editorCanvas").mouseout(function (e) {
    handleMouseOut(e);
});

I was hoping it was this simple:

    function rotateImage() {
        var rotateCanvas = document.getElementById('editorCanvas');
        var rotateContext = canvas.getContext('2d');
        rotateContext.rotate(90 * Math.PI / 180);
    }

But I think the only way is through redrawing the image?

Is there any way of rotating an image on a canvas without redrawing it, if not how would I go about rotating an image on a canvas?

Share Improve this question edited May 15, 2015 at 16:17 Alex Saidani asked May 14, 2015 at 15:07 Alex SaidaniAlex Saidani 1,3172 gold badges16 silver badges32 bronze badges 1
  • 1 The image is simply drawn on the canvas, it's not "attached" to it. So if you want to change it, be moving or rotating, you will need to erase the canvas and redraw. You can do it without redrawing, but you will see the placement of the old non-erased image with the rotated image on top. – Spencer Wieczorek Commented May 14, 2015 at 15:11
Add a ment  | 

3 Answers 3

Reset to default 3

You can use a second in-memory canvas to rotate the existing canvas content if you don't want to store all the mands necessary to recreate the canvas.

  • Create an second in-memory canvas.
  • Copy your main canvas onto the second canvas.
  • Clear your main canvas.
  • Set the rotation point as center of the main canvas.
  • (but you can set any rotation point you desire)
  • Rotate by 90 degrees (==PI/2).
  • Redraw the second canvas back to the (now rotated) main canvas.
  • Clean up -- unrotate and untranslate the main canvas.

Example code and a Demo:

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;

var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent./u/139992952/multple/leftarrow.png";
function start(){

  ctx.drawImage(img,cw/2-img.width/2,ch/2-img.width/2);

  $('#rotate').click(function(){
    rotate(cw/2,ch/2,90);
  });

}

function rotate(rotationPointX,rotationPointY,degreeRotation){

  // Create an second in-memory canvas:
  var mCanvas=document.createElement('canvas');
  mCanvas.width=canvas.width;
  mCanvas.height=canvas.height;
  var mctx=mCanvas.getContext('2d');

  // Draw your canvas onto the second canvas
  mctx.drawImage(canvas,0,0);

  // Clear your main canvas
  ctx.clearRect(0,0,canvas.width,canvas.height);

  // Rotate the main canvas

  // set the rotation point as center of the canvas
  // (but you can set any rotation point you desire)
  ctx.translate(rotationPointX,rotationPointY);

  // rotate by 90 degrees (==PI/2)
  var radians=degreeRotation/180*Math.PI;
  ctx.rotate(radians);


  // Draw the second canvas back to the (now rotated) main canvas:
  ctx.drawImage(mCanvas,-canvas.width/2,-canvas.height/2);

  // clean up -- unrotate and untranslate
  ctx.rotate(-radians);
  ctx.translate(-canvas.width/2,-canvas.height/2);

}
body{ background-color: ivory; }
#canvas{border:1px solid red;}
<script src="https://ajax.googleapis./ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<button id=rotate>Rotate the existing image.</button>
<br>
<canvas id="canvas" width=300 height=300></canvas>

[ add rotate() into questioners code ]

var canvas = document.getElementById("editorCanvas");
var ctx = canvas.getContext("2d");

var canvasOffset = $("#editorCanvas").offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;

var startX;
var startY;
var isDown = false;


var pi2 = Math.PI * 2;
var resizerRadius = 4;
var rr = resizerRadius * resizerRadius;
var draggingResizer = {
  x: 0,
  y: 0
};
var imageX = 0;
var imageY;
var imageWidth, imageHeight, imageRight, imageBottom;
var draggingImage = false;
var startX;
var startY;

var img = new Image();
img.crossOrigin='anonymous';
img.onload = function () {

  var ratio = img.width / img.height;

  imageWidth = 71;
  imageHeight = imageWidth / ratio;
  imageY = (245-imageHeight)/2;
  if (imageHeight > 245) {
    imageHeight = 245;
    imageWidth = imageHeight * ratio;
    imageY = 0;
  }

  imageX = ((canvas.width-imageWidth)/2);
  imageY = ((canvas.height-imageHeight)/2);

  imageRight = imageX + imageWidth;
  imageBottom = imageY + imageHeight;

  draw(true, false);
}
img.src='https://dl.dropboxusercontent./u/139992952/multple/leftarrow.png';

function draw(withAnchors, withBorders) {

  // clear the canvas
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // draw the image
  ctx.drawImage(img, 0, 0, img.width, img.height, imageX, imageY, imageWidth, imageHeight);

  // optionally draw the draggable anchors
  if (withAnchors) {
    drawDragAnchor(imageX, imageY);
    drawDragAnchor(imageRight, imageY);
    drawDragAnchor(imageRight, imageBottom);
    drawDragAnchor(imageX, imageBottom);
  }

  // optionally draw the connecting anchor lines
  if (withBorders) {
    ctx.beginPath();
    ctx.moveTo(imageX, imageY);
    ctx.lineTo(imageRight, imageY);
    ctx.lineTo(imageRight, imageBottom);
    ctx.lineTo(imageX, imageBottom);
    ctx.closePath();
    ctx.stroke();
  }

}

function drawDragAnchor(x, y) {
  ctx.beginPath();
  ctx.arc(x, y, resizerRadius, 0, pi2, false);
  ctx.closePath();
  ctx.fill();
}

function anchorHitTest(x, y) {

  var dx, dy;

  // top-left
  dx = x - imageX;
  dy = y - imageY;
  if (dx * dx + dy * dy <= rr) {
    return (0);
  }
  // top-right
  dx = x - imageRight;
  dy = y - imageY;
  if (dx * dx + dy * dy <= rr) {
    return (1);
  }
  // bottom-right
  dx = x - imageRight;
  dy = y - imageBottom;
  if (dx * dx + dy * dy <= rr) {
    return (2);
  }
  // bottom-left
  dx = x - imageX;
  dy = y - imageBottom;
  if (dx * dx + dy * dy <= rr) {
    return (3);
  }
  return (-1);

}


function hitImage(x, y) {
  return (x > imageX && x < imageX + imageWidth && y > imageY && y < imageY + imageHeight);
}


function handleMouseDown(e) {
  startX = parseInt(e.clientX - offsetX);
  startY = parseInt(e.clientY - offsetY);
  draggingResizer = anchorHitTest(startX, startY);
  draggingImage = draggingResizer < 0 && hitImage(startX, startY);
}

function handleMouseUp(e) {
  draggingResizer = -1;
  draggingImage = false;
  draw(true, false);
}

function handleMouseOut(e) {
  handleMouseUp(e);
}

function handleMouseMove(e) {

  if (draggingResizer > -1) {

    mouseX = parseInt(e.clientX - offsetX);
    mouseY = parseInt(e.clientY - offsetY);

    // resize the image
    switch (draggingResizer) {
      case 0:
        //top-left
        imageX = mouseX;
        imageWidth = imageRight - mouseX;
        imageY = mouseY;
        imageHeight = imageBottom - mouseY;
        break;
      case 1:
        //top-right
        imageY = mouseY;
        imageWidth = mouseX - imageX;
        imageHeight = imageBottom - mouseY;
        break;
      case 2:
        //bottom-right
        imageWidth = mouseX - imageX;
        imageHeight = mouseY - imageY;
        break;
      case 3:
        //bottom-left
        imageX = mouseX;
        imageWidth = imageRight - mouseX;
        imageHeight = mouseY - imageY;
        break;
    }

    if(imageWidth<25){imageWidth=25;}
    if(imageHeight<25){imageHeight=25;}

    // set the image right and bottom
    imageRight = imageX + imageWidth;
    imageBottom = imageY + imageHeight;

    // redraw the image with resizing anchors
    draw(true, true);

  } else if (draggingImage) {

    imageClick = false;

    mouseX = parseInt(e.clientX - offsetX);
    mouseY = parseInt(e.clientY - offsetY);

    // move the image by the amount of the latest drag
    var dx = mouseX - startX;
    var dy = mouseY - startY;
    imageX += dx;
    imageY += dy;
    imageRight += dx;
    imageBottom += dy;
    // reset the startXY for next time
    startX = mouseX;
    startY = mouseY;

    // redraw the image with border
    draw(false, true);

  }


}

$("#editorCanvas").mousedown(function (e) {
  handleMouseDown(e);
});
$("#editorCanvas").mousemove(function (e) {
  handleMouseMove(e);
});
$("#editorCanvas").mouseup(function (e) {
  handleMouseUp(e);
});
$("#editorCanvas").mouseout(function (e) {
  handleMouseOut(e);
});

function rotate(rotationPointX,rotationPointY,degreeRotation){

  // Create an second in-memory canvas:
  var mCanvas=document.createElement('canvas');
  mCanvas.width=canvas.width;
  mCanvas.height=canvas.height;
  var mctx=mCanvas.getContext('2d');

  // Draw your canvas onto the second canvas
  mctx.drawImage(canvas,0,0);

  // Clear your main canvas
  ctx.clearRect(0,0,canvas.width,canvas.height);

  // Rotate the main canvas

  // set the rotation point as center of the canvas
  // (but you can set any rotation point you desire)
  ctx.translate(rotationPointX,rotationPointY);

  // rotate by 90 degrees (==PI/2)
  var radians=degreeRotation/180*Math.PI;
  ctx.rotate(radians);


  // Draw the second canvas back to the (now rotated) main canvas:
  ctx.drawImage(mCanvas,-canvas.width/2,-canvas.height/2);

  // clean up -- unrotate and untranslate
  ctx.rotate(-radians);
  ctx.translate(-canvas.width/2,-canvas.height/2);

}

$('#rotate').click(function(){
  rotate(canvas.width/2,canvas.height/2,90);
});
body{ background-color: ivory; }
canvas{border:1px solid red;}
<script src="https://ajax.googleapis./ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<button id=rotate>Rotate</button>
<br>
<canvas id="editorCanvas" width=300 height=300></canvas>

The simplest way is to draw canvas to itself using "copy" posite mode. "copy" will clear the background when drawing something new to it:

Example

var ctx = document.querySelector("canvas").getContext("2d");
ctx.moveTo(75, 10);                      // some graphics...
ctx.lineTo(30, 140);
ctx.lineTo(120, 140);
ctx.fill();

// prep canvas for next actions
ctx.translate(75, 75);                   // translate to canvas center
ctx.rotate(Math.PI*0.5);                 // add rotation transform
ctx.globalCompositeOperation = "copy";   // set p. mode to "copy"

// rotate on each click
document.querySelector("button").onclick = function() {
  // canvas uses itself as source, source region, target offset to
  // pensate for translation above so it rotates around center:
  ctx.drawImage(ctx.canvas,  0, 0, 150, 150,  -75, -75, 150, 150);
};
body {background:#eee}
<canvas width=150></canvas>
<button>Rotate</button>

Example 2 - setup done each time

var ctx = document.querySelector("canvas").getContext("2d");
ctx.moveTo(75, 10);                      // some graphics...
ctx.lineTo(30, 140);
ctx.lineTo(120, 140);
ctx.fill();

// rotate on each click
document.querySelector("button").onclick = function() {
  ctx.save();
  // prep canvas for rotation
  ctx.translate(75, 75);                   // translate to canvas center
  ctx.rotate(Math.PI*0.5);                 // add rotation transform
  ctx.globalCompositeOperation = "copy";   // set p. mode to "copy"
  ctx.drawImage(ctx.canvas,  0, 0, 150, 150,  -75, -75, 150, 150);
  ctx.restore();
};

// or use, instead of save/restore, at the end:
// ctx.setTransform(1,0,0,1,0,0);
// ctx.globalCompositeOperation = "source-over";
body {background:#eee}
<canvas width=150></canvas>
<button>Rotate</button>

Exactly what spencer was saying, canvas is more like a piece of paper that you draw on. If you draw a circle, you can't just rotate it, you have to erase it then draw it in its new position.

You will have to keep track of your objects states (rotation, position, color, etc..) then use those to redraw to the canvas.

If you want a more set and done solution for moving objects, try using ocanvas. It provides a framework to deal with objects rather than drawing pixels. It's a step up from standard canvas without getting to plex.

发布评论

评论列表(0)

  1. 暂无评论