Search code examples
javascriptcanvasrotationline

How to rotate only a line around it's middle point?


I want to display a windmill on js canvas. For now, I display a green line on a cube. I have a problem with rotating the line around it's middle point. Also, I don't want my cube to move. Save() doesn't seem to work? I don't know what I'm doing wrong. I tried looking the answer online but they don't seem to work or I don't understand them. Elements in my canvas somehow disappear.

    var x = 600;

    function init() 
    {
        window.requestAnimationFrame(draw);
    }

    function draw() 
    {
    var ctx = document.getElementById('canvas').getContext('2d');

    ctx.clearRect(0, 0, 600, 600);

    // sciana przednia
    ctx.lineWidth = 1;
    ctx.strokeStyle = "#000000";
    ctx.strokeRect(x/2,x/2,x/4,x/4);

    //sciana gorna
    ctx.beginPath();
    ctx.lineWidth = 1;
    ctx.strokeStyle = "#000000";
    ctx.moveTo(x/2,x/2);
    ctx.lineTo(x-x/3,x/4+x/6);
    ctx.lineTo(x-x/8,x/4+x/6);
    ctx.lineTo(x/2+x/4,x/2);    
    ctx.closePath();
    ctx.stroke();

    //sciana prawa
    ctx.beginPath();
    ctx.lineWidth = 1;
    ctx.strokeStyle = "#000000";
    ctx.moveTo(x/2+x/4,x/2+x/4);
    ctx.lineTo(x-x/8,x/2+x/7); 
    ctx.lineTo(x-x/8,x/4+x/6);
    ctx.stroke();
    ctx.closePath();

    //raczka
    ctx.beginPath();
    ctx.lineWidth = 5;
    ctx.strokeStyle = "#808080";
    ctx.moveTo(x/2+x/5,x/2-x/5);
    ctx.lineTo(x/2+x/5,x/2-x/8+50);
    ctx.stroke();
    ctx.closePath();

    ctx.save();
    //smiglo
    ctx.beginPath();
    ctx.translate(x/2, x/2);              
    ctx.rotate( (Math.PI / 180) * 25); 
    ctx.translate(-x/2, -x/2);
    ctx.fillStyle = "#00cc00";
    ctx.fillRect(x/2+x/5-100,x/2-x/5,200,10);
    ctx.closePath();
    ctx.restore();

    window.requestAnimationFrame(draw);
    }

    init();

var canvas = document.getElementById('canvas')
var ctx = canvas.getContext('2d');
var x = 600;

function init() {
  window.requestAnimationFrame(draw);
}

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // sciana przednia
  ctx.lineWidth = 1;
  ctx.strokeStyle = "#000000";
  ctx.strokeRect(x / 2, x / 2, x / 4, x / 4);

  //sciana gorna
  ctx.beginPath();
  ctx.lineWidth = 1;
  ctx.strokeStyle = "#000000";
  ctx.moveTo(x / 2, x / 2);
  ctx.lineTo(x - x / 3, x / 4 + x / 6);
  ctx.lineTo(x - x / 8, x / 4 + x / 6);
  ctx.lineTo(x / 2 + x / 4, x / 2);
  ctx.closePath();
  ctx.stroke();

  //sciana prawa
  ctx.beginPath();
  ctx.lineWidth = 1;
  ctx.strokeStyle = "#000000";
  ctx.moveTo(x / 2 + x / 4, x / 2 + x / 4);
  ctx.lineTo(x - x / 8, x / 2 + x / 7);
  ctx.lineTo(x - x / 8, x / 4 + x / 6);
  ctx.stroke();
  ctx.closePath();

  //raczka
  ctx.beginPath();
  ctx.lineWidth = 5;
  ctx.strokeStyle = "#808080";
  ctx.moveTo(x / 2 + x / 5, x / 2 - x / 5);
  ctx.lineTo(x / 2 + x / 5, x / 2 - x / 8 + 50);
  ctx.stroke();
  ctx.closePath();

  ctx.save();
  //smiglo
  ctx.beginPath();
  ctx.translate(x / 2, x / 2);
  ctx.rotate((Math.PI / 180) * 25);
  ctx.translate(-x / 2, -x / 2);
  ctx.fillStyle = "#00cc00";
  ctx.fillRect(x / 2 + x / 5 - 100, x / 2 - x / 5, 200, 10);
  ctx.closePath();
  ctx.restore();

  window.requestAnimationFrame(draw);
}

init();
<canvas id="canvas" width=600 height=600></canvas>

Edit: I understand now how to rotate around a certain point. I still don't know how to rotate only the line not the whole thing.


Solution

  • Talking about the rotation, I think that you did well: the green line is rotated by 25 degrees in your example. You just need to rotate its middle point.

    But to do so, I think it's better if you make other changes in your code: the part that draws the cube is difficult to handle, whenever you want to edit your code, this part will cause issues. I suggest to isolate it in a drawCube() function and to use proper (x,y) coordinates. According to me, it should look like this:

    function draw() {
        angle += 5;
        ctx.save();
        ctx.clearRect(0, 0, canvas.width, canvas.height);
    
        ctx.translate(canvas.width / 2, canvas.height / 2);
        ctx.rotate(Math.PI / 180 * angle);
        ctx.translate(-canvas.width / 2, -canvas.height / 2);
    
        // It rotates
        drawLine();
        ctx.restore();
    
        // It doesn't rotate
        drawCube();
    }
    

    Then, your code will work. All you will have to do is to make the line rotate around its middle point, but you said that you know how to do it.

    Good luck!

    EDIT: I added a snippet with an working example, and with a cube like yours as well, may help.

    var canvas = document.getElementById('canvas');
    var ctx = canvas.getContext('2d');
    var btnExample = document.getElementById('btnExample');
    var btnCube = document.getElementById('btnCube');
    
    var angle = 0;
    var fps = 1000 / 60;
    
    var propellerWidth = 100;
    var propellerHeight = 10;
    var towerWidth = 2;
    var towerHeight = 100;
    
    var mode = {
    	CUBE: 'CUBE',
    	EXAMPLE: 'EXAMPLE'
    }
    var currentMode = mode.EXAMPLE;
    
    btnExample.onclick = function() {
    	currentMode = mode.EXAMPLE;
    }
    
    btnCube.onclick = function() {
    	currentMode = mode.CUBE;
    }
    
    setInterval(function() {
      angle += 3;
      draw(canvas, ctx, (canvas.width - propellerWidth) / 2, (canvas.height - propellerWidth) / 2, angle);
    }, fps);
    
    function draw(canvas, ctx, cx, cy, angle) {    
        var towerX = cx + propellerWidth / 2 - towerWidth / 2;
        var towerY = cy + propellerWidth / 2;
        var propellerX = (canvas.width - propellerWidth) / 2;
        var propellerY = (canvas.height - propellerHeight) / 2;
        
        ctx.save();
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        
        // Draw other things that don't rotate
        if (currentMode === mode.EXAMPLE) {
        	drawHelp(canvas, ctx);
        }
        
        ctx.translate(canvas.width / 2, canvas.height / 2);
        ctx.rotate(Math.PI / 180 * angle);
        ctx.translate(-canvas.width / 2, -canvas.height / 2);
        
        // Draw things that rotate
        drawPropeller(ctx, propellerX, propellerY, propellerWidth, propellerHeight);
        ctx.restore();
        
        // Draw other things that don't rotate
        if (currentMode === mode.EXAMPLE) {
        	drawTower(ctx, towerX, towerY, towerWidth, towerHeight);
        } else if (currentMode === mode.CUBE) {
        	drawCube(ctx, towerX, towerY, 30);
        }
    }
    
    function drawPropeller(ctx, propellerX, propellerY, propellerWidth, propellerHeight) {
        ctx.fillStyle = 'black';
        ctx.fillRect(propellerX, propellerY, propellerWidth, propellerHeight);
    }
    
    function drawTower(ctx, towerX, towerY, towerWidth, towerHeight) {
        ctx.fillStyle = 'red';
        ctx.fillRect(towerX, towerY, towerWidth, towerHeight);
    }
    
    function drawCube(ctx, x, y, size) {
    	ctx.strokeStyle = 'black';
    	
    	ctx.beginPath();
    	ctx.moveTo(x, y);
    	ctx.lineTo(x, y + size);
    	ctx.closePath();
    	ctx.stroke();
    	
    	var x1 = x - size + (size / 4) + (size / 16);
    	var y1 = y + (size * 1.25);
    	var x2 = x1 + (size / 3);
    	var y2 = y1 - (size * 2 / 3);
    	var x3 = x2 + size;
    	var y3 = y2;
    	var x4 = x3 - (size / 3);
    	var y4 = y1;
    	var x5 = x4;
    	var y5 = y4 + size;
    	var x6 = x3;
    	var y6 = y3 + size;
    
    	ctx.strokeRect(x1, y1, size, size);
    	
    
    	ctx.beginPath();
    	ctx.moveTo(x1, y1);
    	ctx.lineTo(x2, y2);
    	ctx.closePath();
    	ctx.stroke();
    	
    
    	ctx.beginPath();
    	ctx.moveTo(x2, y2);
    	ctx.lineTo(x3, y3);
    	ctx.closePath();
    	ctx.stroke();
    	
    
    	ctx.beginPath();
    	ctx.moveTo(x3, y3);
    	ctx.lineTo(x4, y4);
    	ctx.closePath();
    	ctx.stroke();
    	
    
    	ctx.beginPath();
    	ctx.moveTo(x3, y3);
    	ctx.lineTo(x6, y6);
    	ctx.closePath();
    	ctx.stroke();
    
    	ctx.beginPath();
    	ctx.moveTo(x5, y5);
    	ctx.lineTo(x6, y6);
    	ctx.closePath();
    	ctx.stroke();
    }
    
    function drawHelp(canvas, ctx) {
    	ctx.globalAlpha = 0.2;
      ctx.beginPath();
      ctx.moveTo(0, 0);
      ctx.lineTo(canvas.width, canvas.height);
      ctx.moveTo(canvas.width, 0);
      ctx.lineTo(0, canvas.height);
      ctx.stroke();
      ctx.closePath();
      
      ctx.beginPath();
      ctx.arc(canvas.width / 2, canvas.height / 2, propellerWidth / 2, 0, 2 * Math.PI);
      ctx.stroke();
      ctx.closePath();
      
      ctx.beginPath();
      ctx.arc(canvas.width / 2, canvas.height / 2, propellerWidth / 2, 0, 2 * Math.PI);
      ctx.stroke();
      ctx.closePath();
      
    	ctx.globalAlpha = 1;
    }
    canvas {
      border: 1px solid black;
    }
    <canvas id="canvas" width=200 height=200></canvas>
    <br>
    <button id="btnExample">Example</button>
    <button id="btnCube">Cube</button>