From 465e28a22222a84310803eb14532a460a0089950 Mon Sep 17 00:00:00 2001 From: Paul Kaplan <pkaplan@media.mit.edu> Date: Wed, 25 Jul 2018 12:12:07 -0400 Subject: [PATCH] Use img instead of CostumeCanvas --- .../costume-canvas/costume-canvas.jsx | 134 ------------------ .../sprite-selector-item.css | 2 + .../sprite-selector-item.jsx | 7 +- .../stage-selector/stage-selector.css | 2 + .../stage-selector/stage-selector.jsx | 7 +- .../sprite-selector-item.test.jsx.snap | 22 +-- .../components/sprite-selector-item.test.jsx | 12 -- 7 files changed, 12 insertions(+), 174 deletions(-) delete mode 100644 src/components/costume-canvas/costume-canvas.jsx diff --git a/src/components/costume-canvas/costume-canvas.jsx b/src/components/costume-canvas/costume-canvas.jsx deleted file mode 100644 index 4375572c1..000000000 --- a/src/components/costume-canvas/costume-canvas.jsx +++ /dev/null @@ -1,134 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import svgToImage from 'svg-to-image'; -import xhr from '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.canvas) { - return; - } - - // Draw the costume to the rendered canvas. - const img = this.img; - const context = this.canvas.getContext('2d'); - - // Scale to fit. - let scale; - - // Choose the larger dimension to scale by. - if (img.width > img.height) { - scale = this.canvas.width / img.width; - } else { - scale = this.canvas.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.canvas.width / 2; - const contextTranslateY = this.canvas.height / 2; - - // First, clear the canvas. - context.clearRect(0, 0, - this.canvas.width, this.canvas.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, (svgErr, img) => { - if (!svgErr) { - this.img = img; - this.draw(); - } - }); - } - }); - }, 0); - - } else { - // Raster graphics: create Image and draw it. - const img = new Image(); - img.src = url; - img.onload = () => { - this.img = img; - this.draw(); - }; - } - } - render () { - return ( - <canvas - className={this.props.className} - height={this.props.height * (window.devicePixelRatio || 1)} - style={{ - height: `${this.props.height}px`, - width: `${this.props.width}px` - }} - width={this.props.width * (window.devicePixelRatio || 1)} - ref={c => (this.canvas = c)} // eslint-disable-line react/jsx-sort-props - /> - ); - } -} - -CostumeCanvas.defaultProps = { - width: 100, - height: 100, - direction: 90 -}; - -CostumeCanvas.propTypes = { - className: PropTypes.string, - direction: PropTypes.number, - height: PropTypes.number, - url: PropTypes.string.isRequired, - width: PropTypes.number -}; - -export default CostumeCanvas; diff --git a/src/components/sprite-selector-item/sprite-selector-item.css b/src/components/sprite-selector-item/sprite-selector-item.css index 32a5339a7..24bf17395 100644 --- a/src/components/sprite-selector-item/sprite-selector-item.css +++ b/src/components/sprite-selector-item/sprite-selector-item.css @@ -42,6 +42,8 @@ .sprite-image { margin: auto; user-select: none; + max-width: 32px; + max-height: 32px; } .sprite-info { diff --git a/src/components/sprite-selector-item/sprite-selector-item.jsx b/src/components/sprite-selector-item/sprite-selector-item.jsx index cb04c034a..50554f940 100644 --- a/src/components/sprite-selector-item/sprite-selector-item.jsx +++ b/src/components/sprite-selector-item/sprite-selector-item.jsx @@ -2,7 +2,6 @@ import classNames from 'classnames'; import PropTypes from 'prop-types'; import React from 'react'; -import CostumeCanvas from '../costume-canvas/costume-canvas.jsx'; import CloseButton from '../close-button/close-button.jsx'; import styles from './sprite-selector-item.css'; import {ContextMenuTrigger} from 'react-contextmenu'; @@ -38,11 +37,9 @@ const SpriteSelectorItem = props => ( <div className={styles.number}>{props.number}</div> )} {props.costumeURL ? ( - <CostumeCanvas + <img className={styles.spriteImage} - height={32} - url={props.costumeURL} - width={32} + src={props.costumeURL} /> ) : null} <div className={styles.spriteInfo}> diff --git a/src/components/stage-selector/stage-selector.css b/src/components/stage-selector/stage-selector.css index 4e35dd4b6..a16875990 100644 --- a/src/components/stage-selector/stage-selector.css +++ b/src/components/stage-selector/stage-selector.css @@ -89,6 +89,8 @@ $header-height: calc($stage-menu-height - 2px); border: 1px solid $ui-black-transparent; border-radius: .25rem; box-shadow: inset 0 0 4px $ui-black-transparent; + max-width: 64px; + max-height: 48px; } .add-button { diff --git a/src/components/stage-selector/stage-selector.jsx b/src/components/stage-selector/stage-selector.jsx index 50161bf8c..f5de8b7a8 100644 --- a/src/components/stage-selector/stage-selector.jsx +++ b/src/components/stage-selector/stage-selector.jsx @@ -5,7 +5,6 @@ import {defineMessages, intlShape, injectIntl, FormattedMessage} from 'react-int import Box from '../box/box.jsx'; import ActionMenu from '../action-menu/action-menu.jsx'; -import CostumeCanvas from '../costume-canvas/costume-canvas.jsx'; import styles from './stage-selector.css'; import backdropIcon from '../action-menu/icon--backdrop.svg'; @@ -78,11 +77,9 @@ const StageSelector = props => { </div> </div> {url ? ( - <CostumeCanvas + <img className={styles.costumeCanvas} - height={48} - url={url} - width={64} + src={url} /> ) : null} <div className={styles.label}> diff --git a/test/unit/components/__snapshots__/sprite-selector-item.test.jsx.snap b/test/unit/components/__snapshots__/sprite-selector-item.test.jsx.snap index 2c276ff65..237e3dc29 100644 --- a/test/unit/components/__snapshots__/sprite-selector-item.test.jsx.snap +++ b/test/unit/components/__snapshots__/sprite-selector-item.test.jsx.snap @@ -30,16 +30,9 @@ exports[`SpriteSelectorItemComponent matches snapshot when given a number and de > 5 </div> - <canvas + <img className={undefined} - height={32} - style={ - Object { - "height": "32px", - "width": "32px", - } - } - width={32} + src="https://scratch.mit.edu/foo/bar/pony" /> <div className={undefined} @@ -113,16 +106,9 @@ exports[`SpriteSelectorItemComponent matches snapshot when selected 1`] = ` src="test-file-stub" /> </div> - <canvas + <img className={undefined} - height={32} - style={ - Object { - "height": "32px", - "width": "32px", - } - } - width={32} + src="https://scratch.mit.edu/foo/bar/pony" /> <div className={undefined} diff --git a/test/unit/components/sprite-selector-item.test.jsx b/test/unit/components/sprite-selector-item.test.jsx index 1bf52fe97..49e3fa705 100644 --- a/test/unit/components/sprite-selector-item.test.jsx +++ b/test/unit/components/sprite-selector-item.test.jsx @@ -1,7 +1,6 @@ import React from 'react'; import {mountWithIntl, shallowWithIntl, componentWithIntl} from '../../helpers/intl-helpers.jsx'; import SpriteSelectorItemComponent from '../../../src/components/sprite-selector-item/sprite-selector-item'; -import CostumeCanvas from '../../../src/components/costume-canvas/costume-canvas'; import CloseButton from '../../../src/components/close-button/close-button'; describe('SpriteSelectorItemComponent', () => { @@ -73,17 +72,6 @@ describe('SpriteSelectorItemComponent', () => { expect(onDeleteButtonClick).toHaveBeenCalled(); }); - test('creates a CostumeCanvas when a costume url is defined', () => { - const wrapper = shallowWithIntl(getComponent()); - expect(wrapper.find(CostumeCanvas).exists()).toBe(true); - }); - - test('does not create a CostumeCanvas when a costume url is null', () => { - costumeURL = null; - const wrapper = shallowWithIntl(getComponent()); - expect(wrapper.find(CostumeCanvas).exists()).toBe(false); - }); - test('it has a context menu with delete menu item and callback', () => { const wrapper = mountWithIntl(getComponent()); const contextMenu = wrapper.find('ContextMenu'); -- GitLab