Updated video position css (#519)
This commit is contained in:
Родитель
9a5fa965f6
Коммит
a51dd06417
|
@ -7200,7 +7200,8 @@
|
|||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
|
@ -7218,11 +7219,13 @@
|
|||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
@ -7235,15 +7238,18 @@
|
|||
},
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
|
@ -7346,7 +7352,8 @@
|
|||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
|
@ -7356,6 +7363,7 @@
|
|||
"is-fullwidth-code-point": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
|
@ -7368,17 +7376,20 @@
|
|||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.2.4",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.1",
|
||||
"yallist": "^3.0.0"
|
||||
|
@ -7395,6 +7406,7 @@
|
|||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
|
@ -7467,7 +7479,8 @@
|
|||
},
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
|
@ -7477,6 +7490,7 @@
|
|||
"once": {
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
|
@ -7552,7 +7566,8 @@
|
|||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.1",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
|
@ -7582,6 +7597,7 @@
|
|||
"string-width": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
|
@ -7599,6 +7615,7 @@
|
|||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
|
@ -7637,11 +7654,13 @@
|
|||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.2",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
import React from "react";
|
||||
import * as shortid from "shortid";
|
||||
import { IAssetMetadata, IRegion, RegionType, AppError, ErrorCode,
|
||||
AssetState, EditorMode, IProject, AssetType } from "../../../../models/applicationState";
|
||||
import {
|
||||
IAssetMetadata, IRegion, RegionType, AppError, ErrorCode,
|
||||
AssetState, EditorMode, IProject, AssetType,
|
||||
} from "../../../../models/applicationState";
|
||||
import { CanvasTools } from "vott-ct";
|
||||
import { Player, ControlBar, CurrentTimeDisplay, TimeDivider,
|
||||
BigPlayButton, PlaybackRateMenuButton, VolumeMenuButton } from "video-react";
|
||||
import {
|
||||
Player, ControlBar, CurrentTimeDisplay, TimeDivider,
|
||||
BigPlayButton, PlaybackRateMenuButton, VolumeMenuButton,
|
||||
} from "video-react";
|
||||
import { Editor } from "vott-ct/lib/js/CanvasTools/CanvasTools.Editor";
|
||||
import { RegionData, RegionDataType } from "vott-ct/lib/js/CanvasTools/Core/RegionData";
|
||||
import { TagsDescriptor } from "vott-ct/lib/js/CanvasTools/Core/TagsDescriptor";
|
||||
|
@ -22,6 +26,7 @@ export interface ICanvasProps {
|
|||
interface ICanvasState {
|
||||
loaded: boolean;
|
||||
selectedRegions?: IRegion[];
|
||||
canvasEnabled: boolean;
|
||||
}
|
||||
|
||||
export default class Canvas extends React.Component<ICanvasProps, ICanvasState> {
|
||||
|
@ -116,7 +121,7 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
|
|||
* @description get the boinding box info for the current region
|
||||
* @returns {Object}
|
||||
*/
|
||||
public getSelectedRegionsBounds: () => {id: string, x: number, y: number, width: number, height: number};
|
||||
public getSelectedRegionsBounds: () => { id: string, x: number, y: number, width: number, height: number };
|
||||
|
||||
/**
|
||||
* @name redrawAllRegions
|
||||
|
@ -183,8 +188,7 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
|
|||
|
||||
public editor: Editor;
|
||||
|
||||
private playerRef: React.RefObject<Player>;
|
||||
private selectionRef: React.RefObject<HTMLDivElement>;
|
||||
private videoPlayer: React.RefObject<Player>;
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
@ -192,15 +196,15 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
|
|||
this.state = {
|
||||
loaded: false,
|
||||
selectedRegions: [],
|
||||
canvasEnabled: true,
|
||||
};
|
||||
|
||||
this.playerRef = React.createRef<Player>();
|
||||
this.selectionRef = React.createRef<HTMLDivElement>();
|
||||
this.videoPlayer = React.createRef<Player>();
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
public async componentDidMount() {
|
||||
const ct = CanvasTools;
|
||||
const sz = document.getElementById("editor-zone") as unknown as HTMLDivElement;
|
||||
const sz = document.getElementById("editor-zone") as HTMLDivElement;
|
||||
|
||||
// @ts-ignore
|
||||
this.editor = new ct.Editor(sz);
|
||||
|
@ -236,43 +240,44 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
|
|||
this.editor.onRegionSelected = this.onRegionSelected.bind(this);
|
||||
|
||||
// Upload background image for selection
|
||||
this.updateEditor();
|
||||
await this.updateEditor();
|
||||
}
|
||||
|
||||
public componentDidUpdate(prevProps) {
|
||||
public async componentDidUpdate(prevProps) {
|
||||
if (this.props.selectedAsset.asset.path !== prevProps.selectedAsset.asset.path) {
|
||||
this.updateEditor();
|
||||
await this.updateEditor();
|
||||
if (this.props.selectedAsset.regions.length) {
|
||||
this.updateSelected([]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { selectedAsset } = this.props;
|
||||
|
||||
return (
|
||||
<div id="ct-zone">
|
||||
{ selectedAsset.asset.type === AssetType.Video &&
|
||||
<Player ref={this.playerRef}
|
||||
fluid={false} width={"100%"} height={"100%"}
|
||||
autoPlay={true}
|
||||
poster={""}
|
||||
src={`${selectedAsset.asset.path}`}
|
||||
>
|
||||
<BigPlayButton position="center" />
|
||||
<ControlBar>
|
||||
<CurrentTimeDisplay order={1.1} />
|
||||
<TimeDivider order={1.2} />
|
||||
<PlaybackRateMenuButton rates={[5, 2, 1, 0.5, 0.25]} order={7.1} />
|
||||
<VolumeMenuButton enabled order={7.2} />
|
||||
</ControlBar>
|
||||
</Player>
|
||||
}
|
||||
<div id="selection-zone" ref={this.selectionRef}>
|
||||
<div id="editor-zone" className="full-size" />
|
||||
</div>
|
||||
<div id="ct-zone" className={this.state.canvasEnabled ? "canvas-enabled" : "canvas-disabled"}>
|
||||
{selectedAsset.asset.type === AssetType.Video &&
|
||||
<Player ref={this.videoPlayer}
|
||||
fluid={false} width={"100%"} height={"100%"}
|
||||
autoPlay={true}
|
||||
poster={""}
|
||||
src={`${selectedAsset.asset.path}`}
|
||||
>
|
||||
<BigPlayButton position="center" />
|
||||
<ControlBar>
|
||||
<CurrentTimeDisplay order={1.1} />
|
||||
<TimeDivider order={1.2} />
|
||||
<PlaybackRateMenuButton rates={[5, 2, 1, 0.5, 0.25]} order={7.1} />
|
||||
<VolumeMenuButton enabled order={7.2} />
|
||||
</ControlBar>
|
||||
</Player>
|
||||
}
|
||||
<div id="selection-zone" className={`asset-${this.getAssetType()}`}>
|
||||
<div id="editor-zone" className="full-size" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -369,17 +374,22 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
|
|||
}
|
||||
|
||||
/**
|
||||
* @name updateEditor
|
||||
* @description updates the background of the canvas and draws the asset's regions
|
||||
* @returns {void}
|
||||
* Updates the background of the canvas and draws the asset's regions
|
||||
*/
|
||||
private updateEditor = () => {
|
||||
private async updateEditor() {
|
||||
this.deleteAllRegions();
|
||||
await this.loadAsset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the asset into the canvas editor
|
||||
*/
|
||||
private async loadAsset() {
|
||||
// We need to check if we're looking for a video or image
|
||||
if (this.props.selectedAsset.asset.type === AssetType.Image) {
|
||||
this.loadImage();
|
||||
await this.loadImage();
|
||||
} else if (this.props.selectedAsset.asset.type === AssetType.Video) {
|
||||
this.loadVideo();
|
||||
await this.loadVideo();
|
||||
} else {
|
||||
// We don't know what type of asset this is?
|
||||
throw new AppError(ErrorCode.CanvasError, strings.editorPage.assetError);
|
||||
|
@ -387,61 +397,66 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
|
|||
}
|
||||
|
||||
/**
|
||||
* @name loadVideo
|
||||
* @description loads a video into the canvas
|
||||
* @returns {void}
|
||||
* loads a video into the canvas
|
||||
*/
|
||||
private loadVideo() {
|
||||
if (this.playerRef && this.playerRef.current && this.selectionRef && this.selectionRef.current) {
|
||||
// Update the selection div to remove 2.0em from the height (height of the control bar)
|
||||
this.selectionRef.current.style.height = "calc(100% - 2.0em)";
|
||||
this.playerRef.current.subscribeToStateChange((state, prev) => {
|
||||
// If the video is paused, add this frame to the editor content
|
||||
if (state.paused && !state.waiting && state.hasStarted) {
|
||||
// If we're paused, make sure we're behind the canvas so we can tag
|
||||
this.playerRef.current.manager.rootElement.style.zIndex = 0;
|
||||
this.editor.addContentSource(this.playerRef.current.video.video);
|
||||
this.updateRegions();
|
||||
} else if (!state.paused) {
|
||||
// We need to make sure we're on top if we are playing
|
||||
this.playerRef.current.manager.rootElement.style.zIndex = 9001;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Something has gone majorly wrong to get to this spot
|
||||
throw new AppError(ErrorCode.CanvasError, strings.errors.unknown.message);
|
||||
}
|
||||
this.setState({ canvasEnabled: false });
|
||||
this.videoPlayer.current.subscribeToStateChange(this.onVideoStateChange.bind(this));
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* @name loadImage
|
||||
* @description loads an image into the canvas
|
||||
* @returns {void}
|
||||
* loads an image into the canvas
|
||||
*/
|
||||
private loadImage() {
|
||||
const image = new Image();
|
||||
image.addEventListener("load", (e) => {
|
||||
// @ts-ignore
|
||||
this.editor.addContentSource(e.target);
|
||||
this.updateRegions();
|
||||
return new Promise((resolve) => {
|
||||
const image = new Image();
|
||||
image.addEventListener("load", async (e) => {
|
||||
// @ts-ignore
|
||||
await this.editor.addContentSource(e.target);
|
||||
this.updateRegions();
|
||||
resolve();
|
||||
});
|
||||
image.src = this.props.selectedAsset.asset.path;
|
||||
});
|
||||
image.src = this.props.selectedAsset.asset.path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reacts to changes in the video player state
|
||||
* @param state The current state of the video player
|
||||
* @param prev The previous state of the video player
|
||||
*/
|
||||
private async onVideoStateChange(state, prev) {
|
||||
// If the video is paused, add this frame to the editor content
|
||||
if (state.paused && (state.currentTime !== prev.currentTime || state.seeking !== prev.seeking)) {
|
||||
// If we're paused, make sure we're behind the canvas so we can tag
|
||||
const video = this.videoPlayer.current.video.video as HTMLVideoElement;
|
||||
if (video.videoHeight > 0 && video.videoWidth > 0) {
|
||||
await this.editor.addContentSource(this.videoPlayer.current.video.video);
|
||||
}
|
||||
this.setState({ canvasEnabled: true });
|
||||
this.updateRegions();
|
||||
console.log(state);
|
||||
} else if (!state.paused && state.paused !== prev.paused) {
|
||||
// We need to make sure we're on top if we are playing
|
||||
this.setState({ canvasEnabled: false });
|
||||
}
|
||||
}
|
||||
|
||||
private updateRegions() {
|
||||
if (this.props.selectedAsset.regions.length) {
|
||||
this.props.selectedAsset.regions.forEach((region: IRegion) => {
|
||||
const loadedRegionData = new RegionData(region.boundingBox.left,
|
||||
region.boundingBox.top,
|
||||
region.boundingBox.width,
|
||||
region.boundingBox.height,
|
||||
region.points.map((point) =>
|
||||
new Point2D(point.x, point.y)),
|
||||
this.regionTypeToType(region.type));
|
||||
region.boundingBox.top,
|
||||
region.boundingBox.width,
|
||||
region.boundingBox.height,
|
||||
region.points.map((point) =>
|
||||
new Point2D(point.x, point.y)),
|
||||
this.regionTypeToType(region.type));
|
||||
if (region.tags.length) {
|
||||
this.addRegion(region.id, this.scaleRegionToFrameSize(loadedRegionData),
|
||||
new TagsDescriptor(region.tags.map((tag) => new Tag(tag.name,
|
||||
this.props.project.tags.find((t) => t.name === tag.name).color))));
|
||||
new TagsDescriptor(region.tags.map((tag) => new Tag(tag.name,
|
||||
this.props.project.tags.find((t) => t.name === tag.name).color))));
|
||||
} else {
|
||||
this.addRegion(region.id, this.scaleRegionToFrameSize(loadedRegionData),
|
||||
new TagsDescriptor());
|
||||
|
@ -462,6 +477,17 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
|
|||
});
|
||||
}
|
||||
|
||||
private getAssetType() {
|
||||
switch (this.props.selectedAsset.asset.type) {
|
||||
case AssetType.Image:
|
||||
return "image";
|
||||
case AssetType.Video:
|
||||
return "video";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
private regionTypeToType(regionType: RegionType) {
|
||||
let type;
|
||||
switch (regionType) {
|
||||
|
|
|
@ -135,6 +135,10 @@
|
|||
#ct-zone {
|
||||
position: relative;
|
||||
flex-grow: 1;
|
||||
|
||||
.asset-video {
|
||||
height: calc(100% - 30px);
|
||||
}
|
||||
}
|
||||
|
||||
#selection-zone {
|
||||
|
@ -144,7 +148,6 @@
|
|||
height: 100%;
|
||||
width: 100%;
|
||||
z-index: 0;
|
||||
background-color: $lighter-3;
|
||||
}
|
||||
|
||||
.full-size {
|
||||
|
@ -152,6 +155,36 @@
|
|||
height: 100%;
|
||||
}
|
||||
|
||||
#editor-zone svg {
|
||||
box-shadow: 0px 0px 0.25px #999, 0px 0px 10px rgb(0,0,0);
|
||||
.canvas-enabled {
|
||||
.video-react {
|
||||
z-index: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.canvas-disabled {
|
||||
.video-react {
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
canvas {
|
||||
background-color: #1e2024 !important;
|
||||
}
|
||||
|
||||
.video-react {
|
||||
font-family: inherit;
|
||||
height: calc(100% - 30px) !important;
|
||||
|
||||
&-video {
|
||||
background-color: #1e2024;
|
||||
position: static !important;
|
||||
}
|
||||
|
||||
&-control-bar {
|
||||
position: static !important;
|
||||
}
|
||||
|
||||
* {
|
||||
font-family: inherit;
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче