From 633ecd2399409515221a1486976ddb7964990a1c Mon Sep 17 00:00:00 2001 From: DD Date: Wed, 30 Aug 2017 10:50:05 -0400 Subject: [PATCH 1/4] load an svg string --- src/components/paint-editor.jsx | 10 ++++++++- src/containers/paint-editor.jsx | 27 +++++++++++++++++++++-- src/containers/paper-canvas.jsx | 39 +++++++++++++++++++++------------ src/playground/playground.jsx | 9 +++++++- 4 files changed, 67 insertions(+), 18 deletions(-) diff --git a/src/components/paint-editor.jsx b/src/components/paint-editor.jsx index 1459fe35..faef8b9b 100644 --- a/src/components/paint-editor.jsx +++ b/src/components/paint-editor.jsx @@ -3,6 +3,7 @@ import React from 'react'; import PaperCanvas from '../containers/paper-canvas.jsx'; import BrushMode from '../containers/brush-mode.jsx'; import EraserMode from '../containers/eraser-mode.jsx'; +import PropTypes from 'prop-types'; class PaintEditorComponent extends React.Component { constructor (props) { @@ -20,7 +21,10 @@ class PaintEditorComponent extends React.Component { if (this.state.canvas) { return (
- +
@@ -34,4 +38,8 @@ class PaintEditorComponent extends React.Component { } } +PaintEditorComponent.propTypes = { + svg: PropTypes.string +}; + export default PaintEditorComponent; diff --git a/src/containers/paint-editor.jsx b/src/containers/paint-editor.jsx index 303546ba..219e3fe9 100644 --- a/src/containers/paint-editor.jsx +++ b/src/containers/paint-editor.jsx @@ -4,23 +4,46 @@ import PaintEditorComponent from '../components/paint-editor.jsx'; import {changeMode} from '../reducers/modes'; import Modes from '../modes/modes'; import {connect} from 'react-redux'; +import bindAll from 'lodash.bindall'; class PaintEditor extends React.Component { + constructor (props) { + super(props); + bindAll(this, [ + 'updateSvg' + ]); + } componentDidMount () { document.addEventListener('keydown', this.props.onKeyPress); } + shouldComponentUpdate (newProps) { + return newProps.assetId !== this.props.assetId; + } componentWillUnmount () { document.removeEventListener('keydown', this.props.onKeyPress); } + updateSvg (svg) { + if (!this.props.onUpdate) { + return; + } + this.props.onUpdate( + this.props.assetIndex, + svg + ); + } render () { return ( - + ); } } PaintEditor.propTypes = { - onKeyPress: PropTypes.func.isRequired + assetId: PropTypes.string, + assetIndex: PropTypes.number, + onKeyPress: PropTypes.func.isRequired, + onUpdate: PropTypes.func, + svg: PropTypes.string }; const mapDispatchToProps = dispatch => ({ diff --git a/src/containers/paper-canvas.jsx b/src/containers/paper-canvas.jsx index 4ccb0b58..f44bc10d 100644 --- a/src/containers/paper-canvas.jsx +++ b/src/containers/paper-canvas.jsx @@ -7,27 +7,37 @@ class PaperCanvas extends React.Component { constructor (props) { super(props); bindAll(this, [ - 'setCanvas' + 'setCanvas', + 'importSvg' ]); } componentDidMount () { paper.setup(this.canvas); - // Create a Paper.js Path to draw a line into it: - const path = new paper.Path(); - // Give the stroke a color - path.strokeColor = 'black'; - const start = new paper.Point(100, 100); - // Move to start and draw a line from there - path.moveTo(start); - // Note that the plus operator on Point objects does not work - // in JavaScript. Instead, we need to call the add() function: - path.lineTo(start.add([200, -50])); - // Draw the view now: - paper.view.draw(); + if (this.props.svg) { + this.importSvg(this.props.svg); + } + } + componentWillReceiveProps (newProps) { + if (newProps.svg !== this.props.svg) { + paper.project.activeLayer.removeChildren(); + this.importSvg(newProps.svg); + } } componentWillUnmount () { paper.remove(); } + importSvg (svg) { + paper.project.importSVG(svg, + { + expandShapes: true, + onLoad: function (item) { + while (item.reduce() !== item) { + item = item.reduce(); + } + } + }); + paper.project.view.update(); + } setCanvas (canvas) { this.canvas = canvas; if (this.props.canvasRef) { @@ -44,7 +54,8 @@ class PaperCanvas extends React.Component { } PaperCanvas.propTypes = { - canvasRef: PropTypes.func + canvasRef: PropTypes.func, + svg: PropTypes.string }; export default PaperCanvas; diff --git a/src/playground/playground.jsx b/src/playground/playground.jsx index 9f68704a..a3f2a15f 100644 --- a/src/playground/playground.jsx +++ b/src/playground/playground.jsx @@ -13,10 +13,17 @@ const store = createStore( intlInitialState, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() ); +const svgString = + '' + + '' + + '' + + '' + + '' + + ''; ReactDOM.render(( - + ), appTarget); From b0d29a946ba3ce7bafba3ecfaecd804e92792cc0 Mon Sep 17 00:00:00 2001 From: DD Date: Wed, 30 Aug 2017 18:43:34 -0400 Subject: [PATCH 2/4] Pipe through updateSvg and call it whenever a tool finishes an action --- src/components/paint-editor.jsx | 16 +++++++++++++--- src/containers/blob/blob.js | 9 ++++++++- src/containers/brush-mode.jsx | 5 +++-- src/containers/eraser-mode.jsx | 5 +++-- src/containers/line-mode.jsx | 4 +++- src/containers/paint-editor.jsx | 24 +++++++++++------------- src/playground/playground.jsx | 8 +++++++- 7 files changed, 48 insertions(+), 23 deletions(-) diff --git a/src/components/paint-editor.jsx b/src/components/paint-editor.jsx index cb07fcd1..8cdaaf69 100644 --- a/src/components/paint-editor.jsx +++ b/src/components/paint-editor.jsx @@ -26,9 +26,18 @@ class PaintEditorComponent extends React.Component { canvasRef={this.setCanvas} svg={this.props.svg} /> - - - + + + ); } @@ -41,6 +50,7 @@ class PaintEditorComponent extends React.Component { } PaintEditorComponent.propTypes = { + onUpdateSvg: PropTypes.func.isRequired, svg: PropTypes.string }; diff --git a/src/containers/blob/blob.js b/src/containers/blob/blob.js index bc7f63d1..22f4cda5 100644 --- a/src/containers/blob/blob.js +++ b/src/containers/blob/blob.js @@ -24,9 +24,13 @@ class Blobbiness { return 9; } - constructor () { + /** + * @param {function} updateCallback call when the drawing has changed to let listeners know + */ + constructor (updateCallback) { this.broadBrushHelper = new BroadBrushHelper(); this.segmentBrushHelper = new SegmentBrushHelper(); + this.updateCallback = updateCallback; } /** @@ -113,6 +117,9 @@ class Blobbiness { blob.mergeBrush(lastPath); } + blob.cursorPreview.visible = false; + blob.updateCallback(); + blob.cursorPreview.visible = true; blob.cursorPreview.bringToFront(); blob.cursorPreview.position = event.point; diff --git a/src/containers/brush-mode.jsx b/src/containers/brush-mode.jsx index e751030f..c89e08f9 100644 --- a/src/containers/brush-mode.jsx +++ b/src/containers/brush-mode.jsx @@ -16,7 +16,7 @@ class BrushMode extends React.Component { 'deactivateTool', 'onScroll' ]); - this.blob = new Blobbiness(); + this.blob = new Blobbiness(this.props.onUpdateSvg); } componentDidMount () { if (this.props.isBrushModeActive) { @@ -70,7 +70,8 @@ BrushMode.propTypes = { canvas: PropTypes.instanceOf(Element).isRequired, changeBrushSize: PropTypes.func.isRequired, handleMouseDown: PropTypes.func.isRequired, - isBrushModeActive: PropTypes.bool.isRequired + isBrushModeActive: PropTypes.bool.isRequired, + onUpdateSvg: PropTypes.func.isRequired }; const mapStateToProps = state => ({ diff --git a/src/containers/eraser-mode.jsx b/src/containers/eraser-mode.jsx index af932329..0972ed36 100644 --- a/src/containers/eraser-mode.jsx +++ b/src/containers/eraser-mode.jsx @@ -16,7 +16,7 @@ class EraserMode extends React.Component { 'deactivateTool', 'onScroll' ]); - this.blob = new Blobbiness(); + this.blob = new Blobbiness(this.props.onUpdateSvg); } componentDidMount () { if (this.props.isEraserModeActive) { @@ -66,7 +66,8 @@ EraserMode.propTypes = { brushSize: PropTypes.number.isRequired }), handleMouseDown: PropTypes.func.isRequired, - isEraserModeActive: PropTypes.bool.isRequired + isEraserModeActive: PropTypes.bool.isRequired, + onUpdateSvg: PropTypes.func.isRequired }; const mapStateToProps = state => ({ diff --git a/src/containers/line-mode.jsx b/src/containers/line-mode.jsx index 030b33ba..7648c045 100644 --- a/src/containers/line-mode.jsx +++ b/src/containers/line-mode.jsx @@ -209,6 +209,7 @@ class LineMode extends React.Component { } this.hitResult = null; } + this.props.onUpdateSvg(); // TODO add back undo // if (this.path) { @@ -280,7 +281,8 @@ LineMode.propTypes = { isLineModeActive: PropTypes.bool.isRequired, lineModeState: PropTypes.shape({ lineWidth: PropTypes.number.isRequired - }) + }), + onUpdateSvg: PropTypes.func.isRequired }; const mapStateToProps = state => ({ diff --git a/src/containers/paint-editor.jsx b/src/containers/paint-editor.jsx index cb2a5af9..ac081516 100644 --- a/src/containers/paint-editor.jsx +++ b/src/containers/paint-editor.jsx @@ -5,44 +5,42 @@ import {changeMode} from '../reducers/modes'; import Modes from '../modes/modes'; import {connect} from 'react-redux'; import bindAll from 'lodash.bindall'; +import paper from 'paper'; class PaintEditor extends React.Component { constructor (props) { super(props); bindAll(this, [ - 'updateSvg' + 'handleUpdateSvg' ]); } componentDidMount () { document.addEventListener('keydown', this.props.onKeyPress); } - shouldComponentUpdate (newProps) { - return newProps.assetId !== this.props.assetId; - } componentWillUnmount () { document.removeEventListener('keydown', this.props.onKeyPress); } - updateSvg (svg) { - if (!this.props.onUpdate) { + handleUpdateSvg () { + if (!this.props.onUpdateSvg) { return; } - this.props.onUpdate( - this.props.assetIndex, - svg + this.props.onUpdateSvg( + paper.project.exportSVG({asString: true}) // TODO can this be made independent of paper ); } render () { return ( - + ); } } PaintEditor.propTypes = { - assetId: PropTypes.string, - assetIndex: PropTypes.number, onKeyPress: PropTypes.func.isRequired, - onUpdate: PropTypes.func, + onUpdateSvg: PropTypes.func.isRequired, svg: PropTypes.string }; diff --git a/src/playground/playground.jsx b/src/playground/playground.jsx index a3f2a15f..39cdc45c 100644 --- a/src/playground/playground.jsx +++ b/src/playground/playground.jsx @@ -20,10 +20,16 @@ const svgString = '' + '' + ''; +const onUpdateSvg = function () { + return; +}; ReactDOM.render(( - + ), appTarget); From 36016bbd117d8f214b4e54b822e4d90c3c33e2e4 Mon Sep 17 00:00:00 2001 From: DD Date: Tue, 5 Sep 2017 15:45:56 -0400 Subject: [PATCH 3/4] Handle removing the viewbox and centering --- src/components/paint-editor.jsx | 4 ++++ src/containers/paint-editor.jsx | 13 +++++++++++-- src/containers/paper-canvas.jsx | 29 ++++++++++++++++++++++------- src/playground/playground.jsx | 19 ++++++++++++------- 4 files changed, 49 insertions(+), 16 deletions(-) diff --git a/src/components/paint-editor.jsx b/src/components/paint-editor.jsx index 8cdaaf69..306bde8f 100644 --- a/src/components/paint-editor.jsx +++ b/src/components/paint-editor.jsx @@ -24,6 +24,8 @@ class PaintEditorComponent extends React.Component {
@@ -41,6 +48,8 @@ class PaintEditor extends React.Component { PaintEditor.propTypes = { onKeyPress: PropTypes.func.isRequired, onUpdateSvg: PropTypes.func.isRequired, + rotationCenterX: PropTypes.number, + rotationCenterY: PropTypes.number, svg: PropTypes.string }; diff --git a/src/containers/paper-canvas.jsx b/src/containers/paper-canvas.jsx index f44bc10d..fdd6e44d 100644 --- a/src/containers/paper-canvas.jsx +++ b/src/containers/paper-canvas.jsx @@ -14,28 +14,41 @@ class PaperCanvas extends React.Component { componentDidMount () { paper.setup(this.canvas); if (this.props.svg) { - this.importSvg(this.props.svg); + this.importSvg(this.props.svg, this.props.rotationCenterX, this.props.rotationCenterY); } } componentWillReceiveProps (newProps) { - if (newProps.svg !== this.props.svg) { - paper.project.activeLayer.removeChildren(); - this.importSvg(newProps.svg); - } + paper.project.activeLayer.removeChildren(); + this.importSvg(newProps.svg, newProps.rotationCenterX, newProps.rotationCenterY); } componentWillUnmount () { paper.remove(); } - importSvg (svg) { - paper.project.importSVG(svg, + importSvg (svg, rotationCenterX, rotationCenterY) { + const imported = paper.project.importSVG(svg, { expandShapes: true, onLoad: function (item) { + // Remove viewbox + if (item.clipped) { + item.clipped = false; + // Consider removing clip mask here? + } while (item.reduce() !== item) { item = item.reduce(); } } }); + if (typeof rotationCenterX !== 'undefined' && typeof rotationCenterY !== 'undefined') { + imported.position = + paper.project.view.center + .add(imported.bounds.width / 2, imported.bounds.height / 2) + .subtract(rotationCenterX, rotationCenterY); + } else { + // Center + imported.position = paper.project.view.center; + } + paper.project.view.update(); } setCanvas (canvas) { @@ -55,6 +68,8 @@ class PaperCanvas extends React.Component { PaperCanvas.propTypes = { canvasRef: PropTypes.func, + rotationCenterX: PropTypes.number, + rotationCenterY: PropTypes.number, svg: PropTypes.string }; diff --git a/src/playground/playground.jsx b/src/playground/playground.jsx index 39cdc45c..f59ef2b0 100644 --- a/src/playground/playground.jsx +++ b/src/playground/playground.jsx @@ -14,19 +14,24 @@ const store = createStore( window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() ); const svgString = - '' + - '' + - '' + - '' + - '' + + '' + + '' + + '' + + '' + ''; -const onUpdateSvg = function () { - return; +const onUpdateSvg = function (newSvgString, rotationCenterX, rotationCenterY) { + console.log(newSvgString); + console.log(`rotationCenterX: ${rotationCenterX} rotationCenterY: ${rotationCenterY}`); }; ReactDOM.render(( From 23693f3764f9f04577b43d92161b126a4913e2cf Mon Sep 17 00:00:00 2001 From: DD Date: Tue, 5 Sep 2017 18:00:41 -0400 Subject: [PATCH 4/4] get rid of null check, since onUpdateSvg is required --- src/containers/paint-editor.jsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/containers/paint-editor.jsx b/src/containers/paint-editor.jsx index eefca91d..ba260313 100644 --- a/src/containers/paint-editor.jsx +++ b/src/containers/paint-editor.jsx @@ -21,9 +21,6 @@ class PaintEditor extends React.Component { document.removeEventListener('keydown', this.props.onKeyPress); } handleUpdateSvg () { - if (!this.props.onUpdateSvg) { - return; - } const bounds = paper.project.activeLayer.bounds; this.props.onUpdateSvg( paper.project.exportSVG({