Skip to content
Snippets Groups Projects
Unverified Commit 05335da6 authored by Paul Kaplan's avatar Paul Kaplan Committed by GitHub
Browse files

Merge pull request #3585 from paulkaplan/sprite-highlighter

Add sprite target highlighter on sprite-tile click
parents a119c96d 844cd32a
No related branches found
No related tags found
No related merge requests found
......@@ -89,7 +89,7 @@ to adjust for the border using a different method */
padding-bottom: calc($stage-full-screen-stage-padding + $stage-full-screen-border-width);
}
.monitor-wrapper, .color-picker-wrapper {
.monitor-wrapper, .color-picker-wrapper, .frame-wrapper {
position: absolute;
top: 0;
left: 0;
......@@ -127,3 +127,18 @@ to adjust for the border using a different method */
.question-wrapper {
pointer-events: auto;
}
.frame {
box-sizing: content-box !important;
background: $motion-transparent;
border: 2px solid $motion-primary;
border-radius: 0.5rem;
animation-name: flash;
animation-duration: 0.75s;
animation-fill-mode: forwards; /* Leave at 0 opacity after animation */
}
@keyframes flash {
0% { opacity: 1; }
100% { opacity: 0; }
}
......@@ -6,6 +6,7 @@ import Box from '../box/box.jsx';
import DOMElementRenderer from '../../containers/dom-element-renderer.jsx';
import Loupe from '../loupe/loupe.jsx';
import MonitorList from '../../containers/monitor-list.jsx';
import TargetHighlight from '../../containers/target-highlight.jsx';
import Question from '../../containers/question.jsx';
import MicIndicator from '../mic-indicator/mic-indicator.jsx';
import {STAGE_DISPLAY_SIZES} from '../../lib/layout-constants.js';
......@@ -63,6 +64,13 @@ const StageComponent = props => {
stageSize={stageDimensions}
/>
</Box>
<Box className={styles.frameWrapper}>
<TargetHighlight
className={styles.frame}
stageHeight={stageDimensions.height}
stageWidth={stageDimensions.width}
/>
</Box>
{isColorPicking && colorInfo ? (
<Box className={styles.colorPickerWrapper}>
<Loupe colorInfo={colorInfo} />
......
import bindAll from 'lodash.bindall';
import React from 'react';
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import VM from 'scratch-vm';
class TargetHighlight extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'getPageCoords'
]);
}
// Transform scratch coordinates into page coordinates
getPageCoords (x, y) {
const {stageWidth, stageHeight, vm} = this.props;
// The renderers "nativeSize" is the [width, height] of the stage in scratch-units
const nativeSize = vm.renderer.getNativeSize();
return [
((stageWidth / nativeSize[0]) * x) + (stageWidth / 2),
-((stageHeight / nativeSize[1]) * y) + (stageHeight / 2)
];
}
render () {
const {
className,
highlightedTargetId,
highlightedTargetTime,
vm
} = this.props;
if (!(highlightedTargetId && vm && vm.renderer)) return null;
const target = vm.runtime.getTargetById(highlightedTargetId);
const bounds = vm.renderer.getBounds(target.drawableID);
const [left, top] = this.getPageCoords(bounds.left, bounds.top);
const [right, bottom] = this.getPageCoords(bounds.right, bounds.bottom);
const pad = 2; // px
return (
<div
className={className}
// Ensure new DOM element each update to restart animation
key={highlightedTargetTime}
style={{
position: 'absolute',
top: `${top - pad}px`,
left: `${left - pad}px`,
width: `${(right - left) + (2 * pad)}px`,
height: `${(bottom - top) + (2 * pad)}px`
}}
/>
);
}
}
TargetHighlight.propTypes = {
className: PropTypes.string,
highlightedTargetId: PropTypes.string,
highlightedTargetTime: PropTypes.number,
stageHeight: PropTypes.number,
stageWidth: PropTypes.number,
vm: PropTypes.instanceOf(VM)
};
const mapStateToProps = state => ({
highlightedTargetTime: state.scratchGui.targets.highlightedTargetTime,
highlightedTargetId: state.scratchGui.targets.highlightedTargetId,
vm: state.scratchGui.vm
});
const mapDispatchToProps = () => ({});
export default connect(
mapStateToProps,
mapDispatchToProps
)(TargetHighlight);
......@@ -18,6 +18,7 @@ import spriteLibraryContent from '../lib/libraries/sprites.json';
import {handleFileUpload, spriteUpload} from '../lib/file-uploader.js';
import sharedMessages from '../lib/shared-messages';
import {emptySprite} from '../lib/empty-assets';
import {highlightTarget} from '../reducers/targets';
class TargetPane extends React.Component {
constructor (props) {
......@@ -109,6 +110,9 @@ class TargetPane extends React.Component {
}
handleSelectSprite (id) {
this.props.vm.setEditingTarget(id);
if (this.props.stage && id !== this.props.stage.id) {
this.props.onHighlightTarget(id);
}
}
handleSurpriseSpriteClick () {
const item = spriteLibraryContent[Math.floor(Math.random() * spriteLibraryContent.length)];
......@@ -195,6 +199,7 @@ class TargetPane extends React.Component {
const {
onActivateTab, // eslint-disable-line no-unused-vars
onReceivedBlocks, // eslint-disable-line no-unused-vars
onHighlightTarget, // eslint-disable-line no-unused-vars
dispatchUpdateRestore, // eslint-disable-line no-unused-vars
...componentProps
} = this.props;
......@@ -266,6 +271,9 @@ const mapDispatchToProps = dispatch => ({
},
dispatchUpdateRestore: restoreState => {
dispatch(setRestore(restoreState));
},
onHighlightTarget: id => {
dispatch(highlightTarget(id));
}
});
......
const UPDATE_TARGET_LIST = 'scratch-gui/targets/UPDATE_TARGET_LIST';
const HIGHLIGHT_TARGET = 'scratch-gui/targets/HIGHLIGHT_TARGET';
const initialState = {
sprites: {},
stage: {}
stage: {},
highlightedTargetId: null,
highlightedTargetTime: null
};
const reducer = function (state, action) {
......@@ -23,6 +26,11 @@ const reducer = function (state, action) {
.filter(target => target.isStage)[0] || {},
editingTarget: action.editingTarget
});
case HIGHLIGHT_TARGET:
return Object.assign({}, state, {
highlightedTargetId: action.targetId,
highlightedTargetTime: action.updateTime
});
default:
return state;
}
......@@ -37,8 +45,16 @@ const updateTargets = function (targetList, editingTarget) {
}
};
};
const highlightTarget = function (targetId) {
return {
type: HIGHLIGHT_TARGET,
targetId: targetId,
updateTime: Date.now()
};
};
export {
reducer as default,
initialState as targetsInitialState,
updateTargets
updateTargets,
highlightTarget
};
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment