mirror of
https://github.com/scratchfoundation/scratch-paint.git
synced 2025-01-10 14:42:13 -05:00
Bitmap gradient (#559)
This commit is contained in:
parent
4ba79cacbb
commit
af3c6694d4
6 changed files with 208 additions and 99 deletions
|
@ -3,16 +3,18 @@ import React from 'react';
|
|||
import {connect} from 'react-redux';
|
||||
import bindAll from 'lodash.bindall';
|
||||
import Modes from '../lib/modes';
|
||||
import GradientTypes from '../lib/gradient-types';
|
||||
|
||||
import FillModeComponent from '../components/bit-fill-mode/bit-fill-mode.jsx';
|
||||
|
||||
import {changeFillColor, DEFAULT_COLOR} from '../reducers/fill-color';
|
||||
import {changeFillColor2} from '../reducers/fill-color-2';
|
||||
import {changeMode} from '../reducers/modes';
|
||||
import {clearSelectedItems} from '../reducers/selected-items';
|
||||
import {clearGradient} from '../reducers/selection-gradient-type';
|
||||
import {changeGradientType} from '../reducers/fill-mode-gradient-type';
|
||||
import {clearSelection} from '../helper/selection';
|
||||
import FillTool from '../helper/bit-tools/fill-tool';
|
||||
import {MIXED} from '../helper/style-path';
|
||||
import {getRotatedColor, MIXED} from '../helper/style-path';
|
||||
|
||||
class BitFillMode extends React.Component {
|
||||
constructor (props) {
|
||||
|
@ -28,8 +30,16 @@ class BitFillMode extends React.Component {
|
|||
}
|
||||
}
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (this.tool && nextProps.color !== this.props.color) {
|
||||
this.tool.setColor(nextProps.color);
|
||||
if (this.tool) {
|
||||
if (nextProps.color !== this.props.color) {
|
||||
this.tool.setColor(nextProps.color);
|
||||
}
|
||||
if (nextProps.color2 !== this.props.color2) {
|
||||
this.tool.setColor2(nextProps.color2);
|
||||
}
|
||||
if (nextProps.fillModeGradientType !== this.props.fillModeGradientType) {
|
||||
this.tool.setGradientType(nextProps.fillModeGradientType);
|
||||
}
|
||||
}
|
||||
|
||||
if (nextProps.isFillModeActive && !this.props.isFillModeActive) {
|
||||
|
@ -43,14 +53,31 @@ class BitFillMode extends React.Component {
|
|||
}
|
||||
activateTool () {
|
||||
clearSelection(this.props.clearSelectedItems);
|
||||
this.props.clearGradient();
|
||||
|
||||
// Force the default brush color if fill is MIXED or transparent
|
||||
const fillColorPresent = this.props.color !== MIXED && this.props.color !== null;
|
||||
if (!fillColorPresent) {
|
||||
this.props.onChangeFillColor(DEFAULT_COLOR);
|
||||
let color = this.props.color;
|
||||
if (this.props.color === MIXED) {
|
||||
color = DEFAULT_COLOR;
|
||||
this.props.onChangeFillColor(DEFAULT_COLOR, 0);
|
||||
}
|
||||
const gradientType = this.props.fillModeGradientType ?
|
||||
this.props.fillModeGradientType : this.props.selectModeGradientType;
|
||||
let color2 = this.props.color2;
|
||||
if (gradientType !== this.props.selectModeGradientType) {
|
||||
if (this.props.selectModeGradientType === GradientTypes.SOLID) {
|
||||
color2 = getRotatedColor(color);
|
||||
this.props.onChangeFillColor(color2, 1);
|
||||
}
|
||||
this.props.changeGradientType(gradientType);
|
||||
}
|
||||
if (this.props.color2 === MIXED) {
|
||||
color2 = getRotatedColor();
|
||||
this.props.onChangeFillColor(color2, 1);
|
||||
}
|
||||
this.tool = new FillTool(this.props.onUpdateImage);
|
||||
this.tool.setColor(this.props.color);
|
||||
this.tool.setColor(color);
|
||||
this.tool.setColor2(color2);
|
||||
this.tool.setGradientType(gradientType);
|
||||
this.tool.activate();
|
||||
}
|
||||
deactivateTool () {
|
||||
|
@ -69,31 +96,41 @@ class BitFillMode extends React.Component {
|
|||
}
|
||||
|
||||
BitFillMode.propTypes = {
|
||||
clearGradient: PropTypes.func.isRequired,
|
||||
changeGradientType: PropTypes.func.isRequired,
|
||||
clearSelectedItems: PropTypes.func.isRequired,
|
||||
color: PropTypes.string,
|
||||
color2: PropTypes.string,
|
||||
fillModeGradientType: PropTypes.oneOf(Object.keys(GradientTypes)),
|
||||
handleMouseDown: PropTypes.func.isRequired,
|
||||
isFillModeActive: PropTypes.bool.isRequired,
|
||||
onChangeFillColor: PropTypes.func.isRequired,
|
||||
onUpdateImage: PropTypes.func.isRequired
|
||||
onUpdateImage: PropTypes.func.isRequired,
|
||||
selectModeGradientType: PropTypes.oneOf(Object.keys(GradientTypes)).isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
fillModeGradientType: state.scratchPaint.fillMode.gradientType, // Last user-selected gradient type
|
||||
color: state.scratchPaint.color.fillColor,
|
||||
isFillModeActive: state.scratchPaint.mode === Modes.BIT_FILL
|
||||
color2: state.scratchPaint.color.fillColor2,
|
||||
isFillModeActive: state.scratchPaint.mode === Modes.BIT_FILL,
|
||||
selectModeGradientType: state.scratchPaint.color.gradientType
|
||||
});
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
clearGradient: () => {
|
||||
dispatch(clearGradient());
|
||||
},
|
||||
clearSelectedItems: () => {
|
||||
dispatch(clearSelectedItems());
|
||||
},
|
||||
changeGradientType: gradientType => {
|
||||
dispatch(changeGradientType(gradientType));
|
||||
},
|
||||
handleMouseDown: () => {
|
||||
dispatch(changeMode(Modes.BIT_FILL));
|
||||
},
|
||||
onChangeFillColor: fillColor => {
|
||||
dispatch(changeFillColor(fillColor));
|
||||
onChangeFillColor: (fillColor, index) => {
|
||||
if (index === 0) {
|
||||
dispatch(changeFillColor(fillColor));
|
||||
} else if (index === 1) {
|
||||
dispatch(changeFillColor2(fillColor));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import {getSelectedLeafItems} from '../helper/selection';
|
|||
import {setSelectedItems} from '../reducers/selected-items';
|
||||
import Modes from '../lib/modes';
|
||||
import Formats from '../lib/format';
|
||||
import {isBitmap, isVector} from '../lib/format';
|
||||
import {isBitmap} from '../lib/format';
|
||||
import GradientTypes from '../lib/gradient-types';
|
||||
|
||||
import FillColorIndicatorComponent from '../components/fill-color-indicator.jsx';
|
||||
|
@ -122,10 +122,10 @@ const mapStateToProps = state => ({
|
|||
gradientType: state.scratchPaint.color.gradientType,
|
||||
isEyeDropping: state.scratchPaint.color.eyeDropper.active,
|
||||
mode: state.scratchPaint.mode,
|
||||
shouldShowGradientTools: isVector(state.scratchPaint.format) &&
|
||||
(state.scratchPaint.mode === Modes.SELECT ||
|
||||
shouldShowGradientTools: state.scratchPaint.mode === Modes.SELECT ||
|
||||
state.scratchPaint.mode === Modes.RESHAPE ||
|
||||
state.scratchPaint.mode === Modes.FILL),
|
||||
state.scratchPaint.mode === Modes.FILL ||
|
||||
state.scratchPaint.mode === Modes.BIT_FILL,
|
||||
textEditTarget: state.scratchPaint.textEditTarget
|
||||
});
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import paper from '@scratch/paper';
|
||||
import {floodFill, floodFillAll} from '../bitmap';
|
||||
import {getRaster} from '../layer';
|
||||
import {floodFill, floodFillAll, getHitBounds} from '../bitmap';
|
||||
import {createGradientObject} from '../style-path';
|
||||
import {createCanvas, getRaster} from '../layer';
|
||||
import GradientTypes from '../../lib/gradient-types';
|
||||
|
||||
const TRANSPARENT = 'rgba(0,0,0,0)';
|
||||
/**
|
||||
|
@ -13,48 +15,92 @@ class FillTool extends paper.Tool {
|
|||
constructor (onUpdateImage) {
|
||||
super();
|
||||
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.
|
||||
this.onMouseDown = this.handleMouseDown;
|
||||
this.onMouseDrag = this.handleMouseDrag;
|
||||
this.onMouseUp = this.handleMouseUp;
|
||||
|
||||
this.color = null;
|
||||
this.changed = false;
|
||||
this.color2 = null;
|
||||
this.gradientType = null;
|
||||
this.active = false;
|
||||
}
|
||||
setColor (color) {
|
||||
// Null color means transparent because that is the standard in vector
|
||||
this.color = color ? color : TRANSPARENT;
|
||||
this.color = color;
|
||||
}
|
||||
setColor2 (color2) {
|
||||
this.color2 = color2;
|
||||
}
|
||||
setGradientType (gradientType) {
|
||||
this.gradientType = gradientType;
|
||||
}
|
||||
handleMouseDown (event) {
|
||||
const context = getRaster().getContext('2d');
|
||||
if (event.event.shiftKey) {
|
||||
this.changed = floodFillAll(event.point.x, event.point.y, this.color, context) || this.changed;
|
||||
} else {
|
||||
this.changed = floodFill(event.point.x, event.point.y, this.color, context) || this.changed;
|
||||
}
|
||||
this.paint(event);
|
||||
}
|
||||
handleMouseDrag (event) {
|
||||
const context = getRaster().getContext('2d');
|
||||
if (event.event.shiftKey) {
|
||||
this.changed = floodFillAll(event.point.x, event.point.y, this.color, context) || this.changed;
|
||||
} else {
|
||||
this.changed = floodFill(event.point.x, event.point.y, this.color, context) || this.changed;
|
||||
}
|
||||
this.paint(event);
|
||||
}
|
||||
handleMouseUp () {
|
||||
if (this.changed) {
|
||||
paint (event) {
|
||||
const sourceContext = getRaster().getContext('2d');
|
||||
let destContext = sourceContext;
|
||||
let color = this.color;
|
||||
// Paint to a mask instead of the original canvas when drawing
|
||||
if (this.gradientType !== GradientTypes.SOLID) {
|
||||
const tmpCanvas = createCanvas();
|
||||
destContext = tmpCanvas.getContext('2d');
|
||||
color = 'black';
|
||||
} else if (!color) {
|
||||
// Null color means transparent because that is the standard in vector
|
||||
color = TRANSPARENT;
|
||||
}
|
||||
let changed = false;
|
||||
if (event.event.shiftKey) {
|
||||
changed = floodFillAll(event.point.x, event.point.y, color, sourceContext, destContext);
|
||||
} else {
|
||||
changed = floodFill(event.point.x, event.point.y, color, sourceContext, destContext);
|
||||
}
|
||||
if (changed && this.gradientType !== GradientTypes.SOLID) {
|
||||
const raster = new paper.Raster({insert: false});
|
||||
raster.canvas = destContext.canvas;
|
||||
raster.onLoad = () => {
|
||||
raster.position = getRaster().position;
|
||||
// Erase what's already there
|
||||
getRaster().getContext().globalCompositeOperation = 'destination-out';
|
||||
getRaster().drawImage(raster.canvas, new paper.Point());
|
||||
getRaster().getContext().globalCompositeOperation = 'source-over';
|
||||
|
||||
// Create the gradient to be masked
|
||||
const hitBounds = getHitBounds(raster);
|
||||
if (!hitBounds.area) return;
|
||||
const gradient = new paper.Shape.Rectangle({
|
||||
insert: false,
|
||||
rectangle: {
|
||||
topLeft: hitBounds.topLeft,
|
||||
bottomRight: hitBounds.bottomRight
|
||||
}
|
||||
});
|
||||
gradient.fillColor = createGradientObject(
|
||||
this.color,
|
||||
this.color2,
|
||||
this.gradientType,
|
||||
gradient.bounds,
|
||||
event.point);
|
||||
const rasterGradient = gradient.rasterize(getRaster().resolution.width, false /* insert */);
|
||||
|
||||
// Mask gradient
|
||||
raster.getContext().globalCompositeOperation = 'source-in';
|
||||
raster.drawImage(rasterGradient.canvas, rasterGradient.bounds.topLeft);
|
||||
|
||||
// Draw masked gradient into raster layer
|
||||
getRaster().drawImage(raster.canvas, new paper.Point());
|
||||
this.onUpdateImage();
|
||||
};
|
||||
} else if (changed) {
|
||||
this.onUpdateImage();
|
||||
this.changed = false;
|
||||
}
|
||||
}
|
||||
deactivateTool () {
|
||||
if (this.changed) {
|
||||
this.onUpdateImage();
|
||||
this.changed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -429,23 +429,26 @@ const colorPixel_ = function (x, y, imageData, newColor) {
|
|||
*
|
||||
* @param {!int} x The x coordinate on the context at which to begin
|
||||
* @param {!int} y The y coordinate on the context at which to begin
|
||||
* @param {!ImageData} imageData The image data to edit
|
||||
* @param {!ImageData} sourceImageData The image data to sample from. This is edited by the function.
|
||||
* @param {!ImageData} destImageData The image data to edit. May match sourceImageData. Should match
|
||||
* size of sourceImageData.
|
||||
* @param {!Array<number>} newColor The color to replace with. A length 4 array [r, g, b, a].
|
||||
* @param {!Array<number>} oldColor The color to replace. A length 4 array [r, g, b, a].
|
||||
* This must be different from newColor.
|
||||
* @param {!Array<Array<int>>} stack The stack of pixels we need to look at
|
||||
*/
|
||||
const floodFillInternal_ = function (x, y, imageData, newColor, oldColor, stack) {
|
||||
while (y > 0 && matchesColor_(x, y - 1, imageData, oldColor)) {
|
||||
const floodFillInternal_ = function (x, y, sourceImageData, destImageData, newColor, oldColor, stack) {
|
||||
while (y > 0 && matchesColor_(x, y - 1, sourceImageData, oldColor)) {
|
||||
y--;
|
||||
}
|
||||
let lastLeftMatchedColor = false;
|
||||
let lastRightMatchedColor = false;
|
||||
for (; y < imageData.height; y++) {
|
||||
if (!matchesColor_(x, y, imageData, oldColor)) break;
|
||||
colorPixel_(x, y, imageData, newColor);
|
||||
for (; y < sourceImageData.height; y++) {
|
||||
if (!matchesColor_(x, y, sourceImageData, oldColor)) break;
|
||||
colorPixel_(x, y, sourceImageData, newColor);
|
||||
colorPixel_(x, y, destImageData, newColor);
|
||||
if (x > 0) {
|
||||
if (matchesColor_(x - 1, y, imageData, oldColor)) {
|
||||
if (matchesColor_(x - 1, y, sourceImageData, oldColor)) {
|
||||
if (!lastLeftMatchedColor) {
|
||||
stack.push([x - 1, y]);
|
||||
lastLeftMatchedColor = true;
|
||||
|
@ -454,8 +457,8 @@ const floodFillInternal_ = function (x, y, imageData, newColor, oldColor, stack)
|
|||
lastLeftMatchedColor = false;
|
||||
}
|
||||
}
|
||||
if (x < imageData.width - 1) {
|
||||
if (matchesColor_(x + 1, y, imageData, oldColor)) {
|
||||
if (x < sourceImageData.width - 1) {
|
||||
if (matchesColor_(x + 1, y, sourceImageData, oldColor)) {
|
||||
if (!lastRightMatchedColor) {
|
||||
stack.push([x + 1, y]);
|
||||
lastRightMatchedColor = true;
|
||||
|
@ -487,15 +490,21 @@ const fillStyleToColor_ = function (fillStyleString) {
|
|||
* @param {!number} x The x coordinate on the context at which to begin
|
||||
* @param {!number} y The y coordinate on the context at which to begin
|
||||
* @param {!string} color A color string, which would go into context.fillStyle
|
||||
* @param {!HTMLCanvas2DContext} context The context in which to draw
|
||||
* @param {!HTMLCanvas2DContext} sourceContext The context from which to sample to determine where to flood fill
|
||||
* @param {!HTMLCanvas2DContext} destContext The context to which to draw. May match sourceContext. Should match
|
||||
* the size of sourceContext.
|
||||
* @return {boolean} True if image changed, false otherwise
|
||||
*/
|
||||
const floodFill = function (x, y, color, context) {
|
||||
const floodFill = function (x, y, color, sourceContext, destContext) {
|
||||
x = ~~x;
|
||||
y = ~~y;
|
||||
const newColor = fillStyleToColor_(color);
|
||||
const oldColor = getColor_(x, y, context);
|
||||
const imageData = context.getImageData(0, 0, context.canvas.width, context.canvas.height);
|
||||
const oldColor = getColor_(x, y, sourceContext);
|
||||
const sourceImageData = sourceContext.getImageData(0, 0, sourceContext.canvas.width, sourceContext.canvas.height);
|
||||
let destImageData = sourceImageData;
|
||||
if (destContext !== sourceContext) {
|
||||
destImageData = new ImageData(sourceContext.canvas.width, sourceContext.canvas.height);
|
||||
}
|
||||
if (oldColor[0] === newColor[0] &&
|
||||
oldColor[1] === newColor[1] &&
|
||||
oldColor[2] === newColor[2] &&
|
||||
|
@ -505,9 +514,9 @@ const floodFill = function (x, y, color, context) {
|
|||
const stack = [[x, y]];
|
||||
while (stack.length) {
|
||||
const pop = stack.pop();
|
||||
floodFillInternal_(pop[0], pop[1], imageData, newColor, oldColor, stack);
|
||||
floodFillInternal_(pop[0], pop[1], sourceImageData, destImageData, newColor, oldColor, stack);
|
||||
}
|
||||
context.putImageData(imageData, 0, 0);
|
||||
destContext.putImageData(destImageData, 0, 0);
|
||||
return true;
|
||||
};
|
||||
|
||||
|
@ -516,29 +525,34 @@ const floodFill = function (x, y, color, context) {
|
|||
* @param {!number} x The x coordinate on the context of the start color
|
||||
* @param {!number} y The y coordinate on the context of the start color
|
||||
* @param {!string} color A color string, which would go into context.fillStyle
|
||||
* @param {!HTMLCanvas2DContext} context The context in which to draw
|
||||
* @param {!HTMLCanvas2DContext} sourceContext The context from which to sample to determine where to flood fill
|
||||
* @param {!HTMLCanvas2DContext} destContext The context to which to draw. May match sourceContext. Should match
|
||||
* @return {boolean} True if image changed, false otherwise
|
||||
*/
|
||||
const floodFillAll = function (x, y, color, context) {
|
||||
const floodFillAll = function (x, y, color, sourceContext, destContext) {
|
||||
x = ~~x;
|
||||
y = ~~y;
|
||||
const newColor = fillStyleToColor_(color);
|
||||
const oldColor = getColor_(x, y, context);
|
||||
const imageData = context.getImageData(0, 0, context.canvas.width, context.canvas.height);
|
||||
const oldColor = getColor_(x, y, sourceContext);
|
||||
const sourceImageData = sourceContext.getImageData(0, 0, sourceContext.canvas.width, sourceContext.canvas.height);
|
||||
let destImageData = sourceImageData;
|
||||
if (destContext !== sourceContext) {
|
||||
destImageData = new ImageData(sourceContext.canvas.width, sourceContext.canvas.height);
|
||||
}
|
||||
if (oldColor[0] === newColor[0] &&
|
||||
oldColor[1] === newColor[1] &&
|
||||
oldColor[2] === newColor[2] &&
|
||||
oldColor[3] === newColor[3]) { // no-op
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < imageData.width; i++) {
|
||||
for (let j = 0; j < imageData.height; j++) {
|
||||
if (matchesColor_(i, j, imageData, oldColor)) {
|
||||
colorPixel_(i, j, imageData, newColor);
|
||||
for (let i = 0; i < sourceImageData.width; i++) {
|
||||
for (let j = 0; j < sourceImageData.height; j++) {
|
||||
if (matchesColor_(i, j, sourceImageData, oldColor)) {
|
||||
colorPixel_(i, j, destImageData, newColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
context.putImageData(imageData, 0, 0);
|
||||
destContext.putImageData(destImageData, 0, 0);
|
||||
return true;
|
||||
};
|
||||
|
||||
|
|
|
@ -55,6 +55,42 @@ const getRotatedColor = function (firstColor) {
|
|||
`hsl(${(color.hue - 72) % 360}, ${color.saturation * 100}, ${Math.max(color.lightness * 100, 10)})`).hex;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert params to a paper.Color gradient object
|
||||
* @param {?string} color1 CSS string, or null for transparent
|
||||
* @param {?string} color2 CSS string, or null for transparent
|
||||
* @param {GradientType} gradientType gradient type
|
||||
* @param {paper.Rectangle} bounds Bounds of the object
|
||||
* @param {paper.Point} radialCenter Where the center of a radial gradient should be, if the gradient is radial
|
||||
* @return {paper.Color} Color object with gradient, may be null or color string if the gradient type is solid
|
||||
*/
|
||||
const createGradientObject = function (color1, color2, gradientType, bounds, radialCenter) {
|
||||
if (gradientType === GradientTypes.SOLID) return color1;
|
||||
if (color1 === null) {
|
||||
color1 = getColorStringForTransparent(color2);
|
||||
}
|
||||
if (color2 === null) {
|
||||
color2 = getColorStringForTransparent(color1);
|
||||
}
|
||||
const halfLongestDimension = Math.max(bounds.width, bounds.height) / 2;
|
||||
const start = gradientType === GradientTypes.RADIAL ? radialCenter :
|
||||
gradientType === GradientTypes.VERTICAL ? bounds.topCenter :
|
||||
gradientType === GradientTypes.HORIZONTAL ? bounds.leftCenter :
|
||||
null;
|
||||
const end = gradientType === GradientTypes.RADIAL ? start.add(new paper.Point(halfLongestDimension, 0)) :
|
||||
gradientType === GradientTypes.VERTICAL ? bounds.bottomCenter :
|
||||
gradientType === GradientTypes.HORIZONTAL ? bounds.rightCenter :
|
||||
null;
|
||||
return {
|
||||
gradient: {
|
||||
stops: [color1, color2],
|
||||
radial: gradientType === GradientTypes.RADIAL
|
||||
},
|
||||
origin: start,
|
||||
destination: end
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when setting fill color
|
||||
* @param {string} colorString color, css format, or null if completely transparent
|
||||
|
@ -497,8 +533,8 @@ export {
|
|||
applyGradientTypeToSelection,
|
||||
applyStrokeColorToSelection,
|
||||
applyStrokeWidthToSelection,
|
||||
createGradientObject,
|
||||
getColorsFromSelection,
|
||||
getColorStringForTransparent,
|
||||
getRotatedColor,
|
||||
MIXED,
|
||||
styleBlob,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import paper from '@scratch/paper';
|
||||
import {getHoveredItem} from '../hover';
|
||||
import {expandBy} from '../math';
|
||||
import {getColorStringForTransparent} from '../style-path';
|
||||
import {createGradientObject} from '../style-path';
|
||||
import GradientTypes from '../../lib/gradient-types';
|
||||
|
||||
class FillTool extends paper.Tool {
|
||||
|
@ -176,37 +176,13 @@ class FillTool extends paper.Tool {
|
|||
// Either pass in a fully defined paper.Color as color1,
|
||||
// or pass in 2 color strings, a gradient type, and a pointer location
|
||||
_setFillItemColor (color1, color2, gradientType, pointerLocation) {
|
||||
let fillColor;
|
||||
const item = this._getFillItem();
|
||||
if (!item) return;
|
||||
if (color1 instanceof paper.Color || gradientType === GradientTypes.SOLID) {
|
||||
fillColor = color1;
|
||||
item.fillColor = color1;
|
||||
} else {
|
||||
if (color1 === null) {
|
||||
color1 = getColorStringForTransparent(color2);
|
||||
}
|
||||
if (color2 === null) {
|
||||
color2 = getColorStringForTransparent(color1);
|
||||
}
|
||||
const halfLongestDimension = Math.max(item.bounds.width, item.bounds.height) / 2;
|
||||
const start = gradientType === GradientTypes.RADIAL ? pointerLocation :
|
||||
gradientType === GradientTypes.VERTICAL ? item.bounds.topCenter :
|
||||
gradientType === GradientTypes.HORIZONTAL ? item.bounds.leftCenter :
|
||||
null;
|
||||
const end = gradientType === GradientTypes.RADIAL ? start.add(new paper.Point(halfLongestDimension, 0)) :
|
||||
gradientType === GradientTypes.VERTICAL ? item.bounds.bottomCenter :
|
||||
gradientType === GradientTypes.HORIZONTAL ? item.bounds.rightCenter :
|
||||
null;
|
||||
fillColor = {
|
||||
gradient: {
|
||||
stops: [color1, color2],
|
||||
radial: gradientType === GradientTypes.RADIAL
|
||||
},
|
||||
origin: start,
|
||||
destination: end
|
||||
};
|
||||
item.fillColor = createGradientObject(color1, color2, gradientType, item.bounds, pointerLocation);
|
||||
}
|
||||
item.fillColor = fillColor;
|
||||
}
|
||||
_getFillItem () {
|
||||
if (this.addedFillItem) {
|
||||
|
|
Loading…
Reference in a new issue