由于摄像机智能功能的越来越丰富,而摄像机本身也可以在不同时间段进行多种智能功能的使用,例如12:30–13:00进行车辆检测; 15:00–16:30进行人脸检测等等;这样的需求诞生后我们就需要设计一个良好的交互方式提供给用户去选择和操作,最后我们选用时间轴的方式去展现上述效果;
github地址
(求★ 代码虽一直在更新,但是文章就一直停滞;其实之前也写过,不过时间隔太久了,就没有继续写下去了,偶然一次机会看到竟然有人给我★了,就又有动力了~~)
如下:
1.时间轴代表一天24小时,精确到分钟;
2.点击时间轴上有效区域可以默认创建一个30分钟的时间段(目前最小也是可到1分钟,但是不建议);
3.时间段可以进行拖拽、伸缩操作,且不与其他时间段进行重叠;
4.点击时间段会弹出一个编辑框,用于设置此时间段范围时间,当前事件及删除此时间段;
5.时间轴本身还提供删除和复制功能:能删除此时间轴上所有时间段和复制己时间段到其他时间轴上(复制时提供是否全覆盖或者部分复制);
初始化:
var defaultTime1=['2:9-5:45', '10:8-13:36', '17:34-20:32'];
var defaultInfos1=[1,2,3];
var timeS1=new TimeSlider({
mountedId:'timeslider1',
language:'cn',
defaultOneTimeBlockTime:15,
defaultTime:defaultTime1,
defaultInfos:defaultInfos
});
获取当前时间轴上所有时间段及事件:
timeS1.get().times;
timeS1.get().infos;
重新设置当前时间轴上时间段及事件:
timeS1.set({
setTimeArray:['2:9-5:45', '10:8-13:36', '17:34-20:32'],
setInfosArray:[4,2]
})
难点1: 让时间段“动起来”
2.由于页面有多个frame存在,防止鼠标滑入另一个frame导致mouseup失效,故引入mouseleave,其所有处理和mouseup一样;
3.有一个小坑需要说一下:由于单击时间段需要弹出一个编辑框,所以引入一个变量去存储当前时间段有无触发mousemove事件,无则说明当前是单击反之为移动;
单击触发的事件顺序:(不移动) mousedown->mouseup 移动触发的事件顺序: mousedown->mousemove->mouseup 不过在IE8下特别明显的是单击依然会触发mousemove事件,高级浏览器也会触发但是出现几率很低;
解决办法是利用mousedown和mouseup之间的时间差来过滤掉一些不必要的mousemove,我这边设置的是100ms,如果时间差大于100ms则认为确实是进行了移动;这也算是一种消抖吧。
对于时间轴的left值,我这边用的是dom.getBoundingClientRect().left+document.documentElement.scrollLeft
因为getBoundingClientRect是获取元素相对于视口的偏移,而后面利用的是pageX进行计算,是针对整个窗口,所以需要加上滚轮的偏移document.documentElement.scrollLeft
或者用jq的$("#id").offset().left 也行,不过我这边已经把jq舍弃了,是用的原生js实现的。
关于dixX这个值就是鼠标相对于时间轴的距离,我们需要算出这个值进行处理,保证我们每次移动时间段的时候,鼠标停留在时间段上的位置不变,算是一个小细节吧;
至于时间段的伸缩,逻辑和移动是大同小异的:往左伸缩需要设置时间段的left及width,而向右伸缩只需要设置时间段的width
难点2:让时间段在“有效范围内动起来”
这句话的意思其实就是让同一个时间轴上的不同时间段在移动的时候之间不互相重叠;
这里我们需要在mousedown事件触发的时候确实一个左边界(left-border)和右边界(right-border),让时间段在给定的边界范围内move;
需要注意的地方就是对于边界的取值,我们有四种可能:
这是时间段在进行拖动的时候的边界讨论,对于伸缩来说:向左伸缩,其右边界就是其结束坐标减去其宽度;向右伸缩,其左边界就是其开始坐标加上其宽度。
难点3: 让时间“精确"
原来是说只要精确到5分钟就行,后来我自己做的时候“自作主张”想精确到一分钟,不做不知道,一做吓一跳。
先交代一下基本情况:
时间轴的长度固定为:720px;
这样算下来每一个小时所占的像素为:720/24=30px;
每一分钟占的像素为720/24/60=0.5px ;
为什么选择0.5px这里也是有原因的:(因为按照这样计算,坐标和时间的互换能一一对应起来 )
比如:
1.我鼠标点击时获取到的坐标我55px
,通过转换可以得出此时的时间为1小时50分钟
,即1:50
.
但是,如果不是0.5
呢?比如我整个时间轴为768px
,那么一分钟为0.533333...px
,则换算出来的时间为1小时43.125
分钟,由于只是精确到了一分钟,所以这种不足一分钟的肯定就舍弃,即1:43
;因为这里我们舍弃了0.125
分钟,所以通过1:43
换算出来的坐标肯定不等于55px
,等于54.933333333...px
.
*坐标不相等会带来什么后果呢?
比如我手动点击时间轴创建了一个时间段,坐标正好为23px
,间隔正好为一个小时(32px
),那么其开始坐标为23px
,结束坐标为55px
,若在这个时间段后面还有一个时间段,正好要将其开始时间设置为1:43
,那么这个时候是设置不下去的,为什么?因为54.93333333px<55px
,超过了其左边界。
还是跟上面一样,点击创建了一个时间段,开始坐标和结束坐标为23px
和55px
,则其时间为0.43-1:43
,这个时候直接手动去设置其时间但是不去改变它的时间,你会发现这个时候滑动条会发生一个特别轻微的抖动,因为这个时候是根据时间转换为坐标,其坐标会变为22.93333333px
和54.933333333px
;
当然这个问题也不是不能解决,就是比较麻烦,比如这边在单击时间轴创建时间段的时候,得到时间后再转换一次坐标,就像上面的1:43
为55px
,这个时候将这个55px
转换为54.933333333px
这样就方便后面的比较了;
2.对于这些无限小数,设置到dom元素身上的时候对于保留位数浏览器自身是会有一定的限制的,不同浏览器应该是不一样的,chrome,IE11是3位,IE8这边是2位;而且每个浏览器对于小数点的取值有可能不一样,有的会四舍五入,有的会向下取整,这样精确度就会有一定的差别。
3.如果你将其坐标设置为通过时间换算出来的循环小数,当你去获取当前时间段是哪一个的时候,我最开始是用元素的left值去坐标数组中寻找,后来发现浏览器其实是会对小数点进行取舍保留的,所以无法获取到正确的值,不过是可以通过把所有时间段的时间保存起来,在搜索其索引的时候去用其显示时间去找,而不是通过坐标去找也可以达到目的的,但是0.5就不会存在这样的问题,不管你用坐标去找还是用时间去找,都可以找到,因为0.5能被整除(通过坐标算时间),乘以0.5(通过时间算坐标)也最多一位小数,浏览器是支持的。
当然是是用0.5px也是会有一定的小‘问题’
比如:
1分钟为0.5px,但是鼠标事件获取到的位置坐标都是整数,这样每移动1px也就是2分钟的步进值,这样代表着你鼠标去移动这个时间段不可能从1分钟到2分钟,只能从1分钟到3分钟,5,7… 其实这样也并不大碍,因为我们可以通过手动设置时间使得时间去精确;
(ps:如果想精确到1min=1px,那么就需要1440px长度的时间轴,这显然不太符合实际情况)
2.而且对于小数点数据,我们在获取元素属性的时候最好用
currentStyle/getComputedStyle
function getStyle(element, attr) {
if (element.currentStyle) {
return element.currentStyle[attr];
} else {
return getComputedStyle(element, false)[attr];
}
return style;
}
getStyle($(".main")[0], "width")
番外:由于最开始在进行研究的时候拿的是768px也就是每分钟0.53333px进行研究的,所以遇到了小数计算的问题,了解一下还是蛮涨见识的
0.1+0.2!=0.3的“自然规律”
在进行小数计算的时候,如果是商品类的标价,最后记得保留位数,因为很有可能计算出来的是一个无限小数,我这边采取了一个比较笨的办法
parseFloat(val.toFixed(point))
比如:var val=0.53333333333;
parseFloat(val.toFixed(4)) //0.5333;