0

I'm trying to rotate only one object in the canvas, not the whole canvas.

My code is as follows:

var canvas = document.getElementById('canvas');
var buttons = document.getElementById('buttons');
var img = document.getElementById('photo');
var ctx = canvas.getContext('2d');
var rect = {};
var drag = false;
var buttons_shown = false;
var angle = 10;
var rotate_angle = 0;


var original_source = img.src;
img.src = original_source;

function init() {
    img.addEventListener('load', function(){
        canvas.width = img.width;
        canvas.height = img.height;
        canvas.addEventListener('mousedown', mouseDown, false);
        canvas.addEventListener('mouseup', mouseUp, false);
        canvas.addEventListener('mousemove', mouseMove, false);
        document.addEventListener("keydown", keyDownPressed, false);
    });


}

function keyDownPressed(e) {
    var keyCode = e.keyCode;
    var left_arrow = 37;
    var right_arrow = 39;
    var up_arrow = 38;
    var down_arrow = 40;

    if(keyCode === left_arrow) {
        onRotateLeft()
    }

    if(keyCode === right_arrow){
        onRotateRight()
    }
}

function mouseDown(e) {
    rect.startX = e.offsetX;
    rect.startY = e.offsetY;
    drag = true;
    buttons_shown = false;
    buttons.classList.add("hide");
}

function mouseUp() { drag = false; buttons_shown = true; }

function onRemoveSelectionClick(e) {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    drag = false;
    buttons_shown = false;
    buttons.classList.add("hide");
}

function onRotateLeft(){
    rotate_angle = rotate_angle - angle;
    canvas.style.transform = 'rotate(' + rotate_angle + 'deg)';    
}

function onRotateRight(){
    rotate_angle = rotate_angle + angle;
    canvas.style.transform = 'rotate(' + rotate_angle + 'deg)';
}

function mouseMove(e) {
    if (drag) {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        rect.w = (e.pageX - this.offsetLeft) - rect.startX;
        rect.h = (e.pageY - this.offsetTop) - rect.startY;
        ctx.shadowBlur = 5;
        ctx.filter = 'blur(10px)';
        ctx.fillRect(rect.startX, rect.startY, rect.w, rect.h);
        ctx.strokeRect(rect.startX, rect.startY, rect.w, rect.h);

    }else{
        if(buttons_shown && buttons.classList.contains("hide")){
            buttons.classList.remove("hide");
        }
    }
}
//
init();
.hide{
    display: none !important;
}

canvas{
  position: absolute;
  left: 0; 
  right: 0; 
  top: 0; 
  bottom: 0; 
  display:inline-block;
  background-color: yellow;
}
<div style="position: relative; overflow: hidden;display:inline-block;">
    <img id="photo" src="http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg"/>
    <canvas id="canvas"></canvas>
</div>


<div id="buttons" class="hide">
    <button onclick="onRotateLeft()">Rotate Left</button>
    <button onclick="onRotateRight()">Rotate right</button><br />
    <button onclick="onRemoveSelectionClick()">Remove Selection</button>
</div>

Thus, if an object is drawn, I show the buttons to rotate it left or right or to delete it. If I click on for example Rotate Left button, it executes the next code:

rotate_angle = rotate_angle - angle;
canvas.style.transform = 'rotate(' + rotate_angle + 'deg)';

But that rotates my whole canvas (yellow background) but I want to rotate only the object inside the canvas.

Any idea?

Boky
  • 11,554
  • 28
  • 93
  • 163

2 Answers2

1

Use canvas transform not CSS transform.

You need to apply the transform in the canvas when you render the rectangle.

The snippet is a copy of your code with some changes.

  • The rendering is done via requestAnimationFrame so as not to have needless renders when the mouse moves.

  • A global flag update is set to true whenever there is need to update the canvas.

  • An array of rectangles is used to store each rectangle.

  • The rectangle object also stores its rotation rect.rotate so that each rectangle has an independent rotation.

  • The global variable rect holds the current rectangle.

  • Events change the global rect object and set the update = true flag, they do not do any rendering.

  • The rectangle is rotated about its center

  • Canvas context ctx.rotate uses radians not degrees

See code for more info.

var canvas = document.getElementById('canvas');
var buttons = document.getElementById('buttons');
var img = document.getElementById('photo');
var ctx = canvas.getContext('2d');
var rect = {};
var drag = false;
var buttons_shown = false;
var angle = 10 * (Math.PI / 180) ;
var rotate_angle = 0;

