From 5a3ec7d2af51542a01ee78db14340b86debf3671 Mon Sep 17 00:00:00 2001 From: MrKBear Date: Fri, 29 Apr 2022 22:10:45 +0800 Subject: [PATCH] Add record function --- source/Component/Recorder/Recorder.scss | 19 ++++++ source/Component/Recorder/Recorder.tsx | 75 +++++++++++++++++++----- source/Context/Status.tsx | 21 +++++++ source/Localization/EN-US.ts | 2 + source/Localization/ZH-CN.ts | 2 + source/Model/Actuator.ts | 2 + source/Model/Clip.ts | 6 ++ source/Panel/ClipPlayer/ClipRecorder.tsx | 53 ++++++++++++++++- 8 files changed, 162 insertions(+), 18 deletions(-) diff --git a/source/Component/Recorder/Recorder.scss b/source/Component/Recorder/Recorder.scss index 84d1461..33a22cc 100644 --- a/source/Component/Recorder/Recorder.scss +++ b/source/Component/Recorder/Recorder.scss @@ -29,6 +29,10 @@ div.recorder-root { } } + div.recorder-slider.disable { + opacity: .6; + } + div.recorder-content { width: 100%; height: 32px; @@ -68,6 +72,11 @@ div.recorder-root { div.ctrl-action-main { font-size: 1.5em; } + + div.ctrl-action.disable { + cursor: not-allowed; + opacity: .6; + } } div.speed-view { @@ -105,6 +114,11 @@ div.recorder-root.light { background-color: $lt-bg-color-lvl3-light; color: $lt-font-color-lvl1-light; } + + div.ctrl-button div.ctrl-action.disable:hover { + background-color: $lt-bg-color-lvl4-light; + color: $lt-font-color-normal-light; + } } } @@ -132,5 +146,10 @@ div.recorder-root.dark { background-color: $lt-bg-color-lvl3-dark; color: $lt-font-color-lvl1-dark; } + + div.ctrl-button div.ctrl-action.disable:hover { + background-color: $lt-bg-color-lvl4-dark; + color: $lt-font-color-normal-dark; + } } } \ No newline at end of file diff --git a/source/Component/Recorder/Recorder.tsx b/source/Component/Recorder/Recorder.tsx index c5ffef2..64ec2b1 100644 --- a/source/Component/Recorder/Recorder.tsx +++ b/source/Component/Recorder/Recorder.tsx @@ -5,20 +5,22 @@ import { Component, ReactNode } from "react"; import "./Recorder.scss"; interface IRecorderProps { - mode?: "P" | "R", + mode: "P" | "R", + running?: boolean, name?: string; fps?: number; allFrame?: number; currentFrame?: number; allTime?: number; currentTime?: number; + action?: () => void; } class Recorder extends Component { private parseTime(time?: number): string { if (time === undefined) { - return "--:--:--:--"; + return "0:0:0:0"; } const h = Math.floor(time / 3600); const m = Math.floor((time % 3600) / 60); @@ -27,7 +29,50 @@ class Recorder extends Component { return `${h}:${m}:${s}:${ms}`; } + private getRecordInfo(): ReactNode { + if (this.props.mode === "P") { + return ; + } + else if (this.props.mode === "R") { + return ; + } + } + + private getActionIcon(): string { + if (this.props.mode === "P") { + if (this.props.running) { + return "Pause"; + } else { + return "Play"; + } + } + else if (this.props.mode === "R") { + if (this.props.running) { + return "Stop"; + } else { + return "CircleStop"; + } + } + return "Play"; + } + public render(): ReactNode { + + const isSliderDisable = this.props.mode === "R"; + const isJumpDisable = this.props.mode === "R"; + return { >
- + {this.getRecordInfo()}
-
+
-
- +
+
-
+
- {this.props.name} + { + this.props.name ? + {this.props.name} : + + }
; diff --git a/source/Context/Status.tsx b/source/Context/Status.tsx index 0a7d1d8..9821508 100644 --- a/source/Context/Status.tsx +++ b/source/Context/Status.tsx @@ -14,6 +14,7 @@ import { PopupController } from "@Context/Popups"; import { Behavior } from "@Model/Behavior"; import { IParameter, IParamValue } from "@Model/Parameter"; import { Actuator } from "@Model/Actuator"; +import { Clip } from "@Model/Clip"; function randomColor(unNormal: boolean = false) { const color = [ @@ -35,14 +36,17 @@ interface IStatusEvent { fileChange: void; renderLoop: number; physicsLoop: number; + recordLoop: number; mouseModChange: void; focusObjectChange: void; focusLabelChange: void; focusBehaviorChange: void; objectChange: void; + focusClipChange: void; rangeLabelChange: void; groupLabelChange: void; groupBehaviorChange: void; + clipChange: void; labelChange: void; rangeAttrChange: void; labelAttrChange: void; @@ -98,6 +102,11 @@ class Status extends Emitter { */ public focusBehavior?: Behavior; + /** + * 焦点行为 + */ + public focusClip?: Clip; + private drawTimer?: NodeJS.Timeout; private delayDraw = () => { @@ -119,11 +128,13 @@ class Status extends Emitter { // 循环事件 this.actuator.on("loop", (t) => { this.emit("physicsLoop", t) }); + this.actuator.on("record", (t) => { this.emit("recordLoop", t) }); // 对象变化事件 this.model.on("objectChange", () => this.emit("objectChange")); this.model.on("labelChange", () => this.emit("labelChange")); this.model.on("behaviorChange", () => this.emit("behaviorChange")); + this.model.on("clipChange", () => this.emit("clipChange")); // 弹窗事件 this.popup.on("popupChange", () => this.emit("popupChange")); @@ -220,6 +231,16 @@ class Status extends Emitter { this.emit("focusBehaviorChange"); } + /** + * 更新焦点行为 + */ + public setClipObject(clip?: Clip) { + if (this.focusClip !== clip) { + this.focusClip = clip; + } + this.emit("focusClipChange"); + } + /** * 修改范围属性 */ diff --git a/source/Localization/EN-US.ts b/source/Localization/EN-US.ts index d91c50c..dd250fc 100644 --- a/source/Localization/EN-US.ts +++ b/source/Localization/EN-US.ts @@ -56,6 +56,8 @@ const EN_US = { "Panel.Title.Behavior.Clip.Player": "Recording", "Panel.Info.Behavior.Clip.Player": "Pre render recorded data", "Panel.Info.Behavior.Clip.Time.Formate": "{current} / {all} / {fps}fps", + "Panel.Info.Behavior.Clip.Record.Formate": "Record: {time}", + "Panel.Info.Behavior.Clip.Uname.Clip": "Waiting for recording...", "Popup.Title.Unnamed": "Popup message", "Popup.Title.Confirm": "Confirm message", "Popup.Action.Yes": "Confirm", diff --git a/source/Localization/ZH-CN.ts b/source/Localization/ZH-CN.ts index a538e1d..8f44553 100644 --- a/source/Localization/ZH-CN.ts +++ b/source/Localization/ZH-CN.ts @@ -56,6 +56,8 @@ const ZH_CN = { "Panel.Title.Behavior.Clip.Player": "录制", "Panel.Info.Behavior.Clip.Player": "预渲染录制数据", "Panel.Info.Behavior.Clip.Time.Formate": "{current} / {all} / {fps} fps", + "Panel.Info.Behavior.Clip.Record.Formate": "录制: {time}", + "Panel.Info.Behavior.Clip.Uname.Clip": "等待录制...", "Popup.Title.Unnamed": "弹窗消息", "Popup.Title.Confirm": "确认消息", "Popup.Action.Yes": "确定", diff --git a/source/Model/Actuator.ts b/source/Model/Actuator.ts index 47dfd04..bd073df 100644 --- a/source/Model/Actuator.ts +++ b/source/Model/Actuator.ts @@ -11,6 +11,7 @@ enum ActuatorModel { interface IActuatorEvent { startChange: boolean; + record: number; loop: number; } @@ -130,6 +131,7 @@ class Actuator extends Emitter { this.mod === ActuatorModel.Offline ) { this.recordClip?.record(this.alignTimer * this.speed); + this.emit("record", this.alignTimer); } this.emit("loop", this.alignTimer); diff --git a/source/Model/Clip.ts b/source/Model/Clip.ts index df8dfcf..a5c0079 100644 --- a/source/Model/Clip.ts +++ b/source/Model/Clip.ts @@ -24,6 +24,11 @@ class Clip { public id: string; + /** + * 时间 + */ + public time: number = 0; + /** * 用户自定义名称 */ @@ -76,6 +81,7 @@ class Clip { duration: t }; + this.time += t; this.frames.push(frame); return frame; diff --git a/source/Panel/ClipPlayer/ClipRecorder.tsx b/source/Panel/ClipPlayer/ClipRecorder.tsx index 9304ff3..65f0a93 100644 --- a/source/Panel/ClipPlayer/ClipRecorder.tsx +++ b/source/Panel/ClipPlayer/ClipRecorder.tsx @@ -1,9 +1,58 @@ import { Component, ReactNode } from "react"; +import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status"; import { Recorder } from "@Component/Recorder/Recorder"; +import { ActuatorModel } from "@Model/Actuator"; -class ClipRecorder extends Component { +@useStatusWithEvent("actuatorStartChange", "focusClipChange", "recordLoop") +class ClipRecorder extends Component { public render(): ReactNode { - return + + let mod: "P" | "R" = this.props.status?.focusClip ? "P" : "R"; + let runner: boolean = false; + let currentTime: number = 0; + + // 是否开始录制 + if (mod === "R") { + + // 是否正在录制 + runner = this.props.status?.actuator.mod === ActuatorModel.Record || + this.props.status?.actuator.mod === ActuatorModel.Offline; + + currentTime = this.props.status?.actuator.recordClip?.time ?? 0; + } + + else if (mod === "P") { + + // 是否正在播放 + runner = this.props.status?.actuator.mod === ActuatorModel.Play; + } + + return { + + // 开启录制 + if (mod === "R" && !runner) { + + // 获取新实例 + let newClip = this.props.status?.model.addClip(); + + // 开启录制时钟 + this.props.status?.actuator.startRecord(newClip!); + console.log("ClipRecorder: Rec start..."); + } + + // 暂停录制 + if (mod === "R" && runner) { + + // 暂停录制时钟 + this.props.status?.actuator.endRecord(); + console.log("ClipRecorder: Rec end..."); + } + }} + /> } }