繁体   English   中英

如何在画布上设置弹跳球的动画

[英]how to animate a bouncing ball on canvas

嗨,我刚刚开始编写Java和HTML等代码,因此我有时会感到挣扎。 因此,我加入了StackOverflow以寻求您的帮助(请对我好:)

我正在尝试使用setInterval()和draw方法制作动画,以创建一个弹跳球,该弹跳球在每帧中移动多个像素。 当它碰到边缘时,应该通过反转相关的水平或垂直像素速度的方向来反弹。

到目前为止,这是我的HTML代码:

<!DOCTYPE html>
  <html lang="en">
  <head>
  <title>Canvas Example</title>
  <script type="text/javascript" src='myanim.js' defer></script>
  <style type=text/css>
    #mycanvas {border:1px solid #000000}
  </style>
  </head>
  <body>
  <canvas id=mycanvas width=600 height=400>Canvas Not Supported
  </canvas>
  </body>
  </html>

我的JavaScript:

var canvas=document.getElementById('mycanvas');
var ctx=canvas.getContext('2d');
var timer;
var fillColour = '#FF0000';
var strokeColour = '#FFFFFF';
var x=0; var y=0;
function frameRate(fps) {
 timer = window.setInterval(updateCanvas,1000/fps);
}
function noLoop() {
 window.clearInterval(timer);
}
function updateCanvas(){
if (Math.random()>0.5)x+=2; else x-=2;
if (Math.random()>0.5)y+=2; else y-=2;
ctx.fillRect(0,0,canvas.width,canvas.height);
draw();
}
function draw(){
ctx.beginPath();
ctx.arc(canvas.width/2+x,canvas.height/2+y,100,0,2*Math.PI);
ctx.stroke();
}
ctx.strokeStyle=strokeColour;
ctx.fillStyle=fillColour;
frameRate(50);

http://jsfiddle.net/6EFqk/265/

我对math.random有所了解,因为我不明白。 有人可以帮我解决这个问题吗? 提前致谢。

动画弹跳球

下面是一个简单的弹跳球的示例。 此答案部分来自另一个答案,但您的问题标题更适合被发现。

主循环。

要设置动画,您需要有一个主循环。 每次需要更新并将所需的所有内容绘制到画布上时,都会调用此函数一次。 此更新通常称为框架。 然后,将主循环称为秒的次数称为帧频。 HTML的典型帧速率是每秒60帧或更低。 HTML5提供了一个特殊的事件调用requestAnimationFrame,该事件调用是为动画设计的,并在最佳时间触发以供您运行主循环。

function mainLoop(){
    // clear the canvas
    // update the position of all the stuff in the animation

    // then request the next animation frame
    window.requestAnimationFrame( mainLoop ); // pase it the name of your main loop
    // the main loop is done. Exiting lets the browser know yo are done and 
    // it presents the redrawn canvas to the screen.
}

要启动动画,只需在完成所有设置后调用mainloop

// starting the animation
mainLoop(); // call main loop

请求动画帧将尽最大努力保持均匀的帧速率。 如果您的mainloop需要很长时间,它也会减慢速度。

弹跳球

下面是一个示例,该示例如何模拟单个弹跳球并正确处理简单的将与画布侧面平行的碰撞。

它演示了如何通过在requestAnimationFramesetTimeout之间切换以调用主循环函数来更改帧速率。

它有一个绘制图像并添加运动模糊的示例(注意,运动模糊需要占用大量GPU(图形处理单元),在大量对象上效果不佳。)

帧间移动

从表面反射对象的正确方法。

您必须考虑到球在各帧之间移动,并且在前一帧的任何时候都可能发生了碰撞。 碰撞后,球到墙壁的距离取决于前一帧击中墙壁的时间。 如果球运动缓慢或快速,这一点很重要。

var dx = 10; // delta x velocity of object in pixels
var wx = 10; // width of object in pixels
var px = 90;  // position of object in pixels
var wallX = 105; // position of wall


px += dx;  // move the ball. Its position is now  100.
           // its right side is at px + wx = 110.
// test if it has it the wall
if(px+wx > wallX){
    dx = -dx; // reflect delta x
    // The object is 5 pixel into the wall.
    // The object has hit the wall some time during the last frame
    // We need to adjust the position as the ball may have been
    // traveling away from the wall for some time during the last frame.
    var dist = (px+wx)-wallX; // get the distance into the wall
    px -= dist*2; // the object hit the wall at position 95 and has been 
                  // traveling away since then so it is easy to just 
                  // subtract 2 times the distance the ball entered the wall
    // the above two lines can be done in one
    // px -= ((px+wx)-wallX)*2;
}

为什么重要

以下是一个球在画布内弹跳的模拟。

