mirror of
https://github.com/scratchfoundation/scratch-paint.git
synced 2025-06-08 19:24:53 -04:00
Import and export bitmaps (#404)
* Rename svg to image * Import/export bitmap * Fix playground and readme * Fix lint * Assume strings are svgs
This commit is contained in:
parent
af29e606d8
commit
3d99044ccf
44 changed files with 350 additions and 301 deletions
README.md
src
components/paint-editor
containers
bit-brush-mode.jsxbit-line-mode.jsxbrush-mode.jsxeraser-mode.jsxfill-color-indicator.jsxfill-mode.jsxline-mode.jsxmode-tools.jsxoval-mode.jsxpaint-editor.jsxpaper-canvas.jsxrect-mode.jsxreshape-mode.jsxrounded-rect-mode.jsxselect-mode.jsxstroke-color-indicator.jsxstroke-width-indicator.jsxtext-mode.jsx
helper
bit-tools
blob-tools
group.jsorder.jsselection-tools
bounding-box-tool.jshandle-tool.jsmove-tool.jsnudge-tool.jspoint-tool.jsreshape-tool.jsrotate-tool.jsscale-tool.jsselect-tool.js
selection.jstools
undo.jsplayground
reducers
test/unit
15
README.md
15
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}
|
||||
/>
|
||||
```
|
||||
|
||||
|
|
|
@ -329,23 +329,24 @@ 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> :
|
||||
isBitmap(props.format) ?
|
||||
<div className={styles.row}>
|
||||
<InputGroup
|
||||
className={classNames(
|
||||
|
@ -357,15 +358,15 @@ const PaintEditorComponent = props => {
|
|||
{/* fill */}
|
||||
<FillColorIndicatorComponent
|
||||
className={styles.modMarginRight}
|
||||
onUpdateSvg={props.onUpdateSvg}
|
||||
onUpdateImage={props.onUpdateImage}
|
||||
/>
|
||||
</InputGroup>
|
||||
<InputGroup className={styles.modModeTools}>
|
||||
<ModeToolsContainer
|
||||
onUpdateSvg={props.onUpdateSvg}
|
||||
onUpdateImage={props.onUpdateImage}
|
||||
/>
|
||||
</InputGroup>
|
||||
</div>
|
||||
</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,6 +471,7 @@ const PaintEditorComponent = props => {
|
|||
{props.intl.formatMessage(messages.bitmap)}
|
||||
</span>
|
||||
</Button> :
|
||||
isBitmap(props.format) ?
|
||||
<Button
|
||||
className={styles.bitmapButton}
|
||||
onClick={props.onSwitchToVector}
|
||||
|
@ -482,7 +484,7 @@ const PaintEditorComponent = props => {
|
|||
<span>
|
||||
{props.intl.formatMessage(messages.vector)}
|
||||
</span>
|
||||
</Button>
|
||||
</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)
|
||||
};
|
||||
|
||||
|
|
|
@ -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 => ({
|
||||
|
|
|
@ -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 => ({
|
||||
|
|
|
@ -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 => ({
|
||||
|
|
|
@ -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 => ({
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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 => ({
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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,22 +127,23 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
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.onUpdateSvg(
|
||||
this.props.onUpdateImage(
|
||||
true /* isVector */,
|
||||
paper.project.exportSVG({
|
||||
asString: true,
|
||||
bounds: 'content',
|
||||
|
@ -155,7 +156,7 @@ class PaintEditor extends React.Component {
|
|||
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({
|
||||
|
|
|
@ -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) {
|
||||
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(svg, rotationCenterX, rotationCenterY);
|
||||
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
|
||||
};
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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 = [];
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue