diff --git a/src/components/stage/stage.css b/src/components/stage/stage.css
index dfce6ba24..17c6e87b4 100644
--- a/src/components/stage/stage.css
+++ b/src/components/stage/stage.css
@@ -8,6 +8,10 @@
*/
display: block;
+ /* Attach border radius directly to canvas to prevent needing overflow:hidden; */
+ border-radius: $space;
+ border: 1px solid $ui-black-transparent;
+
/* @todo: This is for overriding the value being set somewhere. Where is it being set? */
background-color: transparent;
}
@@ -30,10 +34,6 @@
.stage-wrapper {
position: relative;
- border-radius: $space;
- border: 1px solid $ui-black-transparent;
- /* Keep the canvas inside the border radius */
- overflow: hidden;
}
.stage-wrapper-overlay {
@@ -78,3 +78,11 @@
pointer-events: none;
overflow: hidden;
}
+
+.dragging-sprite {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 1000; /* Above everything so it is draggable into other panes */
+ filter: drop-shadow(5px 5px 5px $ui-black-transparent);
+ }
diff --git a/src/components/stage/stage.jsx b/src/components/stage/stage.jsx
index f79bd3d77..37f12706b 100644
--- a/src/components/stage/stage.jsx
+++ b/src/components/stage/stage.jsx
@@ -12,6 +12,7 @@ import styles from './stage.css';
const StageComponent = props => {
const {
canvasRef,
+ dragRef,
height,
isColorPicking,
isFullScreen,
@@ -24,7 +25,7 @@ const StageComponent = props => {
} = props;
const stageSize = getStageSize(isFullScreen, height, width);
-
+
return (
{
)}
+
{isColorPicking ? (
{
StageComponent.propTypes = {
canvasRef: PropTypes.func,
colorInfo: Loupe.propTypes.colorInfo,
+ dragRef: PropTypes.func,
height: PropTypes.number,
isColorPicking: PropTypes.bool,
isFullScreen: PropTypes.bool.isRequired,
@@ -94,6 +102,7 @@ StageComponent.propTypes = {
};
StageComponent.defaultProps = {
canvasRef: () => {},
+ dragRef: () => {},
width: 480,
height: 360
};
diff --git a/src/containers/stage.jsx b/src/containers/stage.jsx
index cf10d5001..688951f73 100644
--- a/src/containers/stage.jsx
+++ b/src/containers/stage.jsx
@@ -34,7 +34,11 @@ class Stage extends React.Component {
'onWheel',
'updateRect',
'questionListener',
- 'setCanvas'
+ 'setCanvas',
+ 'setDragCanvas',
+ 'clearDragCanvas',
+ 'drawDragCanvas',
+ 'positionDragCanvas'
]);
this.state = {
mouseDownTimeoutId: null,
@@ -170,6 +174,7 @@ class Stage extends React.Component {
y: -(spritePosition[1] + this.state.dragOffset[1]),
force: true
});
+ this.positionDragCanvas(mousePosition[0], mousePosition[1]);
}
const coordinates = {
x: mousePosition[0],
@@ -248,11 +253,47 @@ class Stage extends React.Component {
}
this.setState({mouseDownTimeoutId: null});
}
+ drawDragCanvas (drawableData) {
+ const {
+ data,
+ width,
+ height,
+ x,
+ y
+ } = drawableData;
+ this.dragCanvas.width = width;
+ this.dragCanvas.height = height;
+ // Need to convert uint8array from WebGL readPixels into Uint8ClampedArray
+ // for ImageData constructor. Shares underlying buffer, so it is fast.
+ const imageData = new ImageData(
+ new Uint8ClampedArray(data.buffer), width, height);
+ this.dragCanvas.getContext('2d').putImageData(imageData, 0, 0);
+ // Position so that pick location is at (0, 0) so that positionDragCanvas()
+ // can use translation to move to mouse position smoothly.
+ this.dragCanvas.style.left = `${-x}px`;
+ this.dragCanvas.style.top = `${-y}px`;
+ }
+ clearDragCanvas () {
+ this.dragCanvas.width = this.dragCanvas.height = 0;
+ }
+ positionDragCanvas (mouseX, mouseY) {
+ // mouseX/Y are relative to stage top/left, and dragCanvas is already
+ // positioned so that the pick location is at (0,0).
+ this.dragCanvas.style.transform = `translate(${mouseX}px, ${mouseY}px)`;
+ }
onStartDrag (x, y) {
+ if (this.state.dragId) return;
const drawableId = this.renderer.pick(x, y);
if (drawableId === null) return;
const drawableData = this.renderer.extractDrawable(drawableId, x, y);
const targetId = this.props.vm.getTargetIdForDrawableId(drawableId);
+
+ // Only start drags on non-draggable targets in editor drag mode
+ if (!this.props.useEditorDragStyle) {
+ const target = this.props.vm.runtime.getTargetById(targetId);
+ if (!target.draggable) return;
+ }
+
if (targetId === null) return;
this.props.vm.startDrag(targetId);
this.setState({
@@ -260,18 +301,41 @@ class Stage extends React.Component {
dragId: targetId,
dragOffset: drawableData.scratchOffset
});
+ if (this.props.useEditorDragStyle) {
+ this.drawDragCanvas(drawableData);
+ this.positionDragCanvas(x, y);
+ this.props.vm.postSpriteInfo({visible: false});
+ }
}
onStopDrag () {
- this.props.vm.stopDrag(this.state.dragId);
- this.setState({
- isDragging: false,
- dragOffset: null,
- dragId: null
- });
+ const dragId = this.state.dragId;
+ const commonStopDragActions = () => {
+ this.props.vm.stopDrag(dragId);
+ this.setState({
+ isDragging: false,
+ dragOffset: null,
+ dragId: null
+ });
+ };
+ if (this.props.useEditorDragStyle) {
+ // Need to sequence these actions to prevent flickering.
+ this.props.vm.postSpriteInfo({visible: true});
+ setTimeout(() => {
+ this.clearDragCanvas();
+ setTimeout(() => {
+ commonStopDragActions();
+ }, 30);
+ }, 30);
+ } else {
+ commonStopDragActions();
+ }
}
setCanvas (canvas) {
this.canvas = canvas;
}
+ setDragCanvas (canvas) {
+ this.dragCanvas = canvas;
+ }
render () {
const {
vm, // eslint-disable-line no-unused-vars
@@ -282,6 +346,7 @@ class Stage extends React.Component {
({
isColorPicking: state.colorPicker.active,
- isFullScreen: state.stageSize.isFullScreen
+ isFullScreen: state.stageSize.isFullScreen,
+ // Do not use editor drag style in fullscreen mode.
+ useEditorDragStyle: !state.stageSize.isFullScreen
});
const mapDispatchToProps = dispatch => ({