为了说明球在框架之间移动,已对其进行了运动模糊处理以显示其在框架之间的运动。 请注意,这并不是完美的解决方案,因为假定反弹是在球直线运动时发生的,而实际上却是在自由下落且恒加速度下发生的。 但是它仍然可以节省能源。

在正确的测试中,球反弹的高度会随着时间的推移保持不变。 没有能量损失或获取。

右键单击以关闭帧间调整,您会注意到球在每个帧中开始降低其高度。 这是因为在每次碰撞中,球都会损失一点能量,因为在碰撞测试后定位球时,没有考虑球在前一帧中的运动。 当在精确的帧时间发生碰撞时,它将稳定下来。 很难事先确定。

左键单击以减慢模拟帧速率,再次左键单击以恢复正常。

下面的代码并不是真正的答案,它是用来证明在碰撞测试过程中未正确调整位置对模拟整体精度的影响。

 // helper functions. NOT part of the answer var canvas = document.getElementById("canV"); var ctx = canvas.getContext("2d"); var mouseButton = 0; canvas.addEventListener('mousedown',function(event){mouseButton = event.which;}); canvas.addEventListener('mouseup' ,function(){mouseButton = 0;}); canvas.addEventListener("contextmenu", function(e){ e.preventDefault();}, false); var currentSurface = ctx; var createImage = function (w, h) {// create an canvas image of size w,h and attach context 2d var image = document.createElement("canvas"); image.width = w; image.height = h !== undefined?h:w; currentSurface = image.ctx = image.getContext("2d"); return image; } var setColour = function (fillC, strokeC, lineW) { currentSurface.fillStyle = fillC !== undefined ? fillC : currentSurface.fillStyle; currentSurface.strokeStyle = strokeC !== undefined ? strokeC : currentSurface.strokeStyle; currentSurface.lineWidth = lineW !== undefined ? lineW : currentSurface.lineWidth; } var circle = function(x,y,r,how){ currentSurface.beginPath(); currentSurface.arc(x,y,r,0,Math.PI*2); how = how.toLowerCase().replace(/[os]/g,"l"); // how to draw switch(how){ case "f": // fill currentSurface.fill(); break; case "l": currentSurface.stroke(); break; case "lf": currentSurface.stroke(); currentSurface.fill(); break; case "fl": currentSurface.fill(); currentSurface.stroke(); break; } } function createGradImage(size,col1,col2){ var image = createImage(size); var g = currentSurface.createLinearGradient(0,0,0,currentSurface.canvas.height); g.addColorStop(0,col1); g.addColorStop(1,col2); currentSurface.fillStyle = g; currentSurface.fillRect(0,0,currentSurface.canvas.width,currentSurface.canvas.height); return image; } function createColouredBall (ballR,col) { var ball = createImage(ballR*2); var unit = ballR/100; setColour("black"); circle(ballR,ballR,ballR,"f"); setColour("hsl("+col+",100%,30%)"); circle(ballR-unit*3,ballR-unit*3,ballR-unit*7,"f"); setColour("hsl("+col+",100%,50%)"); circle(ballR-unit*10,ballR-unit*10,ballR-unit*16,"f"); setColour("White"); circle(ballR-unit*50,ballR-unit*50,unit*16,"f"); return ball; } //=================================== // _ // /_\\ _ _ ____ __ _____ _ _ // / _ \\| ' \\(_-< VV / -_) '_| // /_/ \\_\\_||_/__/\\_/\\_/\\___|_| // // ================================== // Answer code // lazy coder variables var w = canvas.width; var h = canvas.height; // ball is simulated 5cm var pixSize = 0.24; // in millimeters for simulation // Gravity is 9.8 ms^2 so convert to pixels per frame squared // Assuming constant 60 frames per second. () var gravity = 9800*pixSize/60; gravity *= 0.101; // because Earth's gravity is stupidly large let's move to Pluto // ball 5cm var ballR = (25/pixSize)/2; // radius is 2.5cm for 5cm diamiter ball var ballX = w/2; // get center of canvas var ballY = ballR+3; // start at the top var ballDX = (Math.random()-0.5)*15; // start with random x speed ballDX += ballDX < 0 ? -5 : 5; // make sure it's not too slow var ballDY = 0; // star with no downward speed; var ballLastX = ballX; var ballLastY = ballY; //create an image of the Ball var ball = createColouredBall(ballR,Math.floor(Math.random()*360)); // create an image of ball // create a background. Image is small as it does not have much detail in it var background = createGradImage(16,"#5af","#08C"); // time to run for // Function to draw ball without motion blur // draws the ball with out motion blurred. // image is the image to draw // px and py are the x and y position to draw the ball var drawImage = function(image , px, py){ ctx.drawImage(image, px, py); } // draws the ball motion blurred. This introduces extra complexity var drawMotionBlur = function(image, px, py, dx, dy, steps){ var i, sx, sy; sx = dx / steps; sy = dy / steps; px -= dx; // move back to start position py -= dy; ctx.globalAlpha = 1 / (steps * 0.8); // set alpha to slightly higher for each step for(i = 0; i < steps; i+= 1){ ctx.drawImage(image, px + i * sx, py + i * sy); } ctx.globalAlpha = 1; // reset alpha } // style for text ctx.fillStyle = "white"; ctx.strokeStyle = "black"; ctx.textAlign = "center"; ctx.lineJoin = "round"; // stop some letters getting ears. ctx.lineWidth = 3; ctx.textBaseline = "bottom"; var textCenterX = w/2; var maxHeight = Infinity; var lastMaxHeight = ballY; var slowMotion = false; // slow motion flag var frameTravel = true; // use frame travel in collision test const bSteps = 10; // the fixed motion blur steps var update = function(){ var str, blurSteps; blurSteps = 10; // motion blur ball render steps. This varies depending on the the collision inter frame time. if(mouseButton === 1){ slowMotion = ! slowMotion; mouseButton = 0; } if(mouseButton === 3){ frameTravel = ! frameTravel; ballX = w / 2; // get center of canvas ballY = ballR + 3; // start at the top ballDY = 0; // start at 0 y speed mouseButton = 0; } // clear the canvas with background canvas image ctx.drawImage(background, 0, 0, w, h); ballDY += gravity; // acceleration due to grav // add deltas to ball position ballX += ballDX; ballY += ballDY; // test for collision on left and right walls. Need to // adjust for motion blur if (ballX < ballR) { ballDX = -ballDX; // refect delta x if (frameTravel) { // if using frame travel time // blur the outward traveling ball only for the time it has been traveling away blurSteps = Math.ceil(10 * ((ballX - ballR) / -ballDX)); // get position it should have traveled since ballX -= (ballX - ballR) * 2; }else{ ballX = ballR; // move ball to touching wall blurSteps = 1; // there is no outward motion } } else if (ballX > w - ballR) { ballDX = -ballDX; if (frameTravel) { // if using frame travel time // blur the outward traveling ball only for the time it has been traveling away blurSteps = Math.ceil(10 * ((ballX - (w - ballR)) / -ballDX)); ballX -= (ballX - (w - ballR)) * 2; }else{ ballX = w - ballR; // move ball to touching wall blurSteps = 1; // there is no outward motion } } // Test ball hit ground if (ballY > h - ballR) { ballDY = -ballDY; // to show max height lastMaxHeight = maxHeight; maxHeight = Infinity; if (frameTravel) { // if using frame travel time // blur the outward traveling ball only for the time it has been traveling away blurSteps = Math.ceil(10 * ((ballY - (h - ballR)) / -ballDY)); ballY -= (ballY - (h - ballR)) * 2; }else{ ballY = h - ballR; // move ball to touching wall blurSteps = 1; // there is no outward motion } } // draw the ball motion blured drawMotionBlur( ball, // image to draw ballX - ballR, // offset radius ballY - ballR, ballDX * (blurSteps / bSteps), // speed and adjust for bounced ballDY * (blurSteps / bSteps), blurSteps // number of blurs ); // show max height. Yes it is min but everything is upside down. maxHeight = Math.min(maxHeight,ballY); lastMaxHeight = Math.min(ballY,lastMaxHeight); // show max height ctx.font = "12px arial black"; ctx.beginPath(); ctx.moveTo(0, lastMaxHeight - ballR); ctx.lineTo(w, lastMaxHeight - ballR); ctx.stroke(); ctx.fillText("Max height.", 40, lastMaxHeight - ballR + 6); str = ""; // display status string if(slowMotion){ // show left click help str += "10fps." ctx.fillText("click for 60fps.", textCenterX, 43); }else{ str += "60fps." ctx.fillText("click for 10fps.", textCenterX, 43); } if(frameTravel){ // show mode and right click help str += " Mid frame collision."; ctx.fillText("Right click for Simple collision", textCenterX,55); }else{ str += " Simple collision."; ctx.fillText("Right click for mid frame collision", textCenterX,55); } // display help text ctx.font = "18px arial black"; ctx.strokeText(str, textCenterX, 30); ctx.fillText(str, textCenterX, 28); if(slowMotion){ setTimeout(update, 100); // show in slow motion }else{ requestAnimationFrame(update); // request next frame (1/60) seconds from now } // all done } update(); // to start the ball rolling 
 .canC { width:500px; height:500px;} 
 <canvas class="canC" id="canV" width=500 height=500></canvas> 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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