简体   繁体   中英

Javascript Canvas - Bouncing ball diagonally

So basically I am trying to add bounce to a ball updating the x and y position rather than rotating the canvas.

Is this possible, simply?

Here is what I have, which does the up, down, left and right correctly. But the diagonal ones obviously need a little bit more work.

kick: function(fps, dir, settings){

        var options = {
            speed: 15,
            gravity: .98,
            friction: .99,
            airDrag: .98,
            elasticity: .8,
            angle: 340,
            radius: this.sWidth
        }

        options = deepExtend(options, settings);

        var radians = options.angle * Math.PI/ 180,
            vx = Math.sin(radians) * options.speed,
            vy = Math.cos(radians) * options.speed;

        this.ballStartPosX = this.ball.pos[0];
        this.direc = dir;

        this.vx = vx;
        this.vy = vy;
        this.friction = options.friction;
        this.airDrag = options.airDrag;
        this.elasticity = options.elasticity;
        this.gravity = options.gravity;

        this.shootBall = setInterval(this.shoot.bind(this), fps);
    },

shoot: function () {
        this.ball.pos[0] += this.vx;

        switch(this.direc.toString()) {
            case 'up': this.ball.pos[1] -= this.vy;
            break;
            case 'down': this.ball.pos[1] += this.vy;
            break;
            case 'left': this.ball.pos[0] -= this.vy;
            break;
            case 'right': this.ball.pos[0] += this.vy;
            break;
            case 'up,right': this.ball.pos[1] -= this.vy; this.ball.pos[0] += this.vy;
            break;
            case 'up,left': this.ball.pos[1] -= this.vy; this.ball.pos[0] -= this.vy;
            break;
            case 'down,right': this.ball.pos[1] += this.vy; this.ball.pos[0] += this.vy;
            break;
            case 'down,left': this.ball.pos[1] += this.vy; this.ball.pos[0] -= this.vy;
            break;
        }

        if (this.ball.pos[0] > this.ballStartPosX) {
            this.ball.pos[0] = this.ballStartPosX;
            this.vx = -(this.vx)*this.elasticity;
        }

        this.vx += this.gravity;


        if (this.ball.pos[0] >= this.ballStartPosX) {
            this.vy *= this.friction;
        }


        var speed = Math.sqrt(this.vx*this.vx + this.vy*this.vy);

        if (speed < this.friction) {
            speed = 0;
            clearInterval(this.shootBall);
        }


    }

this is what i want to achieve

在此处输入图片说明

I was thinking maybe if I drew an invisible diagonal line and used that as the touch point. Would that work?

The simplest solution is to use a rotated quadratic curve.

BTW, why did you dismiss this simplest solution?

Anyway...

The "manual" solution without canvas rotation can be done with simple math, but requires many small steps.

The solution involves calculating the 3 controls points of a quadratic curve and then animating your ball along that quadratic curve.

Here's example annotated code and a Demo: http://jsfiddle.net/m1erickson/gQ8RC/

This code does 1 bounce but is repeatable for multiple bounces.

// get references to the canvas and its context
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");

// save PI*2 to a variable since it's used often
var PI2=Math.PI*2;

// declare the starting and ending points of an imaginary line
var p1={x:20,y:250};
var p2={x:200,y:200};

// declare where a bounce will start & end along that line
// pct1==20%==bounce starts 20% of the way between p1 & p2
// pct2==60%==bounce ends 60% of the way between p1 & p2
var pct1=0.20;
var pct2=0.60;

// calculate deltaX & deltaY of an imaginary line
// containing starting point (p1) & ending point (p2)
var dx=p2.x-p1.x;
var dy=p2.y-p1.y;

// calculate starting point of bounce (20% from p1 towards p2)
var x1=p1.x+dx*pct1;
var y1=p1.y+dy*pct1;

// calculate ending point of bounce (60% from p1 towards p2)
var x2=p1.x+dx*pct2;
var y2=p1.y+dy*pct2;

// calculate mid point of bounce ((60-20)/2% from p1 towards p2)
var pctMidpoint=pct1+(pct2-pct1)/2;
var midX=p1.x+dx*pctMidpoint;
var midY=p1.y+dy*pctMidpoint;

// define a distance (d) for the control point of a quadratic curve
// d will indirectly determine how "high" the bounce will be
var d=75;

// calculate a quadratic curve control point on the tangent line at distance d
var ra=Math.atan2(dy,dx);  // radian angle of the imaginary line
var ta=ra-Math.PI/2;       // radian angle tangent to the imaginary line
var controlX=midX+d*Math.cos(ta);
var controlY=midY+d*Math.sin(ta);

// set up an animation that redraws a ball along 
// the calculated quadratic curve

// T will be an interval used to determine where on the quadratic curve 
// to calculate an x,y point
var T=0;

// just testing...tDirection will reverse the ball when it reaches the end of the curve
var tDirection=1;

// start the animation
requestAnimationFrame(animate);

// animate a ball along the quadratic curve
function animate(){

    // request another animation frame
    requestAnimationFrame(animate);

    ctx.clearRect(0,0,canvas.width,canvas.height);

    // calculate the balls next x,y along the curve
    var point=getQuadraticBezierXYatT(
        {x:x1,y:y1},
        {x:controlX,y:controlY},
        {x:x2,y:y2},
        T/100
    );

    // draw the ball
    ctx.beginPath();
    ctx.arc(point.x,point.y,5,0,PI2);
    ctx.closePath();
    ctx.fillStyle="blue";
    ctx.fill();

    // Move the ball to the next interval on the curve
    // Reverse direction when the ball reaches the start/end of the curve
    T+=tDirection;
    if(T<0 || T>100){
        tDirection*=-1;
        T+=tDirection;        
    }

}

// calculate an x,y point along a quadratic curve at interval T
function getQuadraticBezierXYatT(startPt,controlPt,endPt,T) {
    var x = Math.pow(1-T,2) * startPt.x + 2 * (1-T) * T * controlPt.x + Math.pow(T,2) * endPt.x; 
    var y = Math.pow(1-T,2) * startPt.y + 2 * (1-T) * T * controlPt.y + Math.pow(T,2) * endPt.y; 
    return( {x:x,y:y} );
}

(Just to mention that a ball rising along a slope will not physically behave as you have illustrated)

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