mirror of
https://github.com/scratchfoundation/scratch-paint.git
synced 2024-12-22 21:42:30 -05:00
Make style-path helpers generic over fill & stroke
This commit is contained in:
parent
dc430a0111
commit
1ecab99cfb
6 changed files with 215 additions and 120 deletions
|
@ -16,7 +16,7 @@ import {isBitmap} from '../lib/format';
|
|||
import GradientTypes from '../lib/gradient-types';
|
||||
|
||||
import FillColorIndicatorComponent from '../components/fill-color-indicator.jsx';
|
||||
import {applyFillColorToSelection,
|
||||
import {applyColorToSelection,
|
||||
applyGradientTypeToSelection,
|
||||
getRotatedColor,
|
||||
swapColorsInSelection,
|
||||
|
@ -45,11 +45,12 @@ class FillColorIndicator extends React.Component {
|
|||
}
|
||||
handleChangeFillColor (newColor) {
|
||||
// Apply color and update redux, but do not update svg until picker closes.
|
||||
const isDifferent = applyFillColorToSelection(
|
||||
const isDifferent = applyColorToSelection(
|
||||
newColor,
|
||||
this.props.colorIndex,
|
||||
this.props.gradientType === GradientTypes.SOLID,
|
||||
isBitmap(this.props.format),
|
||||
false, // applyToStroke
|
||||
this.props.textEditTarget);
|
||||
this._hasChanged = this._hasChanged || isDifferent;
|
||||
this.props.onChangeFillColor(newColor, this.props.colorIndex);
|
||||
|
@ -59,6 +60,7 @@ class FillColorIndicator extends React.Component {
|
|||
const isDifferent = applyGradientTypeToSelection(
|
||||
gradientType,
|
||||
isBitmap(this.props.format),
|
||||
false, // applyToStroke
|
||||
this.props.textEditTarget);
|
||||
this._hasChanged = this._hasChanged || isDifferent;
|
||||
const hasSelectedItems = getSelectedLeafItems().length > 0;
|
||||
|
@ -92,6 +94,7 @@ class FillColorIndicator extends React.Component {
|
|||
if (getSelectedLeafItems().length) {
|
||||
const isDifferent = swapColorsInSelection(
|
||||
isBitmap(this.props.format),
|
||||
false, // applyToStroke
|
||||
this.props.textEditTarget);
|
||||
this.props.setSelectedItems();
|
||||
this._hasChanged = this._hasChanged || isDifferent;
|
||||
|
|
|
@ -10,7 +10,7 @@ import Formats from '../lib/format';
|
|||
import {isBitmap} from '../lib/format';
|
||||
|
||||
import StrokeColorIndicatorComponent from '../components/stroke-color-indicator.jsx';
|
||||
import {applyStrokeColorToSelection, applyStrokeWidthToSelection} from '../helper/style-path';
|
||||
import {applyColorToSelection, applyStrokeWidthToSelection} from '../helper/style-path';
|
||||
|
||||
class StrokeColorIndicator extends React.Component {
|
||||
constructor (props) {
|
||||
|
@ -40,8 +40,13 @@ class StrokeColorIndicator extends React.Component {
|
|||
this.props.onChangeStrokeWidth(0);
|
||||
}
|
||||
// Apply color and update redux, but do not update svg until picker closes.
|
||||
this._hasChanged =
|
||||
applyStrokeColorToSelection(newColor, isBitmap(this.props.format), this.props.textEditTarget) ||
|
||||
this._hasChanged = applyColorToSelection(
|
||||
newColor,
|
||||
0, // colorIndex,
|
||||
true, // isSolidGradient
|
||||
isBitmap(this.props.format),
|
||||
true, // applyToStroke
|
||||
this.props.textEditTarget) ||
|
||||
this._hasChanged;
|
||||
this.props.onChangeStrokeColor(newColor);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import {changeStrokeColor} from '../reducers/stroke-color';
|
|||
import {changeStrokeWidth} from '../reducers/stroke-width';
|
||||
import StrokeWidthIndicatorComponent from '../components/stroke-width-indicator.jsx';
|
||||
import {getSelectedLeafItems} from '../helper/selection';
|
||||
import {applyStrokeColorToSelection, applyStrokeWidthToSelection, getColorsFromSelection, MIXED}
|
||||
import {applyColorToSelection, applyStrokeWidthToSelection, getColorsFromSelection, MIXED}
|
||||
from '../helper/style-path';
|
||||
import Modes from '../lib/modes';
|
||||
import Formats from '../lib/format';
|
||||
|
@ -25,7 +25,13 @@ class StrokeWidthIndicator extends React.Component {
|
|||
if ((!this.props.strokeWidth || this.props.strokeWidth === 0) && newWidth > 0) {
|
||||
let currentColor = getColorsFromSelection(getSelectedLeafItems(), isBitmap(this.props.format)).strokeColor;
|
||||
if (currentColor === null) {
|
||||
changed = applyStrokeColorToSelection('#000', isBitmap(this.props.format), this.props.textEditTarget) ||
|
||||
changed = applyColorToSelection(
|
||||
'#000',
|
||||
0, // colorIndex,
|
||||
true, // isSolidGradient
|
||||
isBitmap(this.props.format),
|
||||
true, // applyToStroke
|
||||
this.props.textEditTarget) ||
|
||||
changed;
|
||||
currentColor = '#000';
|
||||
} else if (currentColor !== MIXED) {
|
||||
|
|
|
@ -7,13 +7,13 @@ import GradientTypes from '../lib/gradient-types';
|
|||
import parseColor from 'parse-color';
|
||||
import {DEFAULT_COLOR} from '../reducers/fill-style';
|
||||
import {isCompoundPathChild} from '../helper/compound-path';
|
||||
import log from '../log/log';
|
||||
|
||||
const MIXED = 'scratch-paint/style-path/mixed';
|
||||
|
||||
// Check if the item color matches the incoming color. If the item color is a gradient, we assume
|
||||
// that the incoming color never matches, since we don't support gradients yet.
|
||||
const _colorMatch = function (itemColor, incomingColor) {
|
||||
// @todo colorMatch should not be called with gradients as arguments once stroke gradients are supported
|
||||
if (itemColor && itemColor.type === 'gradient') return false;
|
||||
// Either both are null or both are the same color when converted to CSS.
|
||||
return (!itemColor && !incomingColor) ||
|
||||
|
@ -62,7 +62,8 @@ const getRotatedColor = function (firstColor) {
|
|||
* @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
|
||||
* @param {?paper.Point} [radialCenter] Where the center of a radial gradient should be, if the gradient is radial.
|
||||
* Defaults to center of bounds.
|
||||
* @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) {
|
||||
|
@ -74,7 +75,7 @@ const createGradientObject = function (color1, color2, gradientType, bounds, rad
|
|||
color2 = getColorStringForTransparent(color1);
|
||||
}
|
||||
const halfLongestDimension = Math.max(bounds.width, bounds.height) / 2;
|
||||
const start = gradientType === GradientTypes.RADIAL ? radialCenter :
|
||||
const start = gradientType === GradientTypes.RADIAL ? (radialCenter || bounds.center) :
|
||||
gradientType === GradientTypes.VERTICAL ? bounds.topCenter :
|
||||
gradientType === GradientTypes.HORIZONTAL ? bounds.leftCenter :
|
||||
null;
|
||||
|
@ -93,17 +94,25 @@ const createGradientObject = function (color1, color2, gradientType, bounds, rad
|
|||
};
|
||||
|
||||
/**
|
||||
* Called when setting fill color
|
||||
* Called when setting an item's color
|
||||
* @param {string} colorString color, css format, or null if completely transparent
|
||||
* @param {number} colorIndex index of color being changed
|
||||
* @param {boolean} isSolidGradient True if is solid gradient. Sometimes the item has a gradient but the color
|
||||
* picker is set to a solid gradient. This happens when a mix of colors and gradient types is selected.
|
||||
* When changing the color in this case, the solid gradient should override the existing gradient on the item.
|
||||
* @param {?boolean} bitmapMode True if the fill color is being set in bitmap mode
|
||||
* @param {?boolean} bitmapMode True if the color is being set in bitmap mode
|
||||
* @param {?boolean} applyToStroke True if changing the selection's stroke, false if changing its fill.
|
||||
* @param {?string} textEditTargetId paper.Item.id of text editing target, if any
|
||||
* @return {boolean} Whether the color application actually changed visibly.
|
||||
*/
|
||||
const applyFillColorToSelection = function (colorString, colorIndex, isSolidGradient, bitmapMode, textEditTargetId) {
|
||||
const applyColorToSelection = function (
|
||||
colorString,
|
||||
colorIndex,
|
||||
isSolidGradient,
|
||||
bitmapMode,
|
||||
applyToStroke,
|
||||
textEditTargetId
|
||||
) {
|
||||
const items = _getColorStateListeners(textEditTargetId);
|
||||
let changed = false;
|
||||
for (let item of items) {
|
||||
|
@ -112,40 +121,51 @@ const applyFillColorToSelection = function (colorString, colorIndex, isSolidGrad
|
|||
}
|
||||
|
||||
// In bitmap mode, fill color applies to the stroke if there is a stroke
|
||||
if (bitmapMode && item.strokeColor !== null && item.strokeWidth) {
|
||||
if (
|
||||
bitmapMode &&
|
||||
!applyToStroke &&
|
||||
item.strokeColor !== null &&
|
||||
item.strokeWidth
|
||||
) {
|
||||
if (!_colorMatch(item.strokeColor, colorString)) {
|
||||
changed = true;
|
||||
item.strokeColor = colorString;
|
||||
}
|
||||
} else if (isSolidGradient || !item.fillColor || !item.fillColor.gradient ||
|
||||
!item.fillColor.gradient.stops.length === 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const itemColorProp = applyToStroke ? 'strokeColor' : 'fillColor';
|
||||
const itemColor = item[itemColorProp];
|
||||
|
||||
if (isSolidGradient || !itemColor || !itemColor.gradient ||
|
||||
!itemColor.gradient.stops.length === 2) {
|
||||
// Applying a solid color
|
||||
if (!_colorMatch(item.fillColor, colorString)) {
|
||||
if (!_colorMatch(itemColor, colorString)) {
|
||||
changed = true;
|
||||
if (isPointTextItem(item) && !colorString) {
|
||||
// Allows transparent text to be hit
|
||||
item.fillColor = 'rgba(0,0,0,0)';
|
||||
item[itemColorProp] = 'rgba(0,0,0,0)';
|
||||
} else {
|
||||
item.fillColor = colorString;
|
||||
item[itemColorProp] = colorString;
|
||||
}
|
||||
}
|
||||
} else if (!_colorMatch(item.fillColor.gradient.stops[colorIndex].color, colorString)) {
|
||||
} else if (!_colorMatch(itemColor.gradient.stops[colorIndex].color, colorString)) {
|
||||
// Changing one color of an existing gradient
|
||||
changed = true;
|
||||
const otherIndex = colorIndex === 0 ? 1 : 0;
|
||||
if (colorString === null) {
|
||||
colorString = getColorStringForTransparent(item.fillColor.gradient.stops[otherIndex].color.toCSS());
|
||||
colorString = getColorStringForTransparent(itemColor.gradient.stops[otherIndex].color.toCSS());
|
||||
}
|
||||
const colors = [0, 0];
|
||||
colors[colorIndex] = colorString;
|
||||
// If the other color is transparent, its RGB values need to be adjusted for the gradient to be smooth
|
||||
if (item.fillColor.gradient.stops[otherIndex].color.alpha === 0) {
|
||||
if (itemColor.gradient.stops[otherIndex].color.alpha === 0) {
|
||||
colors[otherIndex] = getColorStringForTransparent(colorString);
|
||||
} else {
|
||||
colors[otherIndex] = item.fillColor.gradient.stops[otherIndex].color.toCSS();
|
||||
colors[otherIndex] = itemColor.gradient.stops[otherIndex].color.toCSS();
|
||||
}
|
||||
// There seems to be a bug where setting colors on stops doesn't always update the view, so set gradient.
|
||||
item.fillColor.gradient = {stops: colors, radial: item.fillColor.gradient.radial};
|
||||
itemColor.gradient = {stops: colors, radial: itemColor.gradient.radial};
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
|
@ -154,10 +174,13 @@ const applyFillColorToSelection = function (colorString, colorIndex, isSolidGrad
|
|||
/**
|
||||
* Called to swap gradient colors
|
||||
* @param {?boolean} bitmapMode True if the fill color is being set in bitmap mode
|
||||
* @param {?boolean} applyToStroke True if changing the selection's stroke, false if changing its fill.
|
||||
* @param {?string} textEditTargetId paper.Item.id of text editing target, if any
|
||||
* @return {boolean} Whether the color application actually changed visibly.
|
||||
*/
|
||||
const swapColorsInSelection = function (bitmapMode, textEditTargetId) {
|
||||
const swapColorsInSelection = function (bitmapMode, applyToStroke, textEditTargetId) {
|
||||
if (bitmapMode) return; // @todo
|
||||
|
||||
const items = _getColorStateListeners(textEditTargetId);
|
||||
let changed = false;
|
||||
for (const item of items) {
|
||||
|
@ -166,21 +189,19 @@ const swapColorsInSelection = function (bitmapMode, textEditTargetId) {
|
|||
// that would leave us right where we started.
|
||||
if (isCompoundPathChild(item)) continue;
|
||||
|
||||
if (bitmapMode) {
|
||||
// @todo
|
||||
return;
|
||||
} else if (!item.fillColor || !item.fillColor.gradient || !item.fillColor.gradient.stops.length === 2) {
|
||||
const itemColor = applyToStroke ? item.strokeColor : item.fillColor;
|
||||
if (!itemColor || !itemColor.gradient || !itemColor.gradient.stops.length === 2) {
|
||||
// Only one color; nothing to swap
|
||||
continue;
|
||||
} else if (!item.fillColor.gradient.stops[0].color.equals(item.fillColor.gradient.stops[1].color)) {
|
||||
} else if (!itemColor.gradient.stops[0].color.equals(itemColor.gradient.stops[1].color)) {
|
||||
// Changing one color of an existing gradient
|
||||
changed = true;
|
||||
const colors = [
|
||||
item.fillColor.gradient.stops[1].color.toCSS(),
|
||||
item.fillColor.gradient.stops[0].color.toCSS()
|
||||
itemColor.gradient.stops[1].color.toCSS(),
|
||||
itemColor.gradient.stops[0].color.toCSS()
|
||||
];
|
||||
// There seems to be a bug where setting colors on stops doesn't always update the view, so set gradient.
|
||||
item.fillColor.gradient = {stops: colors, radial: item.fillColor.gradient.radial};
|
||||
itemColor.gradient = {stops: colors, radial: itemColor.gradient.radial};
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
|
@ -190,10 +211,11 @@ const swapColorsInSelection = function (bitmapMode, textEditTargetId) {
|
|||
* Called when setting gradient type
|
||||
* @param {GradientType} gradientType gradient type
|
||||
* @param {?boolean} bitmapMode True if the fill color is being set in bitmap mode
|
||||
* @param {boolean} applyToStroke True if changing the selection's stroke, false if changing its fill.
|
||||
* @param {?string} textEditTargetId paper.Item.id of text editing target, if any
|
||||
* @return {boolean} Whether the color application actually changed visibly.
|
||||
*/
|
||||
const applyGradientTypeToSelection = function (gradientType, bitmapMode, textEditTargetId) {
|
||||
const applyGradientTypeToSelection = function (gradientType, bitmapMode, applyToStroke, textEditTargetId) {
|
||||
const items = _getColorStateListeners(textEditTargetId);
|
||||
let changed = false;
|
||||
for (let item of items) {
|
||||
|
@ -201,40 +223,45 @@ const applyGradientTypeToSelection = function (gradientType, bitmapMode, textEdi
|
|||
item = item.parent;
|
||||
}
|
||||
|
||||
const itemColorProp = applyToStroke ? 'strokeColor' : 'fillColor';
|
||||
const itemColor = item[itemColorProp];
|
||||
|
||||
const hasGradient = itemColor && itemColor.gradient;
|
||||
|
||||
let itemColor1;
|
||||
if (item.fillColor === null || item.fillColor.alpha === 0) {
|
||||
if (itemColor === null || itemColor.alpha === 0) {
|
||||
// Transparent
|
||||
itemColor1 = null;
|
||||
} else if (!item.fillColor.gradient) {
|
||||
} else if (!hasGradient) {
|
||||
// Solid color
|
||||
itemColor1 = item.fillColor.toCSS();
|
||||
} else if (!item.fillColor.gradient.stops[0] || item.fillColor.gradient.stops[0].color.alpha === 0) {
|
||||
itemColor1 = itemColor.toCSS();
|
||||
} else if (!itemColor.gradient.stops[0] || itemColor.gradient.stops[0].color.alpha === 0) {
|
||||
// Gradient where first color is transparent
|
||||
itemColor1 = null;
|
||||
} else {
|
||||
// Gradient where first color is not transparent
|
||||
itemColor1 = item.fillColor.gradient.stops[0].color.toCSS();
|
||||
itemColor1 = itemColor.gradient.stops[0].color.toCSS();
|
||||
}
|
||||
|
||||
let itemColor2;
|
||||
if (!item.fillColor || !item.fillColor.gradient || !item.fillColor.gradient.stops[1]) {
|
||||
if (!hasGradient || !itemColor.gradient.stops[1]) {
|
||||
// If item color is solid or a gradient that has no 2nd color, set the 2nd color based on the first color
|
||||
itemColor2 = getRotatedColor(itemColor1);
|
||||
} else if (item.fillColor.gradient.stops[1].color.alpha === 0) {
|
||||
} else if (itemColor.gradient.stops[1].color.alpha === 0) {
|
||||
// Gradient has 2nd color which is transparent
|
||||
itemColor2 = null;
|
||||
} else {
|
||||
// Gradient has 2nd color which is not transparent
|
||||
itemColor2 = item.fillColor.gradient.stops[1].color.toCSS();
|
||||
itemColor2 = itemColor.gradient.stops[1].color.toCSS();
|
||||
}
|
||||
|
||||
if (bitmapMode) {
|
||||
// @todo Add when we apply gradients to selections in bitmap mode
|
||||
continue;
|
||||
} else if (gradientType === GradientTypes.SOLID) {
|
||||
if (item.fillColor && item.fillColor.gradient) {
|
||||
if (itemColor && itemColor.gradient) {
|
||||
changed = true;
|
||||
item.fillColor = itemColor1;
|
||||
item[itemColorProp] = itemColor1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
@ -245,12 +272,15 @@ const applyGradientTypeToSelection = function (gradientType, bitmapMode, textEdi
|
|||
if (itemColor2 === null) {
|
||||
itemColor2 = getColorStringForTransparent(itemColor1);
|
||||
}
|
||||
if (gradientType === GradientTypes.RADIAL) {
|
||||
const hasRadialGradient = item.fillColor && item.fillColor.gradient && item.fillColor.gradient.radial;
|
||||
|
||||
switch (gradientType) {
|
||||
case GradientTypes.RADIAL: {
|
||||
const hasRadialGradient = hasGradient && itemColor.gradient.radial;
|
||||
if (!hasRadialGradient) {
|
||||
changed = true;
|
||||
const halfLongestDimension = Math.max(item.bounds.width, item.bounds.height) / 2;
|
||||
item.fillColor = {
|
||||
|
||||
item[itemColorProp] = {
|
||||
gradient: {
|
||||
stops: [itemColor1, itemColor2],
|
||||
radial: true
|
||||
|
@ -259,13 +289,15 @@ const applyGradientTypeToSelection = function (gradientType, bitmapMode, textEdi
|
|||
destination: item.position.add(new paper.Point(halfLongestDimension, 0))
|
||||
};
|
||||
}
|
||||
} else if (gradientType === GradientTypes.HORIZONTAL) {
|
||||
const hasHorizontalGradient = item.fillColor && item.fillColor.gradient &&
|
||||
!item.fillColor.gradient.radial &&
|
||||
Math.abs(item.fillColor.origin.y - item.fillColor.destination.y) < 1e-8;
|
||||
break;
|
||||
}
|
||||
case GradientTypes.HORIZONTAL: {
|
||||
const hasHorizontalGradient = hasGradient && !itemColor.gradient.radial &&
|
||||
Math.abs(itemColor.origin.y - itemColor.destination.y) < 1e-8;
|
||||
if (!hasHorizontalGradient) {
|
||||
changed = true;
|
||||
item.fillColor = {
|
||||
|
||||
item[itemColorProp] = {
|
||||
gradient: {
|
||||
stops: [itemColor1, itemColor2]
|
||||
},
|
||||
|
@ -273,12 +305,15 @@ const applyGradientTypeToSelection = function (gradientType, bitmapMode, textEdi
|
|||
destination: item.bounds.rightCenter
|
||||
};
|
||||
}
|
||||
} else if (gradientType === GradientTypes.VERTICAL) {
|
||||
const hasVerticalGradient = item.fillColor && item.fillColor.gradient && !item.fillColor.gradient.radial &&
|
||||
Math.abs(item.fillColor.origin.x - item.fillColor.destination.x) < 1e-8;
|
||||
break;
|
||||
}
|
||||
case GradientTypes.VERTICAL: {
|
||||
const hasVerticalGradient = hasGradient && !itemColor.gradient.radial &&
|
||||
Math.abs(itemColor.origin.x - itemColor.destination.x) < 1e-8;
|
||||
if (!hasVerticalGradient) {
|
||||
changed = true;
|
||||
item.fillColor = {
|
||||
|
||||
item[itemColorProp] = {
|
||||
gradient: {
|
||||
stops: [itemColor1, itemColor2]
|
||||
},
|
||||
|
@ -286,31 +321,8 @@ const applyGradientTypeToSelection = function (gradientType, bitmapMode, textEdi
|
|||
destination: item.bounds.bottomCenter
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when setting stroke color
|
||||
* @param {string} colorString New color, css format
|
||||
* @param {?boolean} bitmapMode True if the stroke color is being set in bitmap mode
|
||||
* @param {?string} textEditTargetId paper.Item.id of text editing target, if any
|
||||
* @return {boolean} Whether the color application actually changed visibly.
|
||||
*/
|
||||
const applyStrokeColorToSelection = function (colorString, bitmapMode, textEditTargetId) {
|
||||
// Bitmap mode doesn't have stroke color
|
||||
if (bitmapMode) return false;
|
||||
|
||||
const items = _getColorStateListeners(textEditTargetId);
|
||||
let changed = false;
|
||||
for (let item of items) {
|
||||
if (item.parent instanceof paper.CompoundPath) {
|
||||
item = item.parent;
|
||||
}
|
||||
if (!_colorMatch(item.strokeColor, colorString)) {
|
||||
changed = true;
|
||||
item.strokeColor = colorString;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
|
@ -339,6 +351,33 @@ const applyStrokeWidthToSelection = function (value, textEditTargetId) {
|
|||
return changed;
|
||||
};
|
||||
|
||||
const _colorStateFromGradient = gradient => {
|
||||
const colorState = {};
|
||||
// Scratch only recognizes 2 color gradients
|
||||
if (gradient.stops.length === 2) {
|
||||
if (gradient.radial) {
|
||||
colorState.gradientType = GradientTypes.RADIAL;
|
||||
} else {
|
||||
// Always use horizontal for linear gradients, since horizontal and vertical gradients
|
||||
// are the same with rotation. We don't want to show MIXED just because anything is rotated.
|
||||
colorState.gradientType = GradientTypes.HORIZONTAL;
|
||||
}
|
||||
colorState.primary = gradient.stops[0].color.alpha === 0 ?
|
||||
null :
|
||||
gradient.stops[0].color.toCSS();
|
||||
colorState.secondary = gradient.stops[1].color.alpha === 0 ?
|
||||
null :
|
||||
gradient.stops[1].color.toCSS();
|
||||
} else {
|
||||
if (gradient.stops.length < 2) log.warn(`Gradient has ${gradient.stops.length} stop(s)`);
|
||||
|
||||
colorState.primary = MIXED;
|
||||
colorState.secondary = MIXED;
|
||||
}
|
||||
|
||||
return colorState;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get state of colors and stroke width for selection
|
||||
* @param {!Array<paper.Item>} selectedItems Selected paper items
|
||||
|
@ -349,12 +388,15 @@ const applyStrokeWidthToSelection = function (value, textEditTargetId) {
|
|||
* Thickness is line thickness, used in the bitmap editor
|
||||
*/
|
||||
const getColorsFromSelection = function (selectedItems, bitmapMode) {
|
||||
// TODO: DRY out this code
|
||||
let selectionFillColorString;
|
||||
let selectionFillColor2String;
|
||||
let selectionStrokeColorString;
|
||||
let selectionStrokeColor2String;
|
||||
let selectionStrokeWidth;
|
||||
let selectionThickness;
|
||||
let selectionGradientType;
|
||||
let selectionFillGradientType;
|
||||
let selectionStrokeGradientType;
|
||||
let firstChild = true;
|
||||
|
||||
for (let item of selectedItems) {
|
||||
|
@ -365,7 +407,9 @@ const getColorsFromSelection = function (selectedItems, bitmapMode) {
|
|||
let itemFillColorString;
|
||||
let itemFillColor2String;
|
||||
let itemStrokeColorString;
|
||||
let itemGradientType = GradientTypes.SOLID;
|
||||
let itemStrokeColor2String;
|
||||
let itemFillGradientType = GradientTypes.SOLID;
|
||||
let itemStrokeGradientType = GradientTypes.SOLID;
|
||||
|
||||
if (!isGroup(item)) {
|
||||
if (item.fillColor) {
|
||||
|
@ -373,25 +417,10 @@ const getColorsFromSelection = function (selectedItems, bitmapMode) {
|
|||
if (isPointTextItem(item) && item.fillColor.alpha === 0) {
|
||||
itemFillColorString = null;
|
||||
} else if (item.fillColor.type === 'gradient') {
|
||||
// Scratch only recognizes 2 color gradients
|
||||
if (item.fillColor.gradient.stops.length === 2) {
|
||||
if (item.fillColor.gradient.radial) {
|
||||
itemGradientType = GradientTypes.RADIAL;
|
||||
} else {
|
||||
// Always use horizontal for linear gradients, since horizontal and vertical gradients
|
||||
// are the same with rotation. We don't want to show MIXED just because anything is rotated.
|
||||
itemGradientType = GradientTypes.HORIZONTAL;
|
||||
}
|
||||
itemFillColorString = item.fillColor.gradient.stops[0].color.alpha === 0 ?
|
||||
null :
|
||||
item.fillColor.gradient.stops[0].color.toCSS();
|
||||
itemFillColor2String = item.fillColor.gradient.stops[1].color.alpha === 0 ?
|
||||
null :
|
||||
item.fillColor.gradient.stops[1].color.toCSS();
|
||||
} else {
|
||||
itemFillColorString = MIXED;
|
||||
itemFillColor2String = MIXED;
|
||||
}
|
||||
const {primary, secondary, gradientType} = _colorStateFromGradient(item.fillColor.gradient);
|
||||
itemFillColorString = primary;
|
||||
itemFillColor2String = secondary;
|
||||
itemFillGradientType = gradientType;
|
||||
} else {
|
||||
itemFillColorString = item.fillColor.alpha === 0 ?
|
||||
null :
|
||||
|
@ -399,15 +428,34 @@ const getColorsFromSelection = function (selectedItems, bitmapMode) {
|
|||
}
|
||||
}
|
||||
if (item.strokeColor) {
|
||||
// Stroke color is fill color in bitmap
|
||||
if (bitmapMode) {
|
||||
itemFillColorString = item.strokeColor.toCSS();
|
||||
} else if (item.strokeColor.type === 'gradient') {
|
||||
itemStrokeColorString = MIXED;
|
||||
|
||||
if (item.strokeColor.type === 'gradient') {
|
||||
const {primary, secondary, gradientType} = _colorStateFromGradient(item.strokeColor.gradient);
|
||||
const strokeColorString = primary;
|
||||
const strokeColor2String = secondary;
|
||||
const strokeGradientType = gradientType;
|
||||
|
||||
// Stroke color is fill color in bitmap
|
||||
if (bitmapMode) {
|
||||
itemFillColorString = strokeColorString;
|
||||
itemFillColor2String = strokeColor2String;
|
||||
itemFillGradientType = strokeGradientType;
|
||||
} else {
|
||||
itemStrokeColorString = strokeColorString;
|
||||
itemStrokeColor2String = strokeColor2String;
|
||||
itemStrokeGradientType = strokeGradientType;
|
||||
}
|
||||
} else {
|
||||
itemStrokeColorString = item.strokeColor.alpha === 0 || !item.strokeWidth ?
|
||||
const strokeColorString = item.strokeColor.alpha === 0 || !item.strokeWidth ?
|
||||
null :
|
||||
item.strokeColor.toCSS();
|
||||
|
||||
// Stroke color is fill color in bitmap
|
||||
if (bitmapMode) {
|
||||
itemFillColorString = strokeColorString;
|
||||
} else {
|
||||
itemStrokeColorString = strokeColorString;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
itemStrokeColorString = null;
|
||||
|
@ -418,7 +466,9 @@ const getColorsFromSelection = function (selectedItems, bitmapMode) {
|
|||
selectionFillColorString = itemFillColorString;
|
||||
selectionFillColor2String = itemFillColor2String;
|
||||
selectionStrokeColorString = itemStrokeColorString;
|
||||
selectionGradientType = itemGradientType;
|
||||
selectionStrokeColor2String = itemStrokeColor2String;
|
||||
selectionFillGradientType = itemFillGradientType;
|
||||
selectionStrokeGradientType = itemStrokeGradientType;
|
||||
selectionStrokeWidth = itemStrokeColorString ? item.strokeWidth : 0;
|
||||
if (item.strokeWidth && item.data && item.data.zoomLevel) {
|
||||
selectionThickness = item.strokeWidth / item.data.zoomLevel;
|
||||
|
@ -430,14 +480,22 @@ const getColorsFromSelection = function (selectedItems, bitmapMode) {
|
|||
if (itemFillColor2String !== selectionFillColor2String) {
|
||||
selectionFillColor2String = MIXED;
|
||||
}
|
||||
if (itemGradientType !== selectionGradientType) {
|
||||
selectionGradientType = GradientTypes.SOLID;
|
||||
if (itemFillGradientType !== selectionFillGradientType) {
|
||||
selectionFillGradientType = GradientTypes.SOLID;
|
||||
selectionFillColorString = MIXED;
|
||||
selectionFillColor2String = MIXED;
|
||||
}
|
||||
if (itemStrokeGradientType !== selectionStrokeGradientType) {
|
||||
selectionStrokeGradientType = GradientTypes.SOLID;
|
||||
selectionStrokeColorString = MIXED;
|
||||
selectionStrokeColor2String = MIXED;
|
||||
}
|
||||
if (itemStrokeColorString !== selectionStrokeColorString) {
|
||||
selectionStrokeColorString = MIXED;
|
||||
}
|
||||
if (itemStrokeColor2String !== selectionStrokeColor2String) {
|
||||
selectionStrokeColor2String = MIXED;
|
||||
}
|
||||
const itemStrokeWidth = itemStrokeColorString ? item.strokeWidth : 0;
|
||||
if (selectionStrokeWidth !== itemStrokeWidth) {
|
||||
selectionStrokeWidth = null;
|
||||
|
@ -445,27 +503,50 @@ const getColorsFromSelection = function (selectedItems, bitmapMode) {
|
|||
}
|
||||
}
|
||||
// Convert selection gradient type from horizontal to vertical if first item is exactly vertical
|
||||
if (selectedItems && selectedItems.length && selectionGradientType !== GradientTypes.SOLID) {
|
||||
// This is because up to this point, we assume all non-radial gradients are horizontal
|
||||
// Otherwise, if there were a mix of horizontal/vertical gradient types in the selection, they would show as MIXED
|
||||
// whereas we want them to show as horizontal (or vertical if the first item is vertical)
|
||||
if (selectedItems && selectedItems.length) {
|
||||
let firstItem = selectedItems[0];
|
||||
if (firstItem.parent instanceof paper.CompoundPath) firstItem = firstItem.parent;
|
||||
const direction = firstItem.fillColor.destination.subtract(firstItem.fillColor.origin);
|
||||
if (Math.abs(direction.angle) === 90) {
|
||||
selectionGradientType = GradientTypes.VERTICAL;
|
||||
|
||||
if (selectionFillGradientType !== GradientTypes.SOLID) {
|
||||
// Stroke color is fill color in bitmap if fill color is missing
|
||||
// TODO: this whole "treat horizontal/vertical gradients specially" logic is janky; refactor at some point
|
||||
const firstItemColor = (bitmapMode && firstItem.strokeColor) ? firstItem.strokeColor : firstItem.fillColor;
|
||||
const direction = firstItemColor.destination.subtract(firstItemColor.origin);
|
||||
if (Math.abs(direction.angle) === 90) {
|
||||
selectionFillGradientType = GradientTypes.VERTICAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectionStrokeGradientType !== GradientTypes.SOLID) {
|
||||
const direction = firstItem.strokeColor.destination.subtract(firstItem.strokeColor.origin);
|
||||
if (Math.abs(direction.angle) === 90) {
|
||||
selectionStrokeGradientType = GradientTypes.VERTICAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bitmapMode) {
|
||||
return {
|
||||
fillColor: selectionFillColorString ? selectionFillColorString : null,
|
||||
fillColor2: selectionFillColor2String ? selectionFillColor2String : null,
|
||||
gradientType: selectionGradientType,
|
||||
fillGradientType: selectionFillGradientType,
|
||||
thickness: selectionThickness
|
||||
};
|
||||
}
|
||||
|
||||
// Treat stroke gradients as MIXED
|
||||
// TODO: remove this once stroke gradients are supported
|
||||
if (selectionStrokeGradientType !== GradientTypes.SOLID) selectionStrokeColorString = MIXED;
|
||||
|
||||
return {
|
||||
fillColor: selectionFillColorString ? selectionFillColorString : null,
|
||||
fillColor2: selectionFillColor2String ? selectionFillColor2String : null,
|
||||
gradientType: selectionGradientType,
|
||||
fillGradientType: selectionFillGradientType,
|
||||
strokeColor: selectionStrokeColorString ? selectionStrokeColorString : null,
|
||||
strokeColor2: selectionStrokeColor2String ? selectionStrokeColor2String : null,
|
||||
strokeGradientType: selectionStrokeGradientType,
|
||||
strokeWidth: selectionStrokeWidth || (selectionStrokeWidth === null) ? selectionStrokeWidth : 0
|
||||
};
|
||||
};
|
||||
|
@ -510,9 +591,8 @@ const styleShape = function (path, options) {
|
|||
};
|
||||
|
||||
export {
|
||||
applyFillColorToSelection,
|
||||
applyColorToSelection,
|
||||
applyGradientTypeToSelection,
|
||||
applyStrokeColorToSelection,
|
||||
applyStrokeWidthToSelection,
|
||||
createGradientObject,
|
||||
getColorsFromSelection,
|
||||
|
|
|
@ -241,7 +241,8 @@ class TextTool extends paper.Tool {
|
|||
content: '',
|
||||
font: this.font,
|
||||
fontSize: 40,
|
||||
// TODO: style using gradient?
|
||||
// TODO: style using gradient
|
||||
// https://github.com/LLK/scratch-paint/issues/1164
|
||||
fillColor: this.colorState.fillColor.primary,
|
||||
// Default leading for both the HTML text area and paper.PointText
|
||||
// is 120%, but for some reason they are slightly off from each other.
|
||||
|
|
|
@ -14,7 +14,7 @@ const reducer = makeColorStyleReducer({
|
|||
defaultColor: DEFAULT_COLOR,
|
||||
selectionPrimaryColorKey: 'fillColor',
|
||||
selectionSecondaryColorKey: 'fillColor2',
|
||||
selectionGradientTypeKey: 'gradientType'
|
||||
selectionGradientTypeKey: 'fillGradientType'
|
||||
});
|
||||
|
||||
// Action creators ==================================
|
||||
|
|
Loading…
Reference in a new issue