很早以前写过一篇用RequireJS包装AjaxChart,当时用Highcharts做图表,在其上封装了一层ajax,最后只是简单套用了一下requireJS。由于当时自己才接触模块化,理解层面还太浅,后来经过其他项目的磨练以及实习获得的见识,想重新结合一个示例来写点前端模块化的开发方式。
项目背景
最近在做一个安全运维监控的项目,其中有一条是根据设备获取到的攻击数据,在地图上做可视化。对比了Highcharts和ECharts
- ECharts对国内地图的支持更多
- ECharts在模块化和扩展方面做的比Highcharts更好
所以最后我选择了基于ECharts去封装。类似的网络攻击的监控地图可看国外的Norse Attack Map,也算是同类的参照。
需求整理
数据要求
- 提供的数据只有IP到IP的攻击,包括攻击时间、攻击类型等,需要自行根据IP定位到相应的经纬度。
展现要求
- 地图提供世界、中国、省份,这三种维度(只针对中国)
- 要在地图上表现出攻击的来源与目标之间的动画
- 需要强调出攻击受灾地区,可一眼看出哪里是重灾区
- 可以循环表现攻击,也可实时刷新攻击数据
目录结构
1 | - index.html 主页面 |
requireJS的config配置
1 | requirejs.config({ |
map封装过程
初步封装 mods/attackMap/main.js
1 | define(function(require){ |
这里我用echarts中的MarkLine作为攻击线,MarkPoint作为受害地点,AttackMap封装了对echarts的操作过程,对外只暴露setView
和setAttacks
两个方法,以实现地图维度的缩放以及攻击线的表现。其中echarts map的通用配置项都拎到了mods/attactMap/mapOption.js
中,这里AttackMap只手工操作部分option,比如根据地图的维度修改MarkLine动画的速率。
应用层 js/app/mainMap.js
1 | require([ |
至此,在应用层页面上,可以通过点击.J_changeView
按钮来切换地图的维度(世界/中国/省份),攻击数据的展现暂时没有ajax调用,只是简单用了mock数据来做,大体效果是一样的。
自定义事件封装
在上面的demo链接中看到,不仅应用层页面的按钮可以切换地图维度,直接点击地图里的”中国”区域也能切换地图,同时又能通知到应用层页面的按钮改变状态。因此应用层页面是需要关心AttackMap的状态(事件)的,同样将鼠标放在攻击线上出现的攻击详情,也是通过监听AttackMap的事件实现的。
1、在 mods/attackMap/main.js 中定义事件类型
1 | // 对外事件 |
2、在AttackMap中实现事件触发器
1 | AttackMap.prototype = { |
3、在AttackMap内部适当的方法中fire
自定义事件
1 | AttackMap.prototype = { |
当触发AttackMap.EVENTS.LINE_HOVERED
事件时,由于应用层页面要绘制攻击详情的浮层,需要知道鼠标位置信息,所以这里fire
时将原生的event对象也传了进去。(注意fire
方法的实现中,传给回调函数的eventObj
对象中,有事件类型type,自定义data,以及原生event对象)
4、在应用层js中监听自定义事件
1 | // 别名 |
点缀的动画效果
时钟模块
比较简单,源码在 js/lib/mods/clock.js 中,下面只列出大体结构。
1 | define(['jquery'], function($){ |
move动画封装
原理是采用的css中transform
动画,我们原本的做法会是先定义两个css class,一个添加transform的各种css规则,另一个class添加与前一项相反(或清除动画)的css规则,然后通过js操控DOM元素,在两个class之间切换。但我觉得这种做法太挫了,可以把相同效果的transform封装起来(避免写大同小异的css class),于是我封装了一个只做move移动的动画util方法。
1 | define(['jquery', 'underscore'], function($, U){ |
基于move动画的滚动表格
在demo中可以看到屏幕下方的攻击数据的表格一直在滚动播放,现在已经很少人还在用<marquee>
这种东西了,好比已经淘汰的用<table>
做页面布局。我这里基于上面的动画util方法,实现了一个滚动播放的table组件。
实现思路是,先要对table元素做预处理,将thead拷贝一份,因为表格滚动时thead是不动的(相当于sticky)。代码结构类似上面的Clock类,主动画逻辑包在setInterval
中。每次动画循环到来时,取出tbody的第一个tr
元素的高度h,然后将table整体向上move这段高度h,move结束后将第一个tr
追加到tbody的队尾。具体实现代码见 js/lib/mods/animTable.js
还有什么欠缺的
最初的展现需求都已实现了,在这过程中封装了AttackMap,并自己实现了自定义事件,完全将echarts对外透明了。同时还产出了几个非主要的js小组件,过程看似拉的很长,但都是一步步自然而然会产生的想法。这里还遗留着一个问题,如何将html模板、样式和js模块捆绑起来,即只需reuqire一下模块,模块相应的css会一并载入。
1 | <!-- 不需要 <link rel="stylesheet" href="moduleA.css"> --> |
我想达到的效果就像上面,应用层页面不需要引组件模块的css,只要inclue一份html模板,require一下对应的js模块。有知道具体做法的吗,我想进一步交流。
demo
感想
在繁忙的项目中抽出时间做些整理和总结,是件重要但不紧急的事情。
和以前写的文章一对比,明显感觉到自己这半年多的成长。