简体   繁体   中英

rotate only an image in a canvas

I'm trying to rotate an image inside a canvas.

Here's my Fiddle: https://jsfiddle.net/kevinludwig11/s6rgpjm9/

I try it with save and restore, but the path is also rotating.

The falcon should fly with his face towards and change the angle in the corners.

Can anybody help me?

Edit: One solution i've found: save the image 360 times with every rotation and load every image in the right position. But i think thats not the smartest solution.

Canvas 2D image lookat transform.

No need to create 360 images to rotate a single image. Also you had a few incorrect ways of doing things.

Code problems

  • Only load the image once. You were loading it each time it was rendered.
  • Use requestAnimationFrame on its own. Putting it inside a timer makes its use completely redundant.
  • If you find yourself typing in long lists of numbers, and especially if you repeat these numbers in other sections of code you should use a single store to hold everything. Eg your paths were all hand coded. Move them into an array then iterate the array for the various things you need to do with the paths. One of the top ten programing rules. "Don't repeat/duplicate anything."

The lookat transform

To do the bird you will need to get the direction it is heading towards so I added a second point on the curves that is ahead of the bird. With these two points (birds pos and lookat pos) I then create a transformation using the lookat direction as the xAxis of the transformation. See function drawImageLookat(image,pos,lookat) I found that the image is not along the X axis so I rotate the bird 90deg after finding the lookat transformation.

Lookat function

// function assumes front (forward) of image is along the x axis to the right
function drawImageLookat(image, point, lookat ) {
    var xAx,xAy; // vector for x Axis of image
    var x,y;
    x = lookat.x - point.x;
    y = lookat.y - point.y;
    var dist = Math.max(0.01,Math.sqrt(x * x + y * y)); // Math.max to avoid zero which will create NaN
    xAx = x / dist; // get x component of x Axis
    xAy = y / dist; // get y component of x Axis
    // y axis is at 90 deg so dont need y axis vector
    ctx.setTransform(   // position the image using transform
        xAx, xAy, // set direction of x Axis
        -xAy, xAx, // set direction oy y axis
        point.x, point.y
    );   
    ctx.drawImage(image, -image.width / 2, -image.height / 2);
}

Demo from fiddle.