var update = true; // when true updates canvas


var original_source = img.src;
img.src = original_source;

function init() {
    img.addEventListener('load', function(){
        canvas.width = img.width;
        canvas.height = img.height;
        canvas.addEventListener('mousedown', mouseDown, false);
        canvas.addEventListener('mouseup', mouseUp, false);
        canvas.addEventListener('mousemove', mouseMove, false);
        document.addEventListener("keydown", keyDownPressed, false);
    });
    // start the rendering loop
    requestAnimationFrame(updateCanvas);


}

// main render loop only updates if update is true
function updateCanvas(){
  if(update){
      drawCanvas();
      update = false;
  }

  requestAnimationFrame(updateCanvas);
}

// array of rectangles
const rectangles = [];

// adds a rectangle
function addRectangle(rect){
   rectangles.push(rect);
   
}

// draws a rectangle with rotation 
function drawRect(rect){
   ctx.setTransform(1,0,0,1,rect.startX + rect.w / 2, rect.startY + rect.h / 2);
   ctx.rotate(rect.rotate);
   ctx.beginPath();
   ctx.rect(-rect.w/2, -rect.h/2, rect.w, rect.h);
   ctx.fill();
   ctx.stroke();


}

// clears canvas sets filters and draws rectangles
function drawCanvas(){
    // restore the default transform as rectangle rendering does not restore the transform.
    ctx.setTransform(1,0,0,1,0,0);
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.shadowBlur = 5;
    ctx.filter = 'blur(10px)';
    rectangles.forEach(drawRect);


}

function keyDownPressed(e) {
    var keyCode = e.keyCode;
    var left_arrow = 37;
    var right_arrow = 39;
    var up_arrow = 38;
    var down_arrow = 40;

    if(keyCode === left_arrow) {
        onRotateLeft()
    }

    if(keyCode === right_arrow){
        onRotateRight()
    }
}

// create new rect add to array 
function mouseDown(e) {
    rect = {
      startX : e.offsetX,
      startY : e.offsetY,
      w : 1,
      h : 1,
      rotate : 0,
    };
    drag = true;
    buttons_shown = false;
    buttons.classList.add("hide");
    addRectangle(rect);
    update = true;
}

function mouseUp() { drag = false; buttons_shown = true; update = true; }


// removes top rectangle and sets next down as current or if none then hides controls
function onRemoveSelectionClick(e) {

    rectangles.pop();
    if(rectangles.length === 0){
      drag = false;
      buttons_shown = false;
      buttons.classList.add("hide");
    }else{
      rect = rectangles[rectangles.length -1];
    }
    update = true;
}

// rotate current rectangle
function onRotateLeft(){
    rect.rotate -= angle;
    update = true;
      
}

function onRotateRight(){
    rect.rotate += angle;
    update = true;
}

function mouseMove(e) {
    if (drag) {
        rect.w = (e.pageX - this.offsetLeft) - rect.startX;
        rect.h = (e.pageY - this.offsetTop) - rect.startY;
        update = true;

    }else{
        if(buttons_shown && buttons.classList.contains("hide")){
            buttons.classList.remove("hide");
        }
    }
}
//
init();
.hide{
    display: none !important;
}

canvas{
  position: absolute;
  left: 0; 
  right: 0; 
  top: 0; 
  bottom: 0; 
  display:inline-block;
  background-color: yellow;
}
<div style="position: relative; overflow: hidden;display:inline-block;">
    <img id="photo" src="http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg"/>
    <canvas id="canvas"></canvas>
</div>


<div id="buttons" class="hide">
    <button onclick="onRotateLeft()">Rotate Left</button>
    <button onclick="onRotateRight()">Rotate right</button><br />
    <button onclick="onRemoveSelectionClick()">Remove Selection</button>
</div>
Community
  • 1
  • 1
Blindman67
  • 51,134
  • 11
  • 73
  • 136
0

You will need a separate canvas for the object; draw the object on this separate canvas and then, depending on what you actually intend to do, you can either:

  • Draw the auxiliary canvas' contents on the main one using context.rotate() combined with context.drawImage();
  • Overlay one canvas on top of another using CSS.

Then again, perhaps you could just use context.rotate() then draw on the main canvas as cited in: HTML5 Canvas Rotate Image

Haroldo_OK
  • 6,612
  • 3
  • 43
  • 80