Add clip model & clipList component & recoder panel #48
@ -57,15 +57,32 @@ div.clip-list-root {
|
|||||||
}
|
}
|
||||||
|
|
||||||
div.clip-item-content {
|
div.clip-item-content {
|
||||||
|
width: calc( 100% - 65px );
|
||||||
|
padding-right: 10px;
|
||||||
|
max-width: 125px;
|
||||||
height: $clip-item-height;
|
height: $clip-item-height;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
|
div {
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.info {
|
||||||
|
opacity: .75;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
div.clip-item:hover {
|
div.clip-item.disable {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.clip-item.able:hover {
|
||||||
|
|
||||||
div.clip-icon-view {
|
div.clip-icon-view {
|
||||||
|
|
||||||
@ -98,7 +115,7 @@ div.dark.clip-list-root {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
div.clip-item:hover {
|
div.clip-item.able:hover {
|
||||||
color: $lt-font-color-lvl2-dark;
|
color: $lt-font-color-lvl2-dark;
|
||||||
background-color: $lt-bg-color-lvl2-dark;
|
background-color: $lt-bg-color-lvl2-dark;
|
||||||
}
|
}
|
||||||
@ -119,7 +136,7 @@ div.light.clip-list-root {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
div.clip-item:hover {
|
div.clip-item.able:hover {
|
||||||
color: $lt-font-color-lvl2-light;
|
color: $lt-font-color-lvl2-light;
|
||||||
background-color: $lt-bg-color-lvl2-light;
|
background-color: $lt-bg-color-lvl2-light;
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { Localization } from "@Component/Localization/Localization";
|
||||||
import { Theme } from "@Component/Theme/Theme";
|
import { Theme } from "@Component/Theme/Theme";
|
||||||
import { Icon } from "@fluentui/react";
|
import { Icon } from "@fluentui/react";
|
||||||
import { Clip } from "@Model/Clip";
|
import { Clip } from "@Model/Clip";
|
||||||
@ -6,20 +7,73 @@ import "./ClipList.scss";
|
|||||||
|
|
||||||
interface IClipListProps {
|
interface IClipListProps {
|
||||||
clips: Clip[];
|
clips: Clip[];
|
||||||
|
focus?: Clip;
|
||||||
|
disable?: boolean;
|
||||||
add?: () => any;
|
add?: () => any;
|
||||||
|
click?: (clip: Clip) => any;
|
||||||
delete?: (clip: Clip) => any;
|
delete?: (clip: Clip) => any;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ClipList extends Component<IClipListProps> {
|
class ClipList extends Component<IClipListProps> {
|
||||||
|
|
||||||
|
private isInnerClick: boolean = false;
|
||||||
|
|
||||||
|
private resolveCallback(fn?: (p: any) => any, p?: any): any {
|
||||||
|
if (this.props.disable) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (fn) {
|
||||||
|
return fn(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseTime(time?: number): string {
|
||||||
|
if (time === undefined) {
|
||||||
|
return "0:0:0:0";
|
||||||
|
}
|
||||||
|
const h = Math.floor(time / 3600);
|
||||||
|
const m = Math.floor((time % 3600) / 60);
|
||||||
|
const s = Math.floor((time % 3600) % 60);
|
||||||
|
const ms = Math.floor((time % 1) * 1000);
|
||||||
|
return `${h}:${m}:${s}:${ms}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getClipInfo(clip: Clip): string {
|
||||||
|
let fps = Math.floor(clip.frames.length / clip.time);
|
||||||
|
if (isNaN(fps)) fps = 0;
|
||||||
|
return `${this.parseTime(clip.time)} ${fps}fps`;
|
||||||
|
}
|
||||||
|
|
||||||
private renderClip(clip: Clip) {
|
private renderClip(clip: Clip) {
|
||||||
|
|
||||||
|
const focus = clip.equal(this.props.focus);
|
||||||
|
const disable = this.props.disable;
|
||||||
|
const classList = ["clip-item"];
|
||||||
|
|
||||||
|
if (focus) {
|
||||||
|
classList.push("focus");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disable) {
|
||||||
|
classList.push("disable");
|
||||||
|
} else {
|
||||||
|
classList.push("able");
|
||||||
|
}
|
||||||
|
|
||||||
return <div
|
return <div
|
||||||
key={clip.id}
|
key={clip.id}
|
||||||
className="clip-item"
|
className={classList.join(" ")}
|
||||||
|
onClick={() => {
|
||||||
|
if (this.isInnerClick) {
|
||||||
|
this.isInnerClick = false;
|
||||||
|
} else {
|
||||||
|
this.resolveCallback(this.props.click, clip);
|
||||||
|
}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div className="clip-item-hole-view">
|
<div className="clip-item-hole-view">
|
||||||
{new Array(4).fill(0).map(() => {
|
{new Array(4).fill(0).map((_, index) => {
|
||||||
return <div className="clip-item-hole"/>
|
return <div className="clip-item-hole" key={index}/>
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<div className="clip-icon-view">
|
<div className="clip-icon-view">
|
||||||
@ -28,21 +82,36 @@ class ClipList extends Component<IClipListProps> {
|
|||||||
iconName="Delete"
|
iconName="Delete"
|
||||||
className="delete"
|
className="delete"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
this.props.delete && this.props.delete(clip);
|
this.isInnerClick = true;
|
||||||
|
this.resolveCallback(this.props.delete, clip);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="clip-item-content">
|
<div className="clip-item-content">
|
||||||
<div className="title">{clip.name}</div>
|
<div className="title">{clip.name}</div>
|
||||||
<div className="info">{clip.frames.length}</div>
|
<div className="info">{
|
||||||
|
clip.isRecording ?
|
||||||
|
<Localization i18nKey="Panel.Info.Behavior.Clip.Uname.Clip"/> :
|
||||||
|
this.getClipInfo(clip)
|
||||||
|
}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderAddButton(): ReactNode {
|
private renderAddButton(): ReactNode {
|
||||||
|
|
||||||
|
const classList = ["clip-item", "add-button"];
|
||||||
|
|
||||||
|
if (this.props.disable) {
|
||||||
|
classList.push("disable");
|
||||||
|
} else {
|
||||||
|
classList.push("able");
|
||||||
|
}
|
||||||
|
|
||||||
return <div
|
return <div
|
||||||
className="clip-item add-button"
|
key="ADD_BUTTON"
|
||||||
onClick={this.props.add}
|
className={classList.join(" ")}
|
||||||
|
onClick={() => this.resolveCallback(this.props.add)}
|
||||||
>
|
>
|
||||||
<Icon iconName="Add"/>
|
<Icon iconName="Add"/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -52,6 +52,7 @@ class Actuator extends Emitter<IActuatorEvent> {
|
|||||||
|
|
||||||
// 记录录制片段
|
// 记录录制片段
|
||||||
this.recordClip = clip;
|
this.recordClip = clip;
|
||||||
|
clip.isRecording = true;
|
||||||
|
|
||||||
// 如果仿真未开启,开启仿真
|
// 如果仿真未开启,开启仿真
|
||||||
if (!this.start()) this.start(true);
|
if (!this.start()) this.start(true);
|
||||||
@ -65,6 +66,9 @@ class Actuator extends Emitter<IActuatorEvent> {
|
|||||||
*/
|
*/
|
||||||
public endRecord() {
|
public endRecord() {
|
||||||
|
|
||||||
|
this.recordClip && (this.recordClip.isRecording = false);
|
||||||
|
this.recordClip = undefined;
|
||||||
|
|
||||||
// 如果仿真未停止,停止仿真
|
// 如果仿真未停止,停止仿真
|
||||||
if (this.start()) this.start(false);
|
if (this.start()) this.start(false);
|
||||||
|
|
||||||
|
@ -44,6 +44,11 @@ class Clip {
|
|||||||
*/
|
*/
|
||||||
public frames: IFrame[] = [];
|
public frames: IFrame[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否正在录制
|
||||||
|
*/
|
||||||
|
public isRecording: boolean = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 录制一帧
|
* 录制一帧
|
||||||
*/
|
*/
|
||||||
|
@ -4,6 +4,7 @@ import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status";
|
|||||||
import { Theme } from "@Component/Theme/Theme";
|
import { Theme } from "@Component/Theme/Theme";
|
||||||
import { Message } from "@Input/Message/Message";
|
import { Message } from "@Input/Message/Message";
|
||||||
import { Clip } from "@Model/Clip";
|
import { Clip } from "@Model/Clip";
|
||||||
|
import { ActuatorModel } from "@Model/Actuator";
|
||||||
import "./ClipPlayer.scss";
|
import "./ClipPlayer.scss";
|
||||||
|
|
||||||
@useStatusWithEvent("clipChange", "focusClipChange", "actuatorStartChange")
|
@useStatusWithEvent("clipChange", "focusClipChange", "actuatorStartChange")
|
||||||
@ -14,8 +15,17 @@ class ClipPlayer extends Component<IMixinStatusProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private renderClipList(clipList: Clip[]): ReactNode {
|
private renderClipList(clipList: Clip[]): ReactNode {
|
||||||
|
|
||||||
|
const disable =
|
||||||
|
!this.props.status?.focusClip &&
|
||||||
|
(
|
||||||
|
this.props.status?.actuator.mod === ActuatorModel.Record ||
|
||||||
|
this.props.status?.actuator.mod === ActuatorModel.Offline
|
||||||
|
);
|
||||||
|
|
||||||
return <ClipList
|
return <ClipList
|
||||||
clips={clipList}
|
clips={clipList}
|
||||||
|
disable={disable}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user