diff --git a/README.md b/README.md index d69a54ad..504f5e13 100644 --- a/README.md +++ b/README.md @@ -60,17 +60,22 @@ Then go to [http://localhost:8601](http://localhost:8601). 601 is supposed to lo ### How to include in your own Node.js App For an example of how to use scratch-paint as a library, check out the `scratch-paint/src/playground` directory. -In `playground.jsx`, you can change the SVG vector that is passed in, and edit the handler `onUpdateSvg`, which is called with the new SVG each time the vector drawing is edited. +In `playground.jsx`, you can change the image that is passed in (which may either be nothing, an SVG string or an HTMLImageElement) and edit the handler `onUpdateImage`, which is called with the new image (either an SVG string or an HTMLCanvasElement) each time the vector drawing is edited. + +If the `imageId` parameter changes, then the paint editor will be cleared, the undo stack reset, and the image re-imported. + +SVGs of up to size 480 x 360 will fit into the view window of the paint editor, while bitmaps of size up to 960 x 720 will fit into the paint editor. One unit of an SVG will appear twice as tall and wide as one unit of a bitmap. This quirky import behavior comes from needing to support legacy projects in Scratch. In your parent component: ``` import PaintEditor from 'scratch-paint'; ... <PaintEditor - svg={optionalSvg} - rotationCenterX={optionalCenterPointXRelativeToSvgTopLeft} - rotationCenterY={optionalCenterPointYRelativeToSvgTopLeft} - onUpdateSvg={handleUpdateSvgFunction} + image={optionalImage} + imageId={optionalId} + rotationCenterX={optionalCenterPointXRelativeToTopLeft} + rotationCenterY={optionalCenterPointYRelativeToTopLeft} + onUpdateImage={handleUpdateImageFunction} /> ``` diff --git a/src/components/paint-editor/paint-editor.jsx b/src/components/paint-editor/paint-editor.jsx index 3fff3bd7..0a84b365 100644 --- a/src/components/paint-editor/paint-editor.jsx +++ b/src/components/paint-editor/paint-editor.jsx @@ -329,43 +329,44 @@ const PaintEditorComponent = props => { {/* fill */} <FillColorIndicatorComponent className={styles.modMarginRight} - onUpdateSvg={props.onUpdateSvg} + onUpdateImage={props.onUpdateImage} /> {/* stroke */} <StrokeColorIndicatorComponent - onUpdateSvg={props.onUpdateSvg} + onUpdateImage={props.onUpdateImage} /> {/* stroke width */} <StrokeWidthIndicatorComponent - onUpdateSvg={props.onUpdateSvg} + onUpdateImage={props.onUpdateImage} /> </InputGroup> <InputGroup className={styles.modModeTools}> <ModeToolsContainer - onUpdateSvg={props.onUpdateSvg} + onUpdateImage={props.onUpdateImage} /> </InputGroup> </div> : - <div className={styles.row}> - <InputGroup - className={classNames( - styles.row, - styles.modDashedBorder, - styles.modLabeledIconHeight - )} - > - {/* fill */} - <FillColorIndicatorComponent - className={styles.modMarginRight} - onUpdateSvg={props.onUpdateSvg} - /> - </InputGroup> - <InputGroup className={styles.modModeTools}> - <ModeToolsContainer - onUpdateSvg={props.onUpdateSvg} - /> - </InputGroup> - </div> + isBitmap(props.format) ? + <div className={styles.row}> + <InputGroup + className={classNames( + styles.row, + styles.modDashedBorder, + styles.modLabeledIconHeight + )} + > + {/* fill */} + <FillColorIndicatorComponent + className={styles.modMarginRight} + onUpdateImage={props.onUpdateImage} + /> + </InputGroup> + <InputGroup className={styles.modModeTools}> + <ModeToolsContainer + onUpdateImage={props.onUpdateImage} + /> + </InputGroup> + </div> : null } </div> ) : null} @@ -375,32 +376,32 @@ const PaintEditorComponent = props => { {props.canvas !== null ? ( // eslint-disable-line no-negated-condition <div className={isVector(props.format) ? styles.modeSelector : styles.hidden}> <SelectMode - onUpdateSvg={props.onUpdateSvg} + onUpdateImage={props.onUpdateImage} /> <ReshapeMode - onUpdateSvg={props.onUpdateSvg} + onUpdateImage={props.onUpdateImage} /> <BrushMode - onUpdateSvg={props.onUpdateSvg} + onUpdateImage={props.onUpdateImage} /> <EraserMode - onUpdateSvg={props.onUpdateSvg} + onUpdateImage={props.onUpdateImage} /> <FillMode - onUpdateSvg={props.onUpdateSvg} + onUpdateImage={props.onUpdateImage} /> <TextMode textArea={props.textArea} - onUpdateSvg={props.onUpdateSvg} + onUpdateImage={props.onUpdateImage} /> <LineMode - onUpdateSvg={props.onUpdateSvg} + onUpdateImage={props.onUpdateImage} /> <OvalMode - onUpdateSvg={props.onUpdateSvg} + onUpdateImage={props.onUpdateImage} /> <RectMode - onUpdateSvg={props.onUpdateSvg} + onUpdateImage={props.onUpdateImage} /> </div> ) : null} @@ -408,10 +409,10 @@ const PaintEditorComponent = props => { {props.canvas !== null ? ( // eslint-disable-line no-negated-condition <div className={isBitmap(props.format) ? styles.modeSelector : styles.hidden}> <BitBrushMode - onUpdateSvg={props.onUpdateSvg} + onUpdateImage={props.onUpdateImage} /> <BitLineMode - onUpdateSvg={props.onUpdateSvg} + onUpdateImage={props.onUpdateImage} /> <BitOvalMode /> <BitRectMode /> @@ -432,11 +433,11 @@ const PaintEditorComponent = props => { > <PaperCanvas canvasRef={props.setCanvas} + image={props.image} + imageId={props.imageId} rotationCenterX={props.rotationCenterX} rotationCenterY={props.rotationCenterY} - svg={props.svg} - svgId={props.svgId} - onUpdateSvg={props.onUpdateSvg} + onUpdateImage={props.onUpdateImage} /> <textarea className={styles.textArea} @@ -470,19 +471,20 @@ const PaintEditorComponent = props => { {props.intl.formatMessage(messages.bitmap)} </span> </Button> : - <Button - className={styles.bitmapButton} - onClick={props.onSwitchToVector} - > - <img - className={styles.bitmapButtonIcon} - draggable={false} - src={bitmapIcon} - /> - <span> - {props.intl.formatMessage(messages.vector)} - </span> - </Button> + isBitmap(props.format) ? + <Button + className={styles.bitmapButton} + onClick={props.onSwitchToVector} + > + <img + className={styles.bitmapButtonIcon} + draggable={false} + src={bitmapIcon} + /> + <span> + {props.intl.formatMessage(messages.vector)} + </span> + </Button> : null } {/* Zoom controls */} <InputGroup className={styles.zoomControls}> @@ -534,7 +536,12 @@ PaintEditorComponent.propTypes = { canUndo: PropTypes.func.isRequired, canvas: PropTypes.instanceOf(Element), colorInfo: Loupe.propTypes.colorInfo, - format: PropTypes.oneOf(Object.keys(Formats)).isRequired, + format: PropTypes.oneOf(Object.keys(Formats)), + image: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.instanceOf(HTMLImageElement) + ]), + imageId: PropTypes.string, intl: intlShape, isEyeDropping: PropTypes.bool, name: PropTypes.string, @@ -548,8 +555,8 @@ PaintEditorComponent.propTypes = { onSwitchToVector: PropTypes.func.isRequired, onUndo: PropTypes.func.isRequired, onUngroup: PropTypes.func.isRequired, + onUpdateImage: PropTypes.func.isRequired, onUpdateName: PropTypes.func.isRequired, - onUpdateSvg: PropTypes.func.isRequired, onZoomIn: PropTypes.func.isRequired, onZoomOut: PropTypes.func.isRequired, onZoomReset: PropTypes.func.isRequired, @@ -557,8 +564,6 @@ PaintEditorComponent.propTypes = { rotationCenterY: PropTypes.number, setCanvas: PropTypes.func.isRequired, setTextArea: PropTypes.func.isRequired, - svg: PropTypes.string, - svgId: PropTypes.string, textArea: PropTypes.instanceOf(Element) }; diff --git a/src/containers/bit-brush-mode.jsx b/src/containers/bit-brush-mode.jsx index eca2cf5f..4487a276 100644 --- a/src/containers/bit-brush-mode.jsx +++ b/src/containers/bit-brush-mode.jsx @@ -52,7 +52,7 @@ class BitBrushMode extends React.Component { color = DEFAULT_COLOR; } this.tool = new BitBrushTool( - this.props.onUpdateSvg + this.props.onUpdateImage ); this.tool.setColor(color); this.tool.setBrushSize(this.props.bitBrushSize); @@ -81,7 +81,7 @@ BitBrushMode.propTypes = { handleMouseDown: PropTypes.func.isRequired, isBitBrushModeActive: PropTypes.bool.isRequired, onChangeFillColor: PropTypes.func.isRequired, - onUpdateSvg: PropTypes.func.isRequired + onUpdateImage: PropTypes.func.isRequired }; const mapStateToProps = state => ({ diff --git a/src/containers/bit-line-mode.jsx b/src/containers/bit-line-mode.jsx index dc2bfd54..a259aae3 100644 --- a/src/containers/bit-line-mode.jsx +++ b/src/containers/bit-line-mode.jsx @@ -52,7 +52,7 @@ class BitLineMode extends React.Component { color = DEFAULT_COLOR; } this.tool = new BitLineTool( - this.props.onUpdateSvg + this.props.onUpdateImage ); this.tool.setColor(color); this.tool.setLineSize(this.props.bitBrushSize); @@ -81,7 +81,7 @@ BitLineMode.propTypes = { handleMouseDown: PropTypes.func.isRequired, isBitLineModeActive: PropTypes.bool.isRequired, onChangeFillColor: PropTypes.func.isRequired, - onUpdateSvg: PropTypes.func.isRequired + onUpdateImage: PropTypes.func.isRequired }; const mapStateToProps = state => ({ diff --git a/src/containers/brush-mode.jsx b/src/containers/brush-mode.jsx index 7584904a..76a45af3 100644 --- a/src/containers/brush-mode.jsx +++ b/src/containers/brush-mode.jsx @@ -21,7 +21,7 @@ class BrushMode extends React.Component { 'deactivateTool' ]); this.blob = new Blobbiness( - this.props.onUpdateSvg, this.props.clearSelectedItems); + this.props.onUpdateImage, this.props.clearSelectedItems); } componentDidMount () { if (this.props.isBrushModeActive) { @@ -85,7 +85,7 @@ BrushMode.propTypes = { handleMouseDown: PropTypes.func.isRequired, isBrushModeActive: PropTypes.bool.isRequired, onChangeFillColor: PropTypes.func.isRequired, - onUpdateSvg: PropTypes.func.isRequired + onUpdateImage: PropTypes.func.isRequired }; const mapStateToProps = state => ({ diff --git a/src/containers/eraser-mode.jsx b/src/containers/eraser-mode.jsx index 838bad44..6ae2cbfb 100644 --- a/src/containers/eraser-mode.jsx +++ b/src/containers/eraser-mode.jsx @@ -17,7 +17,7 @@ class EraserMode extends React.Component { 'deactivateTool' ]); this.blob = new Blobbiness( - this.props.onUpdateSvg, this.props.clearSelectedItems); + this.props.onUpdateImage, this.props.clearSelectedItems); } componentDidMount () { if (this.props.isEraserModeActive) { @@ -62,7 +62,7 @@ EraserMode.propTypes = { }), handleMouseDown: PropTypes.func.isRequired, isEraserModeActive: PropTypes.bool.isRequired, - onUpdateSvg: PropTypes.func.isRequired + onUpdateImage: PropTypes.func.isRequired }; const mapStateToProps = state => ({ diff --git a/src/containers/fill-color-indicator.jsx b/src/containers/fill-color-indicator.jsx index 4af2befa..78d51d26 100644 --- a/src/containers/fill-color-indicator.jsx +++ b/src/containers/fill-color-indicator.jsx @@ -21,10 +21,10 @@ class FillColorIndicator extends React.Component { this._hasChanged = false; } componentWillReceiveProps (newProps) { - const {fillColorModalVisible, onUpdateSvg} = this.props; + const {fillColorModalVisible, onUpdateImage} = this.props; if (fillColorModalVisible && !newProps.fillColorModalVisible) { // Submit the new SVG, which also stores a single undo/redo action. - if (this._hasChanged) onUpdateSvg(); + if (this._hasChanged) onUpdateImage(); this._hasChanged = false; } } @@ -77,7 +77,7 @@ FillColorIndicator.propTypes = { isEyeDropping: PropTypes.bool.isRequired, onChangeFillColor: PropTypes.func.isRequired, onCloseFillColor: PropTypes.func.isRequired, - onUpdateSvg: PropTypes.func.isRequired, + onUpdateImage: PropTypes.func.isRequired, textEditTarget: PropTypes.number }; diff --git a/src/containers/fill-mode.jsx b/src/containers/fill-mode.jsx index 4b67a12a..aa2ff9fb 100644 --- a/src/containers/fill-mode.jsx +++ b/src/containers/fill-mode.jsx @@ -53,7 +53,7 @@ class FillMode extends React.Component { this.tool = new FillTool( this.props.setHoveredItem, this.props.clearHoveredItem, - this.props.onUpdateSvg + this.props.onUpdateImage ); this.tool.setFillColor(this.props.fillColor === MIXED ? DEFAULT_COLOR : this.props.fillColor); this.tool.setPrevHoveredItemId(this.props.hoveredItemId); @@ -82,7 +82,7 @@ FillMode.propTypes = { hoveredItemId: PropTypes.number, isFillModeActive: PropTypes.bool.isRequired, onChangeFillColor: PropTypes.func.isRequired, - onUpdateSvg: PropTypes.func.isRequired, + onUpdateImage: PropTypes.func.isRequired, setHoveredItem: PropTypes.func.isRequired }; diff --git a/src/containers/line-mode.jsx b/src/containers/line-mode.jsx index 42df20f7..a8e895cf 100644 --- a/src/containers/line-mode.jsx +++ b/src/containers/line-mode.jsx @@ -220,7 +220,7 @@ class LineMode extends React.Component { } if (this.path) { - this.props.onUpdateSvg(); + this.props.onUpdateImage(); this.path = null; } this.active = false; @@ -257,7 +257,7 @@ LineMode.propTypes = { isLineModeActive: PropTypes.bool.isRequired, onChangeStrokeColor: PropTypes.func.isRequired, onChangeStrokeWidth: PropTypes.func.isRequired, - onUpdateSvg: PropTypes.func.isRequired + onUpdateImage: PropTypes.func.isRequired }; const mapStateToProps = state => ({ diff --git a/src/containers/mode-tools.jsx b/src/containers/mode-tools.jsx index 20773045..fcf3ee24 100644 --- a/src/containers/mode-tools.jsx +++ b/src/containers/mode-tools.jsx @@ -117,7 +117,7 @@ class ModeTools extends React.Component { } if (changed) { this.props.setSelectedItems(); - this.props.onUpdateSvg(); + this.props.onUpdateImage(); } } handlePointPoints () { @@ -133,7 +133,7 @@ class ModeTools extends React.Component { } if (changed) { this.props.setSelectedItems(); - this.props.onUpdateSvg(); + this.props.onUpdateImage(); } } _handleFlip (horizontalScale, verticalScale) { @@ -160,7 +160,7 @@ class ModeTools extends React.Component { } itemGroup.remove(); - this.props.onUpdateSvg(); + this.props.onUpdateImage(); } handleFlipHorizontal () { this._handleFlip(-1, 1); @@ -194,7 +194,7 @@ class ModeTools extends React.Component { } this.props.incrementPasteOffset(); this.props.setSelectedItems(); - this.props.onUpdateSvg(); + this.props.onUpdateImage(); } } render () { @@ -217,7 +217,7 @@ ModeTools.propTypes = { clearSelectedItems: PropTypes.func.isRequired, clipboardItems: PropTypes.arrayOf(PropTypes.array), incrementPasteOffset: PropTypes.func.isRequired, - onUpdateSvg: PropTypes.func.isRequired, + onUpdateImage: PropTypes.func.isRequired, pasteOffset: PropTypes.number, // Listen on selected items to update hasSelectedPoints selectedItems: diff --git a/src/containers/oval-mode.jsx b/src/containers/oval-mode.jsx index 809488be..40a407a1 100644 --- a/src/containers/oval-mode.jsx +++ b/src/containers/oval-mode.jsx @@ -65,7 +65,7 @@ class OvalMode extends React.Component { this.tool = new OvalTool( this.props.setSelectedItems, this.props.clearSelectedItems, - this.props.onUpdateSvg + this.props.onUpdateImage ); this.tool.setColorState(this.props.colorState); this.tool.activate(); @@ -96,7 +96,7 @@ OvalMode.propTypes = { isOvalModeActive: PropTypes.bool.isRequired, onChangeFillColor: PropTypes.func.isRequired, onChangeStrokeColor: PropTypes.func.isRequired, - onUpdateSvg: PropTypes.func.isRequired, + onUpdateImage: PropTypes.func.isRequired, selectedItems: PropTypes.arrayOf(PropTypes.instanceOf(paper.Item)), setSelectedItems: PropTypes.func.isRequired }; diff --git a/src/containers/paint-editor.jsx b/src/containers/paint-editor.jsx index 659b8336..373d6366 100644 --- a/src/containers/paint-editor.jsx +++ b/src/containers/paint-editor.jsx @@ -35,7 +35,7 @@ class PaintEditor extends React.Component { constructor (props) { super(props); bindAll(this, [ - 'handleUpdateSvg', + 'handleUpdateImage', 'handleUndo', 'handleRedo', 'handleSendBackward', @@ -118,7 +118,7 @@ class PaintEditor extends React.Component { } } } - handleUpdateSvg (skipSnapshot) { + handleUpdateImage (skipSnapshot) { // Store the zoom/pan and restore it after snapshotting // TODO Only doing this because snapshotting at zoom/pan makes export wrong const oldZoom = paper.project.view.zoom; @@ -127,35 +127,36 @@ class PaintEditor extends React.Component { let raster; if (isBitmap(this.props.format)) { - // @todo export bitmap here raster = trim(getRaster()); - if (raster.width === 0 || raster.height === 0) { - raster.remove(); - } else { - paper.project.activeLayer.addChild(raster); - } + raster.remove(); + + this.props.onUpdateImage( + false /* isVector */, + raster.canvas, + paper.project.view.center.x - raster.bounds.x, + paper.project.view.center.y - raster.bounds.y); + } else if (isVector(this.props.format)) { + const guideLayers = hideGuideLayers(true /* includeRaster */); + + // Export at 0.5x + scaleWithStrokes(paper.project.activeLayer, .5, new paper.Point()); + const bounds = paper.project.activeLayer.bounds; + + this.props.onUpdateImage( + true /* isVector */, + paper.project.exportSVG({ + asString: true, + bounds: 'content', + matrix: new paper.Matrix().translate(-bounds.x, -bounds.y) + }), + (paper.project.view.center.x / 2) - bounds.x, + (paper.project.view.center.y / 2) - bounds.y); + + scaleWithStrokes(paper.project.activeLayer, 2, new paper.Point()); + paper.project.activeLayer.applyMatrix = true; + + showGuideLayers(guideLayers); } - - const guideLayers = hideGuideLayers(true /* includeRaster */); - - // Export at 0.5x - scaleWithStrokes(paper.project.activeLayer, .5, new paper.Point()); - const bounds = paper.project.activeLayer.bounds; - - this.props.onUpdateSvg( - paper.project.exportSVG({ - asString: true, - bounds: 'content', - matrix: new paper.Matrix().translate(-bounds.x, -bounds.y) - }), - (paper.project.view.center.x / 2) - bounds.x, - (paper.project.view.center.y / 2) - bounds.y); - - scaleWithStrokes(paper.project.activeLayer, 2, new paper.Point()); - paper.project.activeLayer.applyMatrix = true; - - showGuideLayers(guideLayers); - if (raster) raster.remove(); if (!skipSnapshot) { performSnapshot(this.props.undoSnapshot, this.props.format); @@ -166,28 +167,28 @@ class PaintEditor extends React.Component { paper.project.view.center = oldCenter; } handleUndo () { - performUndo(this.props.undoState, this.props.onUndo, this.props.setSelectedItems, this.handleUpdateSvg); + performUndo(this.props.undoState, this.props.onUndo, this.props.setSelectedItems, this.handleUpdateImage); } handleRedo () { - performRedo(this.props.undoState, this.props.onRedo, this.props.setSelectedItems, this.handleUpdateSvg); + performRedo(this.props.undoState, this.props.onRedo, this.props.setSelectedItems, this.handleUpdateImage); } handleGroup () { - groupSelection(this.props.clearSelectedItems, this.props.setSelectedItems, this.handleUpdateSvg); + groupSelection(this.props.clearSelectedItems, this.props.setSelectedItems, this.handleUpdateImage); } handleUngroup () { - ungroupSelection(this.props.clearSelectedItems, this.props.setSelectedItems, this.handleUpdateSvg); + ungroupSelection(this.props.clearSelectedItems, this.props.setSelectedItems, this.handleUpdateImage); } handleSendBackward () { - sendBackward(this.handleUpdateSvg); + sendBackward(this.handleUpdateImage); } handleSendForward () { - bringForward(this.handleUpdateSvg); + bringForward(this.handleUpdateImage); } handleSendToBack () { - sendToBack(this.handleUpdateSvg); + sendToBack(this.handleUpdateImage); } handleSendToFront () { - bringToFront(this.handleUpdateSvg); + bringToFront(this.handleUpdateImage); } canUndo () { return shouldShowUndo(this.props.undoState); @@ -287,14 +288,14 @@ class PaintEditor extends React.Component { canvas={this.state.canvas} colorInfo={this.state.colorInfo} format={this.props.format} + image={this.props.image} + imageId={this.props.imageId} isEyeDropping={this.props.isEyeDropping} name={this.props.name} rotationCenterX={this.props.rotationCenterX} rotationCenterY={this.props.rotationCenterY} setCanvas={this.setCanvas} setTextArea={this.setTextArea} - svg={this.props.svg} - svgId={this.props.svgId} textArea={this.state.textArea} onGroup={this.handleGroup} onRedo={this.handleRedo} @@ -306,8 +307,8 @@ class PaintEditor extends React.Component { onSwitchToVector={this.props.handleSwitchToVector} onUndo={this.handleUndo} onUngroup={this.handleUngroup} + onUpdateImage={this.handleUpdateImage} onUpdateName={this.props.onUpdateName} - onUpdateSvg={this.handleUpdateSvg} onZoomIn={this.handleZoomIn} onZoomOut={this.handleZoomOut} onZoomReset={this.handleZoomReset} @@ -320,9 +321,14 @@ PaintEditor.propTypes = { changeColorToEyeDropper: PropTypes.func, changeMode: PropTypes.func.isRequired, clearSelectedItems: PropTypes.func.isRequired, - format: PropTypes.oneOf(Object.keys(Formats)).isRequired, + format: PropTypes.oneOf(Object.keys(Formats)), handleSwitchToBitmap: PropTypes.func.isRequired, handleSwitchToVector: PropTypes.func.isRequired, + image: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.instanceOf(HTMLImageElement) + ]), + imageId: PropTypes.string, isEyeDropping: PropTypes.bool, mode: PropTypes.oneOf(Object.keys(Modes)).isRequired, name: PropTypes.string, @@ -330,8 +336,8 @@ PaintEditor.propTypes = { onKeyPress: PropTypes.func.isRequired, onRedo: PropTypes.func.isRequired, onUndo: PropTypes.func.isRequired, + onUpdateImage: PropTypes.func.isRequired, onUpdateName: PropTypes.func.isRequired, - onUpdateSvg: PropTypes.func.isRequired, previousTool: PropTypes.shape({ // paper.Tool activate: PropTypes.func.isRequired, remove: PropTypes.func.isRequired @@ -340,8 +346,6 @@ PaintEditor.propTypes = { rotationCenterX: PropTypes.number, rotationCenterY: PropTypes.number, setSelectedItems: PropTypes.func.isRequired, - svg: PropTypes.string, - svgId: PropTypes.string, textEditing: PropTypes.bool.isRequired, undoSnapshot: PropTypes.func.isRequired, undoState: PropTypes.shape({ diff --git a/src/containers/paper-canvas.jsx b/src/containers/paper-canvas.jsx index bef86deb..e75e2584 100644 --- a/src/containers/paper-canvas.jsx +++ b/src/containers/paper-canvas.jsx @@ -28,6 +28,7 @@ class PaperCanvas extends React.Component { constructor (props) { super(props); bindAll(this, [ + 'checkFormat', 'convertToBitmap', 'convertToVector', 'setCanvas', @@ -51,15 +52,26 @@ class PaperCanvas extends React.Component { paper.settings.handleSize = 0; // Make layers. setupLayers(); - if (this.props.svg) { - this.importSvg(this.props.svg, this.props.rotationCenterX, this.props.rotationCenterY); + if (this.props.image) { + if (isBitmap(this.checkFormat(this.props.image))) { + // import bitmap + this.props.changeFormat(Formats.BITMAP_SKIP_CONVERT); + performSnapshot(this.props.undoSnapshot, this.props.format); + getRaster().drawImage( + this.props.image, + paper.project.view.center.x - this.props.rotationCenterX, + paper.project.view.center.y - this.props.rotationCenterY); + } else if (isVector(this.checkFormat(this.props.image))) { + this.props.changeFormat(Formats.VECTOR_SKIP_CONVERT); + this.importSvg(this.props.image, this.props.rotationCenterX, this.props.rotationCenterY); + } } else { performSnapshot(this.props.undoSnapshot, this.props.format); } } componentWillReceiveProps (newProps) { - if (this.props.svgId !== newProps.svgId) { - this.switchCostume(newProps.svg, newProps.rotationCenterX, newProps.rotationCenterY); + if (this.props.imageId !== newProps.imageId) { + this.switchCostume(newProps.image, newProps.rotationCenterX, newProps.rotationCenterY); } else if (isVector(this.props.format) && newProps.format === Formats.BITMAP) { this.convertToBitmap(); } else if (isBitmap(this.props.format) && newProps.format === Formats.VECTOR) { @@ -77,7 +89,7 @@ class PaperCanvas extends React.Component { } // Backspace, delete if (event.key === 'Delete' || event.key === 'Backspace') { - if (deleteSelection(this.props.mode, this.props.onUpdateSvg)) { + if (deleteSelection(this.props.mode, this.props.onUpdateImage)) { this.props.setSelectedItems(); } } @@ -109,13 +121,14 @@ class PaperCanvas extends React.Component { const img = new Image(); img.onload = () => { const raster = new paper.Raster(img); + raster.remove(); raster.onLoad = () => { const subCanvas = raster.canvas; getRaster().drawImage( subCanvas, new paper.Point(Math.floor(bounds.topLeft.x), Math.floor(bounds.topLeft.y))); paper.project.activeLayer.removeChildren(); - this.props.onUpdateSvg(); + this.props.onUpdateImage(); }; }; img.src = `data:image/svg+xml;charset=utf-8,${svgString}`; @@ -133,9 +146,15 @@ class PaperCanvas extends React.Component { paper.project.activeLayer.addChild(raster); } clearRaster(); - this.props.onUpdateSvg(); + this.props.onUpdateImage(); } - switchCostume (svg, rotationCenterX, rotationCenterY) { + checkFormat (image) { + if (image instanceof HTMLImageElement) return Formats.BITMAP; + if (typeof image === 'string') return Formats.VECTOR; + log.error(`Image could not be read.`); + return null; + } + switchCostume (image, rotationCenterX, rotationCenterY) { for (const layer of paper.project.layers) { if (layer.data.isRasterLayer) { clearRaster(); @@ -147,16 +166,29 @@ class PaperCanvas extends React.Component { this.props.clearSelectedItems(); this.props.clearHoveredItem(); this.props.clearPasteOffset(); - if (svg) { - this.props.changeFormat(Formats.VECTOR_SKIP_CONVERT); - // Store the zoom/pan and restore it after importing a new SVG - const oldZoom = paper.project.view.zoom; - const oldCenter = paper.project.view.center.clone(); - resetZoom(); - this.props.updateViewBounds(paper.view.matrix); - this.importSvg(svg, rotationCenterX, rotationCenterY); - paper.project.view.zoom = oldZoom; - paper.project.view.center = oldCenter; + if (image) { + if (isBitmap(this.checkFormat(image))) { + // import bitmap + this.props.changeFormat(Formats.BITMAP_SKIP_CONVERT); + getRaster().drawImage( + image, + paper.project.view.center.x - rotationCenterX, + paper.project.view.center.y - rotationCenterY); + performSnapshot(this.props.undoSnapshot, this.props.format); + } else if (isVector(this.checkFormat(image))) { + this.props.changeFormat(Formats.VECTOR_SKIP_CONVERT); + // Store the zoom/pan and restore it after importing a new SVG + const oldZoom = paper.project.view.zoom; + const oldCenter = paper.project.view.center.clone(); + resetZoom(); + this.props.updateViewBounds(paper.view.matrix); + this.importSvg(image, rotationCenterX, rotationCenterY); + paper.project.view.zoom = oldZoom; + paper.project.view.center = oldCenter; + } else { + log.error(`Couldn't open image.`); + performSnapshot(this.props.undoSnapshot, this.props.format); + } } else { performSnapshot(this.props.undoSnapshot, this.props.format); } @@ -290,14 +322,17 @@ PaperCanvas.propTypes = { clearPasteOffset: PropTypes.func.isRequired, clearSelectedItems: PropTypes.func.isRequired, clearUndo: PropTypes.func.isRequired, - format: PropTypes.oneOf(Object.keys(Formats)).isRequired, + format: PropTypes.oneOf(Object.keys(Formats)), + image: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.instanceOf(HTMLImageElement) + ]), + imageId: PropTypes.string, mode: PropTypes.oneOf(Object.keys(Modes)), - onUpdateSvg: PropTypes.func.isRequired, + onUpdateImage: PropTypes.func.isRequired, rotationCenterX: PropTypes.number, rotationCenterY: PropTypes.number, setSelectedItems: PropTypes.func.isRequired, - svg: PropTypes.string, - svgId: PropTypes.string, undoSnapshot: PropTypes.func.isRequired, updateViewBounds: PropTypes.func.isRequired }; diff --git a/src/containers/rect-mode.jsx b/src/containers/rect-mode.jsx index ffdfc72c..ca936dfb 100644 --- a/src/containers/rect-mode.jsx +++ b/src/containers/rect-mode.jsx @@ -65,7 +65,7 @@ class RectMode extends React.Component { this.tool = new RectTool( this.props.setSelectedItems, this.props.clearSelectedItems, - this.props.onUpdateSvg + this.props.onUpdateImage ); this.tool.setColorState(this.props.colorState); this.tool.activate(); @@ -96,7 +96,7 @@ RectMode.propTypes = { isRectModeActive: PropTypes.bool.isRequired, onChangeFillColor: PropTypes.func.isRequired, onChangeStrokeColor: PropTypes.func.isRequired, - onUpdateSvg: PropTypes.func.isRequired, + onUpdateImage: PropTypes.func.isRequired, selectedItems: PropTypes.arrayOf(PropTypes.instanceOf(paper.Item)), setSelectedItems: PropTypes.func.isRequired }; diff --git a/src/containers/reshape-mode.jsx b/src/containers/reshape-mode.jsx index 1b84710e..ff499cf9 100644 --- a/src/containers/reshape-mode.jsx +++ b/src/containers/reshape-mode.jsx @@ -45,7 +45,7 @@ class ReshapeMode extends React.Component { this.props.clearHoveredItem, this.props.setSelectedItems, this.props.clearSelectedItems, - this.props.onUpdateSvg + this.props.onUpdateImage ); this.tool.setPrevHoveredItemId(this.props.hoveredItemId); this.tool.activate(); @@ -72,7 +72,7 @@ ReshapeMode.propTypes = { handleMouseDown: PropTypes.func.isRequired, hoveredItemId: PropTypes.number, isReshapeModeActive: PropTypes.bool.isRequired, - onUpdateSvg: PropTypes.func.isRequired, + onUpdateImage: PropTypes.func.isRequired, setHoveredItem: PropTypes.func.isRequired, setSelectedItems: PropTypes.func.isRequired }; diff --git a/src/containers/rounded-rect-mode.jsx b/src/containers/rounded-rect-mode.jsx index fa733d71..bfee9b76 100644 --- a/src/containers/rounded-rect-mode.jsx +++ b/src/containers/rounded-rect-mode.jsx @@ -45,7 +45,7 @@ class RoundedRectMode extends React.Component { this.props.clearHoveredItem, this.props.setSelectedItems, this.props.clearSelectedItems, - this.props.onUpdateSvg + this.props.onUpdateImage ); this.tool.activate(); } @@ -70,7 +70,7 @@ RoundedRectMode.propTypes = { handleMouseDown: PropTypes.func.isRequired, hoveredItemId: PropTypes.number, isRoundedRectModeActive: PropTypes.bool.isRequired, - onUpdateSvg: PropTypes.func.isRequired, + onUpdateImage: PropTypes.func.isRequired, setHoveredItem: PropTypes.func.isRequired, setSelectedItems: PropTypes.func.isRequired }; diff --git a/src/containers/select-mode.jsx b/src/containers/select-mode.jsx index b8c18276..e1a0ed47 100644 --- a/src/containers/select-mode.jsx +++ b/src/containers/select-mode.jsx @@ -49,7 +49,7 @@ class SelectMode extends React.Component { this.props.clearHoveredItem, this.props.setSelectedItems, this.props.clearSelectedItems, - this.props.onUpdateSvg + this.props.onUpdateImage ); this.tool.activate(); } @@ -74,7 +74,7 @@ SelectMode.propTypes = { handleMouseDown: PropTypes.func.isRequired, hoveredItemId: PropTypes.number, isSelectModeActive: PropTypes.bool.isRequired, - onUpdateSvg: PropTypes.func.isRequired, + onUpdateImage: PropTypes.func.isRequired, selectedItems: PropTypes.arrayOf(PropTypes.instanceOf(paper.Item)), setHoveredItem: PropTypes.func.isRequired, setSelectedItems: PropTypes.func.isRequired diff --git a/src/containers/stroke-color-indicator.jsx b/src/containers/stroke-color-indicator.jsx index d88bedfe..c752494c 100644 --- a/src/containers/stroke-color-indicator.jsx +++ b/src/containers/stroke-color-indicator.jsx @@ -21,10 +21,10 @@ class StrokeColorIndicator extends React.Component { this._hasChanged = false; } componentWillReceiveProps (newProps) { - const {strokeColorModalVisible, onUpdateSvg} = this.props; + const {strokeColorModalVisible, onUpdateImage} = this.props; if (strokeColorModalVisible && !newProps.strokeColorModalVisible) { // Submit the new SVG, which also stores a single undo/redo action. - if (this._hasChanged) onUpdateSvg(); + if (this._hasChanged) onUpdateImage(); this._hasChanged = false; } } @@ -76,7 +76,7 @@ StrokeColorIndicator.propTypes = { isEyeDropping: PropTypes.bool.isRequired, onChangeStrokeColor: PropTypes.func.isRequired, onCloseStrokeColor: PropTypes.func.isRequired, - onUpdateSvg: PropTypes.func.isRequired, + onUpdateImage: PropTypes.func.isRequired, strokeColor: PropTypes.string, strokeColorModalVisible: PropTypes.bool.isRequired, textEditTarget: PropTypes.number diff --git a/src/containers/stroke-width-indicator.jsx b/src/containers/stroke-width-indicator.jsx index 663049a2..8a9e6f08 100644 --- a/src/containers/stroke-width-indicator.jsx +++ b/src/containers/stroke-width-indicator.jsx @@ -16,7 +16,7 @@ class StrokeWidthIndicator extends React.Component { } handleChangeStrokeWidth (newWidth) { if (applyStrokeWidthToSelection(newWidth, this.props.textEditTarget)) { - this.props.onUpdateSvg(); + this.props.onUpdateImage(); } this.props.onChangeStrokeWidth(newWidth); } @@ -46,7 +46,7 @@ const mapDispatchToProps = dispatch => ({ StrokeWidthIndicator.propTypes = { disabled: PropTypes.bool.isRequired, onChangeStrokeWidth: PropTypes.func.isRequired, - onUpdateSvg: PropTypes.func.isRequired, + onUpdateImage: PropTypes.func.isRequired, strokeWidth: PropTypes.number, textEditTarget: PropTypes.number }; diff --git a/src/containers/text-mode.jsx b/src/containers/text-mode.jsx index 039a6304..39dd7f35 100644 --- a/src/containers/text-mode.jsx +++ b/src/containers/text-mode.jsx @@ -73,7 +73,7 @@ class TextMode extends React.Component { this.props.textArea, this.props.setSelectedItems, this.props.clearSelectedItems, - this.props.onUpdateSvg, + this.props.onUpdateImage, this.props.setTextEditTarget, ); this.tool.setColorState(this.props.colorState); @@ -105,7 +105,7 @@ TextMode.propTypes = { isTextModeActive: PropTypes.bool.isRequired, onChangeFillColor: PropTypes.func.isRequired, onChangeStrokeColor: PropTypes.func.isRequired, - onUpdateSvg: PropTypes.func.isRequired, + onUpdateImage: PropTypes.func.isRequired, selectedItems: PropTypes.arrayOf(PropTypes.instanceOf(paper.Item)), setSelectedItems: PropTypes.func.isRequired, setTextEditTarget: PropTypes.func.isRequired, diff --git a/src/helper/bit-tools/brush-tool.js b/src/helper/bit-tools/brush-tool.js index c2dbd726..f4fde777 100644 --- a/src/helper/bit-tools/brush-tool.js +++ b/src/helper/bit-tools/brush-tool.js @@ -8,11 +8,11 @@ import {getGuideLayer} from '../layer'; */ class BrushTool extends paper.Tool { /** - * @param {!function} onUpdateSvg A callback to call when the image visibly changes + * @param {!function} onUpdateImage A callback to call when the image visibly changes */ - constructor (onUpdateSvg) { + constructor (onUpdateImage) { super(); - this.onUpdateSvg = onUpdateSvg; + this.onUpdateImage = onUpdateImage; // We have to set these functions instead of just declaring them because // paper.js tools hook up the listeners in the setter functions. @@ -87,7 +87,7 @@ class BrushTool extends paper.Tool { if (event.event.button > 0 || !this.active) return; // only first mouse button forEachLinePoint(this.lastPoint, event.point, this.draw.bind(this)); - this.onUpdateSvg(); + this.onUpdateImage(); this.lastPoint = null; this.active = false; diff --git a/src/helper/bit-tools/line-tool.js b/src/helper/bit-tools/line-tool.js index bd4e8aa0..e20e1a8e 100644 --- a/src/helper/bit-tools/line-tool.js +++ b/src/helper/bit-tools/line-tool.js @@ -9,11 +9,11 @@ import {ART_BOARD_WIDTH, ART_BOARD_HEIGHT} from '../view'; */ class LineTool extends paper.Tool { /** - * @param {!function} onUpdateSvg A callback to call when the image visibly changes + * @param {!function} onUpdateImage A callback to call when the image visibly changes */ - constructor (onUpdateSvg) { + constructor (onUpdateImage) { super(); - this.onUpdateSvg = onUpdateSvg; + this.onUpdateImage = onUpdateImage; // We have to set these functions instead of just declaring them because // paper.js tools hook up the listeners in the setter functions. @@ -103,7 +103,7 @@ class LineTool extends paper.Tool { this.drawTarget = getRaster(); forEachLinePoint(this.startPoint, event.point, this.draw.bind(this)); this.drawTarget = null; - this.onUpdateSvg(); + this.onUpdateImage(); this.lastPoint = null; this.active = false; diff --git a/src/helper/blob-tools/blob.js b/src/helper/blob-tools/blob.js index 5530e9ba..60615f85 100644 --- a/src/helper/blob-tools/blob.js +++ b/src/helper/blob-tools/blob.js @@ -27,13 +27,13 @@ class Blobbiness { } /** - * @param {function} onUpdateSvg call when the drawing has changed to let listeners know + * @param {function} onUpdateImage call when the drawing has changed to let listeners know * @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state */ - constructor (onUpdateSvg, clearSelectedItems) { + constructor (onUpdateImage, clearSelectedItems) { this.broadBrushHelper = new BroadBrushHelper(); this.segmentBrushHelper = new SegmentBrushHelper(); - this.onUpdateSvg = onUpdateSvg; + this.onUpdateImage = onUpdateImage; this.clearSelectedItems = clearSelectedItems; // The following are stored to check whether these have changed and the cursor preview needs to be redrawn. @@ -143,7 +143,7 @@ class Blobbiness { // Remove cursor preview during snapshot, then bring it back blob.cursorPreview.remove(); - blob.onUpdateSvg(); + blob.onUpdateImage(); blob.cursorPreview.parent = getGuideLayer(); // Reset diff --git a/src/helper/group.js b/src/helper/group.js index 5d9232f0..4e23aa7a 100644 --- a/src/helper/group.js +++ b/src/helper/group.js @@ -11,10 +11,10 @@ const isGroup = function (item) { * @param {!Array<paper.Item>} items Root level items to group * @param {!function} clearSelectedItems Function to clear Redux state's selected items * @param {!function} setSelectedItems Function to set Redux state with new list of selected items - * @param {!function} onUpdateSvg Function to let listeners know that SVG has changed. + * @param {!function} onUpdateImage Function to let listeners know that SVG has changed. * @return {paper.Group} the group if one is created, otherwise false. */ -const groupItems = function (items, clearSelectedItems, setSelectedItems, onUpdateSvg) { +const groupItems = function (items, clearSelectedItems, setSelectedItems, onUpdateImage) { if (items.length > 0) { const group = new paper.Group(items); clearSelection(clearSelectedItems); @@ -23,7 +23,7 @@ const groupItems = function (items, clearSelectedItems, setSelectedItems, onUpda group.children[i].selected = true; } setSelectedItems(); - onUpdateSvg(); + onUpdateImage(); return group; } return false; @@ -33,12 +33,12 @@ const groupItems = function (items, clearSelectedItems, setSelectedItems, onUpda * Groups the selected items. Other things are then deselected and the new group is selected. * @param {!function} clearSelectedItems Function to clear Redux state's selected items * @param {!function} setSelectedItems Function to set Redux state with new list of selected items - * @param {!function} onUpdateSvg Function to let listeners know that SVG has changed. + * @param {!function} onUpdateImage Function to let listeners know that SVG has changed. * @return {paper.Group} the group if one is created, otherwise false. */ -const groupSelection = function (clearSelectedItems, setSelectedItems, onUpdateSvg) { +const groupSelection = function (clearSelectedItems, setSelectedItems, onUpdateImage) { const items = getSelectedRootItems(); - return groupItems(items, clearSelectedItems, setSelectedItems, onUpdateSvg); + return groupItems(items, clearSelectedItems, setSelectedItems, onUpdateImage); }; const _ungroupLoop = function (group, recursive, setSelectedItems) { @@ -71,15 +71,15 @@ const _ungroupLoop = function (group, recursive, setSelectedItems) { /** * Ungroups the given items. The new group is selected only if setSelectedItems is passed in. - * onUpdateSvg is called to notify listeners of a change on the SVG only if onUpdateSvg is passed in. + * onUpdateImage is called to notify listeners of a change on the SVG only if onUpdateImage is passed in. * The reason these arguments are optional on ungroupItems is because ungroupItems is used for parts of * SVG import, which shouldn't change the selection or undo state. * * @param {!Array<paper.Item>} items Items to ungroup if they are groups * @param {?function} setSelectedItems Function to set Redux state with new list of selected items - * @param {?function} onUpdateSvg Function to let listeners know that SVG has changed. + * @param {?function} onUpdateImage Function to let listeners know that SVG has changed. */ -const ungroupItems = function (items, setSelectedItems, onUpdateSvg) { +const ungroupItems = function (items, setSelectedItems, onUpdateImage) { if (items.length === 0) { return; } @@ -102,8 +102,8 @@ const ungroupItems = function (items, setSelectedItems, onUpdateSvg) { emptyGroups[j].remove(); } // @todo: enable/disable grouping icons - if (onUpdateSvg) { - onUpdateSvg(); + if (onUpdateImage) { + onUpdateImage(); } }; @@ -112,12 +112,12 @@ const ungroupItems = function (items, setSelectedItems, onUpdateSvg) { * * @param {!function} clearSelectedItems Function to clear Redux state's selected items * @param {!function} setSelectedItems Function to set Redux state with new list of selected items - * @param {!function} onUpdateSvg Function to let listeners know that SVG has changed. + * @param {!function} onUpdateImage Function to let listeners know that SVG has changed. */ -const ungroupSelection = function (clearSelectedItems, setSelectedItems, onUpdateSvg) { +const ungroupSelection = function (clearSelectedItems, setSelectedItems, onUpdateImage) { const items = getSelectedRootItems(); clearSelection(clearSelectedItems); - ungroupItems(items, setSelectedItems, onUpdateSvg); + ungroupItems(items, setSelectedItems, onUpdateImage); }; const getItemsGroup = function (item) { diff --git a/src/helper/order.js b/src/helper/order.js index 6c7254c8..8050722f 100644 --- a/src/helper/order.js +++ b/src/helper/order.js @@ -1,22 +1,22 @@ import {getSelectedRootItems} from './selection'; -const bringToFront = function (onUpdateSvg) { +const bringToFront = function (onUpdateImage) { const items = getSelectedRootItems(); for (const item of items) { item.bringToFront(); } - onUpdateSvg(); + onUpdateImage(); }; -const sendToBack = function (onUpdateSvg) { +const sendToBack = function (onUpdateImage) { const items = getSelectedRootItems(); for (let i = items.length - 1; i >= 0; i--) { items[i].sendToBack(); } - onUpdateSvg(); + onUpdateImage(); }; -const bringForward = function (onUpdateSvg) { +const bringForward = function (onUpdateImage) { const items = getSelectedRootItems(); // Already at front if (items.length === 0 || !items[items.length - 1].nextSibling) { @@ -27,10 +27,10 @@ const bringForward = function (onUpdateSvg) { for (let i = items.length - 1; i >= 0; i--) { items[i].insertAbove(nextSibling); } - onUpdateSvg(); + onUpdateImage(); }; -const sendBackward = function (onUpdateSvg) { +const sendBackward = function (onUpdateImage) { const items = getSelectedRootItems(); // Already at front if (items.length === 0 || !items[0].previousSibling) { @@ -41,7 +41,7 @@ const sendBackward = function (onUpdateSvg) { for (const item of items) { item.insertBelow(previousSibling); } - onUpdateSvg(); + onUpdateImage(); }; const shouldShowSendBackward = function () { diff --git a/src/helper/selection-tools/bounding-box-tool.js b/src/helper/selection-tools/bounding-box-tool.js index 879b8b70..b0e2a9eb 100644 --- a/src/helper/selection-tools/bounding-box-tool.js +++ b/src/helper/selection-tools/bounding-box-tool.js @@ -28,25 +28,25 @@ const BoundingBoxModes = keyMirror({ * On mouse down, the type of function (move, scale, rotate) is determined based on what is clicked * (scale handle, rotate handle, the object itself). This determines the mode of the tool, which then * delegates actions to the MoveTool, RotateTool or ScaleTool accordingly. - * @param {!function} onUpdateSvg A callback to call when the image visibly changes + * @param {!function} onUpdateImage A callback to call when the image visibly changes */ class BoundingBoxTool { /** * @param {Modes} mode Paint editor mode * @param {function} setSelectedItems Callback to set the set of selected items in the Redux state * @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state - * @param {!function} onUpdateSvg A callback to call when the image visibly changes + * @param {!function} onUpdateImage A callback to call when the image visibly changes */ - constructor (mode, setSelectedItems, clearSelectedItems, onUpdateSvg) { - this.onUpdateSvg = onUpdateSvg; + constructor (mode, setSelectedItems, clearSelectedItems, onUpdateImage) { + this.onUpdateImage = onUpdateImage; this.mode = null; this.boundsPath = null; this.boundsScaleHandles = []; this.boundsRotHandles = []; this._modeMap = {}; - this._modeMap[BoundingBoxModes.SCALE] = new ScaleTool(onUpdateSvg); - this._modeMap[BoundingBoxModes.ROTATE] = new RotateTool(onUpdateSvg); - this._modeMap[BoundingBoxModes.MOVE] = new MoveTool(mode, setSelectedItems, clearSelectedItems, onUpdateSvg); + this._modeMap[BoundingBoxModes.SCALE] = new ScaleTool(onUpdateImage); + this._modeMap[BoundingBoxModes.ROTATE] = new RotateTool(onUpdateImage); + this._modeMap[BoundingBoxModes.MOVE] = new MoveTool(mode, setSelectedItems, clearSelectedItems, onUpdateImage); } /** diff --git a/src/helper/selection-tools/handle-tool.js b/src/helper/selection-tools/handle-tool.js index 305681fd..646db4ef 100644 --- a/src/helper/selection-tools/handle-tool.js +++ b/src/helper/selection-tools/handle-tool.js @@ -5,13 +5,13 @@ class HandleTool { /** * @param {function} setSelectedItems Callback to set the set of selected items in the Redux state * @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state - * @param {!function} onUpdateSvg A callback to call when the image visibly changes + * @param {!function} onUpdateImage A callback to call when the image visibly changes */ - constructor (setSelectedItems, clearSelectedItems, onUpdateSvg) { + constructor (setSelectedItems, clearSelectedItems, onUpdateImage) { this.hitType = null; this.setSelectedItems = setSelectedItems; this.clearSelectedItems = clearSelectedItems; - this.onUpdateSvg = onUpdateSvg; + this.onUpdateImage = onUpdateImage; this.selectedItems = []; } /** @@ -80,7 +80,7 @@ class HandleTool { } if (moved) { this.setSelectedItems(); - this.onUpdateSvg(); + this.onUpdateImage(); } this.selectedItems = []; } diff --git a/src/helper/selection-tools/move-tool.js b/src/helper/selection-tools/move-tool.js index 92dc74c0..5a5e4ffd 100644 --- a/src/helper/selection-tools/move-tool.js +++ b/src/helper/selection-tools/move-tool.js @@ -13,14 +13,14 @@ class MoveTool { * @param {Modes} mode Paint editor mode * @param {function} setSelectedItems Callback to set the set of selected items in the Redux state * @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state - * @param {!function} onUpdateSvg A callback to call when the image visibly changes + * @param {!function} onUpdateImage A callback to call when the image visibly changes */ - constructor (mode, setSelectedItems, clearSelectedItems, onUpdateSvg) { + constructor (mode, setSelectedItems, clearSelectedItems, onUpdateImage) { this.mode = mode; this.setSelectedItems = setSelectedItems; this.clearSelectedItems = clearSelectedItems; this.selectedItems = null; - this.onUpdateSvg = onUpdateSvg; + this.onUpdateImage = onUpdateImage; this.boundsPath = null; } @@ -56,7 +56,7 @@ class MoveTool { } this._select(item, true, hitProperties.subselect); } - if (hitProperties.clone) cloneSelection(hitProperties.subselect, this.onUpdateSvg); + if (hitProperties.clone) cloneSelection(hitProperties.subselect, this.onUpdateImage); this.selectedItems = this.mode === Modes.RESHAPE ? getSelectedLeafItems() : getSelectedRootItems(); if (this.boundsPath) { this.selectedItems.push(this.boundsPath); @@ -116,7 +116,7 @@ class MoveTool { this.selectedItems = null; if (moved) { - this.onUpdateSvg(); + this.onUpdateImage(); } } } diff --git a/src/helper/selection-tools/nudge-tool.js b/src/helper/selection-tools/nudge-tool.js index 50f49840..d63b1c3c 100644 --- a/src/helper/selection-tools/nudge-tool.js +++ b/src/helper/selection-tools/nudge-tool.js @@ -8,11 +8,11 @@ import paper from '@scratch/paper'; class NudgeTool { /** * @param {function} boundingBoxTool to control the bounding box - * @param {!function} onUpdateSvg A callback to call when the image visibly changes + * @param {!function} onUpdateImage A callback to call when the image visibly changes */ - constructor (boundingBoxTool, onUpdateSvg) { + constructor (boundingBoxTool, onUpdateImage) { this.boundingBoxTool = boundingBoxTool; - this.onUpdateSvg = onUpdateSvg; + this.onUpdateImage = onUpdateImage; } onKeyDown (event) { if (event.event.target instanceof HTMLInputElement) { @@ -47,7 +47,7 @@ class NudgeTool { if (selected.length === 0) return; if (event.key === 'up' || event.key === 'down' || event.key === 'left' || event.key === 'right') { - this.onUpdateSvg(); + this.onUpdateImage(); } } } diff --git a/src/helper/selection-tools/point-tool.js b/src/helper/selection-tools/point-tool.js index 4cbdfe7c..196810ec 100644 --- a/src/helper/selection-tools/point-tool.js +++ b/src/helper/selection-tools/point-tool.js @@ -8,9 +8,9 @@ class PointTool { /** * @param {function} setSelectedItems Callback to set the set of selected items in the Redux state * @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state - * @param {!function} onUpdateSvg A callback to call when the image visibly changes + * @param {!function} onUpdateImage A callback to call when the image visibly changes */ - constructor (setSelectedItems, clearSelectedItems, onUpdateSvg) { + constructor (setSelectedItems, clearSelectedItems, onUpdateImage) { /** * Deselection often does not happen until mouse up. If the mouse is dragged before * mouse up, deselection is cancelled. This variable keeps track of which paper.Item to deselect. @@ -29,7 +29,7 @@ class PointTool { this.selectedItems = null; this.setSelectedItems = setSelectedItems; this.clearSelectedItems = clearSelectedItems; - this.onUpdateSvg = onUpdateSvg; + this.onUpdateImage = onUpdateImage; } /** @@ -191,7 +191,7 @@ class PointTool { this.selectedItems = null; this.setSelectedItems(); if (moved) { - this.onUpdateSvg(); + this.onUpdateImage(); } } } diff --git a/src/helper/selection-tools/reshape-tool.js b/src/helper/selection-tools/reshape-tool.js index 4e42ac75..0b45a6ba 100644 --- a/src/helper/selection-tools/reshape-tool.js +++ b/src/helper/selection-tools/reshape-tool.js @@ -43,22 +43,22 @@ class ReshapeTool extends paper.Tool { * @param {function} clearHoveredItem Callback to clear the hovered item * @param {function} setSelectedItems Callback to set the set of selected items in the Redux state * @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state - * @param {!function} onUpdateSvg A callback to call when the image visibly changes + * @param {!function} onUpdateImage A callback to call when the image visibly changes */ - constructor (setHoveredItem, clearHoveredItem, setSelectedItems, clearSelectedItems, onUpdateSvg) { + constructor (setHoveredItem, clearHoveredItem, setSelectedItems, clearSelectedItems, onUpdateImage) { super(); this.setHoveredItem = setHoveredItem; this.clearHoveredItem = clearHoveredItem; - this.onUpdateSvg = onUpdateSvg; + this.onUpdateImage = onUpdateImage; this.prevHoveredItemId = null; this.lastEvent = null; this.active = false; this.mode = ReshapeModes.SELECTION_BOX; this._modeMap = {}; this._modeMap[ReshapeModes.FILL] = - new MoveTool(Modes.RESHAPE, setSelectedItems, clearSelectedItems, onUpdateSvg); - this._modeMap[ReshapeModes.POINT] = new PointTool(setSelectedItems, clearSelectedItems, onUpdateSvg); - this._modeMap[ReshapeModes.HANDLE] = new HandleTool(setSelectedItems, clearSelectedItems, onUpdateSvg); + new MoveTool(Modes.RESHAPE, setSelectedItems, clearSelectedItems, onUpdateImage); + this._modeMap[ReshapeModes.POINT] = new PointTool(setSelectedItems, clearSelectedItems, onUpdateImage); + this._modeMap[ReshapeModes.HANDLE] = new HandleTool(setSelectedItems, clearSelectedItems, onUpdateImage); this._modeMap[ReshapeModes.SELECTION_BOX] = new SelectionBoxTool(Modes.RESHAPE, setSelectedItems, clearSelectedItems); @@ -271,7 +271,7 @@ class ReshapeTool extends paper.Tool { if (selected.length === 0) return; if (event.key === 'up' || event.key === 'down' || event.key === 'left' || event.key === 'right') { - this.onUpdateSvg(); + this.onUpdateImage(); } } deactivateTool () { @@ -279,7 +279,7 @@ class ReshapeTool extends paper.Tool { this.clearHoveredItem(); this.setHoveredItem = null; this.clearHoveredItem = null; - this.onUpdateSvg = null; + this.onUpdateImage = null; this.lastEvent = null; } } diff --git a/src/helper/selection-tools/rotate-tool.js b/src/helper/selection-tools/rotate-tool.js index 0c07d788..54049193 100644 --- a/src/helper/selection-tools/rotate-tool.js +++ b/src/helper/selection-tools/rotate-tool.js @@ -5,13 +5,13 @@ import paper from '@scratch/paper'; */ class RotateTool { /** - * @param {!function} onUpdateSvg A callback to call when the image visibly changes + * @param {!function} onUpdateImage A callback to call when the image visibly changes */ - constructor (onUpdateSvg) { + constructor (onUpdateImage) { this.rotItems = []; this.rotGroupPivot = null; this.prevRot = 90; - this.onUpdateSvg = onUpdateSvg; + this.onUpdateImage = onUpdateImage; } /** @@ -54,7 +54,7 @@ class RotateTool { this.rotGroupPivot = null; this.prevRot = 90; - this.onUpdateSvg(); + this.onUpdateImage(); } } diff --git a/src/helper/selection-tools/scale-tool.js b/src/helper/selection-tools/scale-tool.js index a827eb9d..65330e9c 100644 --- a/src/helper/selection-tools/scale-tool.js +++ b/src/helper/selection-tools/scale-tool.js @@ -7,9 +7,9 @@ import {getItems} from '../selection'; */ class ScaleTool { /** - * @param {!function} onUpdateSvg A callback to call when the image visibly changes + * @param {!function} onUpdateImage A callback to call when the image visibly changes */ - constructor (onUpdateSvg) { + constructor (onUpdateImage) { this.active = false; this.boundsPath = null; this.pivot = null; @@ -20,7 +20,7 @@ class ScaleTool { this.itemGroup = null; // Lowest item above all scale items in z index this.itemToInsertBelow = null; - this.onUpdateSvg = onUpdateSvg; + this.onUpdateImage = onUpdateImage; } /** @@ -144,7 +144,7 @@ class ScaleTool { } this.itemGroup.remove(); - this.onUpdateSvg(); + this.onUpdateImage(); this.active = false; } _getRectCornerNameByIndex (index) { diff --git a/src/helper/selection-tools/select-tool.js b/src/helper/selection-tools/select-tool.js index a7afa9a5..c069892f 100644 --- a/src/helper/selection-tools/select-tool.js +++ b/src/helper/selection-tools/select-tool.js @@ -24,15 +24,15 @@ class SelectTool extends paper.Tool { * @param {function} clearHoveredItem Callback to clear the hovered item * @param {function} setSelectedItems Callback to set the set of selected items in the Redux state * @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state - * @param {!function} onUpdateSvg A callback to call when the image visibly changes + * @param {!function} onUpdateImage A callback to call when the image visibly changes */ - constructor (setHoveredItem, clearHoveredItem, setSelectedItems, clearSelectedItems, onUpdateSvg) { + constructor (setHoveredItem, clearHoveredItem, setSelectedItems, clearSelectedItems, onUpdateImage) { super(); this.setHoveredItem = setHoveredItem; this.clearHoveredItem = clearHoveredItem; - this.onUpdateSvg = onUpdateSvg; - this.boundingBoxTool = new BoundingBoxTool(Modes.SELECT, setSelectedItems, clearSelectedItems, onUpdateSvg); - const nudgeTool = new NudgeTool(this.boundingBoxTool, onUpdateSvg); + this.onUpdateImage = onUpdateImage; + this.boundingBoxTool = new BoundingBoxTool(Modes.SELECT, setSelectedItems, clearSelectedItems, onUpdateImage); + const nudgeTool = new NudgeTool(this.boundingBoxTool, onUpdateImage); this.selectionBoxTool = new SelectionBoxTool(Modes.SELECT, setSelectedItems, clearSelectedItems); this.selectionBoxMode = false; this.prevHoveredItemId = null; @@ -140,7 +140,7 @@ class SelectTool extends paper.Tool { this.boundingBoxTool.removeBoundsPath(); this.setHoveredItem = null; this.clearHoveredItem = null; - this.onUpdateSvg = null; + this.onUpdateImage = null; this.boundingBoxTool = null; this.selectionBoxTool = null; } diff --git a/src/helper/selection.js b/src/helper/selection.js index db3bf68c..dc6c65a5 100644 --- a/src/helper/selection.js +++ b/src/helper/selection.js @@ -195,7 +195,7 @@ const getSelectedSegments = function () { return segments; }; -const _deleteItemSelection = function (items, onUpdateSvg) { +const _deleteItemSelection = function (items, onUpdateImage) { // @todo: Update toolbar state on change if (items.length === 0) { return false; @@ -203,12 +203,12 @@ const _deleteItemSelection = function (items, onUpdateSvg) { for (let i = 0; i < items.length; i++) { items[i].remove(); } - onUpdateSvg(); + onUpdateImage(); return true; }; // Return true if anything was removed -const _removeSelectedSegments = function (items, onUpdateSvg) { +const _removeSelectedSegments = function (items, onUpdateImage) { const segmentsToRemove = []; for (let i = 0; i < items.length; i++) { @@ -229,33 +229,33 @@ const _removeSelectedSegments = function (items, onUpdateSvg) { removedSegments = true; } if (removedSegments) { - onUpdateSvg(); + onUpdateImage(); } return removedSegments; }; // Return whether anything was deleted -const deleteSelection = function (mode, onUpdateSvg) { +const deleteSelection = function (mode, onUpdateImage) { if (mode === Modes.RESHAPE) { const selectedItems = getSelectedLeafItems(); // If there are points selected remove them. If not delete the item selected. - if (_removeSelectedSegments(selectedItems, onUpdateSvg)) { + if (_removeSelectedSegments(selectedItems, onUpdateImage)) { return true; } - return _deleteItemSelection(selectedItems, onUpdateSvg); + return _deleteItemSelection(selectedItems, onUpdateImage); } const selectedItems = getSelectedRootItems(); - return _deleteItemSelection(selectedItems, onUpdateSvg); + return _deleteItemSelection(selectedItems, onUpdateImage); }; -const cloneSelection = function (recursive, onUpdateSvg) { +const cloneSelection = function (recursive, onUpdateImage) { const selectedItems = recursive ? getSelectedLeafItems() : getSelectedRootItems(); for (let i = 0; i < selectedItems.length; i++) { const item = selectedItems[i]; item.clone(); item.selected = false; } - onUpdateSvg(); + onUpdateImage(); }; const _checkBoundsItem = function (selectionRect, item, event) { diff --git a/src/helper/tools/fill-tool.js b/src/helper/tools/fill-tool.js index 25a60fcb..918eea40 100644 --- a/src/helper/tools/fill-tool.js +++ b/src/helper/tools/fill-tool.js @@ -9,13 +9,13 @@ class FillTool extends paper.Tool { /** * @param {function} setHoveredItem Callback to set the hovered item * @param {function} clearHoveredItem Callback to clear the hovered item - * @param {!function} onUpdateSvg A callback to call when the image visibly changes + * @param {!function} onUpdateImage A callback to call when the image visibly changes */ - constructor (setHoveredItem, clearHoveredItem, onUpdateSvg) { + constructor (setHoveredItem, clearHoveredItem, onUpdateImage) { super(); this.setHoveredItem = setHoveredItem; this.clearHoveredItem = clearHoveredItem; - this.onUpdateSvg = onUpdateSvg; + this.onUpdateImage = onUpdateImage; // We have to set these functions instead of just declaring them because // paper.js tools hook up the listeners in the setter functions. @@ -150,7 +150,7 @@ class FillTool extends paper.Tool { this.fillItem = null; this.addedFillItem = null; this.fillItemOrigColor = null; - this.onUpdateSvg(); + this.onUpdateImage(); } } _noStroke (item) { diff --git a/src/helper/tools/oval-tool.js b/src/helper/tools/oval-tool.js index dc90006e..4bbd35f8 100644 --- a/src/helper/tools/oval-tool.js +++ b/src/helper/tools/oval-tool.js @@ -15,15 +15,15 @@ class OvalTool extends paper.Tool { /** * @param {function} setSelectedItems Callback to set the set of selected items in the Redux state * @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state - * @param {!function} onUpdateSvg A callback to call when the image visibly changes + * @param {!function} onUpdateImage A callback to call when the image visibly changes */ - constructor (setSelectedItems, clearSelectedItems, onUpdateSvg) { + constructor (setSelectedItems, clearSelectedItems, onUpdateImage) { super(); this.setSelectedItems = setSelectedItems; this.clearSelectedItems = clearSelectedItems; - this.onUpdateSvg = onUpdateSvg; - this.boundingBoxTool = new BoundingBoxTool(Modes.OVAL, setSelectedItems, clearSelectedItems, onUpdateSvg); - const nudgeTool = new NudgeTool(this.boundingBoxTool, onUpdateSvg); + this.onUpdateImage = onUpdateImage; + this.boundingBoxTool = new BoundingBoxTool(Modes.OVAL, setSelectedItems, clearSelectedItems, onUpdateImage); + const nudgeTool = new NudgeTool(this.boundingBoxTool, onUpdateImage); // We have to set these functions instead of just declaring them because // paper.js tools hook up the listeners in the setter functions. @@ -120,7 +120,7 @@ class OvalTool extends paper.Tool { ovalPath.selected = true; this.setSelectedItems(); - this.onUpdateSvg(); + this.onUpdateImage(); } } this.active = false; diff --git a/src/helper/tools/rect-tool.js b/src/helper/tools/rect-tool.js index 8099f126..d9f594b9 100644 --- a/src/helper/tools/rect-tool.js +++ b/src/helper/tools/rect-tool.js @@ -15,15 +15,15 @@ class RectTool extends paper.Tool { /** * @param {function} setSelectedItems Callback to set the set of selected items in the Redux state * @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state - * @param {!function} onUpdateSvg A callback to call when the image visibly changes + * @param {!function} onUpdateImage A callback to call when the image visibly changes */ - constructor (setSelectedItems, clearSelectedItems, onUpdateSvg) { + constructor (setSelectedItems, clearSelectedItems, onUpdateImage) { super(); this.setSelectedItems = setSelectedItems; this.clearSelectedItems = clearSelectedItems; - this.onUpdateSvg = onUpdateSvg; - this.boundingBoxTool = new BoundingBoxTool(Modes.RECT, setSelectedItems, clearSelectedItems, onUpdateSvg); - const nudgeTool = new NudgeTool(this.boundingBoxTool, onUpdateSvg); + this.onUpdateImage = onUpdateImage; + this.boundingBoxTool = new BoundingBoxTool(Modes.RECT, setSelectedItems, clearSelectedItems, onUpdateImage); + const nudgeTool = new NudgeTool(this.boundingBoxTool, onUpdateImage); // We have to set these functions instead of just declaring them because // paper.js tools hook up the listeners in the setter functions. @@ -113,7 +113,7 @@ class RectTool extends paper.Tool { } else { this.rect.selected = true; this.setSelectedItems(); - this.onUpdateSvg(); + this.onUpdateImage(); this.rect = null; } } diff --git a/src/helper/tools/rounded-rect-tool.js b/src/helper/tools/rounded-rect-tool.js index ce92b47c..edea7daf 100644 --- a/src/helper/tools/rounded-rect-tool.js +++ b/src/helper/tools/rounded-rect-tool.js @@ -10,15 +10,15 @@ class RoundedRectTool extends paper.Tool { * @param {function} clearHoveredItem Callback to clear the hovered item * @param {function} setSelectedItems Callback to set the set of selected items in the Redux state * @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state - * @param {!function} onUpdateSvg A callback to call when the image visibly changes + * @param {!function} onUpdateImage A callback to call when the image visibly changes */ - constructor (setHoveredItem, clearHoveredItem, setSelectedItems, clearSelectedItems, onUpdateSvg) { + constructor (setHoveredItem, clearHoveredItem, setSelectedItems, clearSelectedItems, onUpdateImage) { super(); this.setHoveredItem = setHoveredItem; this.clearHoveredItem = clearHoveredItem; this.setSelectedItems = setSelectedItems; this.clearSelectedItems = clearSelectedItems; - this.onUpdateSvg = onUpdateSvg; + this.onUpdateImage = onUpdateImage; this.prevHoveredItemId = null; // We have to set these functions instead of just declaring them because diff --git a/src/helper/tools/text-tool.js b/src/helper/tools/text-tool.js index e7468e9d..006faab1 100644 --- a/src/helper/tools/text-tool.js +++ b/src/helper/tools/text-tool.js @@ -34,18 +34,18 @@ class TextTool extends paper.Tool { * @param {HTMLTextAreaElement} textAreaElement dom element for the editable text field * @param {function} setSelectedItems Callback to set the set of selected items in the Redux state * @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state - * @param {!function} onUpdateSvg A callback to call when the image visibly changes + * @param {!function} onUpdateImage A callback to call when the image visibly changes * @param {!function} setTextEditTarget Call to set text editing target whenever text editing is active */ - constructor (textAreaElement, setSelectedItems, clearSelectedItems, onUpdateSvg, setTextEditTarget) { + constructor (textAreaElement, setSelectedItems, clearSelectedItems, onUpdateImage, setTextEditTarget) { super(); this.element = textAreaElement; this.setSelectedItems = setSelectedItems; this.clearSelectedItems = clearSelectedItems; - this.onUpdateSvg = onUpdateSvg; + this.onUpdateImage = onUpdateImage; this.setTextEditTarget = setTextEditTarget; - this.boundingBoxTool = new BoundingBoxTool(Modes.TEXT, setSelectedItems, clearSelectedItems, onUpdateSvg); - this.nudgeTool = new NudgeTool(this.boundingBoxTool, onUpdateSvg); + this.boundingBoxTool = new BoundingBoxTool(Modes.TEXT, setSelectedItems, clearSelectedItems, onUpdateImage); + this.nudgeTool = new NudgeTool(this.boundingBoxTool, onUpdateImage); this.lastEvent = null; // We have to set these functions instead of just declaring them because @@ -249,7 +249,7 @@ class TextTool extends paper.Tool { handleTextInput (event) { // Save undo state if you paused typing for long enough. if (this.lastTypeEvent && event.timeStamp - this.lastTypeEvent.timeStamp > TextTool.TYPING_TIMEOUT_MILLIS) { - this.onUpdateSvg(); + this.onUpdateImage(); } this.lastTypeEvent = event; if (this.mode === TextTool.TEXT_EDIT_MODE) { @@ -317,7 +317,7 @@ class TextTool extends paper.Tool { // If you finished editing a textbox, save undo state if (this.textBox && this.textBox.content.trim().length) { - this.onUpdateSvg(); + this.onUpdateImage(); } } deactivateTool () { diff --git a/src/helper/undo.js b/src/helper/undo.js index 3f9592d2..cc6db036 100644 --- a/src/helper/undo.js +++ b/src/helper/undo.js @@ -4,7 +4,6 @@ import paper from '@scratch/paper'; import {hideGuideLayers, showGuideLayers, getRaster} from '../helper/layer'; import Formats from '../lib/format'; import {isVector, isBitmap} from '../lib/format'; -import log from '../log/log'; /** * Take an undo snapshot @@ -20,7 +19,7 @@ const performSnapshot = function (dispatchPerformSnapshot, format) { showGuideLayers(guideLayers); }; -const _restore = function (entry, setSelectedItems, onUpdateSvg) { +const _restore = function (entry, setSelectedItems, onUpdateImage) { for (let i = paper.project.layers.length - 1; i >= 0; i--) { const layer = paper.project.layers[i]; if (!layer.data.isBackgroundGuideLayer) { @@ -32,36 +31,30 @@ const _restore = function (entry, setSelectedItems, onUpdateSvg) { setSelectedItems(); getRaster().onLoad = function () { - onUpdateSvg(true /* skipSnapshot */); + onUpdateImage(true /* skipSnapshot */); }; if (getRaster().loaded) { getRaster().onLoad(); } }; -const performUndo = function (undoState, dispatchPerformUndo, setSelectedItems, onUpdateSvg) { +const performUndo = function (undoState, dispatchPerformUndo, setSelectedItems, onUpdateImage) { if (undoState.pointer > 0) { const state = undoState.stack[undoState.pointer - 1]; - _restore(state, setSelectedItems, onUpdateSvg); + _restore(state, setSelectedItems, onUpdateImage); const format = isVector(state.paintEditorFormat) ? Formats.VECTOR_SKIP_CONVERT : isBitmap(state.paintEditorFormat) ? Formats.BITMAP_SKIP_CONVERT : null; - if (!format) { - log.error(`Invalid format: ${state.paintEditorFormat}`); - } dispatchPerformUndo(format); } }; -const performRedo = function (undoState, dispatchPerformRedo, setSelectedItems, onUpdateSvg) { +const performRedo = function (undoState, dispatchPerformRedo, setSelectedItems, onUpdateImage) { if (undoState.pointer >= 0 && undoState.pointer < undoState.stack.length - 1) { const state = undoState.stack[undoState.pointer + 1]; - _restore(state, setSelectedItems, onUpdateSvg); + _restore(state, setSelectedItems, onUpdateImage); const format = isVector(state.paintEditorFormat) ? Formats.VECTOR_SKIP_CONVERT : isBitmap(state.paintEditorFormat) ? Formats.BITMAP_SKIP_CONVERT : null; - if (!format) { - log.error(`Invalid format: ${state.paintEditorFormat}`); - } dispatchPerformRedo(format); } }; diff --git a/src/playground/playground.jsx b/src/playground/playground.jsx index 4cdcacfd..3be25afc 100644 --- a/src/playground/playground.jsx +++ b/src/playground/playground.jsx @@ -28,30 +28,36 @@ class Playground extends React.Component { super(props); bindAll(this, [ 'handleUpdateName', - 'handleUpdateSvg' + 'handleUpdateImage' ]); this.state = { name: 'meow', rotationCenterX: 20, rotationCenterY: 400, - svg: svgString + image: svgString }; } handleUpdateName (name) { this.setState({name}); } - handleUpdateSvg (svg, rotationCenterX, rotationCenterY) { - console.log(svg); + handleUpdateImage (isVector, image, rotationCenterX, rotationCenterY) { + console.log(image); console.log(`rotationCenterX: ${rotationCenterX} rotationCenterY: ${rotationCenterY}`); - this.setState({svg, rotationCenterX, rotationCenterY}); + if (isVector) { + this.setState({image, rotationCenterX, rotationCenterY}); + } else { // is Bitmap + const imageElement = new Image(); + imageElement.src = image.toDataURL("image/png"); + this.setState({imageElement, rotationCenterX, rotationCenterY}); + } } render () { return ( <PaintEditor {...this.state} - svgId="meow" + imageId="meow" onUpdateName={this.handleUpdateName} - onUpdateSvg={this.handleUpdateSvg} + onUpdateImage={this.handleUpdateImage} /> ); } diff --git a/src/reducers/format.js b/src/reducers/format.js index 9243e3fc..4642ef1a 100644 --- a/src/reducers/format.js +++ b/src/reducers/format.js @@ -3,7 +3,7 @@ import log from '../log/log'; import {UNDO, REDO} from './undo'; const CHANGE_FORMAT = 'scratch-paint/formats/CHANGE_FORMAT'; -const initialState = Formats.VECTOR; +const initialState = null; const reducer = function (state, action) { if (typeof state === 'undefined') state = initialState; @@ -13,6 +13,7 @@ const reducer = function (state, action) { case REDO: /* falls through */ case CHANGE_FORMAT: + if (!action.format) return state; if (action.format in Formats) { return action.format; } diff --git a/test/unit/format-reducer.test.js b/test/unit/format-reducer.test.js index 1941aed3..3c1c99c3 100644 --- a/test/unit/format-reducer.test.js +++ b/test/unit/format-reducer.test.js @@ -6,7 +6,7 @@ import {undo, redo} from '../../src/reducers/undo'; test('initialState', () => { let defaultState; - expect(reducer(defaultState /* state */, {type: 'anything'} /* action */) in Formats).toBeTruthy(); + expect(reducer(defaultState /* state */, {type: 'anything'} /* action */)).toBeNull(); }); test('changeFormat', () => {