timer.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. <template>
  2. <div :ref="'timeline-' + guid" class="timeline" ondragstart="return false;" onselectstart="return false;">
  3. <p class="timeline-line"></p>
  4. <div
  5. ref="timeline-main"
  6. class="timeline-main"
  7. :key="'timeline-main-' + guid"
  8. :id="'timeline-main-' + guid"
  9. >
  10. <!-- 时间段 -->
  11. <span
  12. v-for="item in timeArr"
  13. :key="item + guid"
  14. :class="'timeline-text-' + guid"
  15. :style="{ paddingRight: paddingRight + 'px' }"
  16. >{{ item }}</span>
  17. <!-- 录像片段的背景,层级-1 -->
  18. <div class="record-bg-div">
  19. <span class="record-bg-span" v-for="(record, index) in recordRange" :key="index + guid" :style="{ left: record.left + 'px', width: record.width + 'px' }"></span>
  20. </div>
  21. </div>
  22. </div>
  23. </template>
  24. <script>
  25. module.exports = {
  26. data() {
  27. return {
  28. // 时间段数组,不同时间间隔,时间段的数量和值都是不同的
  29. timeArr: [],
  30. // 时间间隔范围对应的下标
  31. intervalNum: 6,
  32. // 时间间隔范围对应的秒数,从1分钟到2小时
  33. intervalArr: [60, 300, 600, 900, 1800, 3600, 7200],
  34. paddingRight: 50,
  35. timer: null,
  36. // 时间轴所选时间,默认显示12点的位置
  37. selectTime: "12:00:00",
  38. guid: "",
  39. parentWidth: 0,
  40. resizeFn: null,
  41. // 录像片段的区间信息,包括开始时间结束时间,左边距和宽度
  42. recordRange: [],
  43. // 上次背景设置日期
  44. lastBgSetDate: ''
  45. };
  46. },
  47. props: {
  48. // 录像片段
  49. recordInfos: {
  50. type: Array,
  51. default() {
  52. return []
  53. }
  54. }
  55. },
  56. watch: {
  57. // 所选时间变化,触发父页面时间点变化方法
  58. selectTime: {
  59. handler(val) {
  60. if (val) {
  61. this.$emit("timer-change", val);
  62. }
  63. },
  64. },
  65. // 录像片段变化时,重新设置背景
  66. recordInfos: {
  67. deep: true,
  68. handler(val) {
  69. if (val && val.length > 0) {
  70. // windowTime格式:2021-07-02 12:10:30
  71. this.lastBgSetDate = val[0].windowTime.split(" ")[0]
  72. this.setBackground(val)
  73. } else {
  74. this.recordRange = []
  75. }
  76. }
  77. }
  78. },
  79. computed: {
  80. // 最小右间距
  81. minRight() {
  82. return Math.ceil(this.parentWidth * 0.03);
  83. },
  84. // 最大右间距
  85. maxRight() {
  86. return Math.ceil(this.parentWidth * 0.09);
  87. },
  88. },
  89. methods: {
  90. // 时间秒数格式化
  91. secToTime(s) {
  92. s = Math.floor(s)
  93. var t;
  94. if (s > -1) {
  95. var hour = Math.floor(s / 3600);
  96. var min = Math.floor(s / 60) % 60;
  97. var sec = s % 60;
  98. if (hour < 10) {
  99. t = "0" + hour + ":";
  100. } else {
  101. t = hour + ":";
  102. }
  103. if (min < 10) {
  104. t += "0";
  105. }
  106. t += min + ":";
  107. if (sec < 10) {
  108. t += "0";
  109. }
  110. t += sec;
  111. }
  112. return t === '24:00:00' ? '23:59:59' : t;
  113. },
  114. // 时间转成秒数
  115. timeToSec(time) {
  116. let arr = time.split(":")
  117. let hour = parseInt(arr[0])
  118. let min = parseInt(arr[1])
  119. let sec = parseInt(arr[2])
  120. return hour * 3600 + min * 60 + sec;
  121. },
  122. // 定位时间,父页面使用
  123. positionTime(time) {
  124. let second = this.timeToSec(time);
  125. let left = second / (24 * 3600 + this.intervalArr[this.intervalNum]) * this.$refs["timeline-main"].offsetWidth;
  126. let boxLeft = this.parentWidth / 2 - Math.ceil(left)
  127. let mainBox = $("#timeline-main-" + this.guid)[0];
  128. mainBox.style.left = boxLeft + "px";
  129. },
  130. // 设置录像背景
  131. setBackground(records) {
  132. this.recordRange = []
  133. if (records && records.length > 0) {
  134. let dayStart = this.lastBgSetDate + " 00:00:00"
  135. let dayEnd = this.lastBgSetDate + " 23:59:59"
  136. records.forEach(item => {
  137. if (item.strBeginTime <= dayEnd) {
  138. item.strBeginTime = item.strBeginTime >= dayStart ? item.strBeginTime : dayStart
  139. item.strEndTime = item.strEndTime <= dayEnd ? item.strEndTime : dayEnd
  140. let location = this.getLocationByTimeRange(item.strBeginTime, item.strEndTime)
  141. this.recordRange.push(location)
  142. }
  143. })
  144. }
  145. },
  146. // 更新录像背景
  147. updateBackground() {
  148. if (this.recordRange && this.recordRange.length > 0) {
  149. let records = JSON.parse(JSON.stringify(this.recordRange))
  150. this.recordRange = []
  151. for (let index = 0; index < records.length; index++) {
  152. let element = this.getLocationByTimeRange(records[index].strBeginTime, records[index].strEndTime)
  153. this.recordRange.push(element)
  154. }
  155. }
  156. },
  157. // 通过时间段获取位置
  158. getLocationByTimeRange(beginTime, endTime) {
  159. let result = {strBeginTime: beginTime, strEndTime: endTime}
  160. let startSecond = this.timeToSec(beginTime.split(" ")[1]);
  161. let left = startSecond / (24 * 3600 + this.intervalArr[this.intervalNum]) * this.$refs["timeline-main"].offsetWidth;
  162. result.left = left.toFixed(1)
  163. let endSecond = this.timeToSec(endTime.split(" ")[1]);
  164. let width = (endSecond - startSecond) / (24 * 3600 + this.intervalArr[this.intervalNum]) * this.$refs["timeline-main"].offsetWidth;
  165. result.width = width.toFixed(1)
  166. return result
  167. },
  168. // 刷新时间点
  169. refresh() {
  170. var d = new Date();
  171. // 初始化时间定为当前的时分
  172. let now = 0;
  173. this.timeArr = [];
  174. //最大时间点24点对应的秒数,也就是86400s
  175. let max = 86400;
  176. while (now < max) {
  177. this.timeArr.push(this.secToTime(now));
  178. now += this.intervalArr[this.intervalNum];
  179. }
  180. this.timeArr.push("23:59:59");
  181. let offsetLeft = this.$refs["timeline-main"].offsetLeft;
  182. this.$nextTick(() => {
  183. let allTimelines = document.querySelectorAll(
  184. ".timeline-text-" + this.guid
  185. );
  186. let offsetWidth = allTimelines[0].offsetWidth;
  187. let num = Math.floor((this.parentWidth / 2 - offsetLeft) / offsetWidth)
  188. let intervalWidth = this.parentWidth / 2 - offsetLeft - (offsetWidth * num)
  189. let nowSecond = this.intervalArr[this.intervalNum] * (num + intervalWidth / offsetWidth)
  190. this.selectTime = this.secToTime(nowSecond)
  191. // 更新录像背景
  192. this.updateBackground()
  193. });
  194. },
  195. // 放大
  196. turnUp() {
  197. if (this.intervalNum <= 0) {
  198. return;
  199. }
  200. if (this.paddingRight >= this.maxRight) {
  201. this.paddingRight = this.minRight;
  202. this.intervalNum -= 1;
  203. } else {
  204. this.paddingRight += 3;
  205. }
  206. // 刷新时间轴
  207. this.refresh();
  208. },
  209. // 缩小
  210. turnDown() {
  211. if (this.intervalNum >= this.intervalArr.length - 1) {
  212. return;
  213. }
  214. if (this.paddingRight <= this.minRight) {
  215. this.paddingRight = this.maxRight;
  216. this.intervalNum += 1;
  217. } else {
  218. this.paddingRight -= 3;
  219. }
  220. // 刷新时间轴
  221. this.refresh();
  222. },
  223. // 节流函数
  224. throttle(fn, delay) {
  225. let timer = null;
  226. return function () {
  227. if (timer) {
  228. return;
  229. }
  230. timer = setTimeout(() => {
  231. fn.apply(this, arguments);
  232. timer = null;
  233. }, delay);
  234. };
  235. },
  236. // 防抖函数
  237. debounce(fn, time, timer) {
  238. var timer = timer || null;
  239. return function () {
  240. let args = arguments;
  241. clearTimeout(timer);
  242. timer = setTimeout(() => {
  243. fn.call(this, args);
  244. }, time);
  245. };
  246. },
  247. },
  248. mounted() {
  249. this.parentWidth = this.$refs["timeline-" + this.guid].offsetWidth;
  250. this.paddingRight = this.maxRight
  251. var box = $("#timeline-main-" + this.guid);
  252. // 初始化默认是12点
  253. this.$nextTick(() => {
  254. this.positionTime("12:00:00")
  255. });
  256. // 刷新时间轴
  257. this.refresh();
  258. // 时间点右边距增加减少事件
  259. const upEvent = this.throttle(this.turnUp, 10);
  260. const downEvent = this.throttle(this.turnDown, 10);
  261. const that = this;
  262. // 鼠标滑轮事件
  263. var scrollFunc = function (e) {
  264. e = e || window.event;
  265. // 阻止冒泡和默认行为
  266. e.stopPropagation();
  267. e.preventDefault();
  268. if (e.wheelDelta) {
  269. //判断浏览器IE,谷歌滑轮事件
  270. if (e.wheelDelta > 0) {
  271. //当滑轮向上滚动时
  272. upEvent();
  273. }
  274. if (e.wheelDelta < 0) {
  275. //当滑轮向下滚动时
  276. downEvent();
  277. }
  278. } else if (e.detail) {
  279. //Firefox滑轮事件
  280. if (e.detail > 0) {
  281. //当滑轮向上滚动时
  282. upEvent();
  283. }
  284. if (e.detail < 0) {
  285. //当滑轮向下滚动时
  286. downEvent();
  287. }
  288. }
  289. return false;
  290. };
  291. //给页面绑定滑轮滚动事件
  292. if (document.addEventListener) {
  293. this.$refs["timeline-main"].addEventListener("DOMMouseScroll", scrollFunc, false);
  294. }
  295. //滚动滑轮触发scrollFunc方法
  296. document.querySelector("#timeline-main-" + this.guid).onmousewheel = scrollFunc;
  297. var body = $("body");
  298. var index = 0;
  299. var x1;
  300. // 按下鼠标左键
  301. box.mousedown(function () {
  302. index = 1; //鼠标按下才能触发onmousemove方法
  303. var x = event.clientX; //鼠标点击的坐标值,x
  304. var left = this.style.left;
  305. left = left.substr(0, left.length - 2); //去掉px
  306. x1 = parseInt(x - left);
  307. });
  308. // 鼠标指针在时间轴中移动
  309. box.mousemove(function () {
  310. if (index === 1) {
  311. let allTimelines = document.querySelectorAll(".timeline-text-" + that.guid);
  312. let offsetWidth = allTimelines[0].offsetWidth;
  313. let left = event.clientX - x1;
  314. if (left > that.parentWidth / 2) {
  315. return;
  316. }
  317. if (left < 0 && -left + that.parentWidth / 2 > offsetWidth * (allTimelines.length - 1)) {
  318. return;
  319. }
  320. this.style.left = left + "px";
  321. let num = Math.floor((that.parentWidth / 2 - left) / offsetWidth)
  322. let intervalWidth = that.parentWidth / 2 - left - (offsetWidth * num)
  323. let nowSecond = that.intervalArr[that.intervalNum] * (num + intervalWidth / offsetWidth)
  324. that.selectTime = that.secToTime(nowSecond)
  325. }
  326. });
  327. // 松开鼠标左键
  328. box.mouseup(function () {
  329. index = 0;
  330. });
  331. body.mouseup(function () {
  332. index = 0;
  333. });
  334. window.addEventListener("resize", (this.resizeFn = this.debounce(() => {
  335. this.parentWidth = this.$refs["timeline-" + this.guid].offsetWidth;
  336. }, 50))
  337. );
  338. },
  339. created() {
  340. this.guid = Math.ceil((1 + Math.random()) * 100000000);
  341. },
  342. deactivated() {
  343. window.removeEventListener("resize", this.resizeFn);
  344. },
  345. };
  346. </script>
  347. <style>
  348. .record-bg-span {
  349. z-index: -1;
  350. background: #EEBBB8;
  351. position: absolute;
  352. height: 100%;
  353. }
  354. .record-bg-div {
  355. width: 100%;
  356. position: absolute;
  357. height: 100%;
  358. top: 0;
  359. }
  360. .timeline {
  361. height: 26px;
  362. overflow-x: hidden;
  363. background: #eee;
  364. position: relative;
  365. width: calc(100% - 120px);
  366. margin-left: 120px;
  367. }
  368. .timeline-line {
  369. position: absolute;
  370. border-right: 1px solid red;
  371. left: 50%;
  372. height: 26px;
  373. margin: 0;
  374. z-index: 1;
  375. }
  376. .timeline-main {
  377. -moz-user-select: none;
  378. -khtml-user-select: none;
  379. user-select: none;
  380. position: absolute;
  381. left: 0;
  382. top: 4px;
  383. cursor: pointer;
  384. opacity: 0.9;
  385. }
  386. </style>