-
Notifications
You must be signed in to change notification settings - Fork 4k
Initial rough sprite/costume/backdrop libraries #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
41c1765
Initial rough sprite/costume/backdrop libraries
tmickel 7a675cd
Merge branch 'master' into feature/libraries
tmickel 93645ea
CostumeCanvas nits
tmickel ee72f23
Idiomatic "map"
tmickel a6bdf09
Various nits
tmickel 14afd85
bindAll throughout
tmickel 7c5cecb
Return the library item object
tmickel 7d36701
Add defaults for library props
tmickel bd22602
Fix the props situation
tmickel 9cc4dcf
Fix id for selected item, return when CostumeCanvas draw fails
tmickel bd900a9
Fix merge duplication of package.json
tmickel 7fcf71d
NL for library item
tmickel File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| const React = require('react'); | ||
| const svgToImage = require('svg-to-image'); | ||
| const xhr = require('xhr'); | ||
|
|
||
| /** | ||
| * @fileoverview | ||
| * A component for rendering Scratch costume URLs to canvases. | ||
| * Use for sprite library, costume library, sprite selector, etc. | ||
| * Props include width, height, and direction (direction in Scratch value). | ||
| */ | ||
|
|
||
| class CostumeCanvas extends React.Component { | ||
| componentDidMount () { | ||
| this.load(); | ||
| } | ||
| componentDidUpdate (prevProps) { | ||
| if (prevProps.url !== this.props.url) { | ||
| this.load(); | ||
| } else { | ||
| if (prevProps.width !== this.props.width || | ||
| prevProps.height !== this.props.height || | ||
| prevProps.direction !== this.props.direction) { | ||
| this.draw(); | ||
| } | ||
| } | ||
| } | ||
| draw () { | ||
| if (!this.refs.costumeCanvas) { | ||
| return; | ||
| } | ||
| // Draw the costume to the rendered canvas. | ||
| const img = this.img; | ||
| const context = this.refs.costumeCanvas.getContext('2d'); | ||
| // Scale to fit. | ||
| let scale; | ||
| // Choose the larger dimension to scale by. | ||
| if (img.width > img.height) { | ||
| scale = this.refs.costumeCanvas.width / img.width; | ||
| } else { | ||
| scale = this.refs.costumeCanvas.height / img.height; | ||
| } | ||
| // Rotate by the Scratch-value direction. | ||
| const angle = (-90 + this.props.direction) * Math.PI / 180; | ||
| // Rotation origin point will be center of the canvas. | ||
| const contextTranslateX = this.refs.costumeCanvas.width / 2; | ||
| const contextTranslateY = this.refs.costumeCanvas.height / 2; | ||
| // First, clear the canvas. | ||
| context.clearRect(0, 0, | ||
| this.refs.costumeCanvas.width, this.refs.costumeCanvas.height); | ||
| // Translate the context to the center of the canvas, | ||
| // then rotate canvas drawing by `angle`. | ||
| context.translate(contextTranslateX, contextTranslateY); | ||
| context.rotate(angle); | ||
| context.drawImage(img, | ||
| 0, 0, img.width, img.height, | ||
| -(scale * img.width / 2), -(scale * img.height / 2), | ||
| scale * img.width, | ||
| scale * img.height); | ||
| // Reset the canvas rotation and translation to 0, (0, 0). | ||
| context.rotate(-angle); | ||
| context.translate(-contextTranslateX, -contextTranslateY); | ||
| } | ||
| load () { | ||
| // Draw the icon on our canvas. | ||
| const url = this.props.url; | ||
| if (url.indexOf('.svg') > -1) { | ||
| // Vector graphics: need to download with XDR and rasterize. | ||
| // Queue request asynchronously. | ||
| setTimeout(() => { | ||
| xhr.get({ | ||
| useXDR: true, | ||
| url: url | ||
| }, (err, response, body) => { | ||
| if (!err) { | ||
| svgToImage(body, (err, img) => { | ||
| if (!err) { | ||
| this.img = img; | ||
| this.draw(); | ||
| } | ||
| }); | ||
| } | ||
| }); | ||
| }, 0); | ||
|
|
||
| } else { | ||
| // Raster graphics: create Image and draw it. | ||
| let img = new Image(); | ||
| img.src = url; | ||
| img.onload = () => { | ||
| this.img = img; | ||
| this.draw(); | ||
| }; | ||
| } | ||
| } | ||
| render () { | ||
| return <canvas | ||
| ref='costumeCanvas' | ||
| width={this.props.width} | ||
| height={this.props.height} | ||
| />; | ||
| } | ||
| } | ||
|
|
||
| CostumeCanvas.defaultProps = { | ||
| width: 100, | ||
| height: 100, | ||
| direction: 90 | ||
| }; | ||
|
|
||
| CostumeCanvas.propTypes = { | ||
| url: React.PropTypes.string, | ||
| width: React.PropTypes.number, | ||
| height: React.PropTypes.number, | ||
| direction: React.PropTypes.number | ||
| }; | ||
|
|
||
| module.exports = CostumeCanvas; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| const React = require('react'); | ||
|
|
||
| const CostumeCanvas = require('./costume-canvas'); | ||
|
|
||
| class LibraryItem extends React.Component { | ||
| render () { | ||
| let style = (this.props.selected) ? | ||
| this.props.selectedGridTileStyle : this.props.gridTileStyle; | ||
| return ( | ||
| <div style={style} onClick={() => this.props.onSelect(this.props.id)}> | ||
| <CostumeCanvas url={this.props.iconURL} /> | ||
| <p>{this.props.name}</p> | ||
| </div> | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| LibraryItem.defaultProps = { | ||
| gridTileStyle: { | ||
| float: 'left', | ||
| width: '140px', | ||
| marginLeft: '5px', | ||
| marginRight: '5px', | ||
| textAlign: 'center', | ||
| cursor: 'pointer' | ||
| }, | ||
| selectedGridTileStyle: { | ||
| float: 'left', | ||
| width: '140px', | ||
| marginLeft: '5px', | ||
| marginRight: '5px', | ||
| textAlign: 'center', | ||
| cursor: 'pointer', | ||
| background: '#aaa', | ||
| borderRadius: '6px' | ||
| } | ||
| }; | ||
|
|
||
| LibraryItem.propTypes = { | ||
| name: React.PropTypes.string, | ||
| iconURL: React.PropTypes.string, | ||
| gridTileStyle: React.PropTypes.object, | ||
| selectedGridTileStyle: React.PropTypes.object, | ||
| selected: React.PropTypes.bool, | ||
| onSelect: React.PropTypes.func, | ||
| id: React.PropTypes.number | ||
| }; | ||
|
|
||
| module.exports = LibraryItem; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| const bindAll = require('lodash.bindall'); | ||
| const React = require('react'); | ||
|
|
||
| const LibraryItem = require('./library-item'); | ||
| const ModalComponent = require('./modal'); | ||
|
|
||
| class LibraryComponent extends React.Component { | ||
| constructor (props) { | ||
| super(props); | ||
| bindAll(this, ['onSelect']); | ||
| this.state = {selectedItem: null}; | ||
| } | ||
| onSelect (id) { | ||
| if (this.state.selectedItem == id) { | ||
| // Double select: select as the library's value. | ||
| this.props.onRequestClose(); | ||
| this.props.onItemSelected(this.props.data[id]); | ||
| } | ||
| this.setState({selectedItem: id}); | ||
| } | ||
| render () { | ||
| let itemId = 0; | ||
| let gridItems = this.props.data.map((dataItem) => { | ||
| let id = itemId; | ||
| itemId++; | ||
| const scratchURL = (dataItem.md5) ? 'https://cdn.assets.scratch.mit.edu/internalapi/asset/' + | ||
| dataItem.md5 + '/get/' : dataItem.rawURL; | ||
| return <LibraryItem | ||
| name={dataItem.name} | ||
| iconURL={scratchURL} | ||
| key={'item_' + id} | ||
| selected={this.state.selectedItem == id} | ||
| onSelect={this.onSelect} | ||
| id={id} | ||
| />; | ||
| }); | ||
|
|
||
| const scrollGridStyle = { | ||
| overflow: 'scroll', | ||
| position: 'absolute', | ||
| top: '70px', | ||
| bottom: '20px', | ||
| left: '30px', | ||
| right: '30px' | ||
| }; | ||
|
|
||
| return ( | ||
| <ModalComponent | ||
| onRequestClose={this.props.onRequestClose} | ||
| visible={this.props.visible} | ||
| > | ||
| <h1>{this.props.title}</h1> | ||
| <div style={scrollGridStyle}> | ||
| {gridItems} | ||
| </div> | ||
| </ModalComponent> | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| LibraryComponent.propTypes = { | ||
| title: React.PropTypes.string, | ||
| data: React.PropTypes.array, | ||
| visible: React.PropTypes.bool, | ||
| onRequestClose: React.PropTypes.func, | ||
| onItemSelected: React.PropTypes.func | ||
| }; | ||
|
|
||
| module.exports = LibraryComponent; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| const React = require('react'); | ||
| const ReactModal = require('react-modal'); | ||
|
|
||
| class ModalComponent extends React.Component { | ||
| render () { | ||
| return ( | ||
| <ReactModal | ||
| ref="modal" | ||
| style={this.props.modalStyle} | ||
| isOpen={this.props.visible} | ||
| onRequestClose={this.props.onRequestClose} | ||
| > | ||
| <div | ||
| onClick={this.props.onRequestClose} | ||
| style={this.props.closeButtonStyle} | ||
| > | ||
| x | ||
| </div> | ||
| {this.props.children} | ||
| </ReactModal> | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| const modalStyle = { | ||
| overlay: { | ||
| zIndex: 1000, | ||
| backgroundColor: 'rgba(0, 0, 0, .75)' | ||
| }, | ||
| content: { | ||
| position: 'absolute', | ||
| overflow: 'visible', | ||
| borderRadius: '6px', | ||
| padding: 0, | ||
| top: '5%', | ||
| bottom: '5%', | ||
| left: '5%', | ||
| right: '5%', | ||
| background: '#fcfcfc' | ||
| } | ||
| }; | ||
|
|
||
| const closeButtonStyle = { | ||
| color: 'rgb(255, 255, 255)', | ||
| background: 'rgb(50, 50, 50)', | ||
| borderRadius: '15px', | ||
| width: '30px', | ||
| height: '25px', | ||
| textAlign: 'center', | ||
| paddingTop: '5px', | ||
| position: 'absolute', | ||
| right: '3px', | ||
| top: '3px', | ||
| cursor: 'pointer' | ||
| }; | ||
|
|
||
| ModalComponent.defaultProps = { | ||
| modalStyle: modalStyle, | ||
| closeButtonStyle: closeButtonStyle | ||
| }; | ||
|
|
||
| ModalComponent.propTypes = { | ||
| children: React.PropTypes.node, | ||
| modalStyle: React.PropTypes.object, | ||
| closeButtonStyle: React.PropTypes.object, | ||
| onRequestClose: React.PropTypes.func, | ||
| visible: React.PropTypes.bool | ||
| }; | ||
|
|
||
| module.exports = ModalComponent; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This comment was marked as abuse.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
This comment was marked as abuse.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.