From 4aa3b20a93be81c84cf3fd6bf31e569bcbb34922 Mon Sep 17 00:00:00 2001 From: DD <liudi08@gmail.com> Date: Fri, 23 Feb 2018 15:35:42 -0500 Subject: [PATCH] Add very simple end animation --- .../sprite-selector/sprite-selector.css | 31 ++++++++++++ .../sprite-selector/sprite-selector.jsx | 23 +++++---- src/components/target-pane/target-pane.jsx | 13 ++--- src/containers/sprite-selector-item.jsx | 32 ++++++++----- src/containers/target-pane.jsx | 16 +------ src/lib/vm-listener-hoc.jsx | 17 +++++-- src/reducers/gui.js | 4 +- src/reducers/hovered-target-sprite.js | 28 ----------- src/reducers/hovered-target.js | 48 +++++++++++++++++++ 9 files changed, 135 insertions(+), 77 deletions(-) delete mode 100644 src/reducers/hovered-target-sprite.js create mode 100644 src/reducers/hovered-target.js diff --git a/src/components/sprite-selector/sprite-selector.css b/src/components/sprite-selector/sprite-selector.css index 781da192d..368a3451c 100644 --- a/src/components/sprite-selector/sprite-selector.css +++ b/src/components/sprite-selector/sprite-selector.css @@ -112,3 +112,34 @@ 75% {transform: rotate(-2deg) scale(1.05);} 100% {transform: rotate(0deg) scale(1.05);} } + +.receivedBlocks { + -webkit-animation: glowing 250ms; + -moz-animation: glowing 250ms; + -o-animation: glowing 250ms; + animation: glowing 250ms; +} + +@-webkit-keyframes glowing { + 10% { -webkit-box-shadow: 0 0 10px #7fff1e; } + 90% { -webkit-box-shadow: 0 0 10px #7fff1e; } + 100% { -webkit-box-shadow: none; } +} + +@-moz-keyframes glowing { + 10% { -moz-box-shadow: 0 0 10px #7fff1e; } + 90% { -moz-box-shadow: 0 0 10px #7fff1e; } + 100% { -moz-box-shadow: none; } +} + +@-o-keyframes glowing { + 10% { box-shadow: 0 0 10px #7fff1e; } + 90% { box-shadow: 0 0 10px #7fff1e; } + 100% { box-shadow: none; } +} + +@keyframes glowing { + 10% { box-shadow: 0 0 10px #7fff1e; } + 90% { box-shadow: 0 0 10px #7fff1e; } + 100% { box-shadow: none; } +} \ No newline at end of file diff --git a/src/components/sprite-selector/sprite-selector.jsx b/src/components/sprite-selector/sprite-selector.jsx index a8cd3fc3f..906479d71 100644 --- a/src/components/sprite-selector/sprite-selector.jsx +++ b/src/components/sprite-selector/sprite-selector.jsx @@ -21,6 +21,8 @@ const messages = defineMessages({ const SpriteSelectorComponent = function (props) { const { + editingTarget, + hoveredTarget, intl, onChangeSpriteDirection, onChangeSpriteName, @@ -30,8 +32,6 @@ const SpriteSelectorComponent = function (props) { onChangeSpriteY, onDeleteSprite, onDuplicateSprite, - onMouseOutSprite, - onMouseOverSprite, onNewSpriteClick, onSelectSprite, raised, @@ -76,10 +76,12 @@ const SpriteSelectorComponent = function (props) { .map(sprite => ( <SpriteSelectorItem assetId={sprite.costume && sprite.costume.assetId} - className={ - (raised && sprite.id !== selectedId) ? - classNames(styles.sprite, styles.raised) : styles.sprite - } + className={hoveredTarget.sprite === sprite.id && + sprite.id !== editingTarget && + hoveredTarget.receivedBlocks ? + classNames(styles.sprite, styles.receivedBlocks) : + raised && sprite.id !== editingTarget ? + classNames(styles.sprite, styles.raised) : styles.sprite} id={sprite.id} key={sprite.id} name={sprite.name} @@ -87,8 +89,6 @@ const SpriteSelectorComponent = function (props) { onClick={onSelectSprite} onDeleteButtonClick={onDeleteSprite} onDuplicateButtonClick={onDuplicateSprite} - onMouseOut={onMouseOutSprite} - onMouseOver={onMouseOverSprite} /> )) } @@ -105,6 +105,11 @@ const SpriteSelectorComponent = function (props) { }; SpriteSelectorComponent.propTypes = { + editingTarget: PropTypes.string, + hoveredTarget: PropTypes.shape({ + hoveredSprite: PropTypes.string, + receivedBlocks: PropTypes.bool + }), intl: intlShape.isRequired, onChangeSpriteDirection: PropTypes.func, onChangeSpriteName: PropTypes.func, @@ -114,8 +119,6 @@ SpriteSelectorComponent.propTypes = { onChangeSpriteY: PropTypes.func, onDeleteSprite: PropTypes.func, onDuplicateSprite: PropTypes.func, - onMouseOutSprite: PropTypes.func, - onMouseOverSprite: PropTypes.func, onNewSpriteClick: PropTypes.func, onSelectSprite: PropTypes.func, raised: PropTypes.bool, diff --git a/src/components/target-pane/target-pane.jsx b/src/components/target-pane/target-pane.jsx index 31ae88485..014ed6498 100644 --- a/src/components/target-pane/target-pane.jsx +++ b/src/components/target-pane/target-pane.jsx @@ -19,6 +19,7 @@ import styles from './target-pane.css'; const TargetPane = ({ backdropLibraryVisible, editingTarget, + hoveredTarget, spriteLibraryVisible, onChangeSpriteDirection, onChangeSpriteName, @@ -28,8 +29,6 @@ const TargetPane = ({ onChangeSpriteY, onDeleteSprite, onDuplicateSprite, - onMouseOutSprite, - onMouseOverSprite, onNewSpriteClick, onRequestCloseSpriteLibrary, onRequestCloseBackdropLibrary, @@ -46,6 +45,8 @@ const TargetPane = ({ > <SpriteSelectorComponent + editingTarget={editingTarget} + hoveredTarget={hoveredTarget} raised={raiseSprites} selectedId={editingTarget} sprites={sprites} @@ -57,8 +58,6 @@ const TargetPane = ({ onChangeSpriteY={onChangeSpriteY} onDeleteSprite={onDeleteSprite} onDuplicateSprite={onDuplicateSprite} - onMouseOutSprite={onMouseOutSprite} - onMouseOverSprite={onMouseOverSprite} onNewSpriteClick={onNewSpriteClick} onSelectSprite={onSelectSprite} /> @@ -113,6 +112,10 @@ TargetPane.propTypes = { backdropLibraryVisible: PropTypes.bool, editingTarget: PropTypes.string, extensionLibraryVisible: PropTypes.bool, + hoveredTarget: PropTypes.shape({ + hoveredSprite: PropTypes.string, + receivedBlocks: PropTypes.bool + }), onChangeSpriteDirection: PropTypes.func, onChangeSpriteName: PropTypes.func, onChangeSpriteSize: PropTypes.func, @@ -121,8 +124,6 @@ TargetPane.propTypes = { onChangeSpriteY: PropTypes.func, onDeleteSprite: PropTypes.func, onDuplicateSprite: PropTypes.func, - onMouseOutSprite: PropTypes.func, - onMouseOverSprite: PropTypes.func, onNewSpriteClick: PropTypes.func, onRequestCloseBackdropLibrary: PropTypes.func, onRequestCloseExtensionLibrary: PropTypes.func, diff --git a/src/containers/sprite-selector-item.jsx b/src/containers/sprite-selector-item.jsx index 87e7db36b..4b81507a0 100644 --- a/src/containers/sprite-selector-item.jsx +++ b/src/containers/sprite-selector-item.jsx @@ -1,9 +1,10 @@ import bindAll from 'lodash.bindall'; import PropTypes from 'prop-types'; import React from 'react'; - import {connect} from 'react-redux'; +import {setHoveredSprite} from '../reducers/hovered-target'; + import SpriteSelectorItemComponent from '../components/sprite-selector-item/sprite-selector-item.jsx'; class SpriteSelectorItem extends React.Component { @@ -33,10 +34,10 @@ class SpriteSelectorItem extends React.Component { this.props.onDuplicateButtonClick(this.props.id); } handleMouseOut () { - this.props.onMouseOut(this.props.id); + this.props.dispatchSetHoveredSprite(null); } handleMouseOver () { - this.props.onMouseOver(this.props.id); + this.props.dispatchSetHoveredSprite(this.props.id); } render () { const { @@ -46,8 +47,7 @@ class SpriteSelectorItem extends React.Component { onClick, onDeleteButtonClick, onDuplicateButtonClick, - onMouseOut, - onMouseOver, + receivedBlocks, /* eslint-enable no-unused-vars */ ...props } = this.props; @@ -56,8 +56,8 @@ class SpriteSelectorItem extends React.Component { onClick={this.handleClick} onDeleteButtonClick={onDeleteButtonClick ? this.handleDelete : null} onDuplicateButtonClick={onDuplicateButtonClick ? this.handleDuplicate : null} - onMouseOut={onMouseOut ? this.handleMouseOut : null} - onMouseOver={onMouseOver ? this.handleMouseOver : null} + onMouseOut={this.handleMouseOut} + onMouseOver={this.handleMouseOver} {...props} /> ); @@ -67,20 +67,28 @@ class SpriteSelectorItem extends React.Component { SpriteSelectorItem.propTypes = { assetId: PropTypes.string, costumeURL: PropTypes.string, + dispatchSetHoveredSprite: PropTypes.func.isRequired, id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), name: PropTypes.string, onClick: PropTypes.func, onDeleteButtonClick: PropTypes.func, onDuplicateButtonClick: PropTypes.func, - onMouseOut: PropTypes.func, - onMouseOver: PropTypes.func, + receivedBlocks: PropTypes.bool, selected: PropTypes.bool }; -const mapStateToProps = (state, {assetId, costumeURL}) => ({ - costumeURL: costumeURL || (assetId && state.vm.runtime.storage.get(assetId).encodeDataURI()) +const mapStateToProps = (state, {assetId, costumeURL, id}) => ({ + costumeURL: costumeURL || (assetId && state.vm.runtime.storage.get(assetId).encodeDataURI()), + receivedBlocks: state.hoveredTarget.receivedBlocks && + state.hoveredTarget.sprite === id +}); +const mapDispatchToProps = dispatch => ({ + dispatchSetHoveredSprite: spriteId => { + dispatch(setHoveredSprite(spriteId)); + } }); export default connect( - mapStateToProps + mapStateToProps, + mapDispatchToProps )(SpriteSelectorItem); diff --git a/src/containers/target-pane.jsx b/src/containers/target-pane.jsx index 5cc156a87..7dc6adb25 100644 --- a/src/containers/target-pane.jsx +++ b/src/containers/target-pane.jsx @@ -9,8 +9,6 @@ import { closeSpriteLibrary } from '../reducers/modals'; -import {setHoveredSprite} from '../reducers/hovered-target-sprite'; - import TargetPaneComponent from '../components/target-pane/target-pane.jsx'; class TargetPane extends React.Component { @@ -25,8 +23,6 @@ class TargetPane extends React.Component { 'handleChangeSpriteY', 'handleDeleteSprite', 'handleDuplicateSprite', - 'handleMouseOutSprite', - 'handleMouseOverSprite', 'handleSelectSprite' ]); } @@ -54,12 +50,6 @@ class TargetPane extends React.Component { handleDuplicateSprite (id) { this.props.vm.duplicateSprite(id); } - handleMouseOutSprite () { - this.props.setHoveredSprite(null); - } - handleMouseOverSprite (id) { - this.props.setHoveredSprite(id); - } handleSelectSprite (id) { this.props.vm.setEditingTarget(id); } @@ -75,8 +65,6 @@ class TargetPane extends React.Component { onChangeSpriteY={this.handleChangeSpriteY} onDeleteSprite={this.handleDeleteSprite} onDuplicateSprite={this.handleDuplicateSprite} - onMouseOutSprite={this.handleMouseOutSprite} - onMouseOverSprite={this.handleMouseOverSprite} onSelectSprite={this.handleSelectSprite} /> ); @@ -94,6 +82,7 @@ TargetPane.propTypes = { const mapStateToProps = state => ({ editingTarget: state.targets.editingTarget, + hoveredTarget: state.hoveredTarget, sprites: Object.keys(state.targets.sprites).reduce((sprites, k) => { let {direction, size, x, y, ...sprite} = state.targets.sprites[k]; if (typeof direction !== 'undefined') direction = Math.round(direction); @@ -118,9 +107,6 @@ const mapDispatchToProps = dispatch => ({ }, onRequestCloseBackdropLibrary: () => { dispatch(closeBackdropLibrary()); - }, - setHoveredSprite: spriteId => { - dispatch(setHoveredSprite(spriteId)); } }); diff --git a/src/lib/vm-listener-hoc.jsx b/src/lib/vm-listener-hoc.jsx index bf541b81b..3ae81fdbb 100644 --- a/src/lib/vm-listener-hoc.jsx +++ b/src/lib/vm-listener-hoc.jsx @@ -8,6 +8,7 @@ import {connect} from 'react-redux'; import {updateEditingTarget, updateTargets} from '../reducers/targets'; import {updateBlockDrag} from '../reducers/block-drag'; import {updateMonitors} from '../reducers/monitors'; +import {setReceivedBlocks} from '../reducers/hovered-target'; /* * Higher Order Component to manage events emitted by the VM @@ -51,8 +52,9 @@ const vmListenerHOC = function (WrappedComponent) { } } handleBlockDragEnd (blocks) { - if (this.props.hoveredTargetSprite && this.props.hoveredTargetSprite !== this.props.editingTarget) { - this.props.vm.shareBlocksToTarget(blocks, this.props.hoveredTargetSprite); + if (this.props.hoveredSprite && this.props.hoveredSprite !== this.props.editingTarget) { + this.props.vm.shareBlocksToTarget(blocks, this.props.hoveredSprite); + this.props.onReceivedBlocks(true); } } handleKeyDown (e) { @@ -86,10 +88,13 @@ const vmListenerHOC = function (WrappedComponent) { const { /* eslint-disable no-unused-vars */ attachKeyboardEvents, + editingTarget, + hoveredSprite, onBlockDragUpdate, onKeyDown, onKeyUp, onMonitorsUpdate, + onReceivedBlocks, onTargetsUpdate, /* eslint-enable no-unused-vars */ ...props @@ -100,11 +105,12 @@ const vmListenerHOC = function (WrappedComponent) { VMListener.propTypes = { attachKeyboardEvents: PropTypes.bool, editingTarget: PropTypes.string, - hoveredTargetSprite: PropTypes.string, + hoveredSprite: PropTypes.string, onBlockDragUpdate: PropTypes.func.isRequired, onKeyDown: PropTypes.func, onKeyUp: PropTypes.func, onMonitorsUpdate: PropTypes.func.isRequired, + onReceivedBlocks: PropTypes.func.isRequired, onTargetsUpdate: PropTypes.func.isRequired, vm: PropTypes.instanceOf(VM).isRequired }; @@ -113,7 +119,7 @@ const vmListenerHOC = function (WrappedComponent) { }; const mapStateToProps = state => ({ vm: state.vm, - hoveredTargetSprite: state.hoveredTargetSprite, + hoveredSprite: state.hoveredTarget.sprite, editingTarget: state.targets.editingTarget }); const mapDispatchToProps = dispatch => ({ @@ -126,6 +132,9 @@ const vmListenerHOC = function (WrappedComponent) { }, onBlockDragUpdate: areBlocksOverGui => { dispatch(updateBlockDrag(areBlocksOverGui)); + }, + onReceivedBlocks: receivedBlocks => { + dispatch(setReceivedBlocks(receivedBlocks)); } }); return connect( diff --git a/src/reducers/gui.js b/src/reducers/gui.js index 61ffa5501..49b9a0120 100644 --- a/src/reducers/gui.js +++ b/src/reducers/gui.js @@ -2,7 +2,7 @@ import {combineReducers} from 'redux'; import colorPickerReducer from './color-picker'; import customProceduresReducer from './custom-procedures'; import blockDragReducer from './block-drag'; -import hoveredTargetSpriteReducer from './hovered-target-sprite'; +import hoveredTargetReducer from './hovered-target'; import intlReducer from './intl'; import modalReducer from './modals'; import monitorReducer from './monitors'; @@ -17,7 +17,7 @@ export default combineReducers({ blockDrag: blockDragReducer, colorPicker: colorPickerReducer, customProcedures: customProceduresReducer, - hoveredTargetSprite: hoveredTargetSpriteReducer, + hoveredTarget: hoveredTargetReducer, intl: intlReducer, stageSize: stageSizeReducer, modals: modalReducer, diff --git a/src/reducers/hovered-target-sprite.js b/src/reducers/hovered-target-sprite.js deleted file mode 100644 index 38da69ca1..000000000 --- a/src/reducers/hovered-target-sprite.js +++ /dev/null @@ -1,28 +0,0 @@ -const SET_HOVERED_SPRITE = 'scratch-gui/hovered-target-sprite/SET_HOVERED_SPRITE'; - -const initialState = null; - -const reducer = function (state, action) { - if (typeof state === 'undefined') state = initialState; - switch (action.type) { - case SET_HOVERED_SPRITE: - return action.spriteId; - default: - return state; - } -}; - -const setHoveredSprite = function (spriteId) { - return { - type: SET_HOVERED_SPRITE, - spriteId: spriteId, - meta: { - throttle: 30 - } - }; -}; - -export { - reducer as default, - setHoveredSprite -}; diff --git a/src/reducers/hovered-target.js b/src/reducers/hovered-target.js new file mode 100644 index 000000000..43bf87ac0 --- /dev/null +++ b/src/reducers/hovered-target.js @@ -0,0 +1,48 @@ +const SET_HOVERED_SPRITE = 'scratch-gui/hovered-target/SET_HOVERED_SPRITE'; +const SET_RECEIVED_BLOCKS = 'scratch-gui/hovered-target/SET_RECEIVED_BLOCKS'; + +const initialState = { + sprite: null, + receivedBlocks: false +}; + +const reducer = function (state, action) { + if (typeof state === 'undefined') state = initialState; + switch (action.type) { + case SET_HOVERED_SPRITE: + return { + sprite: action.spriteId, + receivedBlocks: false + }; + case SET_RECEIVED_BLOCKS: + return { + sprite: state.sprite, + receivedBlocks: action.receivedBlocks + }; + default: + return state; + } +}; + +const setHoveredSprite = function (spriteId) { + return { + type: SET_HOVERED_SPRITE, + spriteId: spriteId, + meta: { + throttle: 30 + } + }; +}; + +const setReceivedBlocks = function (receivedBlocks) { + return { + type: SET_RECEIVED_BLOCKS, + receivedBlocks: receivedBlocks + }; +}; + +export { + reducer as default, + setHoveredSprite, + setReceivedBlocks +}; -- GitLab