Osheep

时光不回头,当下最重要。

闲聊js: 动画、数学与碰撞检测1(css3 3D动画 与 BLFEngine2D 协同作战)

经历了十多篇的渲染器以及闲聊js: 实现一个关键的,最小化的,非场景图类型的精灵系统(上)闲聊js: 实现一个关键的,最小化的,非场景图类型的精灵系统(下),我们已经有了一个可以演示动画,数学,碰撞检测的小类库。今天是第一篇!

先看gif吧:

《闲聊js: 动画、数学与碰撞检测1(css3 3D动画 与 BLFEngine2D 协同作战)》

demo-0-12.gif
  1. 并行动画:物体一边放大,一边绕x轴(pitch,上下转)旋转,一边朝前方平移
  2. 达到要求的位置后,紧接着绕y轴(yaw,左右转)旋转一定角度
  3. 上述过程中,物体的表面显示绕z轴(roll,滚转)旋转的精灵和网格背景

本篇目的:
通过一个css 3D 动画 + BLFRender2D协同作战的Demo,来了解

  • 什么是动画(Animation)
  • 属性动画(Property Animation)
  • 动画按时间发生的顺序进行分类
  • css3对动画的支持
  • css3和BLFRender2D的对接
  • css3中 transition vs animation
  1. 什么是动画:

按照opengl红宝书对动画的定义是: 重绘(repaint)+交换(swapBuffer)

我个人来扩展一下对动画的定义是: 重复的【更新(update) + 重绘(repaint) + 交换(swapBuffer)】

也就是我们BLFEngine2D中的run所做的事情

run(msec) {

        //更新
        this.updateAll(msec);
        //重绘
        this.renderAll();
        
        //重复
        //调用requestAnimationFrame
        requestAnimationFrame((msec) => { this.run(msec) });
    }
  • update中,我们更新我们的位置,角度,缩放,颜色,alpha,…….一切可以变化的属性(例如ios/android中的属性动画)

  • repaint中进行绘图渲染,将渲染结果写入到后备缓冲区中

  • swapBuffer是指渲染后,提交显存显示
    这一步在浏览器中你看不到,因为浏览器(目前只要是绘图库,都是双缓存,最大的好处是避免闪烁)帮你做了这部操作。实际从底层来看,就是经典的双缓存bitblt【闲聊js:创建一个演示用的渲染库4(渲染表面,像素格式,光栅化,位块传输,图形与图像)】操作。如果是全屏独占模式(玩游戏都知道吧,我也不解释了),则仅仅是双显存缓冲区的指针交换,让前缓冲变为后缓存,后缓冲变为前缓冲,交替显示,速度超快(显示的同时,另外一个在渲染,玩过dx/gl的同学应该很了解这个机制)

  1. 属性动画:

在我们的的精灵系统中,对精灵中的某个或某些属性进行更新和重绘。

在css3/ios/android中都有属性动画概念,不了解的话,可以自己google/baidu一下

  1. 动画从时间来看,可以分为连续动画和并行动画两种类型:顺序动画(SequentialAnimation) / 并行动画(ParallelAnimation)
  • 顺序动画(SequentialAnimation): 表达的动作(Action)语义是:
    先…然后…最终….(很显然需前一个动作完成后,发出通知:我完成了,谁来继续下面的事情呢?)

  • 并行动画(ParallelAnimation): 表的的动作(Action)语义是:
    一边…..一边….(例如:物体一边扩大,一边旋转,一边平移)

  1. css3中对动画的支持:

关于css3方面的内容,请自行参考上述文档。本篇不做详解

  1. css3和BLFRender2D的对接(css3代码来自与apple公司的flipcard demo,修改后用于我的demo)

