scratch-paint 0.2.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.circleci/config.yml +113 -0
- package/.editorconfig +1 -1
- package/.gitattributes +1 -0
- package/.husky/.gitattributes +1 -0
- package/.husky/commit-msg +4 -0
- package/CHANGELOG.md +81 -0
- package/commitlint.config.js +4 -0
- package/dist/scratch-paint.js +2650 -4086
- package/dist/scratch-paint.js.map +1 -1
- package/pack/scratch-paint-1.0.2.tgz +0 -0
- package/package.json +62 -42
- package/release.config.js +13 -0
- package/renovate.json5 +7 -0
- package/src/.eslintrc.js +3 -1
- package/src/components/color-picker/color-picker.jsx +11 -1
- package/src/components/fixed-tools/fixed-tools.jsx +1 -2
- package/src/components/forms/slider.jsx +21 -4
- package/src/components/mode-tools/mode-tools.jsx +1 -2
- package/src/components/paint-editor/paint-editor.jsx +1 -2
- package/src/containers/color-indicator.jsx +1 -2
- package/src/containers/fixed-tools.jsx +1 -2
- package/src/containers/line-mode.jsx +1 -2
- package/src/containers/mode-tools.jsx +1 -2
- package/src/containers/paint-editor.jsx +4 -5
- package/src/containers/paper-canvas.jsx +5 -2
- package/src/containers/stroke-color-indicator.jsx +1 -2
- package/src/containers/stroke-width-indicator.jsx +4 -4
- package/src/helper/bit-tools/brush-tool.js +1 -2
- package/src/helper/bit-tools/line-tool.js +1 -2
- package/src/helper/bitmap.js +7 -3
- package/src/helper/selection-tools/move-tool.js +6 -6
- package/src/helper/style-path.js +2 -4
- package/src/helper/tools/fill-tool.js +11 -1
- package/src/helper/undo.js +1 -2
- package/src/hocs/copy-paste-hoc.jsx +1 -2
- package/src/hocs/keyboard-shortcuts-hoc.jsx +1 -2
- package/src/hocs/undo-hoc.jsx +4 -3
- package/src/hocs/update-image-hoc.jsx +6 -6
- package/src/playground/playground.jsx +3 -0
- package/webpack.config.js +1 -3
- package/.travis.yml +0 -80
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "scratch-paint",
|
|
3
|
-
"version": "0.2
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Graphical User Interface for the Scratch 3.0 paint editor, which is used to make and edit sprites for use in projects.",
|
|
5
5
|
"main": "./dist/scratch-paint.js",
|
|
6
6
|
"browser": "./src/index.js",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"build": "npm run clean && webpack --progress --colors --bail",
|
|
9
9
|
"clean": "rimraf ./dist && mkdirp dist && rimraf ./playground && mkdirp playground",
|
|
10
|
-
"deploy": "touch playground/.nojekyll && gh-pages -t -d playground -m \"Build for $(git log --pretty=format:%H -n1)\"",
|
|
10
|
+
"deploy": "touch playground/.nojekyll && gh-pages -t -d playground -m \"[skip ci] Build for $(git log --pretty=format:%H -n1)\"",
|
|
11
11
|
"i18n:push": "tx-push-src scratch-editor paint-editor ./translations/en.json",
|
|
12
12
|
"i18n:src": "rimraf ./translations/messages && babel src > tmp.js && rimraf tmp.js && ./scripts/build-i18n-source.js ./translations/messages/ ./translations/",
|
|
13
13
|
"lint": "eslint . --ext .js,.jsx",
|
|
14
|
+
"prepare": "husky install",
|
|
14
15
|
"start": "webpack-dev-server",
|
|
15
16
|
"test": "npm run lint && npm run unit && NODE_ENV=production npm run build",
|
|
16
|
-
"unit": "jest",
|
|
17
|
+
"unit": "jest --reporters=default",
|
|
17
18
|
"watch": "webpack --progress --colors --watch"
|
|
18
19
|
},
|
|
19
20
|
"author": "Massachusetts Institute of Technology",
|
|
@@ -21,7 +22,7 @@
|
|
|
21
22
|
"homepage": "https://github.com/LLK/scratch-paint#readme",
|
|
22
23
|
"repository": {
|
|
23
24
|
"type": "git",
|
|
24
|
-
"url": "
|
|
25
|
+
"url": "https://github.com/LLK/scratch-paint.git"
|
|
25
26
|
},
|
|
26
27
|
"dependencies": {
|
|
27
28
|
"@scratch/paper": "0.11.20200728195508",
|
|
@@ -31,8 +32,7 @@
|
|
|
31
32
|
"lodash.omit": "4.5.0",
|
|
32
33
|
"minilog": "3.1.0",
|
|
33
34
|
"parse-color": "1.0.0",
|
|
34
|
-
"prop-types": "^15.5.10"
|
|
35
|
-
"scratch-svg-renderer": "0.2.0-prerelease.20200610220938"
|
|
35
|
+
"prop-types": "^15.5.10"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"react": "^16",
|
|
@@ -44,37 +44,42 @@
|
|
|
44
44
|
"react-responsive": "^4",
|
|
45
45
|
"react-style-proptype": "^3",
|
|
46
46
|
"react-tooltip": "^3",
|
|
47
|
-
"redux": "^3"
|
|
47
|
+
"redux": "^3",
|
|
48
|
+
"scratch-render-fonts": "^1.0.0-prerelease.20210401210003"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
51
|
+
"@commitlint/cli": "17.0.3",
|
|
52
|
+
"@commitlint/config-conventional": "17.0.3",
|
|
50
53
|
"autoprefixer": "9.7.4",
|
|
51
54
|
"babel-cli": "6.26.0",
|
|
52
|
-
"babel-core": "
|
|
53
|
-
"babel-eslint": "
|
|
54
|
-
"babel-jest": "
|
|
55
|
-
"babel-loader": "
|
|
55
|
+
"babel-core": "6.26.3",
|
|
56
|
+
"babel-eslint": "10.1.0",
|
|
57
|
+
"babel-jest": "23.6.0",
|
|
58
|
+
"babel-loader": "7.1.5",
|
|
56
59
|
"babel-plugin-react-intl": "3.0.1",
|
|
57
|
-
"babel-plugin-transform-object-rest-spread": "
|
|
58
|
-
"babel-preset-env": "
|
|
59
|
-
"babel-preset-react": "
|
|
60
|
+
"babel-plugin-transform-object-rest-spread": "6.26.0",
|
|
61
|
+
"babel-preset-env": "1.7.0",
|
|
62
|
+
"babel-preset-react": "6.24.1",
|
|
60
63
|
"css-loader": "3.4.0",
|
|
61
|
-
"enzyme": "
|
|
62
|
-
"enzyme-adapter-react-16": "
|
|
63
|
-
"eslint": "
|
|
64
|
-
"eslint-config-
|
|
65
|
-
"eslint-
|
|
66
|
-
"eslint-plugin-import": "^2.18.2",
|
|
64
|
+
"enzyme": "3.11.0",
|
|
65
|
+
"enzyme-adapter-react-16": "1.15.6",
|
|
66
|
+
"eslint": "7.32.0",
|
|
67
|
+
"eslint-config-scratch": "7.0.0",
|
|
68
|
+
"eslint-plugin-import": "2.26.0",
|
|
67
69
|
"eslint-plugin-react": "7.20.3",
|
|
68
|
-
"gh-pages": "
|
|
70
|
+
"gh-pages": "3.2.3",
|
|
69
71
|
"html-webpack-plugin": "3.2.0",
|
|
70
|
-
"
|
|
71
|
-
"jest
|
|
72
|
+
"husky": "8.0.1",
|
|
73
|
+
"jest": "22.4.4",
|
|
74
|
+
"jest-canvas-mock": "2.3.1",
|
|
75
|
+
"jest-junit": "13.0.0",
|
|
76
|
+
"json": "9.0.6",
|
|
72
77
|
"lodash.defaultsdeep": "4.6.1",
|
|
73
|
-
"mkdirp": "
|
|
74
|
-
"postcss-import": "
|
|
75
|
-
"postcss-loader": "
|
|
76
|
-
"postcss-simple-vars": "
|
|
77
|
-
"raf": "
|
|
78
|
+
"mkdirp": "1.0.4",
|
|
79
|
+
"postcss-import": "12.0.1",
|
|
80
|
+
"postcss-loader": "3.0.0",
|
|
81
|
+
"postcss-simple-vars": "5.0.2",
|
|
82
|
+
"raf": "3.4.1",
|
|
78
83
|
"react": "16.2.0",
|
|
79
84
|
"react-dom": "16.4.0",
|
|
80
85
|
"react-intl": "2.9.0",
|
|
@@ -83,22 +88,29 @@
|
|
|
83
88
|
"react-redux": "5.1.0",
|
|
84
89
|
"react-responsive": "4.1.0",
|
|
85
90
|
"react-style-proptype": "3.2.2",
|
|
86
|
-
"react-test-renderer": "
|
|
91
|
+
"react-test-renderer": "16.5.1",
|
|
87
92
|
"react-tooltip": "3.8.4",
|
|
88
93
|
"redux": "3.7.2",
|
|
89
|
-
"redux-mock-store": "
|
|
94
|
+
"redux-mock-store": "1.5.4",
|
|
90
95
|
"redux-throttle": "0.1.1",
|
|
91
|
-
"regenerator-runtime": "
|
|
92
|
-
"rimraf": "
|
|
93
|
-
"scratch-l10n": "3.
|
|
94
|
-
"
|
|
95
|
-
"
|
|
96
|
-
"
|
|
97
|
-
"
|
|
98
|
-
"
|
|
99
|
-
"webpack": "
|
|
100
|
-
"
|
|
101
|
-
"webpack
|
|
96
|
+
"regenerator-runtime": "0.13.9",
|
|
97
|
+
"rimraf": "2.7.1",
|
|
98
|
+
"scratch-l10n": "3.14.20220826031556",
|
|
99
|
+
"scratch-render-fonts": "1.0.0-prerelease.20210401210003",
|
|
100
|
+
"scratch-semantic-release-config": "1.0.4",
|
|
101
|
+
"style-loader": "1.3.0",
|
|
102
|
+
"svg-url-loader": "3.0.3",
|
|
103
|
+
"tap": "14.11.0",
|
|
104
|
+
"uglifyjs-webpack-plugin": "2.2.0",
|
|
105
|
+
"url-loader": "2.3.0",
|
|
106
|
+
"webpack": "4.46.0",
|
|
107
|
+
"webpack-cli": "3.3.12",
|
|
108
|
+
"webpack-dev-server": "3.11.2"
|
|
109
|
+
},
|
|
110
|
+
"config": {
|
|
111
|
+
"commitizen": {
|
|
112
|
+
"path": "cz-conventional-changelog"
|
|
113
|
+
}
|
|
102
114
|
},
|
|
103
115
|
"jest": {
|
|
104
116
|
"setupFiles": [
|
|
@@ -111,5 +123,13 @@
|
|
|
111
123
|
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/test/__mocks__/fileMock.js",
|
|
112
124
|
"\\.(css|less)$": "<rootDir>/test/__mocks__/styleMock.js"
|
|
113
125
|
}
|
|
114
|
-
}
|
|
126
|
+
},
|
|
127
|
+
"jest-junit": {
|
|
128
|
+
"outputDirectory": "./test/results"
|
|
129
|
+
},
|
|
130
|
+
"browserslist": [
|
|
131
|
+
"last 3 versions",
|
|
132
|
+
"Safari >= 8",
|
|
133
|
+
"iOS >= 8"
|
|
134
|
+
]
|
|
115
135
|
}
|
package/renovate.json5
ADDED
package/src/.eslintrc.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
/* eslint-disable import/no-commonjs */
|
|
1
2
|
module.exports = {
|
|
3
|
+
/* eslint-enable import/no-commonjs */
|
|
2
4
|
root: true,
|
|
3
|
-
extends: ['scratch', 'scratch/es6', 'scratch/react', 'import'],
|
|
5
|
+
extends: ['scratch', 'scratch/es6', 'scratch/react', 'plugin:import/recommended'],
|
|
4
6
|
env: {
|
|
5
7
|
browser: true
|
|
6
8
|
},
|
|
@@ -5,7 +5,7 @@ import {defineMessages, FormattedMessage, injectIntl, intlShape} from 'react-int
|
|
|
5
5
|
import classNames from 'classnames';
|
|
6
6
|
import parseColor from 'parse-color';
|
|
7
7
|
|
|
8
|
-
import Slider from '../forms/slider.jsx';
|
|
8
|
+
import Slider, {CONTAINER_WIDTH, HANDLE_WIDTH} from '../forms/slider.jsx';
|
|
9
9
|
import LabeledIconButton from '../labeled-icon-button/labeled-icon-button.jsx';
|
|
10
10
|
import styles from './color-picker.css';
|
|
11
11
|
import GradientTypes from '../../lib/gradient-types';
|
|
@@ -53,6 +53,16 @@ class ColorPickerComponent extends React.Component {
|
|
|
53
53
|
throw new Error(`Unknown channel for color sliders: ${channel}`);
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
+
|
|
57
|
+
// The sliders are a rounded capsule shape, and the slider handles are circles. As a consequence, when the
|
|
58
|
+
// slider handle is fully to one side, its center is actually moved away from the start/end of the slider by
|
|
59
|
+
// the slider handle's radius, meaning that the effective range of the slider excludes the rounded caps.
|
|
60
|
+
// To compensate for this, position the first stop to where the rounded cap ends, and position the last stop
|
|
61
|
+
// to where the rounded cap begins.
|
|
62
|
+
const halfHandleWidth = HANDLE_WIDTH / 2;
|
|
63
|
+
stops[0] += ` 0 ${halfHandleWidth}px`;
|
|
64
|
+
stops[stops.length - 1] += ` ${CONTAINER_WIDTH - halfHandleWidth}px 100%`;
|
|
65
|
+
|
|
56
66
|
return `linear-gradient(to left, ${stops.join(',')})`;
|
|
57
67
|
}
|
|
58
68
|
render () {
|
|
@@ -12,12 +12,11 @@ import Button from '../button/button.jsx';
|
|
|
12
12
|
import ButtonGroup from '../button-group/button-group.jsx';
|
|
13
13
|
import Dropdown from '../dropdown/dropdown.jsx';
|
|
14
14
|
import {defineMessages, injectIntl, intlShape} from 'react-intl';
|
|
15
|
-
import Formats from '../../lib/format';
|
|
15
|
+
import Formats, {isVector} from '../../lib/format';
|
|
16
16
|
import Input from '../forms/input.jsx';
|
|
17
17
|
import InputGroup from '../input-group/input-group.jsx';
|
|
18
18
|
import Label from '../forms/label.jsx';
|
|
19
19
|
import LabeledIconButton from '../labeled-icon-button/labeled-icon-button.jsx';
|
|
20
|
-
import {isVector} from '../../lib/format';
|
|
21
20
|
import layout from '../../lib/layout-constants';
|
|
22
21
|
import {hideLabel} from '../../lib/hide-label';
|
|
23
22
|
import styles from './fixed-tools.css';
|
|
@@ -17,15 +17,21 @@ class SliderComponent extends React.Component {
|
|
|
17
17
|
'handleMouseUp',
|
|
18
18
|
'handleMouseMove',
|
|
19
19
|
'handleClickBackground',
|
|
20
|
-
'setBackground'
|
|
20
|
+
'setBackground',
|
|
21
|
+
'setHandle'
|
|
21
22
|
]);
|
|
23
|
+
|
|
24
|
+
// Distance from the left edge of the slider handle to the mouse down/click event
|
|
25
|
+
this.handleClickOffset = 0;
|
|
22
26
|
}
|
|
23
27
|
|
|
24
|
-
handleMouseDown () {
|
|
28
|
+
handleMouseDown (event) {
|
|
25
29
|
document.addEventListener('mousemove', this.handleMouseMove);
|
|
26
30
|
document.addEventListener('mouseup', this.handleMouseUp);
|
|
27
31
|
document.addEventListener('touchmove', this.handleMouseMove, {passive: false});
|
|
28
32
|
document.addEventListener('touchend', this.handleMouseUp);
|
|
33
|
+
|
|
34
|
+
this.handleClickOffset = getEventXY(event).x - this.handle.getBoundingClientRect().left;
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
handleMouseUp () {
|
|
@@ -41,20 +47,29 @@ class SliderComponent extends React.Component {
|
|
|
41
47
|
}
|
|
42
48
|
|
|
43
49
|
handleClickBackground (event) {
|
|
50
|
+
// Because the slider handle is a child of the "background" element this handler is registered to, it calls this
|
|
51
|
+
// when clicked as well. We only want to change the slider value if the user clicked on the background itself.
|
|
52
|
+
if (event.target !== this.background) return;
|
|
53
|
+
// Move slider handle's center to the cursor
|
|
54
|
+
this.handleClickOffset = HANDLE_WIDTH / 2;
|
|
44
55
|
this.props.onChange(this.scaleMouseToSliderPosition(event));
|
|
45
56
|
}
|
|
46
57
|
|
|
47
58
|
scaleMouseToSliderPosition (event){
|
|
48
59
|
const {x} = getEventXY(event);
|
|
49
60
|
const backgroundBBox = this.background.getBoundingClientRect();
|
|
50
|
-
const scaledX = x - backgroundBBox.left;
|
|
51
|
-
return Math.max(0, Math.min(100, 100 * scaledX / backgroundBBox.width));
|
|
61
|
+
const scaledX = x - backgroundBBox.left - this.handleClickOffset;
|
|
62
|
+
return Math.max(0, Math.min(100, 100 * scaledX / (backgroundBBox.width - HANDLE_WIDTH)));
|
|
52
63
|
}
|
|
53
64
|
|
|
54
65
|
setBackground (ref) {
|
|
55
66
|
this.background = ref;
|
|
56
67
|
}
|
|
57
68
|
|
|
69
|
+
setHandle (ref) {
|
|
70
|
+
this.handle = ref;
|
|
71
|
+
}
|
|
72
|
+
|
|
58
73
|
render () {
|
|
59
74
|
const halfHandleWidth = HANDLE_WIDTH / 2;
|
|
60
75
|
const pixelMin = halfHandleWidth;
|
|
@@ -76,6 +91,7 @@ class SliderComponent extends React.Component {
|
|
|
76
91
|
>
|
|
77
92
|
<div
|
|
78
93
|
className={styles.handle}
|
|
94
|
+
ref={this.setHandle}
|
|
79
95
|
style={{
|
|
80
96
|
left: `${handleOffset}px`
|
|
81
97
|
}}
|
|
@@ -99,3 +115,4 @@ SliderComponent.defaultProps = {
|
|
|
99
115
|
};
|
|
100
116
|
|
|
101
117
|
export default SliderComponent;
|
|
118
|
+
export {CONTAINER_WIDTH, HANDLE_WIDTH};
|
|
@@ -17,8 +17,7 @@ import Input from '../forms/input.jsx';
|
|
|
17
17
|
import InputGroup from '../input-group/input-group.jsx';
|
|
18
18
|
import LabeledIconButton from '../labeled-icon-button/labeled-icon-button.jsx';
|
|
19
19
|
import Modes from '../../lib/modes';
|
|
20
|
-
import Formats from '../../lib/format';
|
|
21
|
-
import {isBitmap, isVector} from '../../lib/format';
|
|
20
|
+
import Formats, {isBitmap, isVector} from '../../lib/format';
|
|
22
21
|
import {hideLabel} from '../../lib/hide-label';
|
|
23
22
|
import styles from './mode-tools.css';
|
|
24
23
|
|
|
@@ -34,8 +34,7 @@ import StrokeColorIndicatorComponent from '../../containers/stroke-color-indicat
|
|
|
34
34
|
import StrokeWidthIndicatorComponent from '../../containers/stroke-width-indicator.jsx';
|
|
35
35
|
import TextMode from '../../containers/text-mode.jsx';
|
|
36
36
|
|
|
37
|
-
import Formats from '../../lib/format';
|
|
38
|
-
import {isBitmap, isVector} from '../../lib/format';
|
|
37
|
+
import Formats, {isBitmap, isVector} from '../../lib/format';
|
|
39
38
|
import styles from './paint-editor.css';
|
|
40
39
|
|
|
41
40
|
import bitmapIcon from './icons/bitmap.svg';
|
|
@@ -5,8 +5,7 @@ import parseColor from 'parse-color';
|
|
|
5
5
|
import {injectIntl, intlShape} from 'react-intl';
|
|
6
6
|
|
|
7
7
|
import {getSelectedLeafItems} from '../helper/selection';
|
|
8
|
-
import Formats from '../lib/format';
|
|
9
|
-
import {isBitmap} from '../lib/format';
|
|
8
|
+
import Formats, {isBitmap} from '../lib/format';
|
|
10
9
|
import GradientTypes from '../lib/gradient-types';
|
|
11
10
|
|
|
12
11
|
import ColorIndicatorComponent from '../components/color-indicator.jsx';
|
|
@@ -15,8 +15,7 @@ import {getSelectedLeafItems} from '../helper/selection';
|
|
|
15
15
|
import {bringToFront, sendBackward, sendToBack, bringForward} from '../helper/order';
|
|
16
16
|
import {groupSelection, ungroupSelection} from '../helper/group';
|
|
17
17
|
|
|
18
|
-
import Formats from '../lib/format';
|
|
19
|
-
import {isBitmap} from '../lib/format';
|
|
18
|
+
import Formats, {isBitmap} from '../lib/format';
|
|
20
19
|
import bindAll from 'lodash.bindall';
|
|
21
20
|
|
|
22
21
|
class FixedTools extends React.Component {
|
|
@@ -8,12 +8,11 @@ import ColorStyleProptype from '../lib/color-style-proptype';
|
|
|
8
8
|
import {clearSelection} from '../helper/selection';
|
|
9
9
|
import {endPointHit, touching} from '../helper/snapping';
|
|
10
10
|
import {drawHitPoint, removeHitPoint} from '../helper/guides';
|
|
11
|
-
import {styleShape} from '../helper/style-path';
|
|
11
|
+
import {styleShape, MIXED} from '../helper/style-path';
|
|
12
12
|
import {changeStrokeColor, clearStrokeGradient} from '../reducers/stroke-style';
|
|
13
13
|
import {changeStrokeWidth} from '../reducers/stroke-width';
|
|
14
14
|
import {changeMode} from '../reducers/modes';
|
|
15
15
|
import {clearSelectedItems} from '../reducers/selected-items';
|
|
16
|
-
import {MIXED} from '../helper/style-path';
|
|
17
16
|
import {snapDeltaToAngle} from '../helper/math';
|
|
18
17
|
|
|
19
18
|
import LineModeComponent from '../components/line-mode/line-mode.jsx';
|
|
@@ -18,8 +18,7 @@ import {
|
|
|
18
18
|
import {HANDLE_RATIO, ensureClockwise} from '../helper/math';
|
|
19
19
|
import {getRaster} from '../helper/layer';
|
|
20
20
|
import {flipBitmapHorizontal, flipBitmapVertical, selectAllBitmap} from '../helper/bitmap';
|
|
21
|
-
import {isBitmap} from '../lib/format';
|
|
22
|
-
import Formats from '../lib/format';
|
|
21
|
+
import Formats, {isBitmap} from '../lib/format';
|
|
23
22
|
import Modes from '../lib/modes';
|
|
24
23
|
|
|
25
24
|
class ModeTools extends React.Component {
|
|
@@ -23,10 +23,8 @@ import {convertToBitmap, convertToVector} from '../helper/bitmap';
|
|
|
23
23
|
import {resetZoom, zoomOnSelection, OUTERMOST_ZOOM_LEVEL} from '../helper/view';
|
|
24
24
|
import EyeDropperTool from '../helper/tools/eye-dropper';
|
|
25
25
|
|
|
26
|
-
import Modes from '../lib/modes';
|
|
27
|
-
import {
|
|
28
|
-
import Formats from '../lib/format';
|
|
29
|
-
import {isBitmap, isVector} from '../lib/format';
|
|
26
|
+
import Modes, {BitmapModes, VectorModes} from '../lib/modes';
|
|
27
|
+
import Formats, {isBitmap, isVector} from '../lib/format';
|
|
30
28
|
import bindAll from 'lodash.bindall';
|
|
31
29
|
|
|
32
30
|
/**
|
|
@@ -129,7 +127,7 @@ class PaintEditor extends React.Component {
|
|
|
129
127
|
if (this.props.format === Formats.VECTOR && isBitmap(prevProps.format)) {
|
|
130
128
|
convertToVector(this.props.clearSelectedItems, this.props.onUpdateImage);
|
|
131
129
|
} else if (isVector(prevProps.format) && this.props.format === Formats.BITMAP) {
|
|
132
|
-
convertToBitmap(this.props.clearSelectedItems, this.props.onUpdateImage);
|
|
130
|
+
convertToBitmap(this.props.clearSelectedItems, this.props.onUpdateImage, this.props.fontInlineFn);
|
|
133
131
|
}
|
|
134
132
|
}
|
|
135
133
|
componentWillUnmount () {
|
|
@@ -344,6 +342,7 @@ PaintEditor.propTypes = {
|
|
|
344
342
|
changeMode: PropTypes.func.isRequired,
|
|
345
343
|
clearSelectedItems: PropTypes.func.isRequired,
|
|
346
344
|
format: PropTypes.oneOf(Object.keys(Formats)), // Internal, up-to-date data format
|
|
345
|
+
fontInlineFn: PropTypes.func,
|
|
347
346
|
handleSwitchToBitmap: PropTypes.func.isRequired,
|
|
348
347
|
handleSwitchToVector: PropTypes.func.isRequired,
|
|
349
348
|
image: PropTypes.oneOfType([
|
|
@@ -11,8 +11,10 @@ import {undoSnapshot, clearUndoState} from '../reducers/undo';
|
|
|
11
11
|
import {isGroup, ungroupItems} from '../helper/group';
|
|
12
12
|
import {clearRaster, convertBackgroundGuideLayer, getRaster, setupLayers} from '../helper/layer';
|
|
13
13
|
import {clearSelectedItems} from '../reducers/selected-items';
|
|
14
|
-
import {
|
|
15
|
-
|
|
14
|
+
import {
|
|
15
|
+
ART_BOARD_WIDTH, ART_BOARD_HEIGHT, CENTER, MAX_WORKSPACE_BOUNDS,
|
|
16
|
+
clampViewBounds, resetZoom, setWorkspaceBounds, zoomToFit, resizeCrosshair
|
|
17
|
+
} from '../helper/view';
|
|
16
18
|
import {ensureClockwise, scaleWithStrokes} from '../helper/math';
|
|
17
19
|
import {clearHoveredItem} from '../reducers/hover';
|
|
18
20
|
import {clearPasteOffset} from '../reducers/clipboard';
|
|
@@ -195,6 +197,7 @@ class PaperCanvas extends React.Component {
|
|
|
195
197
|
const paperCanvas = this;
|
|
196
198
|
// Pre-process SVG to prevent parsing errors (discussion from #213)
|
|
197
199
|
// 1. Remove svg: namespace on elements.
|
|
200
|
+
// TODO: remove
|
|
198
201
|
svg = svg.split(/<\s*svg:/).join('<');
|
|
199
202
|
svg = svg.split(/<\/\s*svg:/).join('</');
|
|
200
203
|
// 2. Add root svg namespace if it does not exist.
|
|
@@ -2,9 +2,8 @@ import {connect} from 'react-redux';
|
|
|
2
2
|
import {defineMessages} from 'react-intl';
|
|
3
3
|
|
|
4
4
|
import {changeColorIndex} from '../reducers/color-index';
|
|
5
|
-
import {changeStrokeColor, changeStrokeColor2} from '../reducers/stroke-style';
|
|
5
|
+
import {changeStrokeColor, changeStrokeColor2, changeStrokeGradientType} from '../reducers/stroke-style';
|
|
6
6
|
import {changeStrokeWidth} from '../reducers/stroke-width';
|
|
7
|
-
import {changeStrokeGradientType} from '../reducers/stroke-style';
|
|
8
7
|
import {openStrokeColor, closeStrokeColor} from '../reducers/modals';
|
|
9
8
|
import {getSelectedLeafItems} from '../helper/selection';
|
|
10
9
|
import {setSelectedItems} from '../reducers/selected-items';
|
|
@@ -7,12 +7,12 @@ import {changeStrokeColor, changeStrokeColor2, changeStrokeGradientType} from '.
|
|
|
7
7
|
import {changeStrokeWidth} from '../reducers/stroke-width';
|
|
8
8
|
import StrokeWidthIndicatorComponent from '../components/stroke-width-indicator.jsx';
|
|
9
9
|
import {getSelectedLeafItems} from '../helper/selection';
|
|
10
|
-
import {
|
|
11
|
-
|
|
10
|
+
import {
|
|
11
|
+
applyColorToSelection, applyStrokeWidthToSelection, getColorsFromSelection, MIXED
|
|
12
|
+
} from '../helper/style-path';
|
|
12
13
|
import GradientTypes from '../lib/gradient-types';
|
|
13
14
|
import Modes from '../lib/modes';
|
|
14
|
-
import Formats from '../lib/format';
|
|
15
|
-
import {isBitmap} from '../lib/format';
|
|
15
|
+
import Formats, {isBitmap} from '../lib/format';
|
|
16
16
|
|
|
17
17
|
class StrokeWidthIndicator extends React.Component {
|
|
18
18
|
constructor (props) {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import paper from '@scratch/paper';
|
|
2
|
-
import {getRaster} from '../layer';
|
|
2
|
+
import {getRaster, getGuideLayer} from '../layer';
|
|
3
3
|
import {forEachLinePoint, getBrushMark} from '../bitmap';
|
|
4
|
-
import {getGuideLayer} from '../layer';
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Tool for drawing with the bitmap brush and eraser
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import paper from '@scratch/paper';
|
|
2
|
-
import {getRaster} from '../layer';
|
|
2
|
+
import {getRaster, createCanvas, getGuideLayer} from '../layer';
|
|
3
3
|
import {forEachLinePoint, getBrushMark} from '../bitmap';
|
|
4
|
-
import {createCanvas, getGuideLayer} from '../layer';
|
|
5
4
|
import {ART_BOARD_WIDTH, ART_BOARD_HEIGHT} from '../view';
|
|
6
5
|
|
|
7
6
|
/**
|
package/src/helper/bitmap.js
CHANGED
|
@@ -3,8 +3,8 @@ import {createCanvas, clearRaster, getRaster, hideGuideLayers, showGuideLayers}
|
|
|
3
3
|
import {getGuideColor} from './guides';
|
|
4
4
|
import {clearSelection} from './selection';
|
|
5
5
|
import {ART_BOARD_WIDTH, ART_BOARD_HEIGHT, CENTER, MAX_WORKSPACE_BOUNDS} from './view';
|
|
6
|
-
import {inlineSvgFonts} from 'scratch-svg-renderer';
|
|
7
6
|
import Formats from '../lib/format';
|
|
7
|
+
import log from '../log/log';
|
|
8
8
|
|
|
9
9
|
const forEachLinePoint = function (point1, point2, callback) {
|
|
10
10
|
// Bresenham line algorithm
|
|
@@ -404,7 +404,7 @@ const getTrimmedRaster = function (shouldInsert) {
|
|
|
404
404
|
return trimmedRaster;
|
|
405
405
|
};
|
|
406
406
|
|
|
407
|
-
const convertToBitmap = function (clearSelectedItems, onUpdateImage) {
|
|
407
|
+
const convertToBitmap = function (clearSelectedItems, onUpdateImage, optFontInlineFn) {
|
|
408
408
|
// @todo if the active layer contains only rasters, drawing them directly to the raster layer
|
|
409
409
|
// would be more efficient.
|
|
410
410
|
|
|
@@ -424,7 +424,11 @@ const convertToBitmap = function (clearSelectedItems, onUpdateImage) {
|
|
|
424
424
|
svg.setAttribute('shape-rendering', 'crispEdges');
|
|
425
425
|
|
|
426
426
|
let svgString = (new XMLSerializer()).serializeToString(svg);
|
|
427
|
-
|
|
427
|
+
if (optFontInlineFn) {
|
|
428
|
+
svgString = optFontInlineFn(svgString);
|
|
429
|
+
} else {
|
|
430
|
+
log.error('Fonts may be converted to bitmap incorrectly if fontInlineFn prop is not set on PaintEditor.');
|
|
431
|
+
}
|
|
428
432
|
|
|
429
433
|
// Put anti-aliased SVG into image, and dump image back into canvas
|
|
430
434
|
const img = new Image();
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import paper from '@scratch/paper';
|
|
2
|
-
import Modes from '../../lib/modes';
|
|
3
|
-
import {BitmapModes} from '../../lib/modes';
|
|
2
|
+
import Modes, {BitmapModes} from '../../lib/modes';
|
|
4
3
|
import {isGroup} from '../group';
|
|
5
4
|
import {isCompoundPathItem, getRootItem} from '../item';
|
|
6
5
|
import {checkPointsClose, snapDeltaToAngle} from '../math';
|
|
7
6
|
import {getActionBounds, CENTER} from '../view';
|
|
8
|
-
import {
|
|
9
|
-
|
|
7
|
+
import {
|
|
8
|
+
clearSelection, cloneSelection, getSelectedLeafItems, getSelectedRootItems, setItemSelection
|
|
9
|
+
} from '../selection';
|
|
10
10
|
import {getDragCrosshairLayer, CROSSHAIR_FULL_OPACITY} from '../layer';
|
|
11
11
|
|
|
12
12
|
/** Snap to align selection center to rotation center within this distance */
|
|
@@ -128,7 +128,7 @@ class MoveTool {
|
|
|
128
128
|
|
|
129
129
|
point.x = Math.max(actionBounds.left, Math.min(point.x, actionBounds.right));
|
|
130
130
|
point.y = Math.max(actionBounds.top, Math.min(point.y, actionBounds.bottom));
|
|
131
|
-
|
|
131
|
+
|
|
132
132
|
const dragVector = point.subtract(event.downPoint);
|
|
133
133
|
let snapVector;
|
|
134
134
|
|
|
@@ -168,7 +168,7 @@ class MoveTool {
|
|
|
168
168
|
bounds = item.bounds;
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
|
-
|
|
171
|
+
|
|
172
172
|
if (this.firstDrag) {
|
|
173
173
|
// Show the center crosshair above the selected item while dragging.
|
|
174
174
|
getDragCrosshairLayer().visible = true;
|
package/src/helper/style-path.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import paper from '@scratch/paper';
|
|
2
|
-
import {getSelectedLeafItems} from './selection';
|
|
2
|
+
import {getSelectedLeafItems, getItems} from './selection';
|
|
3
3
|
import {isPointTextItem} from './item';
|
|
4
4
|
import {isGroup} from './group';
|
|
5
|
-
import {getItems} from './selection';
|
|
6
5
|
import GradientTypes from '../lib/gradient-types';
|
|
7
6
|
import {DEFAULT_COLOR} from '../reducers/fill-style';
|
|
8
7
|
import {isCompoundPathChild} from '../helper/compound-path';
|
|
@@ -634,8 +633,7 @@ const styleShape = function (path, options) {
|
|
|
634
633
|
);
|
|
635
634
|
}
|
|
636
635
|
}
|
|
637
|
-
|
|
638
|
-
if (options.hasOwnProperty('strokeWidth')) path.strokeWidth = options.strokeWidth;
|
|
636
|
+
if (Object.prototype.hasOwnProperty.call(options, 'strokeWidth')) path.strokeWidth = options.strokeWidth;
|
|
639
637
|
};
|
|
640
638
|
|
|
641
639
|
export {
|
|
@@ -21,6 +21,7 @@ class FillTool extends paper.Tool {
|
|
|
21
21
|
|
|
22
22
|
// We have to set these functions instead of just declaring them because
|
|
23
23
|
// paper.js tools hook up the listeners in the setter functions.
|
|
24
|
+
this.onMouseDown = this.handleMouseDown;
|
|
24
25
|
this.onMouseMove = this.handleMouseMove;
|
|
25
26
|
this.onMouseUp = this.handleMouseUp;
|
|
26
27
|
|
|
@@ -91,7 +92,7 @@ class FillTool extends paper.Tool {
|
|
|
91
92
|
setPrevHoveredItemId (prevHoveredItemId) {
|
|
92
93
|
this.prevHoveredItemId = prevHoveredItemId;
|
|
93
94
|
}
|
|
94
|
-
|
|
95
|
+
updateFillPreview (event) {
|
|
95
96
|
const hoveredItem = getHoveredItem(event, this.getHitOptions(), true /* subselect */);
|
|
96
97
|
if ((!hoveredItem && this.prevHoveredItemId) || // There is no longer a hovered item
|
|
97
98
|
(hoveredItem && !this.prevHoveredItemId) || // There is now a hovered item
|
|
@@ -152,6 +153,15 @@ class FillTool extends paper.Tool {
|
|
|
152
153
|
this._setFillItemColor(this.fillColor, this.fillColor2, this.gradientType, event.point);
|
|
153
154
|
}
|
|
154
155
|
}
|
|
156
|
+
handleMouseDown (event) {
|
|
157
|
+
// on touch, the user might touch-and-hold to preview what the fill tool would do
|
|
158
|
+
// if they don't move their finger at all after the "mouse down" event
|
|
159
|
+
// then this might be our only chance to give them a good preview
|
|
160
|
+
this.updateFillPreview(event);
|
|
161
|
+
}
|
|
162
|
+
handleMouseMove (event) {
|
|
163
|
+
this.updateFillPreview(event);
|
|
164
|
+
}
|
|
155
165
|
handleMouseUp (event) {
|
|
156
166
|
if (event.event.button > 0) return; // only first mouse button
|
|
157
167
|
if (this.fillItem) {
|
package/src/helper/undo.js
CHANGED
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
import paper from '@scratch/paper';
|
|
4
4
|
import {hideGuideLayers, showGuideLayers, getRaster} from '../helper/layer';
|
|
5
5
|
import {getSelectedLeafItems} from '../helper/selection';
|
|
6
|
-
import Formats from '../lib/format';
|
|
7
|
-
import {isVector, isBitmap} from '../lib/format';
|
|
6
|
+
import Formats, {isVector, isBitmap} from '../lib/format';
|
|
8
7
|
import log from '../log/log';
|
|
9
8
|
|
|
10
9
|
/**
|
|
@@ -12,8 +12,7 @@ import {
|
|
|
12
12
|
getSelectedRootItems
|
|
13
13
|
} from '../helper/selection';
|
|
14
14
|
import {getTrimmedRaster} from '../helper/bitmap';
|
|
15
|
-
import {isBitmap} from '../lib/format';
|
|
16
|
-
import Formats from '../lib/format';
|
|
15
|
+
import Formats, {isBitmap} from '../lib/format';
|
|
17
16
|
import Modes from '../lib/modes';
|
|
18
17
|
|
|
19
18
|
import {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';
|
|
@@ -13,8 +13,7 @@ import {groupSelection, shouldShowGroup, ungroupSelection, shouldShowUngroup} fr
|
|
|
13
13
|
import {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';
|
|
14
14
|
import {changeMode} from '../reducers/modes';
|
|
15
15
|
|
|
16
|
-
import {isBitmap} from '../lib/format';
|
|
17
|
-
import Formats from '../lib/format';
|
|
16
|
+
import Formats, {isBitmap} from '../lib/format';
|
|
18
17
|
import Modes from '../lib/modes';
|
|
19
18
|
|
|
20
19
|
const KeyboardShortcutsHOC = function (WrappedComponent) {
|