HTML5 Canvas开发详解(7) -- 高级动画

2022-04-07 16:01:11 浏览数 (2)

1. 用户交互

用户交互,指的是用户可以借助鼠标或键盘参与到Canvas动画中去,来实现一些互动的效果。这节主要基于鼠标事件来实现一些用户交互功能。

1.1 捕获物体

1.1.1 矩形的捕获

可以通过获取鼠标点击时的坐标来判断是否捕获了矩形。如果鼠标点击坐标落在矩形上,则说明捕获了这个矩形;如果鼠标点击坐标没有落在矩形上,则说明没有捕获到这个矩形。

语法:

代码语言:javascript复制
if(mouse.x > rect.x && 
mouse.x < rect.x   rect.width &&
mouse.y > rect.y &&
mouse.y < rect.y   rect.height
){
    //...
}

1.1.2 圆的捕获

在Canvas中,对于圆来说,可以根据鼠标与圆心之间的距离来判断圆的捕获。如果距离小于圆的半径,说明鼠标落在了圆上面;如果距离大于等于圆的半径,说明鼠标落在了圆外面。

语法:

代码语言:javascript复制
dx = mouse.x - ball.x;
dy = mouse.y - ball.y;
distance = Math.sqrt(dx * dx   dy * dy);
if(distance  < ball.radius){
  //...
}

1.2 拖拽物体

在Canvas中,如果想要拖拽一个物体,一般情况下需要三个步骤:

1)捕获物体:在鼠标按下(mousedown)时,判断鼠标坐标是否落在物体上面,如果落在,就添加两个事件(mousemove和mouseup);

2)移动物体:在鼠标移动(mousemove)中,更新物体坐标为鼠标坐标;

3)松开物体:在鼠标松开(mouseup)时,移除mouseup事件和mousemove事件。

语法:

代码语言:javascript复制
cnv.addEventListener('mousedown', () => {
    document.addEventListener('mousemove', onMouseMove);
    document.addEventListener('mouseup', onMouseUp);
})

1.3 抛掷物体

在拖拽物体时,它会在每一帧中拥有一个新的位置,用“新的位置坐标”减去“旧的位置坐标”就得到每一帧中物体的移动速度。

以小球来说,我们用oldX和oldY分别表示小球旧的位置坐标,新的位置坐标是ball.x和ball.y,可以得到:

代码语言:javascript复制
vx = ball.x - oldX;
vy = ball.y - oldY;

示例:抛掷小球

代码语言:javascript复制
//tools.js

Ball.prototype = {
    ...
    checkMouse(mouse){
        let dx = mouse.x - this.x;
        let dy = mouse.y - this.y;
        let distance = Math.sqrt(dx * dx   dy * dy);
        return distance < this.radius ? true : false;
    }
}
代码语言:javascript复制
//my-canvas.js

methods: {
    ...
    // 捕获/拖拽/抛掷小球
    captureBall(cxt, cnv){
        //鼠标是否按下
        let isMouseDown = false;
        //偏移量
        let dx = 0, dy = 0;
        //重力和反弹消耗
        let gravity = 0.5, bounce = -0.5;
        //鼠标位置,小球旧的坐标
        let mouse, oldX, oldY;
        //生成-3~3的随机速度
        let vx = (Math.random() * 2 - 1) * 3;
        let vy = (Math.random() * 2 - 1) * 3;
        let ball = new Ball(0, 0, 20);
        (function drawFrame(){
            requestAnimationFrame(drawFrame);
            cxt.clearRect(0, 0, cnv.width, cnv.height);
            if(isMouseDown){
                vx = ball.x - oldX;
                vy = ball.y - oldY;
                oldX = ball.x;
                oldY = ball.y;
            }else {
                vy  = gravity;
                ball.x  = vx;
                ball.y  = vy;
            }
            //边界反弹
            if(ball.x < ball.radius){
                ball.x = ball.radius;
                vx = vx * bounce;
            }else if(ball.x > cnv.width - ball.radius){
                ball.x = cnv.width - ball.radius;
                vx = vx * bounce;
            }
            if(ball.y < ball.radius){
                ball.y = ball.radius;
                vy = vy * bounce;
            }else if(ball.y > cnv.height - ball.radius){
                ball.y = cnv.height - ball.radius;
                vy = vy * bounce;
            }
            ball.draw(cxt, 'fill');
        })();
        //获取鼠标位置
        tools.listenMousePosition(cnv, obj => {
            mouse = obj;
        });
        function onMouseMove(){
            ball.x = mouse.x - dx;
            ball.y = mouse.y - dy;
        }
        function onMouseUp(){
            isMouseDown = false;
            document.removeEventListener('mouseup', onMouseUp);
            document.removeEventListener('mousemove', onMouseMove);
        }
        cnv.addEventListener('mousedown', () => {
            if(ball.checkMouse(mouse)){
                isMouseDown = true;
                oldX = ball.x;
                oldY = ball.y;
                dx = mouse.x - ball.x;
                dy = mouse.y - ball.y;
                document.addEventListener('mousemove', onMouseMove);
                document.addEventListener('mouseup', onMouseUp);
             }
        })
    },
}

