fix(meta\biz): 视频添加od, 查看大图点击失效修复

This commit is contained in:
NICE CODE BY DEV 2024-04-26 19:34:28 +08:00
parent 1f3286e67d
commit c9abef2676
44 changed files with 733 additions and 388 deletions

View File

@ -1,5 +1,11 @@
# @zhst/biz
## 0.16.1
### Patch Changes
- zhst/biz: fix: 在业务层控制窗口切换状态
## 0.16.0
### Minor Changes

View File

@ -1,6 +1,6 @@
{
"name": "@zhst/biz",
"version": "0.16.0",
"version": "0.16.1",
"description": "业务库",
"keywords": [
"business",

View File

@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React from 'react';
import VideoPlayerCard from '../../../VideoPlayerCard';
import { VideoPlayerCardProps } from '../../../VideoPlayerCard';
import { Segmented } from 'antd';
@ -53,13 +53,13 @@ export const WindowToggle: React.FC<WindowToggleProps> = (props) => {
<div className='body'>
{
dataSource?.map((item, index) => {
if (size === "large" && index > 0) return
return (
<VideoPlayerCard
key={item.windowKey}
selectedWindowKey={selectedWindowKey}
size={size}
size={size}
handleWindowClick={handleWindowClick}
handleCloseButtonClick={handleCloseButtonClick}
{...item}

View File

@ -1,5 +1,12 @@
# @zhst/utils
## 0.10.2
### Patch Changes
- Updated dependencies
- @zhst/request@0.11.0
## 0.10.1
### Patch Changes

View File

@ -1,6 +1,6 @@
{
"name": "@zhst/func",
"version": "0.10.1",
"version": "0.10.2",
"description": "函数合集",
"keywords": [
"hooks"

View File

@ -1,5 +1,11 @@
# @zhst/hooks
## 0.9.2
### Patch Changes
- @zhst/func@0.10.2
## 0.9.1
### Patch Changes

View File

@ -1,6 +1,6 @@
{
"name": "@zhst/hooks",
"version": "0.9.1",
"version": "0.9.2",
"description": "hooks合集",
"keywords": [
"hooks"

View File

@ -1,5 +1,12 @@
# @zhst/material
## 0.10.4
### Patch Changes
- Updated dependencies
- @zhst/biz@0.16.1
## 0.10.3
### Patch Changes

View File

@ -1,6 +1,6 @@
{
"name": "@zhst/material",
"version": "0.10.3",
"version": "0.10.4",
"description": "物料库",
"keywords": [
"business",

View File

@ -1,5 +1,18 @@
# @zhst/utils
## 0.15.0
### Minor Changes
- 视频添加 OD 框,查看大图首次点击修复
### Patch Changes
- Updated dependencies
- @zhst/meta@0.15.0
- @zhst/func@0.10.2
- @zhst/hooks@0.9.2
## 0.14.0
### Minor Changes

View File

@ -30,31 +30,32 @@ export default {
_this$eventHandleList = this.eventHandleList,
eventHandleList = _this$eventHandleList === void 0 ? [] : _this$eventHandleList,
options = this.options;
//图片事件
// 鼠标滚轮事件
var scaleAble = get(options, 'scaleAble', true);
if (scaleAble) {
var handleWhele = addEventListenerWrapper(canvas, EVENT_WHEEL, this.onWheel.bind(this));
eventHandleList.push(handleWhele);
var handleWheel = addEventListenerWrapper(canvas, EVENT_WHEEL, this.onWheel.bind(this));
eventHandleList.push(handleWheel);
}
// 鼠标 - 拖拽事件
var dragAble = get(options, 'dragAble', true);
if (dragAble) {
var handleDragStart = addEventListenerWrapper(canvas, EVENT_POINTER_DOWN, this.onDragStart.bind(this));
eventHandleList.push(addEventListenerWrapper);
eventHandleList.push(handleDragStart);
var handleDragMove = addEventListenerWrapper(element.ownerDocument, EVENT_POINTER_MOVE, this.onDragMove.bind(this));
eventHandleList.push(handleDragMove);
EVENT_POINTER_UP.trim().split(REGEXP_SPACES).forEach(function (eventName) {
var handleDragEnd = addEventListenerWrapper(element.ownerDocument, eventName, _this.onDragEnd.bind(_this));
EVENT_POINTER_UP.trim().split(REGEXP_SPACES).forEach(function (_eventName) {
var handleDragEnd = addEventListenerWrapper(element.ownerDocument, _eventName, _this.onDragEnd.bind(_this));
eventHandleList.push(handleDragEnd);
});
}
//rect事件
var handleClick = addEventListenerWrapper(canvas, EVENT_CLICK, this.onClick.bind(this));
eventHandleList.push(handleClick);
// const handleLeveal = addEventListenerWrapper(canvas, EVENT_LEAVEL, this.onLeavel.bind(this));
// eventHandleList.push(handleLeveal);
// const handleEnter = addEventListenerWrapper(canvas, EVENT_ENTER, this.onEnter.bind(this));
// eventHandleList.push(handleEnter);
// 鼠标 - 点击事件
var selectAble = get(options, 'selectAble', true);
if (selectAble) {
var handleClick = addEventListenerWrapper(canvas, EVENT_CLICK, this.onClick.bind(this));
eventHandleList.push(handleClick);
}
},
unbind: function unbind() {
var eventHandleList = this.eventHandleList;
@ -67,7 +68,7 @@ export default {
}
}
},
/* 图片事件 */onWheel: function onWheel(event, cropBox) {
/* 鼠标滚轮事件 */onWheel: function onWheel(event, cropBox) {
var _this2 = this;
event.stopPropagation();
event.preventDefault();
@ -97,6 +98,7 @@ export default {
})
}, cropBox);
},
// 鼠标拖拽 - 开始拖拽
onDragStart: function onDragStart(event) {
event.stopPropagation();
// This line is required for preventing page zooming in iOS browsers
@ -119,6 +121,7 @@ export default {
this.action = ACTION_DRAG;
addClass(this.canvas, CLASS_MOVE);
},
// 鼠标拖拽 - 拖拽中
onDragMove: function onDragMove(event) {
event.stopPropagation();
@ -147,6 +150,7 @@ export default {
this.pointer.startX = this.pointer.endX;
this.pointer.startY = this.pointer.endY;
},
// 鼠标拖拽 - 停止拖拽
onDragEnd: function onDragEnd(event) {
event.stopPropagation();
var action = this.action;
@ -158,14 +162,7 @@ export default {
this.point = null;
removeClass(this.canvas, CLASS_MOVE);
},
/* rect事件 */
// onLeavel(event) {
// const pointerCenter = this.windowToCanvasAxis(event);
// this.highlightShape(pointerCenter);
// },
// onEnter(event) {
// this.highlightShape(null);
// },
// 鼠标点击
onClick: function onClick(event) {
event.stopPropagation();
var pointerCenter = this.windowToCanvasAxis(event);

View File

@ -13,7 +13,12 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
//@ts-nocheck
import * as turf from '@turf/turf';
import { AXIS_TYPE_ORIGIN, AXIS_TYPE_CANVAS, AXIS_TYPE_IMAGE } from "./constants";
export function rectToPolygon(axisRect) {
var polygon = turf.polygon([[[setNumberAccuracy(axisRect.x, -2), setNumberAccuracy(axisRect.y, -2)], [setNumberAccuracy(axisRect.x2, -2), setNumberAccuracy(axisRect.y, -2)], [setNumberAccuracy(axisRect.x2, -2), setNumberAccuracy(axisRect.y2, -2)], [setNumberAccuracy(axisRect.x, -2), setNumberAccuracy(axisRect.y2, -2)], [setNumberAccuracy(axisRect.x, -2), setNumberAccuracy(axisRect.y, -2)]]]);
return polygon;
}
//设置数据的精度
//accuracy 表示精度 以原点为中心向左为正,向右为负,
@ -42,7 +47,7 @@ export default {
targetTransform: {
translateX: 0,
translateY: 0,
scale: 0,
scale: 1,
rotate: 0
// rotate: 90,
},
@ -73,7 +78,8 @@ export default {
var targetTransform = this.targetTransform;
var translateX = targetTransform.translateX,
translateY = targetTransform.translateY,
scale = targetTransform.scale;
_targetTransform$scal = targetTransform.scale,
scale = _targetTransform$scal === void 0 ? 1 : _targetTransform$scal;
var axis = _objectSpread(_objectSpread({
x: translateX + x * scale,
y: translateY + y * scale

View File

@ -75,7 +75,14 @@ var Viewer = (_dec = Mixin(Render, Event, Shape, Helper), _dec(_class = /*#__PUR
}, {
key: "build",
value: function build() {
var _this$options = this.options,
_this$options$width = _this$options.width,
width = _this$options$width === void 0 ? 300 : _this$options$width,
_this$options$height = _this$options.height,
height = _this$options$height === void 0 ? 150 : _this$options$height;
var canvas = document.createElement('canvas');
canvas.width = width || canvas.width;
canvas.height = height || canvas.height;
addClass(canvas, CLASS_CANVAS);
this.element.appendChild(canvas);
this.canvas = canvas;

View File

@ -65,7 +65,6 @@ export default {
});
},
initCanvas: function initCanvas() {
if (!this.image) return;
//通过样式设置 不依赖父元素的prosition
var element = this.element,
canvas = this.canvas,
@ -83,7 +82,7 @@ export default {
var fitTransform = this.calcFitScreen();
this.targetTransform = Object.assign({}, this.targetTransform, fitTransform);
dispatchEvent(this.element, EVENT_VIEWER_TRANSFORM_CHANGE, cloneDeep(this.targetTransform));
//产品需求fitscale 是minscale
//产品需求fitscale 是 minscale
var _options$fitScaleAsMi = options.fitScaleAsMinScale,
fitScaleAsMinScale = _options$fitScaleAsMi === void 0 ? false : _options$fitScaleAsMi;
if (fitScaleAsMinScale) {
@ -106,8 +105,9 @@ export default {
};
loop();
},
// 绘制画布
renderCanvas: function renderCanvas(_ctx) {
if (!this.image || !this.canvas) return;
if (!this.canvas) return;
var containerData = this.containerData,
canvas = this.canvas,
targetTransform = this.targetTransform,
@ -128,14 +128,14 @@ export default {
ctx.setTransform(scale, 0, 0, scale, translateX, translateY);
// ctx.setTransform(scale, 0, 0, scale, translateX, translateY);
//旋转
var centerX = this.image.width / 2;
var centerY = this.image.height / 2;
var centerX = this.image ? this.image.width / 2 : canvas.width;
var centerY = this.image ? this.image.height / 2 : canvas.height;
ctx.translate(centerX, centerY);
ctx.rotate(rotate / 180 * Math.PI);
ctx.translate(-centerX, -centerY);
//图片
ctx.drawImage(this.image, 0, 0);
this.image && ctx.drawImage(this.image, 0, 0);
ctx.restore();
//画图形
ctx.save();

View File

@ -14,13 +14,9 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
import { isNil, isArray, isFunction } from '@zhst/func';
import * as turf from '@turf/turf';
import booleanPointInPolygon from '@turf/boolean-point-in-polygon';
import { setNumberAccuracy } from "./helper";
import { rectToPolygon } from "./helper";
import { SHAPE_TYPE_RECT, SHAPE_TYPE_CIRCLE, EVENT_SHAPE_SELECT } from "./constants";
import { dispatchEvent } from "../utils";
function rectToPolygon(axisRect) {
var polygon = turf.polygon([[[setNumberAccuracy(axisRect.x, -2), setNumberAccuracy(axisRect.y, -2)], [setNumberAccuracy(axisRect.x2, -2), setNumberAccuracy(axisRect.y, -2)], [setNumberAccuracy(axisRect.x2, -2), setNumberAccuracy(axisRect.y2, -2)], [setNumberAccuracy(axisRect.x, -2), setNumberAccuracy(axisRect.y2, -2)], [setNumberAccuracy(axisRect.x, -2), setNumberAccuracy(axisRect.y, -2)]]]);
return polygon;
}
export default {
//store
shapeList: [],
@ -47,20 +43,20 @@ export default {
this.changeZoonAble(true);
}
},
//method
addShape: function addShape(shap) {
//method:添加矩形
addShape: function addShape(shape) {
var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : SHAPE_TYPE_RECT;
this.color = shap.color ? shap.color : '';
if (isNil(shap) || this.disableAdd) return;
this.color = shape.color ? shape.color : '';
if (isNil(shape) || this.disableAdd) return;
var _this$shapeList = this.shapeList,
preShapeList = _this$shapeList === void 0 ? [] : _this$shapeList;
var shapList = isArray(shap) ? shap : [shap];
shapList = shapList.map(function (v) {
var _shapeList = isArray(shape) ? shape : [shape];
_shapeList = _shapeList.map(function (v) {
return _objectSpread(_objectSpread({}, v), {}, {
__SHAPE_TYPE__: type
});
});
this.shapeList = [].concat(_toConsumableArray(preShapeList), _toConsumableArray(shapList));
this.shapeList = [].concat(_toConsumableArray(preShapeList), _toConsumableArray(_shapeList));
},
//
setSelectShapId: function setSelectShapId(id) {
@ -214,9 +210,12 @@ export default {
}
}
},
// 绘制矩形
renderRect: function renderRect(ctx, shape, type) {
//算rect
var axisRect = this.imgRectAxisToCanvasAxisRect(shape);
var axisRect = this.imgRectAxisToCanvasAxisRect(_objectSpread(_objectSpread({}, shape), {}, {
image: ctx.canvas
}));
var rect = {
x: axisRect.x2 > axisRect.x ? axisRect.x : axisRect.x2,
y: axisRect.y2 > axisRect.y ? axisRect.y : axisRect.y2,

View File

@ -1,4 +1,5 @@
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
@ -18,15 +19,14 @@ import { noop, get, addEventListenerWrapper, dataURLToBlob, nextTick, toRealNumb
import Align from 'rc-align';
import { useLatest, useUpdateEffect, useFullscreen, useUnmount } from '@zhst/hooks';
import classNames from 'classnames';
import download from 'downloadjs';
import { Button, message } from '..';
import { message } from '..';
import { IconFont } from '@zhst/icon';
import { Cropper, EVENT_CROP_START, EVENT_CROP_END } from "../ImageEditor";
import { Cropper, EVENT_CROP_START, EVENT_CROP_END, Viewer } from "../ImageEditor";
import FlvPlayer, { FLV_EVENT } from "./components/FlvPlayer";
import Range from "./components/Progress";
import Loading from "./components/Loading";
import { CROP_TYPE } from "../utils/constants";
import { getShowStatus } from "./videoPlayerHelper";
import { downloadFrame, getShowStatus } from "./videoPlayerHelper";
import "./index.less";
var componentName = "zhst-image__video-view";
var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
@ -42,6 +42,16 @@ var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
adjustY: true
}
} : _props$screenshotButt,
_props$autoPlay = props.autoPlay,
autoPlay = _props$autoPlay === void 0 ? false : _props$autoPlay,
odList = props.odList,
showOD = props.showOD,
_props$width = props.width,
width = _props$width === void 0 ? '100%' : _props$width,
_props$height = props.height,
height = _props$height === void 0 ? '532px' : _props$height,
_props$backgroundColo = props.backgroundColor,
backgroundColor = _props$backgroundColo === void 0 ? '#333' : _props$backgroundColo,
_props$screenshotButt2 = props.screenshotButtonRender,
screenshotButtonRender = _props$screenshotButt2 === void 0 ? function () {
return /*#__PURE__*/React.createElement("div", {
@ -53,7 +63,8 @@ var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
}, "\u56DE\u8C03DOM");
} : _props$screenshotButt2,
onCropChange = props.onCropChange,
defaultNormalizationRect = props.defautlNormalizationRect;
defaultNormalizationRect = props.defautlNormalizationRect,
playerProps = props.playerProps;
// ========================== 播放 =========================
//实例参数
var containerRef = useRef(null); //容器ref
@ -257,6 +268,8 @@ var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
console.error(e);
}
});
// 重新加载
var _reload = /*#__PURE__*/function () {
var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee() {
var oldTime;
@ -309,6 +322,8 @@ var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
return _ref.apply(this, arguments);
};
}();
// 进度条操作
var seek = function seek(v) {
if (videoInsRef.current && isVideoLoadFinished) {
setPlayTime(parseFloat(v));
@ -317,6 +332,7 @@ var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
message.warning('待视频加载完,才可操作进度条');
}
};
// ========================== 视频opt bar =========================
var _useFullscreen = useFullscreen(containerRef, {
pageFullscreen: true
@ -354,17 +370,15 @@ var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
_useState22 = _slicedToArray(_useState21, 2),
cropRect = _useState22[0],
setCropRect = _useState22[1];
useEffect(function () {
var _videoInsRef$current4, _videoInsRef$current5;
showCrop ? videoInsRef === null || videoInsRef === void 0 || (_videoInsRef$current4 = videoInsRef.current) === null || _videoInsRef$current4 === void 0 ? void 0 : _videoInsRef$current4.pause() : videoInsRef === null || videoInsRef === void 0 || (_videoInsRef$current5 = videoInsRef.current) === null || _videoInsRef$current5 === void 0 ? void 0 : _videoInsRef$current5.play();
}, [showCrop]);
// 监听showCrop, isReady - 是否可编辑、视频播放组件是否挂载
useEffect(function () {
var handlerCropStart;
var handlerCropEnd;
setCropRect(null);
if (!isReady) return;
if (showCrop) {
var _canvas$parentNode;
var _canvas$parentNode, _videoInsRef$current4;
handlerCropStart = addEventListenerWrapper(corpContainerRef.current, EVENT_CROP_START, function () {
setCropRect(null);
});
@ -412,18 +426,41 @@ var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
};
}
isFirstFlagRef.current = false;
// 初始化圈选工具
cropInsRef.current = new Cropper(corpContainerRef.current, {
showMask: true,
cropBoxLimited: cropBoxLimited,
editAble: false,
img: imageData,
initialCropBoxData: initialCropBoxData
});
videoInsRef === null || videoInsRef === void 0 || (_videoInsRef$current4 = videoInsRef.current) === null || _videoInsRef$current4 === void 0 || _videoInsRef$current4.pause();
} else {
var _videoInsRef$current5;
var _element = videoInsRef.current._mediaElement || {};
videoInsRef === null || videoInsRef === void 0 || (_videoInsRef$current5 = videoInsRef.current) === null || _videoInsRef$current5 === void 0 || _videoInsRef$current5.play();
// 挂载图片选择
cropInsRef.current = new Viewer(corpContainerRef.current, {
scaleAble: false,
selectAble: false,
dragAble: false,
width: _element.clientWidth,
height: _element.clientHeight,
backgroundColor: 'transparent'
});
// 判定是否存在od框
showOD && (odList === null || odList === void 0 ? void 0 : odList.forEach(function (_od) {
var _cropInsRef$current;
cropInsRef === null || cropInsRef === void 0 || (_cropInsRef$current = cropInsRef.current) === null || _cropInsRef$current === void 0 || _cropInsRef$current.addShape(_od);
}));
}
return function () {
var _handlerCropStart, _handlerCropEnd, _cropInsRef$current, _cropInsRef$current$d;
var _handlerCropStart, _handlerCropEnd, _cropInsRef$current2, _cropInsRef$current2$;
(_handlerCropStart = handlerCropStart) === null || _handlerCropStart === void 0 || _handlerCropStart.remove();
(_handlerCropEnd = handlerCropEnd) === null || _handlerCropEnd === void 0 || _handlerCropEnd.remove();
cropInsRef === null || cropInsRef === void 0 || (_cropInsRef$current = cropInsRef.current) === null || _cropInsRef$current === void 0 || (_cropInsRef$current$d = _cropInsRef$current.destroy) === null || _cropInsRef$current$d === void 0 || _cropInsRef$current$d.call(_cropInsRef$current);
cropInsRef === null || cropInsRef === void 0 || (_cropInsRef$current2 = cropInsRef.current) === null || _cropInsRef$current2 === void 0 || (_cropInsRef$current2$ = _cropInsRef$current2.destroy) === null || _cropInsRef$current2$ === void 0 || _cropInsRef$current2$.call(_cropInsRef$current2);
cropInsRef.current = null;
};
}, [showCrop, isReady]);
@ -502,7 +539,7 @@ var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
};
}();
//回调
// 监听showCrop、cropRect - 监听是否可编辑、绘制的矩形
useEffect(function () {
//计算归一化crop rect
var normalizationRect = null;
@ -528,41 +565,11 @@ var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
}, [showCrop, cropRect]);
// ========================== 截帧 =========================
var downloadVideoframe = useCallback( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee4() {
var _videoInsRef$current6, _videoInsRef$current7, video, canvas, ctx, base64;
return _regeneratorRuntime().wrap(function _callee4$(_context4) {
while (1) switch (_context4.prev = _context4.next) {
case 0:
try {
videoInsRef === null || videoInsRef === void 0 || (_videoInsRef$current6 = videoInsRef.current) === null || _videoInsRef$current6 === void 0 || (_videoInsRef$current7 = _videoInsRef$current6.pause) === null || _videoInsRef$current7 === void 0 || _videoInsRef$current7.call(_videoInsRef$current6);
video = videoRef.current;
canvas = document.createElement('canvas');
ctx = canvas.getContext('2d');
//当视频处于还未加载出来时,截屏为黑色图片
if (video.readyState === 0) {
ctx === null || ctx === void 0 || ctx.clearRect(0, 0, canvas.width, canvas.height);
canvas.width = video.offsetWidth;
canvas.height = video.offsetHeight;
// @ts-ignore
ctx.fillStyle = 'black';
ctx === null || ctx === void 0 || ctx.fillRect(0, 0, canvas.width, canvas.height);
base64 = canvas.toDataURL();
} else {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
ctx === null || ctx === void 0 || ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
base64 = canvas.toDataURL('image/png');
}
download(base64);
} catch (error) {
console.error(error);
}
case 1:
case "end":
return _context4.stop();
}
}, _callee4);
})), []);
var downloadVideoFrame = useCallback(function (opt) {
var _videoInsRef$current6, _videoInsRef$current7;
videoInsRef === null || videoInsRef === void 0 || (_videoInsRef$current6 = videoInsRef.current) === null || _videoInsRef$current6 === void 0 || (_videoInsRef$current7 = _videoInsRef$current6.pause) === null || _videoInsRef$current7 === void 0 || _videoInsRef$current7.call(_videoInsRef$current6);
downloadFrame(videoRef.current, opt);
}, []);
// ============================== 暴露出去的方法 ===============================
var latestIsReady = useLatest(isReady);
@ -573,17 +580,31 @@ var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
setShowCrop: function setShowCrop(dispatch) {
var isReady = latestIsReady.current;
if (!isReady) return;
_setShowCrop(dispatch);
_setShowCrop === null || _setShowCrop === void 0 || _setShowCrop(dispatch);
},
downloadVideoframe: downloadVideoframe
downloadVideoFrame: downloadVideoFrame,
pause: function pause() {
var _videoInsRef$current8, _videoInsRef$current9;
(_videoInsRef$current8 = videoInsRef.current) === null || _videoInsRef$current8 === void 0 || (_videoInsRef$current9 = _videoInsRef$current8.pause) === null || _videoInsRef$current9 === void 0 || _videoInsRef$current9.call(_videoInsRef$current8);
},
play: function play() {
var _videoInsRef$current10, _videoInsRef$current11;
(_videoInsRef$current10 = videoInsRef.current) === null || _videoInsRef$current10 === void 0 || (_videoInsRef$current11 = _videoInsRef$current10.play) === null || _videoInsRef$current11 === void 0 || _videoInsRef$current11.call(_videoInsRef$current10);
},
reload: _reload
};
});
return /*#__PURE__*/React.createElement("div", {
className: classNames("".concat(componentName)),
ref: containerRef
}, url && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(FlvPlayer, {
ref: containerRef,
style: {
width: width,
height: height,
backgroundColor: backgroundColor
}
}, url && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(FlvPlayer, _extends({
playId: playSeq,
autoPlay: true,
autoPlay: autoPlay,
className: classNames("".concat(componentName, "-flv")),
type: url.startsWith('http') ? 'mp4' : 'flv',
url: url,
@ -594,12 +615,14 @@ var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
hasAudio: false,
hasVideo: true
},
onCreat: initPlayer
}), /*#__PURE__*/React.createElement("div", {
onCreate: initPlayer
}, playerProps)), /*#__PURE__*/React.createElement("div", {
className: classNames("".concat(componentName, "-crop-container")),
ref: corpContainerRef,
style: {
display: isFullscreen ? 'none' : 'block'
display: isFullscreen ? 'none' : 'block',
width: '100%',
height: '100%'
}
}), showCrop && cropRect && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
ref: alginContainerRef,
@ -625,27 +648,26 @@ var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
cropType: CROP_TYPE['CUSTOM']
}))), !showCrop && /*#__PURE__*/React.createElement("div", {
className: "".concat(componentName, "-opt")
}, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(Button, {
type: "text",
onClick: function onClick() {
if (!isPlay) {
var _videoInsRef$current8;
//播放中暂停
videoInsRef === null || videoInsRef === void 0 || (_videoInsRef$current8 = videoInsRef.current) === null || _videoInsRef$current8 === void 0 || _videoInsRef$current8.play();
_setShowCrop(false);
} else {
var _videoInsRef$current9;
videoInsRef === null || videoInsRef === void 0 || (_videoInsRef$current9 = videoInsRef.current) === null || _videoInsRef$current9 === void 0 || _videoInsRef$current9.pause();
}
}
}, /*#__PURE__*/React.createElement(IconFont, {
styles: {
marginRight: '12px',
color: '#fff',
display: 'flex'
},
onIconClick: function onIconClick() {
if (!isPlay) {
var _videoInsRef$current12;
//播放中暂停
videoInsRef === null || videoInsRef === void 0 || (_videoInsRef$current12 = videoInsRef.current) === null || _videoInsRef$current12 === void 0 || _videoInsRef$current12.play();
_setShowCrop(false);
} else {
var _videoInsRef$current13;
videoInsRef === null || videoInsRef === void 0 || (_videoInsRef$current13 = videoInsRef.current) === null || _videoInsRef$current13 === void 0 || _videoInsRef$current13.pause();
}
},
color: "#1890ff",
icon: !isPlay ? 'icon-shipinbofang' : 'icon-shipinzanting'
}))), /*#__PURE__*/React.createElement("div", {
icon: !isPlay ? 'icon-bofang3' : 'icon-zanting1'
}), /*#__PURE__*/React.createElement("div", {
className: "".concat(componentName, "-opt-range"),
onClick: function onClick(e) {
e.stopPropagation();
@ -656,20 +678,19 @@ var VideoPlayer = /*#__PURE__*/forwardRef(function (props, ref) {
max: showMaxDuration,
showSlider: showSlider,
onChange: seek
}), /*#__PURE__*/React.createElement("div", null, formatDurationTime(playTime), "/", formatDurationTime(showMaxDuration))), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(Button, {
type: "text",
onClick: function onClick(e) {
}), /*#__PURE__*/React.createElement("div", null, formatDurationTime(playTime), "/", formatDurationTime(showMaxDuration))), /*#__PURE__*/React.createElement(IconFont, {
styles: {
display: 'flex',
marginLeft: '12px'
},
color: "#fff",
size: 18,
onIconClick: function onIconClick(e) {
e.stopPropagation();
toggleFullscreen();
}
}, /*#__PURE__*/React.createElement(IconFont, {
styles: {
color: '#fff',
display: 'flex'
},
size: 18,
icon: isFullscreen ? 'icon-cancle_fullscreen' : 'icon-fullscreen'
})))), !!showStatus && /*#__PURE__*/React.createElement(Loading, {
icon: isFullscreen ? 'icon-suoxiao1' : 'icon-quanping1'
})), !!showStatus && /*#__PURE__*/React.createElement(Loading, {
status: showStatus,
reload: function reload() {
return _reload();

View File

@ -1,4 +1,4 @@
var _excluded = ["className", "autoPlay", "config", "onCreat", "playId"];
var _excluded = ["className", "autoPlay", "config", "onCreate", "playId"];
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
@ -45,12 +45,13 @@ var VideoPlayer = /*#__PURE__*/function (_Component) {
autoPlay = _this$props$autoPlay === void 0 ? true : _this$props$autoPlay,
_this$props$config = _this$props.config,
config = _this$props$config === void 0 ? {} : _this$props$config,
onCreat = _this$props.onCreat,
onCreate = _this$props.onCreate,
playId = _this$props.playId,
others = _objectWithoutProperties(_this$props, _excluded);
if ($video) {
if (flvjs.isSupported() && _this.props.url && _this.props.url) {
var reload = function reload() {
var _this$flvPlayer;
if (_this.flvPlayer && _this.flvPlayer.destroy) {
try {
_this.flvPlayer.destroy();
@ -65,7 +66,7 @@ var VideoPlayer = /*#__PURE__*/function (_Component) {
flvPlayer.load();
_this.flvPlayer = flvPlayer;
// @ts-ignore
var controller = _this.flvPlayer._transmuxer._controller;
var controller = (_this$flvPlayer = _this.flvPlayer) === null || _this$flvPlayer === void 0 || (_this$flvPlayer = _this$flvPlayer._transmuxer) === null || _this$flvPlayer === void 0 ? void 0 : _this$flvPlayer._controller;
var wsLoader = controller._ioctl._loader;
var oldWsOnCompleteFunc = wsLoader._onComplete;
wsLoader._onComplete = function () {
@ -82,10 +83,9 @@ var VideoPlayer = /*#__PURE__*/function (_Component) {
oldWsOnCompleteFunc();
};
_this.flvPlayer.reload = reload;
onCreat && onCreat(_this.flvPlayer, $video);
onCreate === null || onCreate === void 0 || onCreate(_this.flvPlayer, $video);
};
reload();
onCreat && onCreat(_this.flvPlayer, $video);
}
}
});
@ -95,9 +95,9 @@ var VideoPlayer = /*#__PURE__*/function (_Component) {
key: "componentWillUnmount",
value: function componentWillUnmount() {
if (this.flvPlayer) {
var _this$flvPlayer, _this$flvPlayer2;
(_this$flvPlayer = this.flvPlayer) === null || _this$flvPlayer === void 0 || _this$flvPlayer.unload();
(_this$flvPlayer2 = this.flvPlayer) === null || _this$flvPlayer2 === void 0 || _this$flvPlayer2.detachMediaElement();
var _this$flvPlayer2, _this$flvPlayer3;
(_this$flvPlayer2 = this.flvPlayer) === null || _this$flvPlayer2 === void 0 || _this$flvPlayer2.unload();
(_this$flvPlayer3 = this.flvPlayer) === null || _this$flvPlayer3 === void 0 || _this$flvPlayer3.detachMediaElement();
}
}
}, {
@ -119,13 +119,11 @@ var VideoPlayer = /*#__PURE__*/function (_Component) {
return /*#__PURE__*/React.createElement("video", {
muted: true,
preload: "metadata",
className: className
// controls={true}
,
style: Object.assign({
className: className,
style: _objectSpread({
width: '100%',
height: '100%'
}, style ? style : {}),
}, style),
ref: this.initFlv
});
}

View File

@ -3,7 +3,7 @@ function _objectWithoutProperties(source, excluded) { if (source == null) return
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
import React from 'react';
import classNames from 'classnames';
import { Slider } from 'antd';
import { Slider, ConfigProvider } from 'antd';
import "./index.less";
var componentName = "zhst-image__range";
export var Range = function Range(props) {
@ -15,6 +15,15 @@ export var Range = function Range(props) {
return /*#__PURE__*/React.createElement("div", {
style: style,
className: classNames("".concat(componentName), !showSlider && "".concat(componentName, "--no-slider"), className)
}, /*#__PURE__*/React.createElement(Slider, others));
}, /*#__PURE__*/React.createElement(ConfigProvider, {
theme: {
components: {
Slider: {
railBg: 'rgba(255, 255, 255, 0.6)',
railHoverBg: 'rgba(255, 255, 255, 0.6)'
}
}
}
}, /*#__PURE__*/React.createElement(Slider, others)));
};
export default Range;

View File

@ -1,8 +1,6 @@
.zhst-image__video-view {
position: relative;
overflow: hidden;
width: 100%;
height: 532px;
background-color: #333;
// &-flv {
@ -37,7 +35,7 @@
height: 32px;
box-sizing: border-box;
align-items: center;
padding: 0 12px;
padding: 0 12px 0 24px;
background-color: rgb(0 0 0 / 80%);
line-height: 32px;

View File

@ -1,3 +1,4 @@
import download from "downloadjs";
export function getShowStatus(isLoadingVideo, isEnd, isError) {
var status = null;
if (isLoadingVideo) {
@ -10,4 +11,41 @@ export function getShowStatus(isLoadingVideo, isEnd, isError) {
status = 'END';
}
return status;
}
}
// 下载功能的可配置属性
// 视屏截帧、下载
export var downloadFrame = function downloadFrame(_videoDom, opt) {
var _ref = opt || {},
_ref$downloadAble = _ref.downloadAble,
downloadAble = _ref$downloadAble === void 0 ? true : _ref$downloadAble,
_ref$fileName = _ref.fileName,
fileName = _ref$fileName === void 0 ? 'image' : _ref$fileName,
_ref$fileType = _ref.fileType,
fileType = _ref$fileType === void 0 ? 'image/png' : _ref$fileType;
try {
var video = _videoDom;
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var base64;
//当视频处于还未加载出来时,截屏为黑色图片
if (video.readyState === 0) {
ctx === null || ctx === void 0 || ctx.clearRect(0, 0, canvas.width, canvas.height);
canvas.width = video.offsetWidth;
canvas.height = video.offsetHeight;
// @ts-ignore
ctx.fillStyle = 'black';
ctx === null || ctx === void 0 || ctx.fillRect(0, 0, canvas.width, canvas.height);
} else {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
ctx === null || ctx === void 0 || ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
}
base64 = canvas.toDataURL(fileType);
downloadAble && download(base64, fileName);
} catch (error) {
console.error(error);
}
};

View File

@ -1,6 +1,6 @@
{
"name": "@zhst/meta",
"version": "0.14.0",
"version": "0.15.0",
"description": "原子组件",
"keywords": [
"meta",

View File

@ -30,21 +30,24 @@ export default {
//method
bind() {
const { canvas, element, eventHandleList = [], options } = this;
//图片事件
// 鼠标滚轮事件
const scaleAble = get(options, 'scaleAble', true);
if (scaleAble) {
const handleWhele = addEventListenerWrapper(canvas, EVENT_WHEEL, this.onWheel.bind(this));
eventHandleList.push(handleWhele);
const handleWheel = addEventListenerWrapper(canvas, EVENT_WHEEL, this.onWheel.bind(this));
eventHandleList.push(handleWheel);
}
// 鼠标 - 拖拽事件
const dragAble = get(options, 'dragAble', true);
if (dragAble) {
const handleDragStart = addEventListenerWrapper(
canvas,
EVENT_POINTER_DOWN,
this.onDragStart.bind(this)
);
eventHandleList.push(addEventListenerWrapper);
eventHandleList.push(handleDragStart);
const handleDragMove = addEventListenerWrapper(
element.ownerDocument,
EVENT_POINTER_MOVE,
@ -53,23 +56,23 @@ export default {
eventHandleList.push(handleDragMove);
EVENT_POINTER_UP.trim()
.split(REGEXP_SPACES)
.forEach((eventName) => {
.forEach((_eventName) => {
const handleDragEnd = addEventListenerWrapper(
element.ownerDocument,
eventName,
_eventName,
this.onDragEnd.bind(this)
);
eventHandleList.push(handleDragEnd);
});
}
//rect事件
const handleClick = addEventListenerWrapper(canvas, EVENT_CLICK, this.onClick.bind(this));
eventHandleList.push(handleClick);
// const handleLeveal = addEventListenerWrapper(canvas, EVENT_LEAVEL, this.onLeavel.bind(this));
// eventHandleList.push(handleLeveal);
// const handleEnter = addEventListenerWrapper(canvas, EVENT_ENTER, this.onEnter.bind(this));
// eventHandleList.push(handleEnter);
// 鼠标 - 点击事件
const selectAble = get(options, 'selectAble', true);
if (selectAble) {
const handleClick = addEventListenerWrapper(canvas, EVENT_CLICK, this.onClick.bind(this));
eventHandleList.push(handleClick);
}
},
unbind() {
const { eventHandleList } = this;
@ -83,7 +86,7 @@ export default {
}
},
/* 图片事件 */
/* 鼠标滚轮事件 */
onWheel(event, cropBox?) {
event.stopPropagation();
event.preventDefault();
@ -123,6 +126,7 @@ export default {
);
},
// 鼠标拖拽 - 开始拖拽
onDragStart(event) {
event.stopPropagation();
// This line is required for preventing page zooming in iOS browsers
@ -150,6 +154,8 @@ export default {
addClass(this.canvas, CLASS_MOVE);
},
// 鼠标拖拽 - 拖拽中
onDragMove(event) {
event.stopPropagation();
@ -175,6 +181,8 @@ export default {
this.pointer.startX = this.pointer.endX;
this.pointer.startY = this.pointer.endY;
},
// 鼠标拖拽 - 停止拖拽
onDragEnd(event) {
event.stopPropagation();
@ -190,14 +198,7 @@ export default {
removeClass(this.canvas, CLASS_MOVE);
},
/* rect事件 */
// onLeavel(event) {
// const pointerCenter = this.windowToCanvasAxis(event);
// this.highlightShape(pointerCenter);
// },
// onEnter(event) {
// this.highlightShape(null);
// },
// 鼠标点击
onClick(event) {
event.stopPropagation();
const pointerCenter = this.windowToCanvasAxis(event);

View File

@ -1,6 +1,21 @@
//@ts-nocheck
import * as turf from '@turf/turf';
import { AXIS_TYPE_ORIGIN, AXIS_TYPE_CANVAS, AXIS_TYPE_IMAGE } from './constants';
export function rectToPolygon(axisRect: any) {
const polygon = turf.polygon([
[
[setNumberAccuracy(axisRect.x, -2), setNumberAccuracy(axisRect.y, -2)],
[setNumberAccuracy(axisRect.x2, -2), setNumberAccuracy(axisRect.y, -2)],
[setNumberAccuracy(axisRect.x2, -2), setNumberAccuracy(axisRect.y2, -2)],
[setNumberAccuracy(axisRect.x, -2), setNumberAccuracy(axisRect.y2, -2)],
[setNumberAccuracy(axisRect.x, -2), setNumberAccuracy(axisRect.y, -2)],
],
]);
return polygon;
}
//设置数据的精度
//accuracy 表示精度 以原点为中心向左为正,向右为负,
//isCeil 表示是否为向上取整
@ -28,7 +43,7 @@ export default {
targetTransform: {
translateX: 0,
translateY: 0,
scale: 0,
scale: 1,
rotate: 0,
// rotate: 90,
},
@ -51,7 +66,7 @@ export default {
},
originAxisToCanvasAxis({ x, y, ...others }) {
const { targetTransform } = this;
const { translateX, translateY, scale } = targetTransform;
const { translateX, translateY, scale = 1 } = targetTransform;
const axis = {
x: translateX + x * scale,
y: translateY + y * scale,

View File

@ -28,6 +28,12 @@ export interface Option {
*/
dragAble?: boolean;
/*
*
* @default: true
*/
selectAble?: boolean;
/*
* fit scale
* @default: false

View File

@ -51,7 +51,6 @@ export default {
},
initCanvas() {
if (!this.image) return;
//通过样式设置 不依赖父元素的prosition
const { element, canvas, limit = {}, options } = this;
const containerData = {
@ -65,7 +64,7 @@ export default {
const fitTransform = this.calcFitScreen();
this.targetTransform = Object.assign({}, this.targetTransform, fitTransform);
dispatchEvent(this.element, EVENT_VIEWER_TRANSFORM_CHANGE, cloneDeep(this.targetTransform));
//产品需求fitscale 是minscale
//产品需求fitscale 是 minscale
const { fitScaleAsMinScale = false } = options;
if (fitScaleAsMinScale) {
this.limit = Object.assign({ minScale: this.targetTransform.scale }, this.limit);
@ -86,8 +85,9 @@ export default {
loop();
},
// 绘制画布
renderCanvas(_ctx) {
if (!this.image || !this.canvas) return;
if (!this.canvas) return;
const { containerData, canvas, targetTransform, options } = this;
const { translateX, translateY, scale, rotate } = targetTransform;
const ctx = _ctx ? _ctx : canvas.getContext('2d');
@ -102,14 +102,14 @@ export default {
ctx.setTransform(scale, 0, 0, scale, translateX, translateY);
// ctx.setTransform(scale, 0, 0, scale, translateX, translateY);
//旋转
const centerX = this.image.width / 2;
const centerY = this.image.height / 2;
const centerX = this.image ? this.image.width / 2 : canvas.width;
const centerY = this.image ? this.image.height / 2 : canvas.height;
ctx.translate(centerX, centerY);
ctx.rotate((rotate / 180) * Math.PI);
ctx.translate(-centerX, -centerY);
//图片
ctx.drawImage(this.image, 0, 0);
this.image && ctx.drawImage(this.image, 0, 0);
ctx.restore();
//画图形

View File

@ -2,22 +2,10 @@
import { isNil, isArray, isFunction } from '@zhst/func';
import * as turf from '@turf/turf';
import booleanPointInPolygon from '@turf/boolean-point-in-polygon';
import { setNumberAccuracy } from './helper';
import { rectToPolygon } from './helper';
import { SHAPE_TYPE_RECT, SHAPE_TYPE_CIRCLE, EVENT_SHAPE_SELECT } from './constants';
import { dispatchEvent } from '../utils';
function rectToPolygon(axisRect: any) {
const polygon = turf.polygon([
[
[setNumberAccuracy(axisRect.x, -2), setNumberAccuracy(axisRect.y, -2)],
[setNumberAccuracy(axisRect.x2, -2), setNumberAccuracy(axisRect.y, -2)],
[setNumberAccuracy(axisRect.x2, -2), setNumberAccuracy(axisRect.y2, -2)],
[setNumberAccuracy(axisRect.x, -2), setNumberAccuracy(axisRect.y2, -2)],
[setNumberAccuracy(axisRect.x, -2), setNumberAccuracy(axisRect.y, -2)],
],
]);
return polygon;
}
export interface Shape {
id?: number | string; //uuid
selectAble?: boolean;
@ -64,14 +52,15 @@ export default {
}
},
//method
addShape(shap: Shape | Array<Shape>, type = SHAPE_TYPE_RECT) {
this.color = shap.color ? shap.color : '';
if (isNil(shap) || this.disableAdd) return;
//method添加矩形
addShape(shape: Shape | Array<Shape>, type = SHAPE_TYPE_RECT) {
this.color = shape.color ? shape.color : '';
if (isNil(shape) || this.disableAdd) return;
const { shapeList: preShapeList = [] } = this;
let shapList = isArray(shap) ? shap : [shap];
shapList = shapList.map((v) => ({ ...v, __SHAPE_TYPE__: type }));
this.shapeList = [...preShapeList, ...shapList];
let _shapeList = isArray(shape) ? shape : [shape];
_shapeList = _shapeList.map((v) => ({ ...v, __SHAPE_TYPE__: type }));
this.shapeList = [...preShapeList, ..._shapeList];
},
//
setSelectShapId(id: number) {
@ -226,9 +215,10 @@ export default {
}
},
// 绘制矩形
renderRect(ctx, shape, type) {
//算rect
const axisRect = this.imgRectAxisToCanvasAxisRect(shape);
const axisRect = this.imgRectAxisToCanvasAxisRect({ ...shape, image: ctx.canvas});
const rect = {
x: axisRect.x2 > axisRect.x ? axisRect.x : axisRect.x2,
y: axisRect.y2 > axisRect.y ? axisRect.y : axisRect.y2,

View File

@ -7,25 +7,26 @@ import {
nextTick,
toRealNumber,
getTransforms,
formatDurationTime
formatDurationTime,
} from '@zhst/func';
import Align from 'rc-align';
import flvJs from 'flv.js'
import { Rect, IScreenshotButtonProp, AlignType } from '@zhst/types'
import { useLatest, useUpdateEffect, useFullscreen, useUnmount } from '@zhst/hooks';
import classNames from 'classnames';
import download from 'downloadjs';
import { Button, message } from '..';
import { message } from '..';
import { IconFont } from '@zhst/icon';
import {
Cropper,
EVENT_CROP_START,
EVENT_CROP_END,
Viewer,
} from '../ImageEditor';
import FlvPlayer, { FLV_EVENT } from './components/FlvPlayer';
import Range from './components/Progress';
import Loading from './components/Loading';
import { CROP_TYPE } from '../utils/constants';
import { getShowStatus } from './videoPlayerHelper'
import { downloadFrame, DownloadFrameOptionProps, getShowStatus } from './videoPlayerHelper'
import './index.less'
const componentName = `zhst-image__video-view`;
@ -33,8 +34,21 @@ const componentName = `zhst-image__video-view`;
export interface VideoViewProps {
/* 播放地址 */
url: string;
width?: string;
height?: string;
backgroundColor?: string; // 背景颜色
autoPlay?: boolean; // 自动播放
showOD?: boolean;
odList?: {
id: string;
x: number;
y: number;
w: number;
h: number;
selectAble?: boolean;
}[]
/* 播放总时间 */
maxDuration?: number;
maxDuration?: number; // 最大播放时长
/* 截图渲染 */
screenshotButtonAlign?: AlignType;
screenshotButtonRender?: (screenshotButtonProp: IScreenshotButtonProp) => ReactElement;
@ -42,13 +56,18 @@ export interface VideoViewProps {
defautlNormalizationRect?: Rect;
/* 截图回调 */
onCropChange?: (showCrop: boolean, normalizationRect: null | Rect) => void;
playerProps?: flvJs.Player
}
export interface VideoViewRef {
/* 当前图片模式 */
cropAble: boolean;
setShowCrop: Dispatch<SetStateAction<boolean>>;
downloadVideoframe: () => void;
downloadVideoFrame: (opt?: DownloadFrameOptionProps) => void; // 视频截帧
pause: () => void;
play: () => void;
reload: () => void; // 重新加载
}
const VideoPlayer = forwardRef<VideoViewRef, VideoViewProps>((props, ref) => {
@ -63,19 +82,26 @@ const VideoPlayer = forwardRef<VideoViewRef, VideoViewProps>((props, ref) => {
adjustY: true,
},
},
autoPlay = false,
odList,
showOD,
width = '100%',
height = '532px',
backgroundColor = '#333',
screenshotButtonRender = () => <div style={{ color: '#fff', width: '80px', top: 0 }}>DOM</div>,
onCropChange,
defautlNormalizationRect: defaultNormalizationRect,
playerProps,
} = props;
// ========================== 播放 =========================
//实例参数
const containerRef: any = useRef(null); //容器ref
const videoRef: any = useRef(null); //video 标签dom
const videoInsRef: any = useRef(null); //flv 实例
const [playSeq, setPlaySeq] = useState(0); // 通过重置playid使FLV组件重新渲染
const videoRemoveListener = useRef(noop); //移除dom监听的中间函数
const loadingTimeRef = useRef<number | null>(0); //最后一次加载时间
const delayLoadingTimer: any = useRef(null); //算loading的定时器
const containerRef: any = useRef(null); //容器ref
const videoRef: any = useRef(null); //video 标签dom
const videoInsRef: any = useRef(null); //flv 实例
const [playSeq, setPlaySeq] = useState(0); // 通过重置playid使FLV组件重新渲染
const videoRemoveListener = useRef(noop); //移除dom监听的中间函数
const loadingTimeRef = useRef<number | null>(0); //最后一次加载时间
const delayLoadingTimer: any = useRef(null); //算loading的定时器
//状态参数
const [isReady, setIsReady] = useState(false); //
const [isPlay, setIsPlay] = useState(false); //当前是否播放
@ -146,6 +172,7 @@ const VideoPlayer = forwardRef<VideoViewRef, VideoViewProps>((props, ref) => {
// 初始化
const latestMaxDuration = useLatest(maxDuration);
const initPlayer = useCallback((ins: any, dom: any) => {
videoRef.current = dom;
videoInsRef.current = ins;
@ -248,6 +275,7 @@ const VideoPlayer = forwardRef<VideoViewRef, VideoViewProps>((props, ref) => {
}
});
// 重新加载
const reload = async () => {
if (videoInsRef.current) {
let oldTime = videoInsRef.current.currentTime;
@ -279,6 +307,8 @@ const VideoPlayer = forwardRef<VideoViewRef, VideoViewProps>((props, ref) => {
setPlayTime(0);
setIsEnd(false);
};
// 进度条操作
const seek = (v: number) => {
if (videoInsRef.current && isVideoLoadFinished) {
setPlayTime(parseFloat(v as any));
@ -287,6 +317,7 @@ const VideoPlayer = forwardRef<VideoViewRef, VideoViewProps>((props, ref) => {
message.warning('待视频加载完,才可操作进度条')
}
};
// ========================== 视频opt bar =========================
const [isFullscreen, { toggleFullscreen }] = useFullscreen(containerRef, {
pageFullscreen: true,
@ -318,15 +349,14 @@ const VideoPlayer = forwardRef<VideoViewRef, VideoViewProps>((props, ref) => {
const alignRef: any = useRef(null);
const [cropRect, setCropRect] = useState<Rect| null>(null);
useEffect(() => {
showCrop ? videoInsRef?.current?.pause() : videoInsRef?.current?.play();
}, [showCrop]);
// 监听showCrop, isReady - 是否可编辑、视频播放组件是否挂载
useEffect(() => {
let handlerCropStart: { remove: () => void; };
let handlerCropEnd: { remove: () => void; };
setCropRect(null);
if (!isReady) return;
if (showCrop) {
handlerCropStart = addEventListenerWrapper(corpContainerRef.current, EVENT_CROP_START, () => {
setCropRect(null);
@ -384,12 +414,32 @@ const VideoPlayer = forwardRef<VideoViewRef, VideoViewProps>((props, ref) => {
}
isFirstFlagRef.current = false;
// 初始化圈选工具
cropInsRef.current = new Cropper(corpContainerRef.current, {
showMask: true,
cropBoxLimited,
editAble: false,
img: imageData,
initialCropBoxData,
});
videoInsRef?.current?.pause()
} else {
const _element = videoInsRef.current._mediaElement || {}
videoInsRef?.current?.play()
// 挂载图片选择
cropInsRef.current = new Viewer(corpContainerRef.current!!, {
scaleAble: false,
selectAble: false,
dragAble: false,
width: _element.clientWidth,
height: _element.clientHeight,
backgroundColor: 'transparent'
});
// 判定是否存在od框
showOD && odList?.forEach(_od => {
cropInsRef?.current?.addShape(_od);
})
}
return () => {
handlerCropStart?.remove();
@ -454,7 +504,7 @@ const VideoPlayer = forwardRef<VideoViewRef, VideoViewProps>((props, ref) => {
};
};
//回调
// 监听showCrop、cropRect - 监听是否可编辑、绘制的矩形
useEffect(() => {
//计算归一化crop rect
let normalizationRect = null;
@ -484,32 +534,9 @@ const VideoPlayer = forwardRef<VideoViewRef, VideoViewProps>((props, ref) => {
}, [showCrop, cropRect]);
// ========================== 截帧 =========================
const downloadVideoframe = useCallback(async () => {
try {
const downloadVideoFrame = useCallback((opt?: DownloadFrameOptionProps) => {
videoInsRef?.current?.pause?.();
let video: any = videoRef.current;
var canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d')
let base64;
//当视频处于还未加载出来时,截屏为黑色图片
if (video.readyState === 0) {
ctx?.clearRect(0, 0, canvas.width, canvas.height);
canvas.width = video.offsetWidth;
canvas.height = video.offsetHeight;
// @ts-ignore
ctx.fillStyle = 'black';
ctx?.fillRect(0, 0, canvas.width, canvas.height);
base64 = canvas.toDataURL();
} else {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
ctx?.drawImage(video, 0, 0, canvas.width, canvas.height);
base64 = canvas.toDataURL('image/png');
}
download(base64);
} catch (error) {
console.error(error);
}
downloadFrame(videoRef.current, opt)
}, []);
// ============================== 暴露出去的方法 ===============================
@ -520,18 +547,33 @@ const VideoPlayer = forwardRef<VideoViewRef, VideoViewProps>((props, ref) => {
setShowCrop: (dispatch) => {
const isReady = latestIsReady.current;
if (!isReady) return;
setShowCrop(dispatch);
setShowCrop?.(dispatch);
},
downloadVideoframe,
downloadVideoFrame,
pause: () => {
videoInsRef.current?.pause?.();
},
play: () => {
videoInsRef.current?.play?.();
},
reload
}));
return (
<div className={classNames(`${componentName}`)} ref={containerRef}>
<div
className={classNames(`${componentName}`)}
ref={containerRef}
style={{
width,
height,
backgroundColor
}}
>
{url && (
<>
<FlvPlayer
playId={playSeq}
autoPlay={true}
autoPlay={autoPlay}
className={classNames(`${componentName}-flv`)}
type={url.startsWith('http') ? 'mp4' : 'flv'}
url={url}
@ -542,7 +584,8 @@ const VideoPlayer = forwardRef<VideoViewRef, VideoViewProps>((props, ref) => {
hasAudio: false,
hasVideo: true,
}}
onCreat={initPlayer}
onCreate={initPlayer}
{...playerProps}
/>
{/* //截图 */}
<div
@ -550,10 +593,12 @@ const VideoPlayer = forwardRef<VideoViewRef, VideoViewProps>((props, ref) => {
ref={corpContainerRef}
style={{
display: isFullscreen ? 'none' : 'block',
width: '100%',
height: '100%'
}}
>
{/* <div ref={corpRef}></div> */}
</div>
{/* OD控件工具 */}
{showCrop && cropRect && (
<>
<div
@ -587,32 +632,27 @@ const VideoPlayer = forwardRef<VideoViewRef, VideoViewProps>((props, ref) => {
</Align>
</>
)}
{/* 视频进度条 */}
{/* 视频工具栏 */}
{!showCrop && (
<div className={`${componentName}-opt`}>
<div>
<Button
type="text"
onClick={() => {
if (!isPlay) {
//播放中暂停
videoInsRef?.current?.play();
setShowCrop(false);
} else {
videoInsRef?.current?.pause();
}
}}
>
<IconFont
styles={{
color: '#fff',
display: 'flex',
}}
color="#1890ff"
icon={!isPlay ? 'icon-shipinbofang' : 'icon-shipinzanting'}
/>
</Button>
</div>
<IconFont
styles={{
marginRight: '12px',
color: '#fff',
display: 'flex',
}}
onIconClick={() => {
if (!isPlay) {
//播放中暂停
videoInsRef?.current?.play();
setShowCrop(false);
} else {
videoInsRef?.current?.pause();
}
}}
color="#1890ff"
icon={!isPlay ? 'icon-bofang3' : 'icon-zanting1'}
/>
<div
className={`${componentName}-opt-range`}
onClick={(e) => {
@ -631,24 +671,19 @@ const VideoPlayer = forwardRef<VideoViewRef, VideoViewProps>((props, ref) => {
{formatDurationTime(playTime)}/{formatDurationTime(showMaxDuration)}
</div>
</div>
<div>
<Button
type="text"
onClick={(e) => {
e.stopPropagation();
toggleFullscreen();
}}
>
<IconFont
styles={{
color: '#fff',
display: 'flex',
}}
size={18}
icon={isFullscreen ? 'icon-cancle_fullscreen' : 'icon-fullscreen'}
/>
</Button>
</div>
<IconFont
styles={{
display: 'flex',
marginLeft: '12px'
}}
color="#fff"
size={18}
onIconClick={(e) => {
e.stopPropagation();
toggleFullscreen();
}}
icon={isFullscreen ? 'icon-suoxiao1' : 'icon-quanping1'}
/>
</div>
)}
{/* mask */}

View File

@ -20,7 +20,7 @@ export interface VideoPlayerProps {
filesize?: number;
url?: string;
autoPlay?: boolean;
onCreat?: any;
onCreate?: any;
/**
* @see https://github.com/Bilibili/flv.js/blob/master/docs/api.md#config
@ -33,7 +33,7 @@ export default class VideoPlayer extends Component<VideoPlayerProps, any> {
curPlayUrl: '',
shouldReinit: false,
};
flvPlayer: any = null;
flvPlayer: flvjs.Player & { reload?: () => void; } | null = null;
videoElement = null;
static getDerivedStateFromProps = (nextProps: { url?: any; playId?: any; }, prevState: { curPlayUrl?: any; playId?: any; }) => {
@ -55,7 +55,7 @@ export default class VideoPlayer extends Component<VideoPlayerProps, any> {
initFlv = ($video: null) => {
this.videoElement = $video;
const { className, autoPlay = true, config = {}, onCreat, playId, ...others } = this.props;
const { className, autoPlay = true, config = {}, onCreate, playId, ...others } = this.props;
if ($video) {
@ -77,27 +77,27 @@ export default class VideoPlayer extends Component<VideoPlayerProps, any> {
flvPlayer.load();
this.flvPlayer = flvPlayer;
// @ts-ignore
let controller = this.flvPlayer._transmuxer._controller
let controller = this.flvPlayer?._transmuxer?._controller
let wsLoader = controller._ioctl._loader
const oldWsOnCompleteFunc = wsLoader._onComplete
wsLoader._onComplete = function() {
if(!controller._remuxer) {
controller._remuxer = {
destroy: () => {
console.log('组件销毁')
},
flushStashedSamples: () => {
console.log("flushStashedSamples")
console.log("flushStashedSamples")
}
}
}
oldWsOnCompleteFunc()
}
this.flvPlayer.reload = reload;
onCreat && onCreat(this.flvPlayer, $video);
onCreate?.(this.flvPlayer, $video);
};
reload();
onCreat && onCreat(this.flvPlayer, $video);
}
}
};
@ -121,14 +121,11 @@ export default class VideoPlayer extends Component<VideoPlayerProps, any> {
muted
preload="metadata"
className={className}
// controls={true}
style={Object.assign(
{
width: '100%',
height: '100%',
},
style ? style : {}
)}
style={{
width: '100%',
height: '100%',
...style
}}
ref={this.initFlv}
/>
);

View File

@ -0,0 +1,107 @@
// @ts-nocheck
import React, { forwardRef, useContext, useEffect, useImperativeHandle, useRef } from 'react'
import flvJs from 'flv.js'
import { ConfigProvider } from '../../..'
export interface FlvPlayerProps {
prefixCls?: string;
className?: string;
autoPlay?: boolean;
config?: flvJs.Config
onCreate?: () => void;
playId?: string;
url: string;
type: string;
style?: React.CSSProperties
}
export interface FlvPlayerRefProps {
}
const { ConfigContext } = ConfigProvider
const FlvPlayer = forwardRef<FlvPlayerRefProps, FlvPlayerProps>((props, ref) => {
const {
prefixCls: customizePrefixCls
} = props
const {
className,
autoPlay = true,
config = {},
onCreate,
url,
style,
type = 'flv',
playId,
...others
} = props;
const { getPrefixCls } = useContext(ConfigContext);
const componentName = getPrefixCls('cropper-view', customizePrefixCls);
const flvPlayerRef = useRef<flvJs.Player>()
// 初始化播放器
useEffect(() => {
if (flvJs.isSupported() && url) {
if (flvPlayerRef.current && flvPlayerRef.current.destroy) {
try {
flvPlayerRef.current.destroy()
} catch (error) {
console.error(error);
}
}
let flvPlayer = flvJs.createPlayer({
type: url.startsWith('http') ? 'mp4' : 'flv',
...others
}, {
deferLoadAfterSourceOpen: false,
...config
});
// @ts-ignore
flvPlayer.attachMediaElement(flvPlayerRef.current);
flvPlayer.load();
flvPlayerRef.current = flvPlayer;
// @ts-ignore
let controller = flvPlayerRef.current?._transmuxer?._controller
let wsLoader = controller._ioctl._loader
const oldWsOnCompleteFunc = wsLoader._onComplete
wsLoader._onComplete = function() {
if(!controller._remuxer) {
controller._remuxer = {
destroy: () => {
console.log('组件销毁')
},
flushStashedSamples: () => {
console.log("flushStashedSamples")
}
}
}
oldWsOnCompleteFunc()
}
}
})
useImperativeHandle(ref, () => ({
player: flvPlayerRef
}))
return (
<div
className={componentName}
>
<video
muted
preload="metadata"
className={className}
style={{
width: '100%',
height: '100%',
...style
}}
ref={flvPlayerRef}
/>
</div>
)
})
export default FlvPlayer

View File

@ -1,6 +1,6 @@
import React from 'react';
import classNames from 'classnames';
import { Slider } from 'antd';
import { Slider, ConfigProvider } from 'antd';
import type { SliderSingleProps } from 'antd'
import './index.less';
@ -24,7 +24,18 @@ export const Range: React.FC<RangeWrapperProps> = (props) => {
)}
>
{/* @ts-ignore */}
<Slider {...others}></Slider>
<ConfigProvider
theme={{
components: {
Slider: {
railBg: 'rgba(255, 255, 255, 0.6)',
railHoverBg: 'rgba(255, 255, 255, 0.6)',
}
}
}}
>
<Slider {...others}></Slider>
</ConfigProvider>
</div>
);
};

View File

@ -1,18 +1,44 @@
import React, { useRef, useState } from 'react';
import { VideoPlayer, Space, Button } from '@zhst/meta'
import { VideoPlayer, Space, Button, VideoViewRef } from '@zhst/meta'
import { VIDEO_URL } from './mock'
export default () => {
const videoRef = useRef(null)
const [url, setUrl] = useState(null)
const videoRef = useRef<VideoViewRef>(null)
const [url] = useState(VIDEO_URL)
return (
<Space>
<Space direction='vertical'>
<Space>
<Button onClick={() => setUrl('ws://10.0.0.120:9033/flv/HaikangNvr/45.flv?ip=10.0.2.103&stime=1712539148&etime=1712539168')}></Button>
<Button onClick={() => videoRef.current?.play()}></Button>
<Button onClick={() => videoRef.current?.pause()}></Button>
<Button onClick={() => videoRef.current?.setShowCrop(true)}></Button>
<Button onClick={() => videoRef.current?.setShowCrop(false)}></Button>
<Button onClick={() => videoRef.current?.setShowCrop(false)}>退</Button>
<Button onClick={() => videoRef.current?.downloadVideoFrame()}></Button>
</Space>
{url && <VideoPlayer ref={videoRef} url={url} />}
<div style={{ width: '800px' }}>
<VideoPlayer
ref={videoRef}
url={url}
showOD
odList={[
{
"id": "123",
"x": 0.5519352,
"y": 0.2965385,
"w": 0.05185461,
"h": 0.24698898,
selectAble: false,
},
{
"id": "456",
"x": 0.58543766,
"y": 0.3203356,
"w": 0.052037954,
"h": 0.2664015
}
]}
/>
</div>
</Space>
)
}

View File

@ -0,0 +1 @@
export const VIDEO_URL = `ws://10.0.0.7:9033/flv/File/test/test_h264_${Math.floor(Math.random() * 6) + 1}.mp4.flv?ip=127.0.0.1`

View File

@ -1,5 +1,6 @@
import React, { useRef, useState } from 'react';
import React, { useState } from 'react';
import { VideoPlayer, Space, Button, Row, Col, InputNumber } from '@zhst/meta'
import { VIDEO_URL } from './mock'
export default () => {
const [urls, setUrls] = useState<string[]>([])
@ -8,7 +9,7 @@ export default () => {
const handlePlay = () => {
let arr = []
for (let i = 0; i < num; i++) {
arr.push(`ws://10.0.0.7:9033/flv/File/test/test_h264_${Math.floor(Math.random() * 6) + 1}.mp4.flv?ip=127.0.0.1`)
arr.push(VIDEO_URL)
}
setUrls(arr)
}
@ -16,12 +17,12 @@ export default () => {
return (
<div>
<Space>
<InputNumber value={num} onChange={(_num: React.SetStateAction<number>) => setNum(_num)} />
<InputNumber value={num} onChange={(_num) => setNum(_num!!)} />
<Button onClick={() => handlePlay()}></Button>
<Button onClick={() => setUrls([])}></Button>
</Space>
<Row gutter={[16,16]}>
{urls.map((url, idx) => (
{urls.map((url) => (
<Col span={8}>
<VideoPlayer key={url} url={url} />
</Col>

View File

@ -0,0 +1,29 @@
import React, { useRef, useState } from 'react';
import { Space, Button, VideoViewRef } from '@zhst/meta'
import { VIDEO_URL } from './mock'
import FlvPlayer from '../components/FlvPlayer/newFlvPlayer';
export default () => {
const videoRef = useRef<VideoViewRef>(null)
const [url] = useState(VIDEO_URL)
return (
<Space direction='vertical'>
<Space>
<Button onClick={() => videoRef.current?.play()}></Button>
</Space>
<div style={{ width: '800px' }}>
<FlvPlayer
ref={videoRef}
url={url}
config={{
enableStashBuffer: true,
stashInitialSize: 1024 * 700,
isLive: true,
hasVideo: true,
}}
/>
</div>
</Space>
)
}

View File

@ -1,8 +1,6 @@
.zhst-image__video-view {
position: relative;
overflow: hidden;
width: 100%;
height: 532px;
background-color: #333;
// &-flv {
@ -37,7 +35,7 @@
height: 32px;
box-sizing: border-box;
align-items: center;
padding: 0 12px;
padding: 0 12px 0 24px;
background-color: rgb(0 0 0 / 80%);
line-height: 32px;

View File

@ -8,15 +8,30 @@ demo:
cols: 2
---
# VideoPlayer 视频播放
## VideoPlayer 视频播放
:::warning{title=注意}
播放进度条操作还未完善,在播放结束和拖拽的过程中都有一定程度卡顿,目前逻辑待优化
:::
### 功能支持
1. 视频播放mp4、ws链接、直播流... ✔
2. 视频操作:播放、暂停、全屏观看、进度条 ✔
3. 视频截图 ✔
4. 下载视频截图 ✔
5. OD 跟随
### 实现思路
#### OD 跟随
根据传递的od框数据实时跟踪目标移动轨迹目前的构想是通过底层的flvjs拉取视频流播放再通过canvas截帧同时传入 OD 对象数组 requestAnimationFrame 去绘制;
<code src="./demo/basic.tsx">基础</code>
<code src="./demo/multiple.tsx">测试视频播放压力</code>
<!-- <code src="./demo/newFlv.tsx" debug>新flv播放器</code> -->
## API

View File

@ -1,3 +1,4 @@
import download from "downloadjs";
export function getShowStatus(isLoadingVideo: boolean, isEnd: boolean, isError: boolean) {
let status = null;
@ -12,3 +13,44 @@ export function getShowStatus(isLoadingVideo: boolean, isEnd: boolean, isError:
}
return status;
}
// 下载功能的可配置属性
export interface DownloadFrameOptionProps {
fileName?: string; // 文件名称
downloadAble?: boolean; // 是否开启下载
fileType?: string;
}
// 视屏截帧、下载
export const downloadFrame = (_videoDom: HTMLVideoElement, opt?: DownloadFrameOptionProps) => {
const {
downloadAble = true,
fileName = 'image',
fileType = 'image/png'
} = opt || {}
try {
let video = _videoDom;
var canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d')
let base64;
//当视频处于还未加载出来时,截屏为黑色图片
if (video.readyState === 0) {
ctx?.clearRect(0, 0, canvas.width, canvas.height);
canvas.width = video.offsetWidth;
canvas.height = video.offsetHeight;
// @ts-ignore
ctx.fillStyle = 'black';
ctx?.fillRect(0, 0, canvas.width, canvas.height);
} else {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
ctx?.drawImage(video, 0, 0, canvas.width, canvas.height);
}
base64 = canvas.toDataURL(fileType);
downloadAble && download(base64, fileName);
} catch (error) {
console.error(error);
}
}

View File

@ -23,6 +23,7 @@ export interface CropperImageProps {
lineConfig?: fabric.Line; // 线条配置
editAble?: boolean; // 是否可编辑
selectedItem?: RectPro
selectAble?: boolean;
// 是否可放大缩小
scaleAble?: boolean;
// 是否展示框选拓展框
@ -77,6 +78,7 @@ const CropperImage = forwardRef<CropperImageRefProps, CropperImageProps>((props,
onCropEnd,
editAble,
onShapeSelected,
selectAble = true,
showToast = false,
customToast = () => <div></div>,
type = 'ract',
@ -106,6 +108,7 @@ const CropperImage = forwardRef<CropperImageRefProps, CropperImageProps>((props,
viewerRef.current = new Viewer(imageRef.current!!, {
image: url,
scaleAble,
selectAble,
dragAble: false,
});

View File

@ -1,5 +1,15 @@
# @zhst/request
## 0.11.0
### Minor Changes
- 视频添加 OD 框,查看大图首次点击修复
### Patch Changes
- @zhst/func@0.10.2
## 0.10.1
### Patch Changes

View File

@ -1,30 +0,0 @@
export interface ReqConfigProps {
timeout?: number;
baseURL?: string;
errorConfig?: {
errorHandler?: (error: any, opts: any) => void;
errorThrower?: (err: any) => void;
};
authorization?: string;
showMsg?: boolean;
onError: (error?: any) => void;
}
export declare const reqConfig: (config: ReqConfigProps) => {
timeout: number;
baseURL: string;
errorConfig: {
errorHandler?: ((error: any, opts: any) => void) | undefined;
errorThrower?: ((err: any) => void) | undefined;
};
authorization?: string | undefined;
showMsg?: boolean | undefined;
onError: (error?: any) => void;
requestInterceptors: (((url: any, options: any) => {
url: any;
options: any;
}) | ((error: any) => Promise<never>))[][];
responseInterceptors: ((response: {
status: number;
data: any;
}) => any)[][];
};

View File

@ -1,30 +0,0 @@
export interface ReqConfigProps {
timeout?: number;
baseURL?: string;
errorConfig?: {
errorHandler?: (error: any, opts: any) => void;
errorThrower?: (err: any) => void;
};
authorization?: string;
showMsg?: boolean;
onError: (error?: any) => void;
}
export declare const reqConfig: (config: ReqConfigProps) => {
timeout: number;
baseURL: string;
errorConfig: {
errorHandler?: ((error: any, opts: any) => void) | undefined;
errorThrower?: ((err: any) => void) | undefined;
};
authorization?: string | undefined;
showMsg?: boolean | undefined;
onError: (error?: any) => void;
requestInterceptors: (((url: any, options: any) => {
url: any;
options: any;
}) | ((error: any) => Promise<never>))[][];
responseInterceptors: ((response: {
status: number;
data: any;
}) => any)[][];
};

View File

@ -1,6 +1,6 @@
{
"name": "@zhst/request",
"version": "0.10.1",
"version": "0.11.0",
"description": "请求库",
"keywords": [
"request",

View File

@ -1,5 +1,11 @@
# @zhst/slave
## 0.6.2
### Patch Changes
- @zhst/func@0.10.2
## 0.6.1
### Patch Changes

View File

@ -1,6 +1,6 @@
{
"name": "@zhst/slave",
"version": "0.6.1",
"version": "0.6.2",
"description": "微前端子应用方法库",
"keywords": [
"slave",