This commit is contained in:
Wallace Breza 2019-02-01 08:45:00 -08:00 коммит произвёл GitHub
Родитель 9a5fa965f6
Коммит a51dd06417
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 172 добавлений и 94 удалений

41
package-lock.json сгенерированный
Просмотреть файл

@ -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;
}
}