ProgressBar.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. <template>
  2. <div class="progress-box-bar" ref="progressbar">
  3. <div class="bar-container1">
  4. <div
  5. class="bar2"
  6. v-for="sec in planVideo"
  7. :key="sec.key"
  8. :style="`width:${sec.width}%;background-color:${sec.color}`"
  9. ></div>
  10. </div>
  11. <canvas
  12. draggable="false"
  13. id="procanvas"
  14. height="60"
  15. @contextmenu="contextMenuClick"
  16. @mousedown="mousedownFunc"
  17. @mouseup="mouseupFunc"
  18. @mousemove="mousemoveFunc"
  19. @mouseout="mouseoutFunc"
  20. ></canvas>
  21. <div class="bar-container">
  22. <div
  23. class="bar1"
  24. v-for="sec in lostVideo"
  25. :key="sec.key"
  26. :style="`width:${sec.width}%;background-color:${sec.color}`"
  27. ></div>
  28. </div>
  29. </div>
  30. </template>
  31. <script>
  32. export default {
  33. name: "ProgressBar",
  34. props: ["timeCell", "startTime", "endTime", "videoData", "lostData"],
  35. data() {
  36. return {
  37. planVideo: [], //{width:百分比,color:颜色}
  38. lostVideo: [], //{width:百分比,color:颜色}
  39. canvas: "",
  40. ctx: "",
  41. canvasW: "",
  42. canvasH: "",
  43. nowTime: "",
  44. minute_per_ruler: 24 * 60, //时间轴显示24小时,单位分钟
  45. half_time: 5 * 60 * 60 * 1000,
  46. graduation_step: 20, //刻度间最小宽度,单位px
  47. start_timestamp: new Date().getTime() - 5 * 60 * 60 * 1000, //开始时间、最左侧时间
  48. end_timestamp: new Date().getTime() + 5 * 60 * 60 * 1000, //结束时间
  49. minutes_per_step: [
  50. 1, 2, 5, 10, 15, 20, 30, 60, 120, 180, 240, 360, 720, 1440,
  51. ],
  52. distance_between_gtitle: 80,
  53. g_isMousedown: false, //拖动mousedown标记
  54. g_isMousemove: false, //拖动mousemove标记
  55. g_mousedownCursor: null, //拖动mousedown的位置
  56. returnTime: null, //mouseup返回时间
  57. downTime: [], //下载时间
  58. downLeft: 0,
  59. showDown: false,
  60. tiaoTop: 40,
  61. tiaoHeight: 20,
  62. };
  63. },
  64. watch: {
  65. timeCell: {
  66. immediate: true,
  67. handler: function () {
  68. this.planVideo = [];
  69. if (this.timeCell.length > 0) {
  70. let tc = this.timeCell;
  71. this.nowTime = this.changeTime(
  72. new Date(this.timeCell[0].beginTime).getTime()
  73. );
  74. this.start_timestamp = new Date(this.timeCell[0].beginTime).getTime();
  75. this.end_timestamp = new Date(
  76. this.timeCell[this.timeCell.length - 1].endTime
  77. ).getTime();
  78. this.minute_per_ruler =
  79. (this.end_timestamp - this.start_timestamp) / (60 * 1000);
  80. let prevEnd = this.start_timestamp;
  81. for (tc of this.timeCell) {
  82. let beginstamp = new Date(tc.beginTime).getTime();
  83. if (prevEnd != beginstamp) {
  84. let percent =
  85. (beginstamp - prevEnd) / 60 / 1000 / this.minute_per_ruler;
  86. this.planVideo.push({
  87. key: prevEnd,
  88. width: percent * 100,
  89. color: "transparent",
  90. });
  91. }
  92. let endStamp = new Date(tc.endTime).getTime();
  93. let percent =
  94. (endStamp - beginstamp) / 60 / 1000 / this.minute_per_ruler;
  95. this.planVideo.push({
  96. key: beginstamp,
  97. width: percent * 100,
  98. color: "rgb(0, 178, 255)",
  99. });
  100. prevEnd = endStamp;
  101. }
  102. if (this.ctx) {
  103. this.clearCanvas();
  104. this.initCanvas();
  105. }
  106. }
  107. },
  108. },
  109. lostData: {
  110. immediate: true,
  111. handler: function () {
  112. let lostVideo = [];
  113. if (this.lostData && this.lostData.length > 0) {
  114. let prevEnd = this.start_timestamp;
  115. let totalPercent = 0;
  116. for (let tc of this.lostData) {
  117. let beginstamp = new Date(tc.beginTime).getTime();
  118. if (prevEnd != beginstamp) {
  119. let percent =
  120. (beginstamp - prevEnd) / 60 / 1000 / this.minute_per_ruler;
  121. totalPercent += percent;
  122. lostVideo.push({
  123. key: prevEnd,
  124. width: percent * 100,
  125. color: "green",
  126. });
  127. }
  128. let endStamp = new Date(tc.endTime).getTime();
  129. let percent =
  130. (endStamp - beginstamp) / 60 / 1000 / this.minute_per_ruler;
  131. totalPercent += percent;
  132. lostVideo.push({
  133. key: beginstamp,
  134. width: percent * 100,
  135. color: "red",
  136. });
  137. prevEnd = endStamp;
  138. }
  139. if (totalPercent != 1) {
  140. lostVideo.push({
  141. key: new Date().valueOf(),
  142. width: (1 - totalPercent) * 100,
  143. color: "green",
  144. });
  145. }
  146. } else {
  147. lostVideo.push({
  148. key: new Date().valueOf(),
  149. width: 100,
  150. color: "green",
  151. });
  152. }
  153. this.lostVideo = lostVideo;
  154. },
  155. },
  156. // startTime(){ //不用
  157. // if(!this.g_isMousedown){
  158. // this.nowTime = this.changeTime(this.startTime+this.half_time);
  159. // this.start_timestamp = this.startTime;
  160. // this.clearCanvas();
  161. // this.initCanvas();
  162. // }
  163. // }
  164. },
  165. mounted() {
  166. this.$nextTick(() => {
  167. this.canvas = document.getElementById("procanvas");
  168. this.ctx = this.canvas.getContext("2d");
  169. this.canvas.width = this.$refs.progressbar.offsetWidth;
  170. this.canvasW = this.canvas.width;
  171. this.canvasH = this.canvas.height;
  172. this.nowTime = this.changeTime(new Date().getTime());
  173. this.initCanvas();
  174. });
  175. },
  176. methods: {
  177. // 初始化canvas
  178. initCanvas: function () {
  179. this.drawCellBg();
  180. //this.add_cells(this.timeCell);
  181. this.add_graduations(this.start_timestamp);
  182. //this.draw_down(this.downTime);
  183. //this.drawTimeTriangle();
  184. },
  185. // // 绘制三角 时间
  186. drawTimeTriangle() {
  187. // this.ctx.beginPath();
  188. // let x = this.canvasW/2;
  189. // let x1 = this.canvasW/2-7;
  190. // let x2 = this.canvasW/2+7;
  191. // this.ctx.moveTo(x1,25);
  192. // this.ctx.lineTo(x2,25);
  193. // this.ctx.lineTo(x,36);
  194. // this.ctx.fillStyle = "#318ed4";
  195. // this.ctx.closePath();
  196. // this.ctx.fill();
  197. // this.ctx.fillStyle = "#fff";
  198. // this.ctx.font = "14px Arial";
  199. // this.ctx.fillText(this.nowTime,x-60,20);
  200. },
  201. // 画背景色
  202. drawCellBg: function () {
  203. this.ctx.fillStyle = "#eaeaea";
  204. this.ctx.fillRect(0, this.tiaoTop, this.canvasW, this.tiaoHeight);
  205. },
  206. // 添加刻度
  207. add_graduations: function (start_timestamp) {
  208. var _this = this;
  209. var px_per_min = _this.canvasW / _this.minute_per_ruler; // px/min
  210. var px_per_ms = _this.canvasW / (_this.minute_per_ruler * 60 * 1000); // px/ms
  211. var px_per_step = _this.graduation_step; // px/格 默认最小值20px
  212. var min_per_step = px_per_step / px_per_min; // min/格
  213. for (let i = 0; i < _this.minutes_per_step.length; i++) {
  214. if (min_per_step <= _this.minutes_per_step[i]) {
  215. //让每格时间在minutes_per_step规定的范围内
  216. min_per_step = _this.minutes_per_step[i];
  217. px_per_step = px_per_min * min_per_step;
  218. break;
  219. }
  220. }
  221. var medium_step = 30;
  222. for (let i = 0; i < _this.minutes_per_step.length; i++) {
  223. if (
  224. _this.distance_between_gtitle / px_per_min <=
  225. _this.minutes_per_step[i]
  226. ) {
  227. medium_step = _this.minutes_per_step[i];
  228. break;
  229. }
  230. }
  231. console.info("add_graduations", _this.canvasW);
  232. var num_steps = _this.canvasW / px_per_step; //总格数
  233. var graduation_left;
  234. var graduation_time;
  235. var ms_offset = _this.ms_to_next_step(
  236. start_timestamp,
  237. min_per_step * 60 * 1000
  238. ); //开始的偏移时间 ms
  239. var px_offset = ms_offset * px_per_ms; //开始的偏移距离 px
  240. var ms_per_step = px_per_step / px_per_ms; // ms/step
  241. for (var i = 0; i < num_steps; i++) {
  242. graduation_left = px_offset + i * px_per_step; // 距离=开始的偏移距离+格数*px/格
  243. graduation_time = start_timestamp + ms_offset + i * ms_per_step; //时间=左侧开始时间+偏移时间+格数*ms/格
  244. var date = new Date(graduation_time);
  245. if ((graduation_time / (60 * 1000)) % medium_step == 0) {
  246. var middle_date = _this.graduation_title(date);
  247. _this.ctx.fillStyle = "#318ed4";
  248. _this.ctx.font = "14px Arial";
  249. _this.ctx.fillText(
  250. middle_date,
  251. i == 0 ? graduation_left : graduation_left - 17,
  252. 35
  253. );
  254. _this.drawLine(
  255. graduation_left,
  256. 50,
  257. graduation_left,
  258. 60,
  259. "#318ed4",
  260. 1
  261. );
  262. } else {
  263. _this.drawLine(
  264. graduation_left,
  265. 55,
  266. graduation_left,
  267. 60,
  268. "rgba(49,142,212,0.7)",
  269. 1
  270. );
  271. }
  272. }
  273. },
  274. // 画刻度线
  275. drawLine: function (beginX, beginY, endX, endY, color, width) {
  276. this.ctx.beginPath();
  277. this.ctx.moveTo(beginX, beginY);
  278. this.ctx.lineTo(endX, endY);
  279. this.ctx.strokeStyle = color;
  280. this.ctx.lineWidth = width;
  281. this.ctx.stroke();
  282. },
  283. // 添加录像块
  284. // add_cells:function(cells){
  285. // var _this = this;
  286. // cells.forEach(cell => {
  287. // _this.draw_cell(cell)
  288. // });
  289. // },
  290. // 绘制录像块
  291. // draw_cell:function(cell){
  292. // var _this = this;
  293. // var px_per_ms = _this.canvasW / (_this.minute_per_ruler * 60 * 1000); // px/ms
  294. // var beginX = (cell.beginTime - _this.start_timestamp) * px_per_ms;
  295. // var cell_width = ( cell.endTime - cell.beginTime) * px_per_ms;
  296. // _this.ctx.fillStyle = "#2d86cd";
  297. // _this.ctx.fillRect(beginX,this.tiaoTop,cell_width,this.tiaoHeight);
  298. // },
  299. // 绘制下载录像块
  300. // draw_down:function(cell){
  301. // var _this = this;
  302. // var px_per_ms = _this.canvasW / (_this.minute_per_ruler * 60 * 1000); // px/ms
  303. // var beginX = cell[0]>cell[1]?(cell[1]- _this.start_timestamp)* px_per_ms:(cell[0]- _this.start_timestamp)* px_per_ms;
  304. // var cell_width = cell[0]>cell[1]?(cell[0]-cell[1])* px_per_ms:(cell[1]-cell[0])* px_per_ms;
  305. // _this.ctx.fillStyle = "#ff0000";
  306. // _this.ctx.fillRect(beginX,this.tiaoTop,cell_width,this.tiaoHeight);
  307. // },
  308. // 返回时间轴上刻度时间
  309. graduation_title: function (datetime) {
  310. return (
  311. (datetime.getHours() < 10
  312. ? "0" + datetime.getHours()
  313. : datetime.getHours()) +
  314. ":" +
  315. ("0" + datetime.getMinutes().toString()).substr(-2)
  316. );
  317. },
  318. // 开始时间 px
  319. ms_to_next_step: function (timestamp, step) {
  320. var remainder = timestamp % step;
  321. return remainder ? step - remainder : 0;
  322. },
  323. // 清除画布
  324. clearCanvas: function () {
  325. this.ctx && this.ctx.clearRect(0, 0, this.canvasW, this.canvasH);
  326. },
  327. // 获取鼠标x位置
  328. get_cursor_x_position: function (e) {
  329. var posx = 0;
  330. if (!e) {
  331. e = window.event;
  332. }
  333. if (e.pageX || e.pageY) {
  334. posx = e.pageX;
  335. } else if (e.clientX || e.clientY) {
  336. posx =
  337. e.clientX +
  338. document.body.scrollLeft +
  339. document.documentElement.scrollLeft;
  340. }
  341. return posx - 455; //调整鼠标误差值
  342. },
  343. // 返回时间日期格式
  344. changeTime: function (time) {
  345. var newTime = new Date(time);
  346. var year = newTime.getFullYear();
  347. var month = newTime.getMonth() + 1;
  348. if (month < 10) {
  349. var month = "0" + month;
  350. }
  351. var date = newTime.getDate();
  352. if (date < 10) {
  353. var date = "0" + date;
  354. }
  355. var hour = newTime.getHours();
  356. if (hour < 10) {
  357. var hour = "0" + hour;
  358. }
  359. var minute = newTime.getMinutes();
  360. if (minute < 10) {
  361. var minute = "0" + minute;
  362. }
  363. var second = newTime.getSeconds();
  364. if (second < 10) {
  365. var second = "0" + second;
  366. }
  367. // return year + "-" + month + "-" + date + " " + hour + ":" + minute + ":" + second;
  368. return hour + ":" + minute + ":" + second;
  369. },
  370. // 鼠标按下事件
  371. mousedownFunc: function (e) {
  372. if (e.button == 2) {
  373. } else {
  374. this.g_isMousedown = true;
  375. }
  376. this.g_mousedownCursor = this.get_cursor_x_position(e); //记住mousedown的位置
  377. },
  378. // 鼠标移动事件
  379. mousemoveFunc: function (e) {
  380. let _this = this;
  381. let pos_x = _this.get_cursor_x_position(e);
  382. let px_per_ms = _this.canvasW / (_this.minute_per_ruler * 60 * 1000); // px/ms
  383. _this.clearCanvas();
  384. if (_this.g_isMousedown) {
  385. var diff_x = pos_x - _this.g_mousedownCursor;
  386. _this.start_timestamp =
  387. _this.start_timestamp - Math.round(diff_x / px_per_ms);
  388. _this.initCanvas();
  389. _this.g_isMousemove = true;
  390. _this.g_mousedownCursor = pos_x;
  391. this.nowTime = this.changeTime(_this.start_timestamp + this.half_time);
  392. } else {
  393. var time = _this.start_timestamp + pos_x / px_per_ms;
  394. _this.initCanvas();
  395. _this.drawLine(pos_x, 30, pos_x, 60, "rgb(49,142,212)", 1);
  396. _this.ctx.fillStyle = "rgb(194, 202, 215)";
  397. _this.ctx.fillText(_this.changeTime(time), pos_x - 65, 35);
  398. }
  399. },
  400. //鼠标释放弹起事件
  401. mouseupFunc: function (e) {
  402. if (e.button == 2) {
  403. if (this.downTime.length == 2) {
  404. this.downLeft = this.g_mousedownCursor;
  405. this.showDown = true;
  406. }
  407. } else {
  408. this.g_isMousedown = false;
  409. this.g_isMousemove = false;
  410. let time = this.start_timestamp + this.half_time;
  411. this.$emit("setDragPlayTime", time);
  412. }
  413. },
  414. //鼠标移除隐藏时间事件
  415. mouseoutFunc: function (e) {
  416. this.clearCanvas();
  417. this.initCanvas();
  418. },
  419. contextMenuClick: function (e) {
  420. e.preventDefault();
  421. return;
  422. var _this = this;
  423. var pos_x = _this.get_cursor_x_position(e);
  424. var px_per_ms = _this.canvasW / (_this.minute_per_ruler * 60 * 1000); // px/ms
  425. var time = _this.start_timestamp + pos_x / px_per_ms;
  426. var flag = false;
  427. var index = 0;
  428. for (let i = 0; i < _this.timeCell.length; i++) {
  429. if (
  430. time >= _this.timeCell[i].beginTime &&
  431. time <= _this.timeCell[i].endTime
  432. ) {
  433. flag = true;
  434. index = i;
  435. }
  436. }
  437. if (flag) {
  438. if (_this.downTime.length < 2) {
  439. _this.downTime.push(time);
  440. if (_this.downTime.length == 2) {
  441. _this.clearCanvas();
  442. _this.initCanvas();
  443. }
  444. } else if (_this.downTime.length == 2) {
  445. // console.log(_this.downTime);
  446. }
  447. }
  448. },
  449. downCLickBack() {
  450. if (this.downTime.length == 2) {
  451. this.downTime.sort(function (a, b) {
  452. return a - b;
  453. });
  454. var userData = this.$store.getters.replayDownVideo.length + 1;
  455. var startTime1 =
  456. this.downTime[0] < this.downTime[1]
  457. ? this.downTime[0]
  458. : this.downTime[1];
  459. var startTime = this.changeTime(startTime1);
  460. var endTime1 =
  461. this.downTime[0] >= this.downTime[1]
  462. ? this.downTime[0]
  463. : this.downTime[1];
  464. var endTime = this.changeTime(endTime1);
  465. var fileName = new Date().getTime() + ".mp4";
  466. var server = this.$store.getters.serverConfig;
  467. var alltime = Math.ceil((endTime1 - startTime1) / 1000);
  468. var obj = {
  469. fileName: fileName,
  470. startTime: startTime,
  471. endTime: endTime,
  472. allTime: alltime,
  473. progress: "0%",
  474. userDate: userData,
  475. downType: 0,
  476. };
  477. var s_index = 0,
  478. e_index = 0;
  479. for (var i = 0; i < this.timeCell.length; i++) {
  480. if (
  481. this.downTime[0] >= this.timeCell[i].beginTime &&
  482. this.downTime[0] <= this.timeCell[i].endTime
  483. ) {
  484. s_index = i;
  485. }
  486. if (
  487. this.downTime[1] >= this.timeCell[i].beginTime &&
  488. this.downTime[1] <= this.timeCell[i].endTime
  489. ) {
  490. e_index = i;
  491. }
  492. }
  493. var fileArr = new Array();
  494. for (var j = s_index; j <= e_index; j++) {
  495. fileArr.push(this.videoData.videoList[j]);
  496. }
  497. var filesJson = JSON.stringify(fileArr);
  498. this.downTime.splice(0, this.downTime.length);
  499. var ss = document
  500. .getElementById("videoOcx")
  501. .StartDownloadVodFile(
  502. server.ServerIP,
  503. server.ServerPort,
  504. filesJson,
  505. fileName,
  506. startTime,
  507. endTime,
  508. userData
  509. );
  510. this.$store.commit("addDownLoadVideo", obj);
  511. this.showDown = false;
  512. }
  513. },
  514. cancelDownClick() {
  515. this.downTime.splice(0, this.downTime.length);
  516. this.showDown = false;
  517. },
  518. },
  519. };
  520. </script>
  521. <style lang="scss" scoped>
  522. .progress-box-bar {
  523. position: relative;
  524. width: 100%;
  525. height: 100%;
  526. }
  527. .progress-box-bar canvas {
  528. width: 100%;
  529. cursor: pointer;
  530. }
  531. .progress-box-bar .downVideo {
  532. position: absolute;
  533. z-index: 10;
  534. top: 0;
  535. left: 300px;
  536. }
  537. .progress-box-bar .downVideo ul {
  538. background-color: #fff;
  539. }
  540. .progress-box-bar .downVideo ul li {
  541. line-height: 20px;
  542. font-size: 12px;
  543. cursor: pointer;
  544. }
  545. .progress-box-bar .downVideo ul li:hover {
  546. background-color: #318ed4;
  547. }
  548. .bar-container {
  549. width: 100%;
  550. height: 20px;
  551. background-color: #eaeaea;
  552. position: relative;
  553. display: flex;
  554. .bar1 {
  555. height: 100%;
  556. // position: absolute;
  557. // top: 0;
  558. // left: 0;
  559. // background: green;
  560. z-index: 2;
  561. }
  562. }
  563. .bar-container1 {
  564. width: 100%;
  565. height: 20px;
  566. background-color: #eaeaea;
  567. position: relative;
  568. top: 20px;
  569. display: flex;
  570. .bar2 {
  571. height: 20px;
  572. // position: absolute;
  573. // top: 0;
  574. // left: 0;
  575. // background: rgb(0, 178, 255);
  576. z-index: 1;
  577. }
  578. }
  579. </style>