mirror of
https://github.com/scratchfoundation/scratch-paint.git
synced 2024-12-22 21:42:30 -05:00
Merge pull request #386 from fsih/doubleRes
Double resolution of canvas
This commit is contained in:
commit
79c46d67ae
9 changed files with 77 additions and 25 deletions
|
@ -17,6 +17,7 @@ import {trim} from '../helper/bitmap';
|
||||||
import {performUndo, performRedo, performSnapshot, shouldShowUndo, shouldShowRedo} from '../helper/undo';
|
import {performUndo, performRedo, performSnapshot, shouldShowUndo, shouldShowRedo} from '../helper/undo';
|
||||||
import {bringToFront, sendBackward, sendToBack, bringForward} from '../helper/order';
|
import {bringToFront, sendBackward, sendToBack, bringForward} from '../helper/order';
|
||||||
import {groupSelection, ungroupSelection} from '../helper/group';
|
import {groupSelection, ungroupSelection} from '../helper/group';
|
||||||
|
import {scaleWithStrokes} from '../helper/math';
|
||||||
import {getSelectedLeafItems} from '../helper/selection';
|
import {getSelectedLeafItems} from '../helper/selection';
|
||||||
import {resetZoom, zoomOnSelection} from '../helper/view';
|
import {resetZoom, zoomOnSelection} from '../helper/view';
|
||||||
import EyeDropperTool from '../helper/tools/eye-dropper';
|
import EyeDropperTool from '../helper/tools/eye-dropper';
|
||||||
|
@ -112,6 +113,9 @@ class PaintEditor extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
const guideLayers = hideGuideLayers(true /* includeRaster */);
|
const guideLayers = hideGuideLayers(true /* includeRaster */);
|
||||||
|
|
||||||
|
// Export at 0.5x
|
||||||
|
scaleWithStrokes(paper.project.activeLayer, .5, new paper.Point());
|
||||||
const bounds = paper.project.activeLayer.bounds;
|
const bounds = paper.project.activeLayer.bounds;
|
||||||
|
|
||||||
this.props.onUpdateSvg(
|
this.props.onUpdateSvg(
|
||||||
|
@ -120,8 +124,11 @@ class PaintEditor extends React.Component {
|
||||||
bounds: 'content',
|
bounds: 'content',
|
||||||
matrix: new paper.Matrix().translate(-bounds.x, -bounds.y)
|
matrix: new paper.Matrix().translate(-bounds.x, -bounds.y)
|
||||||
}),
|
}),
|
||||||
paper.project.view.center.x - bounds.x,
|
(paper.project.view.center.x / 2) - bounds.x,
|
||||||
paper.project.view.center.y - bounds.y);
|
(paper.project.view.center.y / 2) - bounds.y);
|
||||||
|
|
||||||
|
scaleWithStrokes(paper.project.activeLayer, 2, new paper.Point());
|
||||||
|
paper.project.activeLayer.applyMatrix = true;
|
||||||
|
|
||||||
showGuideLayers(guideLayers);
|
showGuideLayers(guideLayers);
|
||||||
if (raster) raster.remove();
|
if (raster) raster.remove();
|
||||||
|
|
|
@ -14,8 +14,8 @@ import {isGroup, ungroupItems} from '../helper/group';
|
||||||
import {clearRaster, getRaster, setupLayers, hideGuideLayers, showGuideLayers} from '../helper/layer';
|
import {clearRaster, getRaster, setupLayers, hideGuideLayers, showGuideLayers} from '../helper/layer';
|
||||||
import {deleteSelection, getSelectedLeafItems} from '../helper/selection';
|
import {deleteSelection, getSelectedLeafItems} from '../helper/selection';
|
||||||
import {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';
|
import {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';
|
||||||
import {pan, resetZoom, zoomOnFixedPoint} from '../helper/view';
|
import {clampViewBounds, pan, resetZoom, zoomOnFixedPoint} from '../helper/view';
|
||||||
import {ensureClockwise} from '../helper/math';
|
import {ensureClockwise, scaleWithStrokes} from '../helper/math';
|
||||||
import {clearHoveredItem} from '../reducers/hover';
|
import {clearHoveredItem} from '../reducers/hover';
|
||||||
import {clearPasteOffset} from '../reducers/clipboard';
|
import {clearPasteOffset} from '../reducers/clipboard';
|
||||||
import {updateViewBounds} from '../reducers/view-bounds';
|
import {updateViewBounds} from '../reducers/view-bounds';
|
||||||
|
@ -41,6 +41,8 @@ class PaperCanvas extends React.Component {
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
document.addEventListener('keydown', this.handleKeyDown);
|
document.addEventListener('keydown', this.handleKeyDown);
|
||||||
paper.setup(this.canvas);
|
paper.setup(this.canvas);
|
||||||
|
paper.view.zoom = .5;
|
||||||
|
clampViewBounds();
|
||||||
|
|
||||||
const context = this.canvas.getContext('2d');
|
const context = this.canvas.getContext('2d');
|
||||||
context.webkitImageSmoothingEnabled = false;
|
context.webkitImageSmoothingEnabled = false;
|
||||||
|
@ -85,6 +87,12 @@ class PaperCanvas extends React.Component {
|
||||||
// @todo if the active layer contains only rasters, drawing them directly to the raster layer
|
// @todo if the active layer contains only rasters, drawing them directly to the raster layer
|
||||||
// would be more efficient.
|
// would be more efficient.
|
||||||
// Export svg
|
// Export svg
|
||||||
|
|
||||||
|
// Store the zoom/pan and restore it after snapshotting
|
||||||
|
const oldZoom = paper.project.view.zoom;
|
||||||
|
const oldCenter = paper.project.view.center.clone();
|
||||||
|
paper.project.view.zoom = 1;
|
||||||
|
|
||||||
const guideLayers = hideGuideLayers(true /* includeRaster */);
|
const guideLayers = hideGuideLayers(true /* includeRaster */);
|
||||||
const bounds = paper.project.activeLayer.bounds;
|
const bounds = paper.project.activeLayer.bounds;
|
||||||
const svg = paper.project.exportSVG({
|
const svg = paper.project.exportSVG({
|
||||||
|
@ -112,6 +120,10 @@ class PaperCanvas extends React.Component {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
img.src = `data:image/svg+xml;charset=utf-8,${svgString}`;
|
img.src = `data:image/svg+xml;charset=utf-8,${svgString}`;
|
||||||
|
|
||||||
|
// Restore old zoom
|
||||||
|
paper.project.view.zoom = oldZoom;
|
||||||
|
paper.project.view.center = oldCenter;
|
||||||
}
|
}
|
||||||
convertToVector () {
|
convertToVector () {
|
||||||
this.props.clearSelectedItems();
|
this.props.clearSelectedItems();
|
||||||
|
@ -207,6 +219,7 @@ class PaperCanvas extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
ensureClockwise(item);
|
ensureClockwise(item);
|
||||||
|
scaleWithStrokes(item, 2, new paper.Point()); // Import at 2x
|
||||||
|
|
||||||
if (typeof rotationCenterX !== 'undefined' && typeof rotationCenterY !== 'undefined') {
|
if (typeof rotationCenterX !== 'undefined' && typeof rotationCenterY !== 'undefined') {
|
||||||
let rotationPoint = new paper.Point(rotationCenterX, rotationCenterY);
|
let rotationPoint = new paper.Point(rotationCenterX, rotationCenterY);
|
||||||
|
@ -214,7 +227,7 @@ class PaperCanvas extends React.Component {
|
||||||
rotationPoint = rotationPoint.subtract(viewBox[0], viewBox[1]);
|
rotationPoint = rotationPoint.subtract(viewBox[0], viewBox[1]);
|
||||||
}
|
}
|
||||||
item.translate(paper.project.view.center
|
item.translate(paper.project.view.center
|
||||||
.subtract(rotationPoint));
|
.subtract(rotationPoint.multiply(2)));
|
||||||
} else {
|
} else {
|
||||||
// Center
|
// Center
|
||||||
item.translate(paper.project.view.center
|
item.translate(paper.project.view.center
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import paper from '@scratch/paper';
|
import paper from '@scratch/paper';
|
||||||
import rasterSrc from './transparent.png';
|
|
||||||
import log from '../log/log';
|
import log from '../log/log';
|
||||||
|
import {ART_BOARD_WIDTH, ART_BOARD_HEIGHT} from './view';
|
||||||
|
|
||||||
const _getLayer = function (layerString) {
|
const _getLayer = function (layerString) {
|
||||||
for (const layer of paper.project.layers) {
|
for (const layer of paper.project.layers) {
|
||||||
|
@ -19,7 +19,10 @@ const clearRaster = function () {
|
||||||
layer.removeChildren();
|
layer.removeChildren();
|
||||||
|
|
||||||
// Generate blank raster
|
// Generate blank raster
|
||||||
const raster = new paper.Raster(rasterSrc);
|
const tmpCanvas = document.createElement('canvas');
|
||||||
|
tmpCanvas.width = ART_BOARD_WIDTH;
|
||||||
|
tmpCanvas.height = ART_BOARD_HEIGHT;
|
||||||
|
const raster = new paper.Raster(tmpCanvas);
|
||||||
raster.parent = layer;
|
raster.parent = layer;
|
||||||
raster.guide = true;
|
raster.guide = true;
|
||||||
raster.locked = true;
|
raster.locked = true;
|
||||||
|
@ -30,7 +33,10 @@ const getRaster = function () {
|
||||||
const layer = _getLayer('isRasterLayer');
|
const layer = _getLayer('isRasterLayer');
|
||||||
// Generate blank raster
|
// Generate blank raster
|
||||||
if (layer.children.length === 0) {
|
if (layer.children.length === 0) {
|
||||||
const raster = new paper.Raster(rasterSrc);
|
const tmpCanvas = document.createElement('canvas');
|
||||||
|
tmpCanvas.width = ART_BOARD_WIDTH;
|
||||||
|
tmpCanvas.height = ART_BOARD_HEIGHT;
|
||||||
|
const raster = new paper.Raster(tmpCanvas);
|
||||||
raster.parent = layer;
|
raster.parent = layer;
|
||||||
raster.guide = true;
|
raster.guide = true;
|
||||||
raster.locked = true;
|
raster.locked = true;
|
||||||
|
@ -158,7 +164,7 @@ const _makeBackgroundGuideLayer = function () {
|
||||||
|
|
||||||
const vBackground = _makeBackgroundPaper(120, 90, '#E5E5E5');
|
const vBackground = _makeBackgroundPaper(120, 90, '#E5E5E5');
|
||||||
vBackground.position = paper.view.center;
|
vBackground.position = paper.view.center;
|
||||||
vBackground.scaling = new paper.Point(4, 4);
|
vBackground.scaling = new paper.Point(8, 8);
|
||||||
vBackground.guide = true;
|
vBackground.guide = true;
|
||||||
vBackground.locked = true;
|
vBackground.locked = true;
|
||||||
|
|
||||||
|
|
|
@ -97,15 +97,34 @@ const expandBy = function (path, amount) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Make item clockwise. Drill down into groups.
|
// Do for all nested items in groups
|
||||||
const ensureClockwise = function (item) {
|
const _doRecursively = function (item, func) {
|
||||||
if (item instanceof paper.Group) {
|
if (item instanceof paper.Group) {
|
||||||
for (const child of item.children) {
|
for (const child of item.children) {
|
||||||
ensureClockwise(child);
|
_doRecursively(child, func);
|
||||||
}
|
}
|
||||||
} else if (item instanceof paper.PathItem) {
|
} else {
|
||||||
|
func(item);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make item clockwise. Drill down into groups.
|
||||||
|
const ensureClockwise = function (root) {
|
||||||
|
_doRecursively(root, item => {
|
||||||
|
if (item instanceof paper.PathItem) {
|
||||||
item.clockwise = true;
|
item.clockwise = true;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Scale item and its strokes by factor
|
||||||
|
const scaleWithStrokes = function (root, factor, pivot) {
|
||||||
|
_doRecursively(root, item => {
|
||||||
|
if (item.strokeWidth) {
|
||||||
|
item.strokeWidth = item.strokeWidth * factor;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
root.scale(factor, pivot);
|
||||||
};
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -115,6 +134,7 @@ export {
|
||||||
expandBy,
|
expandBy,
|
||||||
getRandomInt,
|
getRandomInt,
|
||||||
getRandomBoolean,
|
getRandomBoolean,
|
||||||
|
scaleWithStrokes,
|
||||||
snapDeltaToAngle,
|
snapDeltaToAngle,
|
||||||
sortItemsByZIndex
|
sortItemsByZIndex
|
||||||
};
|
};
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 414 B |
|
@ -1,6 +1,9 @@
|
||||||
import paper from '@scratch/paper';
|
import paper from '@scratch/paper';
|
||||||
import {getSelectedRootItems} from './selection';
|
import {getSelectedRootItems} from './selection';
|
||||||
|
|
||||||
|
const ART_BOARD_WIDTH = 480 * 2;
|
||||||
|
const ART_BOARD_HEIGHT = 360 * 2;
|
||||||
|
|
||||||
const clampViewBounds = () => {
|
const clampViewBounds = () => {
|
||||||
const {left, right, top, bottom} = paper.project.view.bounds;
|
const {left, right, top, bottom} = paper.project.view.bounds;
|
||||||
if (left < 0) {
|
if (left < 0) {
|
||||||
|
@ -9,11 +12,11 @@ const clampViewBounds = () => {
|
||||||
if (top < 0) {
|
if (top < 0) {
|
||||||
paper.project.view.scrollBy(new paper.Point(0, -top));
|
paper.project.view.scrollBy(new paper.Point(0, -top));
|
||||||
}
|
}
|
||||||
if (bottom > 360) {
|
if (bottom > ART_BOARD_HEIGHT) {
|
||||||
paper.project.view.scrollBy(new paper.Point(0, 360 - bottom));
|
paper.project.view.scrollBy(new paper.Point(0, ART_BOARD_HEIGHT - bottom));
|
||||||
}
|
}
|
||||||
if (right > 480) {
|
if (right > ART_BOARD_WIDTH) {
|
||||||
paper.project.view.scrollBy(new paper.Point(480 - right, 0));
|
paper.project.view.scrollBy(new paper.Point(ART_BOARD_WIDTH - right, 0));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -22,7 +25,7 @@ const clampViewBounds = () => {
|
||||||
const zoomOnFixedPoint = (deltaZoom, fixedPoint) => {
|
const zoomOnFixedPoint = (deltaZoom, fixedPoint) => {
|
||||||
const {view} = paper.project;
|
const {view} = paper.project;
|
||||||
const preZoomCenter = view.center;
|
const preZoomCenter = view.center;
|
||||||
const newZoom = Math.max(1, view.zoom + deltaZoom);
|
const newZoom = Math.max(0.5, view.zoom + deltaZoom);
|
||||||
const scaling = view.zoom / newZoom;
|
const scaling = view.zoom / newZoom;
|
||||||
const preZoomOffset = fixedPoint.subtract(preZoomCenter);
|
const preZoomOffset = fixedPoint.subtract(preZoomCenter);
|
||||||
const postZoomOffset = fixedPoint.subtract(preZoomOffset.multiply(scaling))
|
const postZoomOffset = fixedPoint.subtract(preZoomOffset.multiply(scaling))
|
||||||
|
@ -53,7 +56,7 @@ const zoomOnSelection = deltaZoom => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const resetZoom = () => {
|
const resetZoom = () => {
|
||||||
paper.project.view.zoom = 1;
|
paper.project.view.zoom = .5;
|
||||||
clampViewBounds();
|
clampViewBounds();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -63,6 +66,9 @@ const pan = (dx, dy) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
ART_BOARD_HEIGHT,
|
||||||
|
ART_BOARD_WIDTH,
|
||||||
|
clampViewBounds,
|
||||||
pan,
|
pan,
|
||||||
resetZoom,
|
resetZoom,
|
||||||
zoomOnSelection,
|
zoomOnSelection,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import log from '../log/log';
|
import log from '../log/log';
|
||||||
|
|
||||||
const CHANGE_BRUSH_SIZE = 'scratch-paint/brush-mode/CHANGE_BRUSH_SIZE';
|
const CHANGE_BRUSH_SIZE = 'scratch-paint/brush-mode/CHANGE_BRUSH_SIZE';
|
||||||
const initialState = {brushSize: 5};
|
const initialState = {brushSize: 10};
|
||||||
|
|
||||||
const reducer = function (state, action) {
|
const reducer = function (state, action) {
|
||||||
if (typeof state === 'undefined') state = initialState;
|
if (typeof state === 'undefined') state = initialState;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import log from '../log/log';
|
import log from '../log/log';
|
||||||
|
|
||||||
const CHANGE_ERASER_SIZE = 'scratch-paint/eraser-mode/CHANGE_ERASER_SIZE';
|
const CHANGE_ERASER_SIZE = 'scratch-paint/eraser-mode/CHANGE_ERASER_SIZE';
|
||||||
const initialState = {brushSize: 20};
|
const initialState = {brushSize: 40};
|
||||||
|
|
||||||
const reducer = function (state, action) {
|
const reducer = function (state, action) {
|
||||||
if (typeof state === 'undefined') state = initialState;
|
if (typeof state === 'undefined') state = initialState;
|
||||||
|
|
|
@ -3,8 +3,8 @@ import {CHANGE_SELECTED_ITEMS} from './selected-items';
|
||||||
import {getColorsFromSelection} from '../helper/style-path';
|
import {getColorsFromSelection} from '../helper/style-path';
|
||||||
|
|
||||||
const CHANGE_STROKE_WIDTH = 'scratch-paint/stroke-width/CHANGE_STROKE_WIDTH';
|
const CHANGE_STROKE_WIDTH = 'scratch-paint/stroke-width/CHANGE_STROKE_WIDTH';
|
||||||
const MAX_STROKE_WIDTH = 400;
|
const MAX_STROKE_WIDTH = 800;
|
||||||
const initialState = 2;
|
const initialState = 4;
|
||||||
|
|
||||||
const reducer = function (state, action) {
|
const reducer = function (state, action) {
|
||||||
if (typeof state === 'undefined') state = initialState;
|
if (typeof state === 'undefined') state = initialState;
|
||||||
|
|
Loading…
Reference in a new issue