mirror of
https://github.com/scratchfoundation/scratch-paint.git
synced 2024-12-23 05:52:42 -05:00
Bitmap eraser tool (#507)
This commit is contained in:
parent
689d4fb0a7
commit
4cadcb3da3
10 changed files with 186 additions and 29 deletions
|
@ -1,27 +1,26 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx';
|
|
||||||
import ToolSelectComponent from '../tool-select-base/tool-select-base.jsx';
|
import ToolSelectComponent from '../tool-select-base/tool-select-base.jsx';
|
||||||
|
|
||||||
import eraserIcon from './eraser.svg';
|
import eraserIcon from './eraser.svg';
|
||||||
|
|
||||||
const BitEraserComponent = () => (
|
const BitEraserComponent = props => (
|
||||||
<ComingSoonTooltip
|
<ToolSelectComponent
|
||||||
place="right"
|
imgDescriptor={{
|
||||||
tooltipId="bit-eraser-mode"
|
defaultMessage: 'Eraser',
|
||||||
>
|
description: 'Label for the eraser tool',
|
||||||
<ToolSelectComponent
|
id: 'paint.eraserMode.eraser'
|
||||||
disabled
|
}}
|
||||||
imgDescriptor={{
|
imgSrc={eraserIcon}
|
||||||
defaultMessage: 'Eraser',
|
isSelected={props.isSelected}
|
||||||
description: 'Label for the eraser tool',
|
onMouseDown={props.onMouseDown}
|
||||||
id: 'paint.eraserMode.eraser'
|
/>
|
||||||
}}
|
|
||||||
imgSrc={eraserIcon}
|
|
||||||
isSelected={false}
|
|
||||||
onMouseDown={function () {}} // eslint-disable-line react/jsx-no-bind
|
|
||||||
/>
|
|
||||||
</ComingSoonTooltip>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
BitEraserComponent.propTypes = {
|
||||||
|
isSelected: PropTypes.bool.isRequired,
|
||||||
|
onMouseDown: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
export default BitEraserComponent;
|
export default BitEraserComponent;
|
||||||
|
|
|
@ -7,6 +7,7 @@ import React from 'react';
|
||||||
import {changeBrushSize} from '../../reducers/brush-mode';
|
import {changeBrushSize} from '../../reducers/brush-mode';
|
||||||
import {changeBrushSize as changeEraserSize} from '../../reducers/eraser-mode';
|
import {changeBrushSize as changeEraserSize} from '../../reducers/eraser-mode';
|
||||||
import {changeBitBrushSize} from '../../reducers/bit-brush-size';
|
import {changeBitBrushSize} from '../../reducers/bit-brush-size';
|
||||||
|
import {changeBitEraserSize} from '../../reducers/bit-eraser-size';
|
||||||
|
|
||||||
import FontDropdown from '../../containers/font-dropdown.jsx';
|
import FontDropdown from '../../containers/font-dropdown.jsx';
|
||||||
import LiveInputHOC from '../forms/live-input-hoc.jsx';
|
import LiveInputHOC from '../forms/live-input-hoc.jsx';
|
||||||
|
@ -23,6 +24,7 @@ import copyIcon from './icons/copy.svg';
|
||||||
import pasteIcon from './icons/paste.svg';
|
import pasteIcon from './icons/paste.svg';
|
||||||
|
|
||||||
import bitBrushIcon from '../bit-brush-mode/brush.svg';
|
import bitBrushIcon from '../bit-brush-mode/brush.svg';
|
||||||
|
import bitEraserIcon from '../bit-eraser-mode/eraser.svg';
|
||||||
import bitLineIcon from '../bit-line-mode/line.svg';
|
import bitLineIcon from '../bit-line-mode/line.svg';
|
||||||
import brushIcon from '../brush-mode/brush.svg';
|
import brushIcon from '../brush-mode/brush.svg';
|
||||||
import curvedPointIcon from './icons/curved-point.svg';
|
import curvedPointIcon from './icons/curved-point.svg';
|
||||||
|
@ -117,7 +119,13 @@ const ModeToolsComponent = props => {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
case Modes.BIT_ERASER:
|
||||||
|
/* falls through */
|
||||||
case Modes.ERASER:
|
case Modes.ERASER:
|
||||||
|
{
|
||||||
|
const currentIcon = isVector(props.format) ? eraserIcon : bitEraserIcon;
|
||||||
|
const currentEraserValue = isBitmap(props.format) ? props.bitEraserSize : props.eraserValue;
|
||||||
|
const changeFunction = isBitmap(props.format) ? props.onBitEraserSliderChange : props.onEraserSliderChange;
|
||||||
return (
|
return (
|
||||||
<div className={classNames(props.className, styles.modeTools)}>
|
<div className={classNames(props.className, styles.modeTools)}>
|
||||||
<div>
|
<div>
|
||||||
|
@ -125,7 +133,7 @@ const ModeToolsComponent = props => {
|
||||||
alt={props.intl.formatMessage(messages.eraserSize)}
|
alt={props.intl.formatMessage(messages.eraserSize)}
|
||||||
className={styles.modeToolsIcon}
|
className={styles.modeToolsIcon}
|
||||||
draggable={false}
|
draggable={false}
|
||||||
src={eraserIcon}
|
src={currentIcon}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<LiveInput
|
<LiveInput
|
||||||
|
@ -134,11 +142,12 @@ const ModeToolsComponent = props => {
|
||||||
max={MAX_STROKE_WIDTH}
|
max={MAX_STROKE_WIDTH}
|
||||||
min="1"
|
min="1"
|
||||||
type="number"
|
type="number"
|
||||||
value={props.eraserValue}
|
value={currentEraserValue}
|
||||||
onSubmit={props.onEraserSliderChange}
|
onSubmit={changeFunction}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
case Modes.RESHAPE:
|
case Modes.RESHAPE:
|
||||||
return (
|
return (
|
||||||
<div className={classNames(props.className, styles.modeTools)}>
|
<div className={classNames(props.className, styles.modeTools)}>
|
||||||
|
@ -207,6 +216,7 @@ const ModeToolsComponent = props => {
|
||||||
|
|
||||||
ModeToolsComponent.propTypes = {
|
ModeToolsComponent.propTypes = {
|
||||||
bitBrushSize: PropTypes.number,
|
bitBrushSize: PropTypes.number,
|
||||||
|
bitEraserSize: PropTypes.number,
|
||||||
brushValue: PropTypes.number,
|
brushValue: PropTypes.number,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
clipboardItems: PropTypes.arrayOf(PropTypes.array),
|
clipboardItems: PropTypes.arrayOf(PropTypes.array),
|
||||||
|
@ -233,6 +243,7 @@ const mapStateToProps = state => ({
|
||||||
mode: state.scratchPaint.mode,
|
mode: state.scratchPaint.mode,
|
||||||
format: state.scratchPaint.format,
|
format: state.scratchPaint.format,
|
||||||
bitBrushSize: state.scratchPaint.bitBrushSize,
|
bitBrushSize: state.scratchPaint.bitBrushSize,
|
||||||
|
bitEraserSize: state.scratchPaint.bitEraserSize,
|
||||||
brushValue: state.scratchPaint.brushMode.brushSize,
|
brushValue: state.scratchPaint.brushMode.brushSize,
|
||||||
clipboardItems: state.scratchPaint.clipboard.items,
|
clipboardItems: state.scratchPaint.clipboard.items,
|
||||||
eraserValue: state.scratchPaint.eraserMode.brushSize,
|
eraserValue: state.scratchPaint.eraserMode.brushSize,
|
||||||
|
@ -245,6 +256,9 @@ const mapDispatchToProps = dispatch => ({
|
||||||
onBitBrushSliderChange: bitBrushSize => {
|
onBitBrushSliderChange: bitBrushSize => {
|
||||||
dispatch(changeBitBrushSize(bitBrushSize));
|
dispatch(changeBitBrushSize(bitBrushSize));
|
||||||
},
|
},
|
||||||
|
onBitEraserSliderChange: eraserSize => {
|
||||||
|
dispatch(changeBitEraserSize(eraserSize));
|
||||||
|
},
|
||||||
onEraserSliderChange: eraserSize => {
|
onEraserSliderChange: eraserSize => {
|
||||||
dispatch(changeEraserSize(eraserSize));
|
dispatch(changeEraserSize(eraserSize));
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import BitOvalMode from '../../components/bit-oval-mode/bit-oval-mode.jsx';
|
||||||
import BitRectMode from '../../containers/bit-rect-mode.jsx';
|
import BitRectMode from '../../containers/bit-rect-mode.jsx';
|
||||||
import BitTextMode from '../../components/bit-text-mode/bit-text-mode.jsx';
|
import BitTextMode from '../../components/bit-text-mode/bit-text-mode.jsx';
|
||||||
import BitFillMode from '../../components/bit-fill-mode/bit-fill-mode.jsx';
|
import BitFillMode from '../../components/bit-fill-mode/bit-fill-mode.jsx';
|
||||||
import BitEraserMode from '../../components/bit-eraser-mode/bit-eraser-mode.jsx';
|
import BitEraserMode from '../../containers/bit-eraser-mode.jsx';
|
||||||
import BitSelectMode from '../../components/bit-select-mode/bit-select-mode.jsx';
|
import BitSelectMode from '../../components/bit-select-mode/bit-select-mode.jsx';
|
||||||
import Box from '../box/box.jsx';
|
import Box from '../box/box.jsx';
|
||||||
import Button from '../button/button.jsx';
|
import Button from '../button/button.jsx';
|
||||||
|
@ -182,7 +182,9 @@ const PaintEditorComponent = props => (
|
||||||
/>
|
/>
|
||||||
<BitTextMode />
|
<BitTextMode />
|
||||||
<BitFillMode />
|
<BitFillMode />
|
||||||
<BitEraserMode />
|
<BitEraserMode
|
||||||
|
onUpdateImage={props.onUpdateImage}
|
||||||
|
/>
|
||||||
<BitSelectMode />
|
<BitSelectMode />
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
90
src/containers/bit-eraser-mode.jsx
Normal file
90
src/containers/bit-eraser-mode.jsx
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import {connect} from 'react-redux';
|
||||||
|
import bindAll from 'lodash.bindall';
|
||||||
|
import Modes from '../lib/modes';
|
||||||
|
|
||||||
|
import {changeMode} from '../reducers/modes';
|
||||||
|
import {clearSelectedItems} from '../reducers/selected-items';
|
||||||
|
import {clearSelection} from '../helper/selection';
|
||||||
|
|
||||||
|
import BitEraserModeComponent from '../components/bit-eraser-mode/bit-eraser-mode.jsx';
|
||||||
|
import BitBrushTool from '../helper/bit-tools/brush-tool';
|
||||||
|
|
||||||
|
class BitEraserMode extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props);
|
||||||
|
bindAll(this, [
|
||||||
|
'activateTool',
|
||||||
|
'deactivateTool'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
componentDidMount () {
|
||||||
|
if (this.props.isBitEraserModeActive) {
|
||||||
|
this.activateTool(this.props);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
componentWillReceiveProps (nextProps) {
|
||||||
|
if (this.tool && nextProps.bitEraserSize !== this.props.bitEraserSize) {
|
||||||
|
this.tool.setBrushSize(nextProps.bitEraserSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextProps.isBitEraserModeActive && !this.props.isBitEraserModeActive) {
|
||||||
|
this.activateTool();
|
||||||
|
} else if (!nextProps.isBitEraserModeActive && this.props.isBitEraserModeActive) {
|
||||||
|
this.deactivateTool();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shouldComponentUpdate (nextProps) {
|
||||||
|
return nextProps.isBitEraserModeActive !== this.props.isBitEraserModeActive;
|
||||||
|
}
|
||||||
|
activateTool () {
|
||||||
|
clearSelection(this.props.clearSelectedItems);
|
||||||
|
this.tool = new BitBrushTool(
|
||||||
|
this.props.onUpdateImage,
|
||||||
|
true /* isEraser */
|
||||||
|
);
|
||||||
|
this.tool.setBrushSize(this.props.bitEraserSize);
|
||||||
|
|
||||||
|
this.tool.activate();
|
||||||
|
}
|
||||||
|
deactivateTool () {
|
||||||
|
this.tool.deactivateTool();
|
||||||
|
this.tool.remove();
|
||||||
|
this.tool = null;
|
||||||
|
}
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<BitEraserModeComponent
|
||||||
|
isSelected={this.props.isBitEraserModeActive}
|
||||||
|
onMouseDown={this.props.handleMouseDown}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BitEraserMode.propTypes = {
|
||||||
|
bitEraserSize: PropTypes.number.isRequired,
|
||||||
|
clearSelectedItems: PropTypes.func.isRequired,
|
||||||
|
handleMouseDown: PropTypes.func.isRequired,
|
||||||
|
isBitEraserModeActive: PropTypes.bool.isRequired,
|
||||||
|
onUpdateImage: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapStateToProps = state => ({
|
||||||
|
bitEraserSize: state.scratchPaint.bitEraserSize,
|
||||||
|
isBitEraserModeActive: state.scratchPaint.mode === Modes.BIT_ERASER
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
clearSelectedItems: () => {
|
||||||
|
dispatch(clearSelectedItems());
|
||||||
|
},
|
||||||
|
handleMouseDown: () => {
|
||||||
|
dispatch(changeMode(Modes.BIT_ERASER));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(BitEraserMode);
|
|
@ -124,6 +124,9 @@ class PaintEditor extends React.Component {
|
||||||
case Modes.BIT_RECT:
|
case Modes.BIT_RECT:
|
||||||
this.props.changeMode(Modes.RECT);
|
this.props.changeMode(Modes.RECT);
|
||||||
break;
|
break;
|
||||||
|
case Modes.BIT_ERASER:
|
||||||
|
this.props.changeMode(Modes.ERASER);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
this.props.changeMode(Modes.BRUSH);
|
this.props.changeMode(Modes.BRUSH);
|
||||||
}
|
}
|
||||||
|
@ -138,6 +141,9 @@ class PaintEditor extends React.Component {
|
||||||
case Modes.RECT:
|
case Modes.RECT:
|
||||||
this.props.changeMode(Modes.BIT_RECT);
|
this.props.changeMode(Modes.BIT_RECT);
|
||||||
break;
|
break;
|
||||||
|
case Modes.ERASER:
|
||||||
|
this.props.changeMode(Modes.BIT_ERASER);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
this.props.changeMode(Modes.BIT_BRUSH);
|
this.props.changeMode(Modes.BIT_BRUSH);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,15 +4,17 @@ import {forEachLinePoint, getBrushMark} from '../bitmap';
|
||||||
import {getGuideLayer} from '../layer';
|
import {getGuideLayer} from '../layer';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tool for drawing with the bitmap brush.
|
* Tool for drawing with the bitmap brush and eraser
|
||||||
*/
|
*/
|
||||||
class BrushTool extends paper.Tool {
|
class BrushTool extends paper.Tool {
|
||||||
/**
|
/**
|
||||||
* @param {!function} onUpdateImage A callback to call when the image visibly changes
|
* @param {!function} onUpdateImage A callback to call when the image visibly changes
|
||||||
|
* @param {boolean} isEraser True if brush should erase
|
||||||
*/
|
*/
|
||||||
constructor (onUpdateImage) {
|
constructor (onUpdateImage, isEraser) {
|
||||||
super();
|
super();
|
||||||
this.onUpdateImage = onUpdateImage;
|
this.onUpdateImage = onUpdateImage;
|
||||||
|
this.isEraser = isEraser;
|
||||||
|
|
||||||
// We have to set these functions instead of just declaring them because
|
// We have to set these functions instead of just declaring them because
|
||||||
// paper.js tools hook up the listeners in the setter functions.
|
// paper.js tools hook up the listeners in the setter functions.
|
||||||
|
@ -39,7 +41,14 @@ class BrushTool extends paper.Tool {
|
||||||
this.tmpCanvas = getBrushMark(this.size, this.color);
|
this.tmpCanvas = getBrushMark(this.size, this.color);
|
||||||
}
|
}
|
||||||
const roundedUpRadius = Math.ceil(this.size / 2);
|
const roundedUpRadius = Math.ceil(this.size / 2);
|
||||||
|
const context = getRaster().getContext('2d');
|
||||||
|
if (this.isEraser) {
|
||||||
|
context.globalCompositeOperation = 'destination-out';
|
||||||
|
}
|
||||||
getRaster().drawImage(this.tmpCanvas, new paper.Point(~~x - roundedUpRadius, ~~y - roundedUpRadius));
|
getRaster().drawImage(this.tmpCanvas, new paper.Point(~~x - roundedUpRadius, ~~y - roundedUpRadius));
|
||||||
|
if (this.isEraser) {
|
||||||
|
context.globalCompositeOperation = 'source-over';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
updateCursorIfNeeded () {
|
updateCursorIfNeeded () {
|
||||||
if (!this.size) {
|
if (!this.size) {
|
||||||
|
@ -57,7 +66,7 @@ class BrushTool extends paper.Tool {
|
||||||
this.cursorPreview.remove();
|
this.cursorPreview.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.tmpCanvas = getBrushMark(this.size, this.color);
|
this.tmpCanvas = getBrushMark(this.size, this.color, this.isEraser);
|
||||||
this.cursorPreview = new paper.Raster(this.tmpCanvas);
|
this.cursorPreview = new paper.Raster(this.tmpCanvas);
|
||||||
this.cursorPreview.guide = true;
|
this.cursorPreview.guide = true;
|
||||||
this.cursorPreview.parent = getGuideLayer();
|
this.cursorPreview.parent = getGuideLayer();
|
||||||
|
|
|
@ -86,9 +86,10 @@ const fillEllipse = function (centerX, centerY, radiusX, radiusY, context) {
|
||||||
/**
|
/**
|
||||||
* @param {!number} size The diameter of the brush
|
* @param {!number} size The diameter of the brush
|
||||||
* @param {!string} color The css color of the brush
|
* @param {!string} color The css color of the brush
|
||||||
|
* @param {?boolean} isEraser True if we want the brush mark for the eraser
|
||||||
* @return {HTMLCanvasElement} a canvas with the brush mark printed on it
|
* @return {HTMLCanvasElement} a canvas with the brush mark printed on it
|
||||||
*/
|
*/
|
||||||
const getBrushMark = function (size, color) {
|
const getBrushMark = function (size, color, isEraser) {
|
||||||
size = ~~size;
|
size = ~~size;
|
||||||
const canvas = document.createElement('canvas');
|
const canvas = document.createElement('canvas');
|
||||||
const roundedUpRadius = Math.ceil(size / 2);
|
const roundedUpRadius = Math.ceil(size / 2);
|
||||||
|
@ -96,7 +97,8 @@ const getBrushMark = function (size, color) {
|
||||||
canvas.height = roundedUpRadius * 2;
|
canvas.height = roundedUpRadius * 2;
|
||||||
const context = canvas.getContext('2d');
|
const context = canvas.getContext('2d');
|
||||||
context.imageSmoothingEnabled = false;
|
context.imageSmoothingEnabled = false;
|
||||||
context.fillStyle = color;
|
context.fillStyle = isEraser ? 'white' : color;
|
||||||
|
// @todo add outline for erasers
|
||||||
// Small squares for pixel artists
|
// Small squares for pixel artists
|
||||||
if (size <= 5) {
|
if (size <= 5) {
|
||||||
if (size % 2) {
|
if (size % 2) {
|
||||||
|
|
|
@ -4,6 +4,7 @@ const Modes = keyMirror({
|
||||||
BIT_BRUSH: null,
|
BIT_BRUSH: null,
|
||||||
BIT_LINE: null,
|
BIT_LINE: null,
|
||||||
BIT_RECT: null,
|
BIT_RECT: null,
|
||||||
|
BIT_ERASER: null,
|
||||||
BRUSH: null,
|
BRUSH: null,
|
||||||
ERASER: null,
|
ERASER: null,
|
||||||
LINE: null,
|
LINE: null,
|
||||||
|
@ -19,7 +20,8 @@ const Modes = keyMirror({
|
||||||
const BitmapModes = keyMirror({
|
const BitmapModes = keyMirror({
|
||||||
BIT_BRUSH: null,
|
BIT_BRUSH: null,
|
||||||
BIT_LINE: null,
|
BIT_LINE: null,
|
||||||
BIT_RECT: null
|
BIT_RECT: null,
|
||||||
|
BIT_ERASER: null
|
||||||
});
|
});
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|
31
src/reducers/bit-eraser-size.js
Normal file
31
src/reducers/bit-eraser-size.js
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import log from '../log/log';
|
||||||
|
|
||||||
|
const CHANGE_BIT_ERASER_SIZE = 'scratch-paint/eraser-mode/CHANGE_BIT_ERASER_SIZE';
|
||||||
|
const initialState = 40;
|
||||||
|
|
||||||
|
const reducer = function (state, action) {
|
||||||
|
if (typeof state === 'undefined') state = initialState;
|
||||||
|
switch (action.type) {
|
||||||
|
case CHANGE_BIT_ERASER_SIZE:
|
||||||
|
if (isNaN(action.eraserSize)) {
|
||||||
|
log.warn(`Invalid eraser size: ${action.eraserSize}`);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
return Math.max(1, action.eraserSize);
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Action creators ==================================
|
||||||
|
const changeBitEraserSize = function (eraserSize) {
|
||||||
|
return {
|
||||||
|
type: CHANGE_BIT_ERASER_SIZE,
|
||||||
|
eraserSize: eraserSize
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
reducer as default,
|
||||||
|
changeBitEraserSize
|
||||||
|
};
|
|
@ -1,6 +1,7 @@
|
||||||
import {combineReducers} from 'redux';
|
import {combineReducers} from 'redux';
|
||||||
import modeReducer from './modes';
|
import modeReducer from './modes';
|
||||||
import bitBrushSizeReducer from './bit-brush-size';
|
import bitBrushSizeReducer from './bit-brush-size';
|
||||||
|
import bitEraserSizeReducer from './bit-eraser-size';
|
||||||
import brushModeReducer from './brush-mode';
|
import brushModeReducer from './brush-mode';
|
||||||
import eraserModeReducer from './eraser-mode';
|
import eraserModeReducer from './eraser-mode';
|
||||||
import colorReducer from './color';
|
import colorReducer from './color';
|
||||||
|
@ -17,6 +18,7 @@ import undoReducer from './undo';
|
||||||
export default combineReducers({
|
export default combineReducers({
|
||||||
mode: modeReducer,
|
mode: modeReducer,
|
||||||
bitBrushSize: bitBrushSizeReducer,
|
bitBrushSize: bitBrushSizeReducer,
|
||||||
|
bitEraserSize: bitEraserSizeReducer,
|
||||||
brushMode: brushModeReducer,
|
brushMode: brushModeReducer,
|
||||||
color: colorReducer,
|
color: colorReducer,
|
||||||
clipboard: clipboardReducer,
|
clipboard: clipboardReducer,
|
||||||
|
|
Loading…
Reference in a new issue