Your code that I took from the fiddle https://jsfiddle.net/kevinludwig11/s6rgpjm9/ and modified to run as your question implies.

 var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); // only load image once var birdImage = new Image(); birdImage.src = 'http://www.happy-innovation.de/images/Falke_Flug.png'; birdImage.onload = function(){animate()}; // start animation when image has loaded // set starting values var speed = 0.25 var percent = speed; var direction = speed; var length = 300; function animate() { ctx.setTransform(1,0,0,1,0,0); // restore default transform incase its not ctx.clearRect(0, 0, canvas.width, canvas.height); percent += direction; // need to keep the position away from the ends as there is no lookat beyond the path. if(percent >= length - speed){ percent = length- speed; direction = -speed; }else if(percent <= speed){ percent = speed; direction = speed; } draw(percent,direction); requestAnimationFrame(animate); } function P(x,y){return {x,y}}; // quick way to create a point var paths = [ {col : 'red', points : [P(100, 200), P(600, 350), P( 700, 400)]}, {col : "green", points : [P(700, 400), P( 900, 500), P( 200, 600), P( 950, 900)]}, {col : "blue", points : [P(950, 900), P(1200, 950), P( 300, 200), P( 150, 1200)]}, {col : "brown", points : [P(150, 1200),P( 120, 1700),P( 1000, 700),P(850, 1500)]}, {col : "Purple",points : [P(850, 1500),P(800, 1900), P( 200, 900), P( 250, 1800)]}, {col : "yellow", points : [P(250, 1800),P(250, 1950), P( 600, 1500),P(950, 1800)]}, ] // draw the current frame based on sliderValue function draw(sliderValue,direction) { var getPos = false; // true if need pos on current curve var getForwardPos = false; // true if need pos on current curve var percent,percent1; // get the percentage on curves var birdPos; // get bird pos var birdLookAtPos; // get bird look at pos ctx.lineWidth = 5; for(var i = 0; i < paths.length; i ++){ var path = paths[i]; // get a path from array var p = path.points; ctx.strokeStyle = path.col; ctx.beginPath(); ctx.moveTo(p[0].x,p[0].y); if(sliderValue >= i * 50 && sliderValue < (i+1) * 50){ getPos = true; percent = (sliderValue % 50) / 50; } if(sliderValue + direction >= i * 50 && sliderValue + direction < (i+1) * 50){ getForwardPos = true; percent1 = ((sliderValue + direction) % 50) / 50; } if(p.length > 3){ ctx.bezierCurveTo(p[1].x,p[1].y,p[2].x,p[2].y,p[3].x,p[3].y); if(getPos){ birdPos = getCubicBezierXYatPercent(p[0],p[1],p[2],p[3],percent); getPos = false; } if(getForwardPos){ birdLookAtPos = getCubicBezierXYatPercent(p[0],p[1],p[2],p[3],percent1); getForwardPos = false; } }else{ ctx.quadraticCurveTo(p[1].x,p[1].y,p[2].x,p[2].y); if(getPos){ birdPos = getQuadraticBezierXYatPercent(p[0],p[1],p[2],percent); getPos = false; } if(getForwardPos){ birdLookAtPos = getQuadraticBezierXYatPercent(p[0],p[1],p[2],percent1); getForwardPos = false; } } ctx.stroke(); } drawImageLookingAt(birdImage,birdPos,birdLookAtPos); } function drawImageLookingAt(image, point, lookat ) { if(lookat === undefined){ // if no lookat then exit or it will crash. return; } var xAx,xAy; // vector for x Axis of image var x,y; x = lookat.x - point.x; y = lookat.y - point.y; var dist = Math.max(0.01,Math.sqrt(x * x + y * y)); // Math.max to avoid zero which will create NaN xAx = x / dist; // get x component of x Axis xAy = y / dist; // get y component of x Axis // y axis is at 90 deg so dont need y axis vector ctx.setTransform( // position the image using transform xAx, xAy, // set direction of x Axis -xAy, xAx, // set direction oy y axis point.x, point.y ); // bird is pointing in the wrong direction. Not along x axis // so rotate the image 90 deg clockwise ctx.rotate(Math.PI / 2); ctx.drawImage(image, -image.width / 2, -image.height / 2); ctx.setTransform(1,0,0,1,0,0); // Restore default Not really needed if you only use setTransform to do transforms // but in case you use transform, rotate, translate or scale you need to reset the // transform. } // line: percent is 0-1 function getLineXYatPercent(startPt, endPt, percent) { var dx = endPt.x - startPt.x; var dy = endPt.y - startPt.y; var X = startPt.x + dx * percent; var Y = startPt.y + dy * percent; return ({ x: X, y: Y }); } // quadratic bezier: percent is 0-1 function getQuadraticBezierXYatPercent(startPt, controlPt, endPt, percent) { var x = Math.pow(1 - percent, 2) * startPt.x + 2 * (1 - percent) * percent * controlPt.x + Math.pow(percent, 2) * endPt.x; var y = Math.pow(1 - percent, 2) * startPt.y + 2 * (1 - percent) * percent * controlPt.y + Math.pow(percent, 2) * endPt.y; return ({ x: x, y: y }); } // cubic bezier percent is 0-1 function getCubicBezierXYatPercent(startPt, controlPt1, controlPt2, endPt, percent) { var x = CubicN(percent, startPt.x, controlPt1.x, controlPt2.x, endPt.x); var y = CubicN(percent, startPt.y, controlPt1.y, controlPt2.y, endPt.y); return ({ x: x, y: y }); } // cubic helper formula at percent distance function CubicN(pct, a, b, c, d) { var t2 = pct * pct; var t3 = t2 * pct; return a + (-a * 3 + pct * (3 * a - a * pct)) * pct + (3 * b + pct * (-6 * b + b * 3 * pct)) * pct + (c * 3 - c * 3 * pct) * t2 + d * t3; } 
 <canvas height="1961" width="1000" id="canvas"></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