美文网首页让前端飞Web前端之路程序员
闲聊js16: 动画、数学与碰撞检测2(实现一个简单的事件分发和

闲聊js16: 动画、数学与碰撞检测2(实现一个简单的事件分发和

作者: 随风而行之青衫磊落险峰行 | 来源:发表于2017-08-22 11:01 被阅读121次

    技术具有"继承性、延展性"

    修正为:

    技术具有"延续性,延展性"

    写生.jpg

    本篇目的:

    • 为什么不能使用DOM内置的事件分发系统
    • 暂时先选择鼠标事件
    • 实现一个简单的鼠标事件分发和处理框架
    • demo演示与验证

    由于动画,数学,碰撞等demo演示,需要进行事件交互。
    目前还没有事件分发系统,因此今天我们来实现一个简单的事件分发系统以及事件处理的流程

    为什么不能使用DOM内置的冒泡事件系统

    很简单,因为DOM事件系统仅能分发到canvas元素。

    而我们的2D精灵系统是直接通过canvas2D绘制出来的,并没有并入到DOM文档树中,因此DOM 事件分发到canvas后,就结束了。

    所以我们要在事件分发到canvas后,继续进行分发给每个精灵,让他们有机会接受到事件并进行处理。这就是今天的任务!!

    暂时先选择鼠标事件

    原因:

    • 在手机端,竖屏显示范围太小了,查了一些资料,发现h5目前没有一个通用的进入页面直接横屏的功能,需要研究一下变通的实现方式。

    • 如果一旦实现手机端自动横屏效果,我会将mouse事件更改为touch事件,也就是几句代码的事情

    • touch/mouse事件最大区别是move事件。只要鼠标移动就会产生mousemove事件。而touchmove事件必须按下移动才产生。后面我一些例子,建立在mousemove基础上

    • 关键原因就是我现在还没实现h5页面自动横屏效果。导致手机演示不是很爽。

    实现一个简单的鼠标事件分发和处理系统

    1. 需要一个函数,将窗口客户区坐标转换到canvas2D坐标表示,该函数就加在BLFRender2D中去吧:
       ToCanvasCoord(x, y) {
            var bbox = this.context.canvas.getBoundingClientRect();
            return {
                x: x - bbox.left * (this.context.canvas.width / bbox.width),
                y: y - bbox.top * (this.context.canvas.height / bbox.height)
            };
        }
    

    原因和如何使用,等会demo会演示和解释

    1. BLFSprite基类中增加鼠标事件处理方法:
       onMouseDown(x, y) {
            console.log("deal mouseDown event");
            return false;
        }
    
        onMouseUp(x, y) {
            console.log("deal mouseUp event");
            return false;
        }
    
        onMouseMove(x, y) {
            console.log("deal mouseMove event");
            return false;
        }
    

    关于返回值true/false,请看下面的代码注释

    1. BLFSpriteManager中增加事件分发函数:
    dispatchMouseDownEvent(x, y) {
            //超级简单,用于演示用的事件分发体系
            //事件分发是核心之一
            //目前比较流行的有:dom3 level3事件系统
            //                ios responder 职责链事件系统
            //                android中那个复杂无比,效率底下的事件分发系统(会被骂吗?哈哈)
    
            for (let i = this.sprites.length - 1; i >= 0; i--) {
                //如果i指向的精灵事件处理完成(true),就中断事件分发
                if (this.sprites[i].onMouseDown(x, y))
                    return;
            }
        }
    
        dispatchMouseUpEvent(x, y) {
            for (let i = this.sprites.length - 1; i >= 0; i--) {
                //如果i指向的精灵事件处理完成(true),就中断事件分发
                if (this.sprites[i].onMouseUp(x, y))
                    return;
            }
        }
    
        dispatchMouseMoveEvent(x, y) {
            for (let i = this.sprites.length - 1; i >= 0; i--) {
                //如果i指向的精灵事件处理完成(true),就中断事件分发
                if (this.sprites[i].onMouseMove(x, y))
                    return;
            }
        }
    
    • 如果循环中当前i指向的精灵事件处理完成(true),就中断事件分发,退出函数
    • 事件分发循环是从后到前方式(let i = this.sprites.length - 1; i >= 0; i--),这是因为当两个精灵重叠时,肯定是上面的精灵进行事件处理,而不是被遮挡住的精灵(先前景,后背景)
    • 而绘制时,肯定是先背景,后前景(昨天孩子写生油画,老师讲解也强调如此,经典的画家算法)
    1. 最后,在BLFEngine2D的构造函数中将DOM 事件转发到各个精灵上:
           canvas.addEventListener("mousedown", (e) => {
                //先将客户区的点转换为相对canvas坐标系的点表示
                let pt = this.render.ToCanvasCoord(e.clientX, e.clientY);
                console.log("e.clientX = " + e.clientX + " e.clientY = " + e.clientY);
                console.log("canvasX = " + pt.x + " canvasY = " + pt.y);
                this.sprMgr.dispatchMouseDownEvent(pt.x, pt.y);
            }, false);
    
            canvas.addEventListener("mouseup", (e) => {
                //先将客户区的点转换为相对canvas坐标系的点表示
                let pt = this.render.ToCanvasCoord(e.clientX, e.clientY);
                this.sprMgr.dispatchMouseUpEvent(pt.x, pt.y);
            }, false);
    
            canvas.addEventListener("mousemove", (e) => {
                //先将客户区的点转换为相对canvas坐标系的点表示
                let pt = this.render.ToCanvasCoord(e.clientX, e.clientY);
                this.sprMgr.dispatchMouseMoveEvent(pt.x, pt.y);
            }, false);         
    
    • 关键的一步是将客户区的点转换为相对canvas坐标系的点表示。可能的情况是你的canvs画布的原点和客户区的原点没有重合时,如果不进行坐标变换,会出现非常大的问题

    • 关于DOM3 level3 冒泡事件,我个人是非常喜欢的一种事件分发体系。其实使用中有很多诀窍。本系列中,我会专门抽一章来聊一聊冒泡事件。实际上,我从webkit中抽取了很多超级有用的代码,包括冒泡事件系统(webkit我个人感觉应该是超过100万行代码的庞大c++程序)

    demo演示与验证:
    以前我们的demo都是canvs画布与客户区原点对齐的
    现在我们改变一下:将canvas右移100个像素

        <style>
            .canvas {
                position: absolute;
                left: 100px;
            }
        </style>
    
    • 使用绝对定位,left设置为100px,css一定要用单位哦

    添加画布,使用canvas样式:

    <canvas id="myCanvas" class="canvas" width="800" height="600" style="border: 1px solid black">你的浏览器还不支持哦</canvas>
    
    mousedown.png

    客户区: 649 392
    canvas: 549 384

    丈量.png

    红色框部分左右差100 px
    蓝色框部分上下差8 px

    问题: 为什么客户区和canvas上下差8px?(有兴趣可以留言回答)

    本篇结束,下一篇我们进入鼠标hitTest方面的内容

    附:
    昨天晚上听了一堂视听课,关于mysql MyISAM/InnoDB 索引底层实现(B+树)。收获是巨大的。只有了解了原理,才能有效的使用!。

    其实我个人一直很努力的学习各种树结构,它太重要了!!

    • quake3中实现的自适应哈夫曼树(用于网络传输数据最小化)

    • quake3 BSP树(整个quake引擎之所以如此高效,就是因为使用了binary space partitioning tree,空间二叉分割树)

    • 分割空间,加快渲染,与碰撞检测的quadtree(二维分割,四叉树,广泛用于地形渲染和碰撞检测),octree(三维分割,八叉树)

    • kd树(四叉/八叉都是kd的特殊形式)

    • 用于ui/2d/3d中组成场景用的通用树结构

    • 用于加快室内场景管理,动态光影计算的portal树

    • 各种加快碰撞检测的包围体层次树

    • 还有就是数据结构中各种经典的树结构:二叉树,红黑树,还有昨天的b树/b+树......

    树是非常非常非常重要的数据结构,如果想深入了解图形,数据库,游戏引擎等,那么各种树的数据结构一定要了解,并且知道用在哪些方面

    设计模式在UI系统开发中的应用(导读)这篇文章中,提供了一个通用树结构相关图,很值得研究

    树1.png 遍历策略.png 遍历策略2.png 遍历策略3.png

    在闲聊c++系列中,我的文章以基础知识为主。别看现在外面的新技术层出不穷,但本质都是很基础的知识。
    一个新技术不可能是凭空出来的,技术都是具有:
    继承性 : 从上到下
    延展性 : 从左到右
    突然发现,好有哲理哦!!!

    修正为:
    一个新技术不可能是凭空出来的,技术都是具有:
    延续性 : 从上到下
    延展性 : 从左到右

    这样比较押韵!!

    相关文章

      网友评论

        本文标题:闲聊js16: 动画、数学与碰撞检测2(实现一个简单的事件分发和

        本文链接:https://www.haomeiwen.com/subject/liykdxtx.html