示例效果:

2. 高级动画

2.1 缓动动画

缓动动画,指的是带有一定缓冲效果的动画,在动画过程中,物体在某一段时间会“渐进加速”或“渐进减速”,从而让物体运动看起来更为自然而逼真。

在Canvas中,实现缓动动画,一般需要五个步骤:

1)定义一个0~1之间的缓动系数easing;

2)计算出物体与终点之间的距离;

3)计算出当前速度,其中当前速度 = 距离 * 缓动系数;

4)计算新的位置,其中新的位置 = 当前位置 当前速度;

5)重复执行2~4步,直到物体达到目标。

语法:

代码语言:javascript复制
//targetX、targetY:表示目标的横坐标和纵坐标
//easing:表示缓动系数
//vx、vy:表示物体在x轴方向和y轴方向上的速度
let targetX = 任意位置;
let targetY = 任意位置;
//动画循环
let vx = (targetX - object.x) * easing;
let vy = (targetY - object.y) * easing;

示例:缓动的小球

代码语言:javascript复制
//my-canvas.js

methods: {
    ...
    //缓动动画
    slowActionAnimation(cxt, cnv){
        let ball = new Ball(0, 0);
        let targetX = cnv.width * 3 / 4;
        let targetY = cnv.width / 2;
        let easing = 0.05;
        (function frame() {
            requestAnimationFrame(frame);
            cxt.clearRect(0, 0, cnv.width, cnv.height);
            let vx = (targetX - ball.x) * easing;
            let vy = (targetY - ball.y) * easing;
            ball.x  = vx;
            ball.y  = vy;
            ball.draw(cxt, 'fill');
        })()
    }
}

示例效果:

在Canvas中,缓动动画不仅可以用于物体的运动,还可以应用于物体的其他各种属性,包括大小、颜色、透明度以及旋转等。

不管缓动动画应用于哪些方面,其实现思路是一样的,主要是以下两个步骤

1)当前速度 = (最终值 - 当前值)* 缓动系数;

2)新的值 = 当前值 当前速度。

2.2 弹性动画

在缓动动画中,物体滑动到终点就停下来了,在弹性动画中,物体滑动到终点后还会来回反弹一会,直至停止。

从技术上来说,缓动动画和弹性动画有以下几个共同点:

1)需要设置一个终点;

2)需要确定物体到终点的距离;

3)运动和距离是成正比的。

两者的不同在于“运动和距离是成正比的”这一点的实现方式不一样:

1)在缓动动画中,跟距离成正比的是“速度”;

2)在弹性动画中,跟距离成正比的是“加速度”。

语法:

代码语言:javascript复制
let targetX = 任意位置;
let targetY = 任意位置;
let ax = (targetX - object.x) * spring;
let ay = (targetY - object.y) * spring;
let vx  = ax;
let vy  = ay;
vx *= friction;
vy *= friction;
object.x  = vx;
object.y  = vy;