这些操作比较通用,就定义在BLFES6Lib.js中吧!

    //为了防止和jquery名字发生冲突,增加name后缀
   //例如jquery中是addClass,这里就变成了addClassName

    Element.prototype.hasClassName = function (a) {
        return new RegExp("(?:^|\\s+)" + a + "(?:\\s+|$)").test(this.className);
    };

    Element.prototype.addClassName = function (a) {
        if (!this.hasClassName(a)) {
            this.className = [this.className, a].join(" ");
        }
    };

    Element.prototype.removeClassName = function (b) {
        if (this.hasClassName(b)) {
            var a = this.className;
            this.className = a.replace(new RegExp("(?:^|\\s+)" + b + "(?:\\s+|$)", "g"), " ");
        }
    };

    Element.prototype.toggleClassName = function (a) {
        this[this.hasClassName(a) ? "removeClassName" : "addClassName"](a);
    };
     
    //上面代码都很简单,下面这个是加强版的函数
    Element.prototype.changeClassName = function(remove,add) {
        if(this.hasClassName(remove))
            this.removeClassName(remove);
        if(add)
            this.addClassName(add);
    }
  • 需求描述:
    1. 并行动画:物体一边放大,一边绕x轴(pitch,上下转)旋转,一边朝前方平移
    2. 达到要求的位置后,紧接着绕y轴(yaw,左右转)旋转一定角度
    3. 上述过程中,物体的表面显示绕z轴(roll,滚转)旋转的精灵和网格背景
    4. yaw/pitch/roll【欧拉角】来源于飞行器的姿态描述,在3D中用于描述物体的朝向,先暂时了解一下吧。其实很多基础知识,专业术语要了解,然后才能更精确的google/baidu,否则范围太广,搜索不到
《闲聊js: 动画、数学与碰撞检测1(css3 3D动画 与 BLFEngine2D 协同作战)》

timg.jpg
  • css3动画:
        .container {
            width: 600px;
            height: 400x;
            margin: 0 auto 40px;
            border: 2px solid red;
            perspective: 1200px;
            /* 设置3D投影矩阵*/
        }
        
        @keyframes myTransform {
            from {
                transform: translate3d(0px, 0px, -5000px) rotateX(-720deg);
                /*从-720度*/
            }
            to {
                transform: translate3d(0px, 0px, 0px) rotateX(720deg);
                /*到720度*/
            }
        }
        
        @keyframes myYrotate {
            from {
                transform: rotateY(0deg);
            }
            to {
                transform: rotateY(720deg);
            }
        }
        
        /*并行动画*/
        .animation {
            width: 100%;
            height: 100%;
            transform-style: preserve-3d;
            /*使用3d变换*/
            animation: myTransform 5s;
        }
        
         /*顺序动画第二步*/
        .animation1 {
            width: 100%;
            height: 100%;
            transform-style: preserve-3d;
            /*使用3d变换*/
            animation: myYrotate 5s;
        }
  • html层次树及css3使用如下:
<body>
    <h1>随风而行之青衫磊落险峰行 BLFRender2D + CSSAnimation3D Demo</h1>

    <section class="container">
        <canvas id="myCanvas" class="animation" width="600" height="400">
        你的浏览器还不支持哦
    </canvas>
    </section>
</body>
  • js控制代码:
  var init = function() {
        let animationDiv = document.getElementById("myCanvas");
        //初始化时候,canvas上的动画使用的是上下旋转及向前平移(典型的并行动画),一旦完成后
        //触发webkitAnimationEnd事件
        //对webkitAnimationEnd事件进行处理,切换css为左右旋转,很典型的顺序动画
         //当完成一个动画序列后,浏览器会发送一个完成事件
        //我们对该事件进行处理就可以了animationDiv.addEventListener("webkitAnimationEnd", function() {
            animationDiv.changeClassName("animation", "animation1");
        }, false);
    };

    //挂接init事件处理函数
    window.addEventListener('DOMContentLoaded', init, false);

    let canvas = document.getElementById("myCanvas");
    let context = canvas.getContext('2d');
    let engine = new BLFEngine2D(context);
    let spr2 = new BLFDemoSprite(true);
    spr2.y = 200;
    spr2.rotateSpeed = -1; //逆时针
    engine.sprMgr.addSprite(new BLFGridSprite("background"));
    engine.sprMgr.addSprite(spr2);
    engine.run(); //run的是BLFSprite精灵系统的动画

运行一下看效果:http://htmlpreview.github.io/?https://github.com/jackyblf/BLF_JS_Demos/blob/master/css3withBLFRender2D.html

可能看不到效果,我也不知道为什么,也懒得花时间解决了

大家可以到我的github上下载代码
如果感觉不错,给我的github加颗小星星,鼓励鼓励我!

对了,在使用css3中的animation/transition中遇到一些问题,然后查了一些资料结合实践,总结一下一起分享吧:

  1. transition简单易用,但是有如下几个缺点:
  1. 没法自动运行(init中),只能靠事件触发(例如click)
  2. 无法循环运行,只能一次结束
  3. 只能设置开始,结束状态,无中间任意多的过度状态设置
  4. 只能animate一个属性,例如设置了移动就不能旋转
    综上所述,transition全部的缺点就是animation的优点!
点赞