Skip to content

Commit

Permalink
Move frame bar on ACE to the top of the window (#12590)
Browse files Browse the repository at this point in the history
* WIP moving X axis to top

* Positioning bar according to range

* Add active frame indicator to range frame bar

* Linting

* Add proper observers to avoid ismounted
  • Loading branch information
carolhmj committed May 31, 2022
1 parent 27c6742 commit 27e6423
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ export class Context {

onSelectToActivated = new Observable<{ from: number; to: number }>();

onRangeFrameBarResized = new Observable<number>();
onPlayheadMoved = new Observable<number>();

lockLastFrameValue: boolean = false;
lockLastFrameFrame: boolean = false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ export class CanvasComponent extends React.Component<ICanvasComponentProps, ICan
public render() {
return (
<div id="canvas-zone">
<GraphComponent globalState={this.props.globalState} context={this.props.context} />
<FrameBarComponent globalState={this.props.globalState} context={this.props.context} />
<GraphComponent globalState={this.props.globalState} context={this.props.context} />
<PlayHeadComponent context={this.props.context} globalState={this.props.globalState} />
<RangeFrameBarComponent context={this.props.context} globalState={this.props.globalState} />
{this.props.context.activeAnimations.length > 0 && <div id="angle-mode" />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ interface IPlayHeadComponentProps {
context: Context;
}

interface IPlayHeadPixelLocator {
minFrame: number;
maxFrame: number;
width: number;
offset: number;
scale: number;
}

interface IPlayHeadComponentState {}

export class PlayHeadComponent extends React.Component<IPlayHeadComponentProps, IPlayHeadComponentState> {
Expand All @@ -18,8 +26,15 @@ export class PlayHeadComponent extends React.Component<IPlayHeadComponentProps,
private _playHeadCircle: React.RefObject<HTMLDivElement>;
private _onBeforeRenderObserver: Nullable<Observer<Scene>>;
private _onActiveAnimationChangedObserver: Nullable<Observer<IActiveAnimationChangedOptions>>;
private _onRangeFrameBarResizedObserver: Nullable<Observer<number>>;
private _onMoveToFrameRequiredObserver: Nullable<Observer<number>>;
private _onGraphMovedObserver: Nullable<Observer<number>>;
private _onGraphScaledObserver: Nullable<Observer<number>>;
private _viewScale = 1;
private _offsetX = 0;
private _offsetRange = 10;
private _viewWidth = 748;
private readonly _rangeWidthToPlayheadWidth = 40;

private _pointerIsDown: boolean;

Expand All @@ -35,6 +50,10 @@ export class PlayHeadComponent extends React.Component<IPlayHeadComponentProps,
this.forceUpdate();
});

this._onRangeFrameBarResizedObserver = this.props.context.onRangeFrameBarResized.add((width) => {
this._viewWidth = width - this._rangeWidthToPlayheadWidth;
});

this._onBeforeRenderObserver = this.props.context.scene.onBeforeRenderObservable.add(() => {
if (this.props.context.activeAnimations.length === 0) {
return;
Expand All @@ -54,19 +73,19 @@ export class PlayHeadComponent extends React.Component<IPlayHeadComponentProps,
}
});

this.props.context.onMoveToFrameRequired.add((frame) => {
this._onMoveToFrameRequiredObserver = this.props.context.onMoveToFrameRequired.add((frame) => {
this.props.context.moveToFrame(frame);
this._moveHead(frame);
});

this.props.context.onGraphMoved.add((x) => {
this._onGraphMovedObserver = this.props.context.onGraphMoved.add((x) => {
this._offsetX = x;
this.forceUpdate();

this._moveHead(this.props.context.activeFrame);
});

this.props.context.onGraphScaled.add((scale) => {
this._onGraphScaledObserver = this.props.context.onGraphScaled.add((scale) => {
this._viewScale = 1 / scale;
this.forceUpdate();

Expand All @@ -83,6 +102,7 @@ export class PlayHeadComponent extends React.Component<IPlayHeadComponentProps,
this._playHeadCircle.current.innerHTML = frame.toFixed(0);

this.props.context.activeFrame = frame;
this.props.context.onPlayheadMoved.notifyObservers(frame);
}

private _frameToPixel(frame: number) {
Expand All @@ -92,13 +112,12 @@ export class PlayHeadComponent extends React.Component<IPlayHeadComponentProps,
return (((frame - minFrame) / (maxFrame - minFrame)) * this._graphAbsoluteWidth + this._offsetX) * this._viewScale;
}

private _pixelToFrame(pixel: number) {
private _pixelToFrame(pixel: number, locator: IPlayHeadPixelLocator) {
const { minFrame, maxFrame, width, offset, scale } = locator;
const animation = this.props.context.activeAnimations[0];
const keys = animation.getKeys();
const minFrame = this.props.context.referenceMinFrame;
const maxFrame = this.props.context.referenceMaxFrame;

return Math.max(((pixel / this._viewScale - this._offsetX) / this._graphAbsoluteWidth) * (maxFrame - minFrame) + minFrame, keys[0].frame);
return Math.max(((pixel / scale - offset) / width) * (maxFrame - minFrame) + minFrame, keys[0].frame);
}

componentWillUnmount() {
Expand All @@ -110,26 +129,62 @@ export class PlayHeadComponent extends React.Component<IPlayHeadComponentProps,
if (this._onActiveAnimationChangedObserver) {
this.props.context.onActiveAnimationChanged.remove(this._onActiveAnimationChangedObserver);
}

if (this._onRangeFrameBarResizedObserver) {
this.props.context.onRangeFrameBarResized.remove(this._onRangeFrameBarResizedObserver);
}

if (this._onMoveToFrameRequiredObserver) {
this.props.context.onMoveToFrameRequired.remove(this._onMoveToFrameRequiredObserver);
}

if (this._onGraphMovedObserver) {
this.props.context.onGraphMoved.remove(this._onGraphMovedObserver);
}

if (this._onGraphScaledObserver) {
this.props.context.onGraphScaled.remove(this._onGraphScaledObserver);
}
}

private _getPixelValues(isRange: boolean): IPlayHeadPixelLocator {
let minFrame, maxFrame, width, offset, scale;
if (isRange) {
minFrame = this.props.context.fromKey;
maxFrame = this.props.context.toKey;
width = this._viewWidth;
offset = this._offsetRange;
scale = 1;
} else {
minFrame = this.props.context.referenceMinFrame;
maxFrame = this.props.context.referenceMaxFrame;
width = this._graphAbsoluteWidth;
offset = this._offsetX;
scale = this._viewScale;
}
return { minFrame, maxFrame, width, offset, scale };
}

private _onPointerDown(evt: React.PointerEvent<HTMLDivElement>) {
private _onPointerDown(evt: React.PointerEvent<HTMLDivElement>, isRange: boolean) {
evt.preventDefault();

this._pointerIsDown = true;
evt.currentTarget.setPointerCapture(evt.pointerId);

const frame = this._pixelToFrame(evt.nativeEvent.offsetX);
const locator = this._getPixelValues(isRange);
const frame = this._pixelToFrame(evt.nativeEvent.offsetX, locator);
this.props.context.moveToFrame(frame);

this._moveHead(frame);
}

private _onPointerMove(evt: React.PointerEvent<HTMLDivElement>) {
private _onPointerMove(evt: React.PointerEvent<HTMLDivElement>, isRange: boolean) {
if (!this._pointerIsDown) {
return;
}

const frame = this._pixelToFrame(evt.nativeEvent.offsetX);
const locator = this._getPixelValues(isRange);
const frame = this._pixelToFrame(evt.nativeEvent.offsetX, locator);
this.props.context.moveToFrame(frame);

this._moveHead(frame);
Expand All @@ -153,8 +208,14 @@ export class PlayHeadComponent extends React.Component<IPlayHeadComponentProps,
</div>
<div
id="play-head-control"
onPointerDown={(evt) => this._onPointerDown(evt)}
onPointerMove={(evt) => this._onPointerMove(evt)}
onPointerDown={(evt) => this._onPointerDown(evt, false)}
onPointerMove={(evt) => this._onPointerMove(evt, false)}
onPointerUp={(evt) => this._onPointerUp(evt)}
></div>
<div
id="play-head-control-2"
onPointerDown={(evt) => this._onPointerDown(evt, true)}
onPointerMove={(evt) => this._onPointerMove(evt, true)}
onPointerUp={(evt) => this._onPointerUp(evt)}
></div>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export class RangeFrameBarComponent extends React.Component<IRangeFrameBarCompon
private _isMounted = false;

private _onActiveAnimationChangedObserver: Nullable<Observer<IActiveAnimationChangedOptions>>;
private _onPlayheadMovedObserver: Nullable<Observer<number>>;
private _onFrameManuallyEnteredObserver: Nullable<Observer<number>>;

constructor(props: IRangeFrameBarComponentProps) {
super(props);
Expand All @@ -42,6 +44,10 @@ export class RangeFrameBarComponent extends React.Component<IRangeFrameBarCompon
this.forceUpdate();
});

this._onPlayheadMovedObserver = this.props.context.onPlayheadMoved.add(() => {
this.forceUpdate();
});

this.props.context.onFrameSet.add(() => {
if (!this._isMounted) {
return;
Expand All @@ -50,6 +56,14 @@ export class RangeFrameBarComponent extends React.Component<IRangeFrameBarCompon
this.forceUpdate();
});

this._onFrameManuallyEnteredObserver = this.props.context.onFrameManuallyEntered.add(() => {
if (!this._isMounted) {
return;
}

this.forceUpdate();
});

this.props.context.onRangeUpdated.add(() => {
if (!this._isMounted) {
return;
Expand All @@ -67,6 +81,12 @@ export class RangeFrameBarComponent extends React.Component<IRangeFrameBarCompon
if (this._onActiveAnimationChangedObserver) {
this.props.context.onActiveAnimationChanged.remove(this._onActiveAnimationChangedObserver);
}
if (this._onPlayheadMovedObserver) {
this.props.context.onPlayheadMoved.remove(this._onPlayheadMovedObserver);
}
if (this._onFrameManuallyEnteredObserver) {
this.props.context.onFrameManuallyEntered.remove(this._onFrameManuallyEnteredObserver);
}

this._isMounted = false;
}
Expand All @@ -77,6 +97,7 @@ export class RangeFrameBarComponent extends React.Component<IRangeFrameBarCompon
}

this._viewWidth = this._svgHost.current.clientWidth;
this.props.context.onRangeFrameBarResized.notifyObservers(this._viewWidth);
this.forceUpdate();
}

Expand Down Expand Up @@ -106,6 +127,34 @@ export class RangeFrameBarComponent extends React.Component<IRangeFrameBarCompon
});
}

private _buildActiveFrame() {
if (this.props.context.activeFrame !== null && this.props.context.activeFrame !== undefined) {
return null;
}

const from = this.props.context.fromKey;
const to = this.props.context.toKey;

const range = to - from;
const convertRatio = range / this._viewWidth;

const x = (this.props.context.activeFrame - from) / convertRatio;

return (
<line
key={"line-activeFrame"}
x1={x}
y1="0px"
x2={x}
y2="40px"
style={{
stroke: "#ffffff",
strokeWidth: 0.5,
}}
></line>
);
}

private _buildFrames() {
if (this.props.context.activeAnimations.length === 0) {
return null;
Expand Down Expand Up @@ -178,6 +227,7 @@ export class RangeFrameBarComponent extends React.Component<IRangeFrameBarCompon
{this.props.context.activeAnimations.map((a) => {
return this._dropKeyFrames(a);
})}
{this._buildActiveFrame()}
</svg>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
#canvas-zone {
display: grid;
grid-template-columns: 100%;
grid-template-rows: 1fr 30px 10px 40px;
grid-template-rows: 30px 1fr 10px 40px;
overflow: hidden;
position: relative;

#graph {
grid-column: 1;
grid-row: 1;
grid-row: 2;
width: 100%;
height: 100%;
background: #222222;
Expand Down Expand Up @@ -115,7 +115,7 @@

#frame-bar {
grid-column: 1;
grid-row: 2;
grid-row: 1;
width: 100%;
height: 100%;
background: #222222;
Expand Down Expand Up @@ -153,13 +153,22 @@

#play-head-control {
grid-column: 1;
grid-row: 2;
grid-row: 1;
position: absolute;
height: 30px;
left: 40px;
width: calc(100% - 40px);
}

#play-head-control-2 {
grid-column: 1;
grid-row: 4;
position: absolute;
height: 30px;
left: 0px;
width: 100%;
}

#play-head {
grid-column: 1;
grid-row: 1 / 3;
Expand All @@ -170,13 +179,13 @@
margin-left: 40px;
display: grid;
grid-template-columns: 100%;
grid-template-rows: 1fr 22px;
grid-template-rows: 22px 1fr;
transform: translateX(-50%);
pointer-events: none;
z-index: 3;

#play-head-bar {
grid-row: 1;
grid-row: 2;
grid-column: 1;
justify-self: center;
width: 1.5px;
Expand All @@ -186,7 +195,7 @@
}

#play-head-circle {
grid-row: 2;
grid-row: 1;
grid-column: 1;
width: 22px;
height: 22px;
Expand All @@ -204,7 +213,7 @@

#angle-mode {
grid-column: 1;
grid-row: 2;
grid-row: 1;
width: 40px;
height: 100%;
z-index: 1;
Expand Down

0 comments on commit 27e6423

Please sign in to comment.