示例:绳球运动

代码语言:javascript复制
//my-canvas.vue

methods: {
    //绳球运动
    ropeBallMove(cxt, cnv){
        let mouse;
        let vx = 0, vy = 0;
        let spring = 0.02, friction = 0.95;
        let bounce = -0.5, gravity = 1;
        let ball = new Ball(cnv.width / 2, cnv.height / 2);
        tools.listenMousePosition(cnv, val => {
            mouse = val;
        });
        (function frame(){
            requestAnimationFrame(frame);
            cxt.clearRect(0, 0, cnv.width, cnv.height);
            if(mouse){
                let ax = (mouse.x - ball.x) * spring;
                let ay = (mouse.y - ball.y) * spring;
                vx  = ax;
                vy  = ay;
            }
            vy  = gravity;
            vx *= friction;
            vy *= friction;
            ball.x  = vx;
            ball.y  = vy;
            //边界反弹
            if(ball.x < ball.radius){
                ball.x = ball.radius;
                vx = vx * bounce;
            }else if(ball.x > cnv.width - ball.radius){
                ball.x = cnv.width - ball.radius;
                vx = vx * bounce;
            }
            if(ball.y < ball.radius){
                ball.y = ball.radius;
                vy = vy * bounce;
            }else if(ball.y > cnv.height - ball.radius){
                ball.y = cnv.height - ball.radius;
                vy = vy * bounce;
            }
            ball.draw(cxt, 'fill');
            if(mouse){
                cxt.beginPath();
                cxt.moveTo(ball.x, ball.y);
                cxt.lineTo(mouse.x, mouse.y);
                cxt.stroke();
            }
        })()
    },
}

示例效果:

3. 游戏开发

3.1 Box2D

Box2D是暴雪工程师Erin catto使用C 编写的一个非常优秀的物理引擎,在Box2D这个物理引擎里,可以模拟真实世界的运动情况,其中物体的运动、旋转和碰撞反应等都会遵循牛顿运动三大定律。

Box2D最初是C 编写的,由于它开发非常方便,极大提高了游戏开发效率,因此后来又衍生出了Flash、Java、Object-C和JavaScript等多种语言版本。

在Box2D中,集成了大量的物理力学和运动学的计算,我们只需要调用Box2D引擎中相应的对象或函数,就可以模拟现实生活中的匀速、减速、摩擦力、碰撞反弹等各种真实的物理运动。

JavaScript版本的Box2D有两个,一个是BoxDJS(已停止更新),另一个是Box2DWeb。如果我们想要使用Box2DWeb,只需要引入Box2D.js或Box2D.min.js就可以使用了。

3.2 HTML5游戏引擎

游戏引擎,就是可以为开发者提供编写游戏所需的各种工具,能够让我们非常容易和快速地开发一款游戏,而不需要从零开始。

3.2.1 Cocos2d-JS

Cocos2d-JS是Cocos2d-x的JavaScript版本,真正跨全平台的游戏引擎,采用原生JavaScript语言,可以发布到包括Web平台、iOS、Android、Windows Phone8、Mac、Windows等平台。

Cocos2d-JS具有易于使用、高效、灵活、免费、社区支持等特点。

3.2.2 Egret

Egret是国内一个非常流行的基于TypeScript语言开发的HTML5游戏引擎,遵循HTML5标准的2D、3D引擎,解决了HTML5性能问题及碎片化问题,灵活地满足开发者开发2D或3D游戏的需求,并有着极强的跨平台运行能力。

3.2.3 LayaAir

LayaAir相对前两者而言,性能最佳,号称“H5游戏引擎性能之王”。LayaAir裸跑性能堪比APP,支持2D、3D、VR开发,支持多语言开发,包括JavaScript、ActionScript、TypeScript,它的工具链成熟丰富,应用领域广泛。

3.2.4 Lufylegend

Lufylegend是国内一位资深游戏开发工程师个人独立开发的一个HTML5开源框架,不需要很多复杂的配置,直接引用js文件即可使用。

0 人点赞