Commit a0b118cc authored by 李纪文's avatar 李纪文

feat: 增加vms视频回放

parent d84fc0f8
...@@ -15,8 +15,14 @@ group: ...@@ -15,8 +15,14 @@ group:
**需安装的依赖项: ezuikit-js axios (1.npm i ezuikit-js 2.npm i axios)** **需安装的依赖项: ezuikit-js axios (1.npm i ezuikit-js 2.npm i axios)**
### 视频
<code src="./demos/dmeo1.tsx"> <code src="./demos/dmeo1.tsx">
### 视频回放
<code src="./demos/dmeo2.tsx">
## 参数说明 ## 参数说明
| JessibucaObj 常用参数(对萤石 EZOPEN 协议不生效) | 说明 | 类型 | 默认值 | | JessibucaObj 常用参数(对萤石 EZOPEN 协议不生效) | 说明 | 类型 | 默认值 |
......
import { request } from '@wisdom-utils/utils';
const REQUEST_METHOD_GET = 'get';
const REQUEST_METHOD_POST = 'post';
// eslint-disable-next-line no-undef
const baseURI = typeof DUMI_TYPE !== 'undefined' && DUMI_TYPE === 'dumi' ? '/api' : '';
const CommonPath = window?.globalConfig?.hasGateWay ? '/PandaCore/GateWay' : '';
// 录像回放
export function videoPlayback(params) {
return request({
url: `${baseURI}${CommonPath}/pandavms/camera/playback`,
method: REQUEST_METHOD_GET,
params,
});
}
// 根据时间轴时间选定 查看历史视频
export function newPlayback(params) {
return request({
url: `${baseURI}${CommonPath}/pandavms/camera/newpalyback`,
method: REQUEST_METHOD_GET,
params,
});
}
import React, { useRef, useEffect, useState } from 'react';
import Video from '../recVideo';
const Demo2 = (props) => {
const jessibuca = useRef(null);
let JessibucaObj = {
operateBtns: {
// fullscreen: false,
screenshot: false,
},
loadingText: '演示视频加载中',
decoder: '/JessibucaVideo/decoder.js',
};
// 若是在子应用中,则需加上子应用名称,且兼容基座
// 如下:
// let _url = '/civ_energy/JessibucaVideo/decoder.js'
// let JessibucaObj = {
// decoder: window.__POWERED_BY_QIANKUN__ ? '/civbase' + _url : _url
// }
// let VideoInfo = {
// key: '123638446', //'1CEB209F-8BC7-44B5-9F6B-3D8FCB855E76',
// dataRate: `/2`,
// fullUrl: 'ws://172.16.19.19:8080/jessica/1CEB209F-8BC7-44B5-9F6B-3D8FCB855E76',
// useFullUrl: true,
// cameraName: `摄像头s8`,
// appKey: '6c44c8e92d1c4d75a9818756025df550',
// appSecret: '78b7dc88f9f4bf19c2b1aabfdd995244',
// protocol: '萤石EZOPEN',
//};
let VideoParam = {
id: '9745259F-76B5-4ECB-BDD7-8B1B2C5C84CD',
name: '3L00AE9PAJ00034',
protocol: '乐橙云HTTP-FLV',
username: 'lc0f4b952c86c34c4b',
password: 'dfdcae9267bf4964ae09998e16f016',
pandavmsHost: 'ws://192.168.12.154:8080/',
address: '3L00AE9PAJ00034',
};
VideoParam = {
channel: '2',
// dataRate: 'Sub', // Main 主码流 Sub 子码流
name: 'RTSP银河湾五期_通道_2',
id: '46D6E990-8049-4A61-B592-8503BDFAA07A',
username: 'admin',
password: 'zls_1234@abcd',
address: '172.16.19.2',
protocol: 'RTSP',
pandavmsHost: 'ws://192.168.8.30:7000/', // pandavms后端主机地址 eg: ws://172.16.19.19:8080/
hoursRuler: 48,
beginTime: '2024-01-01 00:00:00', // 回放开始时间
endTime: '2024-01-02 23:59:59', // 回放结束时间
};
const [VideoInfo, setVideoInfo] = useState(VideoParam);
useEffect(() => {
// 事件 ,返回视频信息
jessibuca &&
jessibuca.current &&
jessibuca.current.on('videoInfo', function (data) {
console.log('width:', data.width, 'height:', data.width);
});
// 事件 ,播放视频之后的回调
jessibuca &&
jessibuca.current &&
jessibuca.current.on('play', function (data) {
console.log('play:', jessibuca);
});
// 事件 ,暂停视频之后的回调
jessibuca &&
jessibuca.current &&
jessibuca.current.on('pause', function (data) {
console.log('pause:', data);
});
// ******
// 其他事件需查看在线文档
// ******
return () => {
console.log('销毁。。。。。。。。。。。。。。。。。。。。。。。。。');
stopVideo();
};
}, []);
const stopVideo = () => {
jessibuca && jessibuca.current && jessibuca.current.pause && jessibuca.current.pause();
};
const startVideo = () => {
if (jessibuca && jessibuca.current) {
jessibuca.current.play();
// console.log(jessibuca.current.isPlaying());
}
};
const testVideo = () => {
// console.log(jessibuca.current.isPlaying());
setVideoInfo({
channel: '2',
// dataRate: 'Sub', // Main 主码流 Sub 子码流
name: 'RTSP银河湾五期_通道_2',
id: '46D6E990-8049-4A61-B592-8503BDFAA07A',
username: 'admin',
password: 'zls_1234@abcd',
address: '172.16.19.2',
protocol: 'RTSP',
pandavmsHost: 'ws://192.168.8.30:7000/', // pandavms后端主机地址 eg: ws://172.16.19.19:8080/
hoursRuler: 24,
beginTime: '2024-01-01 00:00:00',
endTime: '2024-01-01 23:59:59',
});
};
const destroyVideo = () => {
if (VideoParam.protocol != '萤石EZOPEN')
jessibuca && jessibuca.current && jessibuca.current.destroy();
};
return (
<div>
<button onClick={stopVideo}>暂停</button>
<button onClick={startVideo}>开始</button>
<button onClick={testVideo}>测试</button>
<button onClick={destroyVideo}>销毁</button>
<div style={{ height: '700px' }}>
<Video {...{ JessibucaObj: JessibucaObj, VideoInfo: VideoInfo }} ref={jessibuca} />
</div>
</div>
);
};
export default Demo2;
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
import EZUIKit, { log } from './ezuikit-js/ezuikit'; import EZUIKit, { log } from 'ezuikit-js';
import axios from 'axios'; import axios from 'axios';
import React, { import React, {
......
const parseNumber = (prop) => parseFloat(prop) || 0;
const getSize = (el) => {
if (el === window || el === document.body) {
return [window.innerWidth, window.innerHeight];
}
let temporary = false;
if (!el.parentNode && document.body) {
temporary = true;
document.body.appendChild(el);
}
const rect = el.getBoundingClientRect();
const styles = getComputedStyle(el);
const height =
(rect.height | 0) +
parseNumber(styles.getPropertyValue('margin-top')) +
parseNumber(styles.getPropertyValue('margin-bottom'));
const width =
(rect.width | 0) +
parseNumber(styles.getPropertyValue('margin-left')) +
parseNumber(styles.getPropertyValue('margin-right'));
if (temporary && document.body) {
document.body.removeChild(el);
}
return [width, height];
};
export default getSize;
import React from 'react';
import moment from 'moment';
import RvResponsiveCanvas from './responsiveCanvas';
export default class TimeSlider extends React.Component {
constructor(props) {
super(props);
//const that = this;
this.state = {
minTimestamp: this.props.minTimestamp,
maxTimestamp: this.props.maxTimestamp,
playTimestamp: this.props.playTimestamp,
timecell: this.props.timecell,
hoursPerRuler: this.props.hoursPerRuler,
};
this.redrawFlag = false;
}
componentDidMount() {
setTimeout(() => {
this.initCanval(false);
}, 100);
}
componentDidUpdate() {
this.initCanval(true);
}
static getDerivedStateFromProps(nextProps, prevState) {
const { maxTimestamp, minTimestamp, playTimestamp, timecell } = nextProps;
// 当传入的type发生变化的时候,更新state
if (
maxTimestamp !== prevState.maxTimestamp ||
minTimestamp !== prevState.minTimestamp ||
playTimestamp !== prevState.playTimestamp ||
timecell !== prevState.timecell
) {
return {
minTimestamp,
maxTimestamp,
playTimestamp,
timecell,
};
}
// 否则,对于state不进行任何操作
return null;
}
initCanval = () => {
if (!this.redrawFlag) {
this.ctx = this.canvas.getContext('2d');
this.minutesPerStep = [1, 2, 5, 10, 15, 20, 30, 60, 120, 180, 240, 360, 720, 1440]; // min/格
this.graduationStep = 20; //刻度间最小宽度,单位px
this.hoursPerRuler = this.state.hoursPerRuler || 24; //时间轴显示24小时
this.startTimestamp = moment().startOf('day').valueOf();
this.distanceBetweenGtitle = 80;
this.zoom = 24;
this.gIsMouseout = false; //拖动mousedown标记
this.gIsMousewheel = false; //拖动mousedown标记
this.gIsMousedown = false; //拖动mousedown标记
this.gIsMousemove = false; //拖动mousemove标记
this.gMousedownCursor = null; //拖动mousedown的位置
this.gIsDragStartTime = null;
this.hoverTooltip = null;
}
this.canvansW = this.canvas.width;
this.canVansH = this.canvas.height;
if (this.gIsMousewheel || this.gIsMousedown || this.gIsMousemove) return;
this.timecell = this.state.timecell;
this.maxTimestamp = this.state.maxTimestamp;
this.minTimestamp = this.state.minTimestamp;
this.playTimestamp = this.state.playTimestamp;
this.onBeforeClickRulerCallback = null;
if (this.redrawFlag) {
this.clearCanvas();
}
this.init(
this.startTimestamp,
this.playTimestamp,
this.hoverTooltip,
this.timecell,
this.redrawFlag,
);
this.redrawFlag = true;
};
/**
* 初始化
*
* @param {any} startTimestamp 最左侧时间
* @param {any} timecell 录像段数组
* @param {any} redrawFlag 是否重绘标记
*/
init = (startTimestamp, playTimestamp, hoverTooltip, timecell, redrawFlag) => {
if (startTimestamp < this.minTimestamp) {
// eslint-disable-next-line no-param-reassign
startTimestamp = this.minTimestamp;
}
const startEndX = this.hoursPerRuler * 60 * 60 * 1000;
const end_timestramp = startTimestamp + startEndX;
if (end_timestramp > this.maxTimestamp) {
// eslint-disable-next-line no-param-reassign
startTimestamp = this.maxTimestamp - startEndX;
}
this.timecell = timecell;
this.startTimestamp = startTimestamp;
this.playTimestamp = playTimestamp;
this.drawCellBg();
this.addGraduations(startTimestamp);
this.addCells(timecell);
this.drawLine(0, this.canVansH, this.canvansW, this.canVansH, 'rgb(255, 255, 255)', 1); //底线
var px_per_ms = this.canvansW / startEndX;
var pos_x = (playTimestamp - this.startTimestamp) * px_per_ms;
this.drawLine(pos_x, 0, pos_x, 33, '#E8B700', 2); //中间播放点时间线
if (!redrawFlag) {
//只有第一次进入才需要添加事件
this.addEvents();
}
var time = startTimestamp + (this.hoursPerRuler * 3600 * 1000) / 2;
this.ctx.fillStyle = '#E8B700';
var _pos_x = pos_x - 60;
if (this.canvansW < pos_x + 60) {
_pos_x = this.canvansW - 110;
} else if (pos_x < 60) {
_pos_x = 0;
}
this.ctx.font = '15px Arial';
this.ctx.fillText(this.formatTime(playTimestamp), _pos_x, 53);
if (this.hoverTooltip) {
this.drawLine(
this.hoverTooltip.linePos,
0,
this.hoverTooltip.linePos,
50,
'rgba(232,183,0,0.7)',
1,
);
this.ctx.fillStyle = 'rgba(232,183,0,0.7)';
var _pos_x = this.hoverTooltip.linePos - 50;
if (this.canvansW < this.hoverTooltip.linePos + 50) {
_pos_x = this.canvansW - 100;
} else if (this.hoverTooltip.linePos < 50) {
_pos_x = 0;
}
this.ctx.font = '14px Arial';
this.ctx.fillText(this.formatTime(this.hoverTooltip.time), _pos_x, 70);
}
};
/**
* 绘制添加刻度
*
* @param {any} startTimestamp 最左侧时间
*/
addGraduations = (startTimestamp) => {
var _this = this;
var px_per_min = _this.canvansW / (_this.hoursPerRuler * 60); // px/min
var px_per_ms = _this.canvansW / (_this.hoursPerRuler * 60 * 60 * 1000); // px/ms
var px_per_step = _this.graduationStep; // px/格 默认最小值20px
var min_per_step = px_per_step / px_per_min; // min/格
for (var i = 0; i < _this.minutesPerStep.length; i++) {
if (min_per_step <= _this.minutesPerStep[i]) {
//让每格时间在minutes_per_step规定的范围内
min_per_step = _this.minutesPerStep[i];
px_per_step = px_per_min * min_per_step;
break;
}
}
var medium_step = 30;
for (var i = 0; i < _this.minutesPerStep.length; i++) {
if (_this.distanceBetweenGtitle / px_per_min <= _this.minutesPerStep[i]) {
medium_step = _this.minutesPerStep[i];
break;
}
}
var num_steps = _this.canvansW / px_per_step; //总格数
var graduation_left;
var graduation_time;
var caret_class;
var lineH;
var ms_offset = _this.msToNextStep(startTimestamp, min_per_step * 60 * 1000); //开始的偏移时间 ms
var px_offset = ms_offset * px_per_ms; //开始的偏移距离 px
var ms_per_step = px_per_step / px_per_ms; // ms/step
for (var i = 0; i < num_steps; i++) {
graduation_left = px_offset + i * px_per_step; // 距离=开始的偏移距离+格数*px/格
graduation_time = startTimestamp + ms_offset + i * ms_per_step; //时间=左侧开始时间+偏移时间+格数*ms/格
var date = new Date(graduation_time);
if (date.getUTCHours() == 0 && date.getUTCMinutes() == 0) {
caret_class = 'big';
lineH = 25;
var big_date = _this.graduationTitle(date);
_this.ctx.font = '12px Arial';
_this.ctx.fillText(big_date, graduation_left - 20, 30);
_this.ctx.fillStyle = 'rgba(255,255,255,0.55)'; //不改
} else if ((graduation_time / (60 * 1000)) % medium_step == 0) {
caret_class = 'middle';
lineH = 15;
var middle_date = _this.graduationTitle(date);
_this.ctx.font = '12px Arial';
_this.ctx.fillText(middle_date, graduation_left - 20, 30);
_this.ctx.fillStyle = 'rgba(255,255,255,0.7)';
} else {
lineH = 10;
}
// 不能播放视频的时间刻度--灰色 ok
_this.drawLine(graduation_left, 0, graduation_left, lineH, 'rgba(151,158,167,1)', 1);
}
};
/**
* 绘制线
*
* @param {any} beginX
* @param {any} beginY
* @param {any} endX
* @param {any} endY
* @param {any} color
* @param {any} width
*/
drawLine = (beginX, beginY, endX, endY, color, width) => {
this.ctx.beginPath();
this.ctx.moveTo(beginX, beginY);
this.ctx.lineTo(endX, endY);
this.ctx.strokeStyle = color;
this.ctx.lineWidth = width;
this.ctx.stroke();
};
/**
* 添加录像段
*
* @param {any} cells 录像数组
*/
addCells = (cells) => {
var _this = this;
cells.forEach((cell) => {
_this.drawCell(cell);
});
};
/**
* 绘制录像块
*
* @param {any} cell Cell包括beginTime ms;endTime ms;style
*/
drawCell = (cell) => {
var _this = this;
var px_per_ms = _this.canvansW / (_this.hoursPerRuler * 60 * 60 * 1000); // px/ms
var beginX = (cell.beginTime - _this.startTimestamp) * px_per_ms;
var cell_width = (cell.endTime - cell.beginTime) * px_per_ms;
_this.ctx.fillStyle = cell.style.background;
_this.ctx.fillRect(beginX, 0, cell_width, 15);
};
/** 绘制录像块背景 */
drawCellBg = () => {
this.ctx.fillStyle = 'rgb(255,255,255)';
this.ctx.fillRect(0, 0, this.canvansW, 15);
};
/** 时间轴事件 */
addEvents = () => {
var _this = this;
if (_this.canvas.addEventListener) {
_this.canvas.addEventListener('mousewheel', _this.mousewheelFunc.bind(_this));
_this.canvas.addEventListener('mousedown', _this.mousedownFunc.bind(_this));
_this.canvas.addEventListener('mousemove', _this.mousemoveFunc.bind(_this));
_this.canvas.addEventListener('mouseup', _this.mouseupFunc.bind(_this));
_this.canvas.addEventListener('mouseout', _this.mouseoutFunc.bind(_this));
}
};
/** 拖动/点击 mousedown事件 */
mousedownFunc = (e) => {
this.gIsMousedown = true;
this.gMousedownCursor = this.getCursorXPosition(e); //记住mousedown的位置
this.gIsDragStartTime = moment();
};
/** 拖动/鼠标hover显示 mousemove事件 */
mousemoveFunc = (e) => {
var _this = this;
_this.hoverTooltip = null;
var pos_x = _this.getCursorXPosition(e);
var px_per_ms = _this.canvansW / (_this.hoursPerRuler * 60 * 60 * 1000); // px/ms
_this.clearCanvas();
if (_this.gIsMousedown) {
var diff_x = pos_x - _this.gMousedownCursor;
_this.startTimestamp = _this.startTimestamp - Math.round(diff_x / px_per_ms);
_this.init(
_this.startTimestamp,
_this.playTimestamp,
_this.hoverTooltip,
_this.timecell,
true,
);
_this.gIsMousemove = true;
_this.gMousedownCursor = pos_x;
} else {
var time = _this.startTimestamp + pos_x / px_per_ms;
_this.hoverTooltip = {
linePos: pos_x,
time: time,
};
_this.init(
_this.startTimestamp,
_this.playTimestamp,
_this.hoverTooltip,
_this.timecell,
true,
);
}
};
/** 拖动/点击 mouseup事件 */
mouseupFunc = (e) => {
var _this = this;
//避免点击被误认为拖拽
const dragTimeLen = moment().diff(this.gIsDragStartTime);
if (dragTimeLen < 100) {
_this.gIsMousemove = false;
_this.gIsMousedown = false;
}
if (_this.gIsMousemove) {
//拖动 事件
_this.gIsMousemove = false;
_this.gIsMousedown = false;
//_this.playTimestamp = _this.startTimestamp + (_this.hoursPerRuler * 3600 * 1000) / 2;
} else {
// click 事件
_this.gIsMousedown = false;
var posx = _this.getCursorXPosition(e); //鼠标距离 px
var ms_per_px = (_this.zoom * 3600 * 1000) / _this.canvansW; // ms/px
_this.playTimestamp = _this.startTimestamp + posx * ms_per_px;
_this.setPlaytimestamp(_this.playTimestamp);
_this.playTimestampChange();
}
};
/**
* 鼠标移出隐藏时间 mouseout事件
*
* @param {any} e
*/
mouseoutFunc = () => {
var _this = this;
_this.gIsMouseout = true;
_this.clearCanvas();
_this.hoverTooltip = null;
_this.init(_this.startTimestamp, _this.playTimestamp, _this.hoverTooltip, _this.timecell, true);
_this.gIsMouseout = false;
};
/** 滚轮放大缩小,以时间轴中心为准 mousewheel事件 */
mousewheelFunc = (event) => {
var _this = this;
_this.gIsMousewheel = true;
if (event && event.preventDefault) {
event.preventDefault();
} else {
window.event.returnValue = false;
return false;
}
var e = window.event || event;
var delta = Math.max(-1, Math.min(1, e.wheelDelta || -e.detail));
var pos_x = _this.getCursorXPosition(e);
var px_per_ms = _this.canvansW / (_this.hoursPerRuler * 60 * 60 * 1000);
var eventPositionTime = _this.startTimestamp + pos_x / px_per_ms; //m
// s 记住当前中间的时间
//var middle_time = _this.startTimestamp + (_this.hoursPerRuler * 3600 * 1000) / 2; //ms 记住当前中间的时间
if (delta < 0) {
_this.zoom = _this.zoom + 4;
if (_this.zoom >= 24) {
_this.zoom = 24; //放大最大24小时
}
_this.hoursPerRuler = _this.zoom;
} else if (delta > 0) {
// 放大
_this.zoom = _this.zoom - 4;
if (_this.zoom <= 1) {
_this.zoom = 1; //缩小最小1小时
}
_this.hoursPerRuler = _this.zoom;
}
_this.clearCanvas();
var px_per_ms2 = _this.canvansW / (_this.hoursPerRuler * 60 * 60 * 1000);
_this.startTimestamp = eventPositionTime - pos_x / px_per_ms2;
//_this.startTimestamp = middle_time - (_this.hoursPerRuler * 3600 * 1000) / 2; //startTimestamp = 当前中间的时间 - zoom/2
_this.init(_this.startTimestamp, _this.playTimestamp, _this.hoverTooltip, _this.timecell, true);
_this.gIsMousewheel = false;
};
/**
* 获取鼠标posx
*
* @param {any} e
*/
getCursorXPosition = (e) => {
var posx = 0;
if (!e) {
// eslint-disable-next-line no-param-reassign
e = window.event;
}
/*if (e.pageX || e.pageY) {
posx = e.pageX;
}else if (e.clientX || e.clientY) {
posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
}*/
posx = e.offsetX;
return posx;
};
/**
* 返回时间轴上刻度的时间
*
* @param {any} datetime New Date 格式
*/
graduationTitle = (datetime) => {
if (datetime.getHours() == 0 && datetime.getMinutes() == 0 && datetime.getMilliseconds() == 0) {
/* return ('0' + datetime.getDate().toString()).substr(-2) + '.' +
('0' + (datetime.getMonth() + 1).toString()).substr(-2) + '.' +
datetime.getFullYear();*/
return (
datetime.getFullYear() +
'-' +
('0' + (datetime.getMonth() + 1).toString()).substr(-2) +
'-' +
('0' + datetime.getDate().toString()).substr(-2)
);
}
return datetime.getHours() + ':' + ('0' + datetime.getMinutes().toString()).substr(-2);
};
/**
* 返回 2018-01-01 10:00:00 格式时间
*
* @param {any} time
*/
formatTime = (time) => {
var newTime = new Date(time);
var year = newTime.getFullYear();
var month = newTime.getMonth() + 1;
if (month < 10) {
var month = '0' + month;
}
var date = newTime.getDate();
if (date < 10) {
var date = '0' + date;
}
var hour = newTime.getHours();
if (hour < 10) {
var hour = '0' + hour;
}
var minute = newTime.getMinutes();
if (minute < 10) {
var minute = '0' + minute;
}
var second = newTime.getSeconds();
if (second < 10) {
var second = '0' + second;
}
return year + '-' + month + '-' + date + ' ' + hour + ':' + minute + ':' + second;
};
/**
* 左侧开始时间的偏移,返回单位ms
*
* @param {any} timestamp
* @param {any} step
*/
msToNextStep = (timestamp, step) => {
var remainder = timestamp % step;
return remainder ? step - remainder : 0;
};
/**
* 设置时间,让这个时间点跳到中间红线处
*
* @param {any} playTimestamp 单位ms
*/
setPlaytimestamp = (playTimestamp) => {
this.clearCanvas();
// this.startTimestamp = playTimestamp - (this.hoursPerRuler * 60 * 60 * 1000) / 2;
this.init(this.startTimestamp, playTimestamp, this.hoverTooltip, this.timecell, true);
};
/** 清除canvas 每次重新绘制需要先清除 */
clearCanvas = () => {
this.ctx.clearRect(0, 0, 1500, 150);
};
/** 返回点击或者拖动的时间点 */
playTimestampChange = () => {
var _this = this;
if (_this.playTimestamp != null) {
if (this.props.playTimestampChange) {
const selectedCell = this.timecell.find(
(cell) => cell.beginTime <= _this.playTimestamp && cell.endTime >= _this.playTimestamp,
);
this.props.playTimestampChange(
_this.playTimestamp,
selectedCell,
selectedCell && _this.playTimestamp - selectedCell.beginTime,
);
}
}
};
draw() {
// Draw whatever
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
}
render() {
return (
<div style={{ height: 72 }}>
<RvResponsiveCanvas
canvasRef={(el) => (this.canvas = el)}
scale={1}
style={{
cursor: 'pointer',
border: '1px solid #cccccc',
backgroundColor: '#000000',
}}
onDragStart={() => {
return false;
}}
onResize={() => this.initCanval()}
/>
</div>
);
}
}
import React, { Component } from 'react';
import getSize from './get-size';
export default class RvResponsiveCanvas extends Component {
static defaultProps = {
scale: typeof window !== 'undefined' ? window.devicePixelRatio : 1,
};
state = {
width: 0,
height: 0,
};
$canvas;
componentDidMount() {
window.addEventListener('resize', this.handleResize, false);
this.setSize();
}
componentWillUnmount() {
window.removeEventListener('resize', this.handleResize, false);
}
handleResize = () => {
this.setSize();
this.props.onResize(this.state.width, this.state.height);
};
setSize = () => {
const parent = this.$canvas.parentElement;
if (!parent) {
return;
}
const [width, height] = getSize(parent);
this.setState({ width, height });
};
setRef = (el) => {
if (!el) {
return;
}
const { canvasRef } = this.props;
this.$canvas = el;
if (typeof canvasRef === 'function') {
canvasRef(el);
}
};
render() {
const { scale, onResize, canvasRef, style, ...props } = this.props;
const { width, height } = this.state;
return (
<canvas
{...props}
ref={this.setRef}
width={width * scale}
height={height * scale}
style={{ ...style, width, height, minWidth: '952px' }}
/>
);
}
}
import { Button, message, Modal } from 'antd';
import { useEffect, useState, useContext, useRef, useImperativeHandle } from 'react';
import TestVideo from '../index';
import classNames from 'classnames';
import { DatePicker, TimePicker, Calendar, theme, ConfigProvider } from 'antd';
import Empty from '@wisdom-components/empty';
import { videoPlayback, newPlayback } from '../apis';
import './index.less';
import moment from 'moment';
import TimeSlider from './TimeSlider';
const RecVideo = (props, ref) => {
const { getPrefixCls } = useContext(ConfigProvider.ConfigContext);
const prefixCls = getPrefixCls('rec-video-view');
const { VideoInfo, JessibucaObj } = props;
const jessibuca = useRef(null);
const [hoursRuler, setHoursRuler] = useState(VideoInfo.hoursRuler || 24);
const [showId, setShowId] = useState(null); //视频ID
const [peridos, setPeridos] = useState([]); //可播放视频时间段
const [playTimestamp, setPlayTimestamp] = useState(null); //当前正在播放时间段
const [minTimestamp, setMinTimestamp] = useState(null); //时间轴最小时间
const [maxTimestamp, setMaxTimestamp] = useState(null); //时间轴最大时间
useEffect(() => {
changeReplayCfg();
}, [props.VideoInfo]);
useImperativeHandle(ref, () => {
// changeVal 就是暴露给父组件的方法, newVal是父组件传递的参数
return jessibuca.current; // _video&& _video.current&&_video.current.jessibuca
});
useEffect(() => {
const { endTime = moment().format('YYYY-MM-DD 23:59:59') } = VideoInfo;
const edTimes = moment(endTime).format('YYYY-MM-DD HH:mm:ss');
if (playTimestamp) {
let momentObj = moment(playTimestamp);
let formattedTime = momentObj.format('YYYY-MM-DD HH:mm:ss');
const edDates = moment(edTimes).format('YYYY-MM-DD HH:mm:ss');
const params = {
id: VideoInfo.id,
startTime: formattedTime,
endTime: edDates,
'site-code': window?.globalConfig?.userInfo?.LocalSite || '',
};
rePaly(params);
}
}, [playTimestamp]);
const changeReplayCfg = () => {
const {
beginTime = moment().format('YYYY-MM-DD 00:00:00'),
endTime = moment().format('YYYY-MM-DD 23:59:59'),
} = VideoInfo;
const hoursPerRuler = calculateHours(beginTime, endTime) || 24;
const stTimes = moment(beginTime).format('YYYY-MM-DD HH:mm:ss');
const edTimes = moment(endTime).format('YYYY-MM-DD HH:mm:ss');
const param = {
id: VideoInfo.id,
startTime: stTimes,
endTime: edTimes,
'site-code': window?.globalConfig?.userInfo?.LocalSite || '',
};
setHoursRuler(hoursPerRuler);
setMinTimestamp(moment(stTimes).valueOf());
setMaxTimestamp(moment(edTimes).valueOf());
getVideoPlBack(param);
};
const getVideoPlBack = (param) => {
videoPlayback(param).then((res) => {
if (res.code === 200) {
setPeridos(
res.data.peridos.map((times, i) => {
let beginTime = moment(times.StartTime.replaceAll('T', ' ').replaceAll('Z', ' '));
let endTime = moment(times.EndTime.replaceAll('T', ' ').replaceAll('Z', ' '));
return {
...times,
idx: i,
beginTime: beginTime.valueOf(),
endTime: endTime.valueOf(),
style: { background: '#637DEC' },
};
}),
);
setShowId(res.data.url);
} else {
message.warn(res.msg);
setShowId(null);
}
});
};
const rePaly = (params) => {
newPlayback(params).then((res) => {
if (res.code === 200) {
setShowId(res.data);
} else {
message.warn(res.msg);
}
});
};
const calculateHours = (time1, time2) => {
const date1 = new Date(time1);
const date2 = new Date(time2);
const diff = Math.abs(date1.getTime() - date2.getTime());
return Math.ceil(diff / (1000 * 60 * 60));
};
return (
<div className={classNames(prefixCls)}>
{showId ? (
<>
<div className={classNames(`${prefixCls}-video`)}>
{showId && (
<TestVideo
VideoInfo={{ ...VideoInfo, id: showId }}
JessibucaObj={{ ...JessibucaObj }}
key={showId}
ref={jessibuca}
/>
)}
</div>
{/* 时间轴 */}
<div className={classNames(`${prefixCls}-time`)}>
{peridos.length ? (
<TimeSlider
minTimestamp={minTimestamp}
key={JSON.stringify(peridos) + (hoursRuler || 24)}
maxTimestamp={maxTimestamp}
hoursPerRuler={hoursRuler || 24}
playTimestamp={playTimestamp ? playTimestamp : peridos[0].beginTime}
playTimestampChange={(time, recordInfo, playOffset) => {
if (recordInfo && playOffset) {
setPlayTimestamp(time);
} else {
message.warn('当前时间节点没有视频可以播放哦!');
}
}}
timecell={peridos}
/>
) : null}
</div>
</>
) : (
<div className={classNames(`${prefixCls}-empty`)}>
<Empty theme={'dark'} description={''} />
</div>
)}
</div>
);
};
export default React.forwardRef(RecVideo);
@root-entry-name: 'default';
@import '~antd/es/style/themes/index.less';
@rec-video-view-prefix-cls: ~'@{ant-prefix}-rec-video-view';
.@{rec-video-view-prefix-cls} {
display: flex;
align-items: center;
flex-direction: column;
height: 100%;
width: 100%;
&-video {
flex: 1;
width: 100%;
}
&-time {
flex: none;
width: 100%;
}
&-empty {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
:global {
.timeslider-canvas-wrap-his {
height: 72px;
width: 100%;
}
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment