Alejandro Toledo 5 år sedan
förälder
incheckning
e2b6055e7e

+ 2 - 2
inspector/src/components/actionTabs/tabs/propertyGrids/animations/anchorSvgPoint.tsx

@@ -40,9 +40,9 @@ export class AnchorSvgPoint extends React.Component<IAnchorSvgPointProps, { visi
         return (
             <>
                 <svg x={this.state.visiblePoint.x} y={this.state.visiblePoint.y} style={{ overflow: "visible" }} onClick={() => this.select()}>
-                    <circle type={this.props.type} data-id={this.props.index} className={`draggable control-point ${this.props.active ? "active" : ""}`} cx="0" cy="0" r="0.75%" stroke="white" strokeWidth={this.props.selected ? 0 : 0} fill={this.props.active ? "#e9db1e" : "white"} />
+                    <circle type={this.props.type} data-id={this.props.index} className={`draggable control-point ${this.props.active ? "active" : ""}`} cx="0" cy="0" r="0.75%" stroke="aqua" strokeWidth={this.props.selected ? 1 : 0} fill={this.props.selected ? "red" : "#e9db1e"} />
                 </svg>
-                <svg x={this.props.control.x} y={this.props.control.y} style={{ overflow: "visible", display: "none" }} onClick={() => this.select()}>
+                <svg x={this.props.control.x} y={this.props.control.y} style={{ overflow: "visible", display: "none" }}>
                     <circle type={this.props.type} data-id={this.props.index} className={`control-point ${this.props.active ? "active" : ""}`} cx="0" cy="0" r="0.7%" stroke="white" strokeWidth={this.props.selected ? 0 : 0} fill={this.props.active ? "#e9db1e" : "white"} />
                 </svg>
                 <line className={`control-point ${this.props.active ? "active" : ""}`} x1={this.props.anchor.x} y1={this.props.anchor.y} x2={this.state.visiblePoint.x} y2={this.state.visiblePoint.y} strokeWidth="0.8%" />

+ 3 - 5
inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationCurveEditorComponent.tsx

@@ -25,7 +25,6 @@ import { ScaleLabel } from "./scale-label";
 require("./curveEditor.scss");
 
 interface IAnimationCurveEditorComponentProps {
-    close: (event: any) => void;
     playOrPause?: () => void;
     scene: Scene;
     entity: IAnimatable | TargetedAnimation;
@@ -374,7 +373,7 @@ export class AnimationCurveEditorComponent extends React.Component<
     selectedControlPoint(type: string, id: string) {
         let updatedKeyframes = this.state.svgKeyframes?.map((kf) => {
             if (kf.id === id) {
-                this.setState({ isFlatTangentMode: false });
+                //this.setState({ isFlatTangentMode: false });
                 if (type === "left") {
                     kf.isLeftActive = !kf.isLeftActive;
                     kf.isRightActive = false;
@@ -1023,7 +1022,7 @@ export class AnimationCurveEditorComponent extends React.Component<
         } else {
             const { easingMode, easingType, usesTangents, valueType, highestFrame, name, targetProperty } = this.getAnimationData(animation);
 
-            keyframes = this.flatTangents(keyframes, valueType);
+            //keyframes = this.flatTangents(keyframes, valueType); // This will break because we are using setState before mounted...
             const startKey = keyframes[0];
             let middle = this._heightScale / this._scaleFactor; //?
             let collection: ICurveData[] = [];
@@ -1445,7 +1444,7 @@ export class AnimationCurveEditorComponent extends React.Component<
                     const firstFrame = keys[0].frame;
                     const LastFrame = this.state.selected.getHighestFrame();
                     this._mainAnimatable = this.props.scene.beginAnimation(target, firstFrame, LastFrame, this.state.isLooping);
-                    this._mainAnimatable.pause();
+                    this._mainAnimatable.stop();
                 }
             }
         }
@@ -1662,7 +1661,6 @@ export class AnimationCurveEditorComponent extends React.Component<
                     setKeyframeValue={this.setKeyframeValueFromInput}
                     enabled={this.state.selected === null || this.state.selected === undefined ? false : true}
                     title={this._entityName}
-                    close={this.props.close}
                     actionableKeyframe={this.state.actionableKeyframe}
                     handleFrameChange={(e) => this.handleFrameChange(e)}
                     handleValueChange={(e) => this.handleValueChange(e)}

+ 211 - 316
inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationPropertyGridComponent.tsx

@@ -1,349 +1,244 @@
-import * as React from 'react';
-
-import { Observable, Observer } from 'babylonjs/Misc/observable';
-import { Scene } from 'babylonjs/scene';
-
-import { PropertyChangedEvent } from '../../../../propertyChangedEvent';
-import { ButtonLineComponent } from '../../../lines/buttonLineComponent';
-import { LineContainerComponent } from '../../../lineContainerComponent';
-import { SliderLineComponent } from '../../../lines/sliderLineComponent';
-import { LockObject } from '../lockObject';
-import { GlobalState } from '../../../../globalState';
-import { Animation } from 'babylonjs/Animations/animation';
-import { Animatable } from 'babylonjs/Animations/animatable';
-import { AnimationPropertiesOverride } from 'babylonjs/Animations/animationPropertiesOverride';
-import { AnimationRange } from 'babylonjs/Animations/animationRange';
-import { CheckBoxLineComponent } from '../../../lines/checkBoxLineComponent';
-import { Nullable } from 'babylonjs/types';
-import { FloatLineComponent } from '../../../lines/floatLineComponent';
-import { TextLineComponent } from '../../../lines/textLineComponent';
-import { IAnimatable } from 'babylonjs/Animations/animatable.interface';
-import { AnimationCurveEditorComponent } from '../animations/animationCurveEditorComponent';
-import { PopupComponent } from '../../../../popupComponent';
+import * as React from "react";
+
+import { Observable, Observer } from "babylonjs/Misc/observable";
+import { Scene } from "babylonjs/scene";
+
+import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
+import { ButtonLineComponent } from "../../../lines/buttonLineComponent";
+import { LineContainerComponent } from "../../../lineContainerComponent";
+import { SliderLineComponent } from "../../../lines/sliderLineComponent";
+import { LockObject } from "../lockObject";
+import { GlobalState } from "../../../../globalState";
+import { Animation } from "babylonjs/Animations/animation";
+import { Animatable } from "babylonjs/Animations/animatable";
+import { AnimationPropertiesOverride } from "babylonjs/Animations/animationPropertiesOverride";
+import { AnimationRange } from "babylonjs/Animations/animationRange";
+import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
+import { Nullable } from "babylonjs/types";
+import { FloatLineComponent } from "../../../lines/floatLineComponent";
+import { TextLineComponent } from "../../../lines/textLineComponent";
+import { IAnimatable } from "babylonjs/Animations/animatable.interface";
+import { AnimationCurveEditorComponent } from "../animations/animationCurveEditorComponent";
+import { PopupComponent } from "../../../../popupComponent";
 
 interface IAnimationGridComponentProps {
-  globalState: GlobalState;
-  animatable: IAnimatable;
-  scene: Scene;
-  lockObject: LockObject;
-  onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+    globalState: GlobalState;
+    animatable: IAnimatable;
+    scene: Scene;
+    lockObject: LockObject;
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
 }
 
-export class AnimationGridComponent extends React.Component<
-  IAnimationGridComponentProps,
-  { currentFrame: number }
-> {
-  private _animations: Nullable<Animation[]> = null;
-  private _ranges: AnimationRange[];
-  private _mainAnimatable: Nullable<Animatable>;
-  private _onBeforeRenderObserver: Nullable<Observer<Scene>>;
-  private _isPlaying = false;
-  private timelineRef: React.RefObject<SliderLineComponent>;
-  private _isCurveEditorOpen = false;
-  private _animationControl = {
-    from: 0,
-    to: 0,
-    loop: false,
-  };
+export class AnimationGridComponent extends React.Component<IAnimationGridComponentProps, { currentFrame: number }> {
+    private _animations: Nullable<Animation[]> = null;
+    private _ranges: AnimationRange[];
+    private _mainAnimatable: Nullable<Animatable>;
+    private _onBeforeRenderObserver: Nullable<Observer<Scene>>;
+    private _isPlaying = false;
+    private timelineRef: React.RefObject<SliderLineComponent>;
+    private _isCurveEditorOpen = false;
+    private _animationControl = {
+        from: 0,
+        to: 0,
+        loop: false,
+    };
+
+    constructor(props: IAnimationGridComponentProps) {
+        super(props);
+
+        this.state = { currentFrame: 0 };
+
+        const animatableAsAny = this.props.animatable as any;
+
+        this._ranges = animatableAsAny.getAnimationRanges ? animatableAsAny.getAnimationRanges() : [];
+        if (animatableAsAny.getAnimatables) {
+            const animatables = animatableAsAny.getAnimatables();
+            this._animations = new Array<Animation>();
+
+            animatables.forEach((animatable: IAnimatable) => {
+                if (animatable.animations) {
+                    this._animations!.push(...animatable.animations);
+                }
+            });
+
+            if (animatableAsAny.animations) {
+                this._animations!.push(...animatableAsAny.animations);
+            }
 
-  constructor(props: IAnimationGridComponentProps) {
-    super(props);
+            // Extract from and to
+            if (this._animations && this._animations.length) {
+                this._animations.forEach((animation) => {
+                    let keys = animation.getKeys();
 
-    this.state = { currentFrame: 0 };
+                    if (keys && keys.length > 0) {
+                        if (keys[0].frame < this._animationControl.from) {
+                            this._animationControl.from = keys[0].frame;
+                        }
+                        const lastKeyIndex = keys.length - 1;
+                        if (keys[lastKeyIndex].frame > this._animationControl.to) {
+                            this._animationControl.to = keys[lastKeyIndex].frame;
+                        }
+                    }
+                });
+            }
+        }
 
-    const animatableAsAny = this.props.animatable as any;
+        this.timelineRef = React.createRef();
+    }
 
-    this._ranges = animatableAsAny.getAnimationRanges
-      ? animatableAsAny.getAnimationRanges()
-      : [];
-    if (animatableAsAny.getAnimatables) {
-      const animatables = animatableAsAny.getAnimatables();
-      this._animations = new Array<Animation>();
+    playOrPause() {
+        const animatable = this.props.animatable;
+        this._isPlaying = this.props.scene.getAllAnimatablesByTarget(animatable).length > 0;
 
-      animatables.forEach((animatable: IAnimatable) => {
-        if (animatable.animations) {
-          this._animations!.push(...animatable.animations);
+        if (this._isPlaying) {
+            this.props.scene.stopAnimation(this.props.animatable);
+            this._mainAnimatable = null;
+        } else {
+            this._mainAnimatable = this.props.scene.beginAnimation(this.props.animatable, this._animationControl.from, this._animationControl.to, this._animationControl.loop);
         }
-      });
-
-      if (animatableAsAny.animations) {
-        this._animations!.push(...animatableAsAny.animations);
-      }
-
-      // Extract from and to
-      if (this._animations && this._animations.length) {
-        this._animations.forEach((animation) => {
-          let keys = animation.getKeys();
+        this.forceUpdate();
+    }
 
-          if (keys && keys.length > 0) {
-            if (keys[0].frame < this._animationControl.from) {
-              this._animationControl.from = keys[0].frame;
+    componentDidMount() {
+        this._onBeforeRenderObserver = this.props.scene.onBeforeRenderObservable.add(() => {
+            if (!this._isPlaying || !this._mainAnimatable) {
+                return;
             }
-            const lastKeyIndex = keys.length - 1;
-            if (keys[lastKeyIndex].frame > this._animationControl.to) {
-              this._animationControl.to = keys[lastKeyIndex].frame;
-            }
-          }
+            this.setState({ currentFrame: this._mainAnimatable.masterFrame });
         });
-      }
     }
 
-    this.timelineRef = React.createRef();
-  }
-
-  playOrPause() {
-    const animatable = this.props.animatable;
-    this._isPlaying =
-      this.props.scene.getAllAnimatablesByTarget(animatable).length > 0;
-
-    if (this._isPlaying) {
-      this.props.scene.stopAnimation(this.props.animatable);
-      this._mainAnimatable = null;
-    } else {
-      this._mainAnimatable = this.props.scene.beginAnimation(
-        this.props.animatable,
-        this._animationControl.from,
-        this._animationControl.to,
-        this._animationControl.loop
-      );
+    componentWillUnmount() {
+        if (this._onBeforeRenderObserver) {
+            this.props.scene.onBeforeRenderObservable.remove(this._onBeforeRenderObserver);
+            this._onBeforeRenderObserver = null;
+        }
     }
-    this.forceUpdate();
-  }
 
-  componentDidMount() {
-    this._onBeforeRenderObserver = this.props.scene.onBeforeRenderObservable.add(
-      () => {
-        if (!this._isPlaying || !this._mainAnimatable) {
-          return;
+    onCurrentFrameChange(value: number) {
+        if (!this._mainAnimatable) {
+            return;
         }
-        this.setState({ currentFrame: this._mainAnimatable.masterFrame });
-      }
-    );
-  }
 
-  componentWillUnmount() {
-    if (this._onBeforeRenderObserver) {
-      this.props.scene.onBeforeRenderObservable.remove(
-        this._onBeforeRenderObserver
-      );
-      this._onBeforeRenderObserver = null;
+        this._mainAnimatable.goToFrame(value);
+        this.setState({ currentFrame: value });
     }
-  }
 
-  onCurrentFrameChange(value: number) {
-    if (!this._mainAnimatable) {
-      return;
+    onChangeFromOrTo() {
+        this.playOrPause();
+        if (this._isPlaying) {
+            this.playOrPause();
+        }
     }
 
-    this._mainAnimatable.goToFrame(value);
-    this.setState({ currentFrame: value });
-  }
-
-  onChangeFromOrTo() {
-    this.playOrPause();
-    if (this._isPlaying) {
-      this.playOrPause();
+    onOpenAnimationCurveEditor() {
+        this._isCurveEditorOpen = true;
     }
-  }
-
-  onOpenAnimationCurveEditor() {
-    this._isCurveEditorOpen = true;
-  }
 
-  onCloseAnimationCurveEditor(window: Window | null) {
-    this._isCurveEditorOpen = false;
-    if (window !== null) {
-      window.close();
+    onCloseAnimationCurveEditor(window: Window | null) {
+        this._isCurveEditorOpen = false;
+        if (window !== null) {
+            window.close();
+        }
     }
-  }
-
-  render() {
-    const animatable = this.props.animatable;
-    const animatableAsAny = this.props.animatable as any;
-
-    let animatablesForTarget = this.props.scene.getAllAnimatablesByTarget(
-      animatable
-    );
-    this._isPlaying = animatablesForTarget.length > 0;
 
-    if (this._isPlaying && !this._mainAnimatable) {
-      this._mainAnimatable = animatablesForTarget[0];
-      if (this._mainAnimatable) {
-        this._animationControl.from = this._mainAnimatable.fromFrame;
-        this._animationControl.to = this._mainAnimatable.toFrame;
-        this._animationControl.loop = this._mainAnimatable.loopAnimation;
-      }
-    }
+    render() {
+        const animatable = this.props.animatable;
+        const animatableAsAny = this.props.animatable as any;
 
-    let animations = animatable.animations;
+        let animatablesForTarget = this.props.scene.getAllAnimatablesByTarget(animatable);
+        this._isPlaying = animatablesForTarget.length > 0;
 
-    return (
-      <div>
-        {this._ranges.length > 0 && (
-          <LineContainerComponent
-            globalState={this.props.globalState}
-            title='ANIMATION RANGES'
-          >
-            {this._ranges.map((range, i) => {
-              return (
-                <ButtonLineComponent
-                  key={range.name + i}
-                  label={range.name}
-                  onClick={() => {
-                    this._mainAnimatable = null;
-                    this.props.scene.beginAnimation(
-                      animatable,
-                      range.from,
-                      range.to,
-                      true
-                    );
-                  }}
-                />
-              );
-            })}
-          </LineContainerComponent>
-        )}
-        {animations && (
-          <>
-            <LineContainerComponent
-              globalState={this.props.globalState}
-              title='ANIMATIONS'
-            >
-              <TextLineComponent
-                label='Count'
-                value={animations.length.toString()}
-              />
-              <ButtonLineComponent
-                label='Edit'
-                onClick={() => this.onOpenAnimationCurveEditor()}
-              />
-              {animations.map((anim, i) => {
-                return (
-                  <TextLineComponent
-                    key={anim.targetProperty + i}
-                    label={'#' + i + ' >'}
-                    value={anim.targetProperty}
-                  />
-                );
-              })}
+        if (this._isPlaying && !this._mainAnimatable) {
+            this._mainAnimatable = animatablesForTarget[0];
+            if (this._mainAnimatable) {
+                this._animationControl.from = this._mainAnimatable.fromFrame;
+                this._animationControl.to = this._mainAnimatable.toFrame;
+                this._animationControl.loop = this._mainAnimatable.loopAnimation;
+            }
+        }
 
-              {this._isCurveEditorOpen && (
-                <PopupComponent
-                  id='curve-editor'
-                  title='Curve Animation Editor'
-                  size={{ width: 1024, height: 490 }}
-                  onOpen={(window: Window) => {}}
-                  onClose={(window: Window) =>
-                    this.onCloseAnimationCurveEditor(window)
-                  }
-                >
-                  <AnimationCurveEditorComponent
-                    scene={this.props.scene}
-                    entity={animatableAsAny}
-                    close={(event) =>
-                      this.onCloseAnimationCurveEditor(event.view)
-                    }
-                    lockObject={this.props.lockObject}
-                    playOrPause={() => this.playOrPause()}
-                    globalState={this.props.globalState}
-                  />
-                </PopupComponent>
-              )}
-            </LineContainerComponent>
-            {animations.length > 0 && (
-              <LineContainerComponent
-                globalState={this.props.globalState}
-                title='ANIMATION GENERAL CONTROL'
-              >
-                <FloatLineComponent
-                  lockObject={this.props.lockObject}
-                  isInteger={true}
-                  label='From'
-                  target={this._animationControl}
-                  propertyName='from'
-                  onChange={() => this.onChangeFromOrTo()}
-                />
-                <FloatLineComponent
-                  lockObject={this.props.lockObject}
-                  isInteger={true}
-                  label='To'
-                  target={this._animationControl}
-                  propertyName='to'
-                  onChange={() => this.onChangeFromOrTo()}
-                />
-                <CheckBoxLineComponent
-                  label='Loop'
-                  onSelect={(value) => (this._animationControl.loop = value)}
-                  isSelected={() => this._animationControl.loop}
-                />
-                {this._isPlaying && (
-                  <SliderLineComponent
-                    ref={this.timelineRef}
-                    label='Current frame'
-                    minimum={this._animationControl.from}
-                    maximum={this._animationControl.to}
-                    step={
-                      (this._animationControl.to -
-                        this._animationControl.from) /
-                      1000.0
-                    }
-                    directValue={this.state.currentFrame}
-                    onInput={(value) => this.onCurrentFrameChange(value)}
-                  />
+        let animations = animatable.animations;
+
+        return (
+            <div>
+                {this._ranges.length > 0 && (
+                    <LineContainerComponent globalState={this.props.globalState} title="ANIMATION RANGES">
+                        {this._ranges.map((range, i) => {
+                            return (
+                                <ButtonLineComponent
+                                    key={range.name + i}
+                                    label={range.name}
+                                    onClick={() => {
+                                        this._mainAnimatable = null;
+                                        this.props.scene.beginAnimation(animatable, range.from, range.to, true);
+                                    }}
+                                />
+                            );
+                        })}
+                    </LineContainerComponent>
                 )}
-                <ButtonLineComponent
-                  label={this._isPlaying ? 'Stop' : 'Play'}
-                  onClick={() => this.playOrPause()}
-                />
-                {(this._ranges.length > 0 ||
-                  (this._animations && this._animations.length > 0)) && (
-                  <>
-                    <CheckBoxLineComponent
-                      label='Enable override'
-                      onSelect={(value) => {
-                        if (value) {
-                          animatableAsAny.animationPropertiesOverride = new AnimationPropertiesOverride();
-                          animatableAsAny.animationPropertiesOverride.blendingSpeed = 0.05;
-                        } else {
-                          animatableAsAny.animationPropertiesOverride = null;
-                        }
-                        this.forceUpdate();
-                      }}
-                      isSelected={() =>
-                        animatableAsAny.animationPropertiesOverride != null
-                      }
-                      onValueChanged={() => this.forceUpdate()}
-                    />
-                    {animatableAsAny.animationPropertiesOverride != null && (
-                      <div>
-                        <CheckBoxLineComponent
-                          label='Enable blending'
-                          target={animatableAsAny.animationPropertiesOverride}
-                          propertyName='enableBlending'
-                          onPropertyChangedObservable={
-                            this.props.onPropertyChangedObservable
-                          }
-                        />
-                        <SliderLineComponent
-                          label='Blending speed'
-                          target={animatableAsAny.animationPropertiesOverride}
-                          propertyName='blendingSpeed'
-                          minimum={0}
-                          maximum={0.1}
-                          step={0.01}
-                          onPropertyChangedObservable={
-                            this.props.onPropertyChangedObservable
-                          }
-                        />
-                      </div>
-                    )}
-                  </>
+                {animations && (
+                    <>
+                        <LineContainerComponent globalState={this.props.globalState} title="ANIMATIONS">
+                            <TextLineComponent label="Count" value={animations.length.toString()} />
+                            <ButtonLineComponent label="Edit" onClick={() => this.onOpenAnimationCurveEditor()} />
+                            {animations.map((anim, i) => {
+                                return <TextLineComponent key={anim.targetProperty + i} label={"#" + i + " >"} value={anim.targetProperty} />;
+                            })}
+
+                            {this._isCurveEditorOpen && (
+                                <PopupComponent id="curve-editor" title="Curve Animation Editor" size={{ width: 1024, height: 490 }} onOpen={(window: Window) => {}} onClose={(window: Window) => this.onCloseAnimationCurveEditor(window)}>
+                                    <AnimationCurveEditorComponent scene={this.props.scene} entity={animatableAsAny} lockObject={this.props.lockObject} playOrPause={() => this.playOrPause()} globalState={this.props.globalState} />
+                                </PopupComponent>
+                            )}
+                        </LineContainerComponent>
+                        {animations.length > 0 && (
+                            <LineContainerComponent globalState={this.props.globalState} title="ANIMATION GENERAL CONTROL">
+                                <FloatLineComponent lockObject={this.props.lockObject} isInteger={true} label="From" target={this._animationControl} propertyName="from" onChange={() => this.onChangeFromOrTo()} />
+                                <FloatLineComponent lockObject={this.props.lockObject} isInteger={true} label="To" target={this._animationControl} propertyName="to" onChange={() => this.onChangeFromOrTo()} />
+                                <CheckBoxLineComponent label="Loop" onSelect={(value) => (this._animationControl.loop = value)} isSelected={() => this._animationControl.loop} />
+                                {this._isPlaying && (
+                                    <SliderLineComponent
+                                        ref={this.timelineRef}
+                                        label="Current frame"
+                                        minimum={this._animationControl.from}
+                                        maximum={this._animationControl.to}
+                                        step={(this._animationControl.to - this._animationControl.from) / 1000.0}
+                                        directValue={this.state.currentFrame}
+                                        onInput={(value) => this.onCurrentFrameChange(value)}
+                                    />
+                                )}
+                                <ButtonLineComponent label={this._isPlaying ? "Stop" : "Play"} onClick={() => this.playOrPause()} />
+                                {(this._ranges.length > 0 || (this._animations && this._animations.length > 0)) && (
+                                    <>
+                                        <CheckBoxLineComponent
+                                            label="Enable override"
+                                            onSelect={(value) => {
+                                                if (value) {
+                                                    animatableAsAny.animationPropertiesOverride = new AnimationPropertiesOverride();
+                                                    animatableAsAny.animationPropertiesOverride.blendingSpeed = 0.05;
+                                                } else {
+                                                    animatableAsAny.animationPropertiesOverride = null;
+                                                }
+                                                this.forceUpdate();
+                                            }}
+                                            isSelected={() => animatableAsAny.animationPropertiesOverride != null}
+                                            onValueChanged={() => this.forceUpdate()}
+                                        />
+                                        {animatableAsAny.animationPropertiesOverride != null && (
+                                            <div>
+                                                <CheckBoxLineComponent label="Enable blending" target={animatableAsAny.animationPropertiesOverride} propertyName="enableBlending" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                                                <SliderLineComponent label="Blending speed" target={animatableAsAny.animationPropertiesOverride} propertyName="blendingSpeed" minimum={0} maximum={0.1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                                            </div>
+                                        )}
+                                    </>
+                                )}
+                            </LineContainerComponent>
+                        )}
+                    </>
                 )}
-              </LineContainerComponent>
-            )}
-          </>
-        )}
-      </div>
-    );
-  }
+            </div>
+        );
+    }
 }

+ 0 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/animations/graphActionsBar.tsx

@@ -14,7 +14,6 @@ interface IGraphActionsBarProps {
     lerpMode: boolean;
     actionableKeyframe: IActionableKeyFrame;
     title: string;
-    close: (event: any) => void;
     enabled: boolean;
     setKeyframeValue: (actionableKeyframe: IActionableKeyFrame) => void;
 }

+ 18 - 5
inspector/src/components/actionTabs/tabs/propertyGrids/animations/svgDraggableArea.tsx

@@ -31,6 +31,7 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps, {
     private _playheadSelected: boolean;
     private _movedX: number;
     private _movedY: number;
+    private _isControlKeyPress: boolean;
     readonly _dragBuffer: number;
     readonly _draggingMultiplier: number;
 
@@ -47,6 +48,7 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps, {
         this._movedY = 0;
         this._dragBuffer = 4;
         this._draggingMultiplier = 10;
+        this._isControlKeyPress = false;
 
         this.state = { panX: 0, panY: 0 };
     }
@@ -87,8 +89,10 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps, {
         }
 
         if (e.target.classList.contains("pannable")) {
-            this._active = true;
-            this._panStart.set(e.clientX - e.currentTarget.getBoundingClientRect().left, e.clientY - e.currentTarget.getBoundingClientRect().top);
+            if (this._isControlKeyPress) {
+                this._active = true;
+                this._panStart.set(e.clientX - e.currentTarget.getBoundingClientRect().left, e.clientY - e.currentTarget.getBoundingClientRect().top);
+            }
         }
     }
 
@@ -102,9 +106,11 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps, {
 
             if (coord !== undefined) {
                 if (e.target.classList.contains("pannable")) {
-                    if (this._panStart.x !== 0 && this._panStart.y !== 0) {
-                        this._panStop.set(e.clientX - e.currentTarget.getBoundingClientRect().left, e.clientY - e.currentTarget.getBoundingClientRect().top);
-                        this.panDirection();
+                    if (this._isControlKeyPress) {
+                        if (this._panStart.x !== 0 && this._panStart.y !== 0) {
+                            this._panStop.set(e.clientX - e.currentTarget.getBoundingClientRect().left, e.clientY - e.currentTarget.getBoundingClientRect().top);
+                            this.panDirection();
+                        }
                     }
                 }
                 if (e.currentTarget.classList.contains("linear") && this._playheadDrag !== 0 && this._playheadSelected) {
@@ -225,6 +231,7 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps, {
         e.preventDefault();
         if (e.keyCode === 17) {
             this._draggableArea.current?.style.setProperty("cursor", "grab");
+            this._isControlKeyPress = true;
         }
     }
 
@@ -232,6 +239,12 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps, {
         e.preventDefault();
         if (e.keyCode === 17) {
             this._draggableArea.current?.style.setProperty("cursor", "initial");
+            this._isControlKeyPress = false;
+            this._active = false;
+            this._panStart.set(0, 0);
+            this._panStop.set(0, 0);
+            this._movedX = 0;
+            this._movedY = 0;
         }
 
         if (e.keyCode === 8 || e.keyCode === 46) {

+ 77 - 126
inspector/src/components/actionTabs/tabs/propertyGrids/animations/targetedAnimationPropertyGridComponent.tsx

@@ -1,145 +1,96 @@
-import * as React from 'react';
+import * as React from "react";
 
-import { Observable } from 'babylonjs/Misc/observable';
-import { TargetedAnimation } from 'babylonjs/Animations/animationGroup';
-import { Scene } from 'babylonjs/scene';
+import { Observable } from "babylonjs/Misc/observable";
+import { TargetedAnimation } from "babylonjs/Animations/animationGroup";
+import { Scene } from "babylonjs/scene";
 
-import { PropertyChangedEvent } from '../../../../propertyChangedEvent';
-import { ButtonLineComponent } from '../../../lines/buttonLineComponent';
-import { LineContainerComponent } from '../../../lineContainerComponent';
-import { TextLineComponent } from '../../../lines/textLineComponent';
-import { LockObject } from '../lockObject';
-import { GlobalState } from '../../../../globalState';
-import { TextInputLineComponent } from '../../../lines/textInputLineComponent';
-import { PopupComponent } from '../../../../popupComponent';
-import { AnimationCurveEditorComponent } from '../animations/animationCurveEditorComponent';
-import { AnimationGroup } from 'babylonjs/Animations/animationGroup';
+import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
+import { ButtonLineComponent } from "../../../lines/buttonLineComponent";
+import { LineContainerComponent } from "../../../lineContainerComponent";
+import { TextLineComponent } from "../../../lines/textLineComponent";
+import { LockObject } from "../lockObject";
+import { GlobalState } from "../../../../globalState";
+import { TextInputLineComponent } from "../../../lines/textInputLineComponent";
+import { PopupComponent } from "../../../../popupComponent";
+import { AnimationCurveEditorComponent } from "../animations/animationCurveEditorComponent";
+import { AnimationGroup } from "babylonjs/Animations/animationGroup";
 
 interface ITargetedAnimationGridComponentProps {
-  globalState: GlobalState;
-  targetedAnimation: TargetedAnimation;
-  scene: Scene;
-  lockObject: LockObject;
-  onSelectionChangedObservable?: Observable<any>;
-  onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+    globalState: GlobalState;
+    targetedAnimation: TargetedAnimation;
+    scene: Scene;
+    lockObject: LockObject;
+    onSelectionChangedObservable?: Observable<any>;
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
 }
 
-export class TargetedAnimationGridComponent extends React.Component<
-  ITargetedAnimationGridComponentProps
-> {
-  private _isCurveEditorOpen: boolean;
-  private _animationGroup: AnimationGroup | undefined;
-  constructor(props: ITargetedAnimationGridComponentProps) {
-    super(props);
-    this._animationGroup = this.props.scene.animationGroups.find((ag) => {
-      let ta = ag.targetedAnimations.find(
-        (ta) => ta === this.props.targetedAnimation
-      );
-      return ta !== undefined;
-    });
-  }
+export class TargetedAnimationGridComponent extends React.Component<ITargetedAnimationGridComponentProps> {
+    private _isCurveEditorOpen: boolean;
+    private _animationGroup: AnimationGroup | undefined;
+    constructor(props: ITargetedAnimationGridComponentProps) {
+        super(props);
+        this._animationGroup = this.props.scene.animationGroups.find((ag) => {
+            let ta = ag.targetedAnimations.find((ta) => ta === this.props.targetedAnimation);
+            return ta !== undefined;
+        });
+    }
 
-  onOpenAnimationCurveEditor() {
-    this._isCurveEditorOpen = true;
-  }
+    onOpenAnimationCurveEditor() {
+        this._isCurveEditorOpen = true;
+    }
 
-  onCloseAnimationCurveEditor(window: Window | null) {
-    this._isCurveEditorOpen = false;
-    if (window !== null) {
-      window.close();
+    onCloseAnimationCurveEditor(window: Window | null) {
+        this._isCurveEditorOpen = false;
+        if (window !== null) {
+            window.close();
+        }
     }
-  }
 
-  playOrPause() {
-    if (this._animationGroup) {
-      if (this._animationGroup.isPlaying) {
-        this._animationGroup.stop();
-      } else {
-        this._animationGroup.start();
-      }
-      this.forceUpdate();
+    playOrPause() {
+        if (this._animationGroup) {
+            if (this._animationGroup.isPlaying) {
+                this._animationGroup.stop();
+            } else {
+                this._animationGroup.start();
+            }
+            this.forceUpdate();
+        }
     }
-  }
 
-  deleteAnimation() {
-    if (this._animationGroup) {
-      let index = this._animationGroup.targetedAnimations.indexOf(
-        this.props.targetedAnimation
-      );
+    deleteAnimation() {
+        if (this._animationGroup) {
+            let index = this._animationGroup.targetedAnimations.indexOf(this.props.targetedAnimation);
 
-      if (index > -1) {
-        this._animationGroup.targetedAnimations.splice(index, 1);
-        this.props.onSelectionChangedObservable?.notifyObservers(null);
+            if (index > -1) {
+                this._animationGroup.targetedAnimations.splice(index, 1);
+                this.props.onSelectionChangedObservable?.notifyObservers(null);
 
-        if (this._animationGroup.isPlaying) {
-          this._animationGroup.stop();
-          this._animationGroup.start();
+                if (this._animationGroup.isPlaying) {
+                    this._animationGroup.stop();
+                    this._animationGroup.start();
+                }
+            }
         }
-      }
     }
-  }
 
-  render() {
-    const targetedAnimation = this.props.targetedAnimation;
+    render() {
+        const targetedAnimation = this.props.targetedAnimation;
 
-    return (
-      <div className='pane'>
-        <LineContainerComponent
-          globalState={this.props.globalState}
-          title='GENERAL'
-        >
-          <TextLineComponent
-            label='Class'
-            value={targetedAnimation.getClassName()}
-          />
-          <TextInputLineComponent
-            lockObject={this.props.lockObject}
-            label='Name'
-            target={targetedAnimation.animation}
-            propertyName='name'
-            onPropertyChangedObservable={this.props.onPropertyChangedObservable}
-          />
-          {targetedAnimation.target.name && (
-            <TextLineComponent
-              label='Target'
-              value={targetedAnimation.target.name}
-              onLink={() =>
-                this.props.globalState.onSelectionChangedObservable.notifyObservers(
-                  targetedAnimation
-                )
-              }
-            />
-          )}
-          <ButtonLineComponent
-            label='Edit animation'
-            onClick={() => this.onOpenAnimationCurveEditor()}
-          />
-          {this._isCurveEditorOpen && (
-            <PopupComponent
-              id='curve-editor'
-              title='Curve Animation Editor'
-              size={{ width: 1024, height: 512 }}
-              onOpen={(window: Window) => {}}
-              onClose={(window: Window) =>
-                this.onCloseAnimationCurveEditor(window)
-              }
-            >
-              <AnimationCurveEditorComponent
-                scene={this.props.scene}
-                entity={targetedAnimation as any}
-                playOrPause={() => this.playOrPause()}
-                lockObject={this.props.lockObject}
-                globalState={this.props.globalState}
-                close={(event) => this.onCloseAnimationCurveEditor(event.view)}
-              />
-            </PopupComponent>
-          )}
-          <ButtonLineComponent
-            label='Dispose'
-            onClick={() => this.deleteAnimation()}
-          />
-        </LineContainerComponent>
-      </div>
-    );
-  }
+        return (
+            <div className="pane">
+                <LineContainerComponent globalState={this.props.globalState} title="GENERAL">
+                    <TextLineComponent label="Class" value={targetedAnimation.getClassName()} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Name" target={targetedAnimation.animation} propertyName="name" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    {targetedAnimation.target.name && <TextLineComponent label="Target" value={targetedAnimation.target.name} onLink={() => this.props.globalState.onSelectionChangedObservable.notifyObservers(targetedAnimation)} />}
+                    <ButtonLineComponent label="Edit animation" onClick={() => this.onOpenAnimationCurveEditor()} />
+                    {this._isCurveEditorOpen && (
+                        <PopupComponent id="curve-editor" title="Curve Animation Editor" size={{ width: 1024, height: 512 }} onOpen={(window: Window) => {}} onClose={(window: Window) => this.onCloseAnimationCurveEditor(window)}>
+                            <AnimationCurveEditorComponent scene={this.props.scene} entity={targetedAnimation as any} playOrPause={() => this.playOrPause()} lockObject={this.props.lockObject} globalState={this.props.globalState} />
+                        </PopupComponent>
+                    )}
+                    <ButtonLineComponent label="Dispose" onClick={() => this.deleteAnimation()} />
+                </LineContainerComponent>
+            </div>
+        );
+    }
 }