mirror of
https://github.com/scratchfoundation/scratch-paint.git
synced 2024-12-22 13:32:28 -05:00
parent
680b403de6
commit
8056a788e7
6 changed files with 71 additions and 53 deletions
|
@ -23,7 +23,7 @@
|
|||
"url": "git+ssh://git@github.com/LLK/scratch-paint.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@scratch/paper": "0.11.20180802201231",
|
||||
"@scratch/paper": "0.11.20180806212106",
|
||||
"classnames": "2.2.5",
|
||||
"keymirror": "0.1.1",
|
||||
"lodash.bindall": "4.4.0",
|
||||
|
|
|
@ -4,12 +4,10 @@ import bindAll from 'lodash.bindall';
|
|||
|
||||
import Box from '../box/box.jsx';
|
||||
|
||||
import {LOUPE_RADIUS} from '../../helper/tools/eye-dropper';
|
||||
import {LOUPE_RADIUS, ZOOM_SCALE} from '../../helper/tools/eye-dropper';
|
||||
|
||||
import styles from './loupe.css';
|
||||
|
||||
const ZOOM_SCALE = 3;
|
||||
|
||||
class LoupeComponent extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
|
@ -21,38 +19,40 @@ class LoupeComponent extends React.Component {
|
|||
this.draw();
|
||||
}
|
||||
draw () {
|
||||
const boxSize = 6 / ZOOM_SCALE;
|
||||
const boxLineWidth = 1 / ZOOM_SCALE;
|
||||
const colorRingWidth = 15 / ZOOM_SCALE;
|
||||
const boxSize = 5;
|
||||
const boxLineWidth = 1;
|
||||
const colorRingWidth = 15;
|
||||
const loupeRadius = ZOOM_SCALE * LOUPE_RADIUS;
|
||||
const loupeDiameter = loupeRadius * 2;
|
||||
|
||||
const color = this.props.colorInfo.color;
|
||||
|
||||
const ctx = this.canvas.getContext('2d');
|
||||
this.canvas.width = ZOOM_SCALE * (LOUPE_RADIUS * 2);
|
||||
this.canvas.height = ZOOM_SCALE * (LOUPE_RADIUS * 2);
|
||||
this.canvas.width = loupeDiameter;
|
||||
this.canvas.height = loupeDiameter;
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.fillRect(0, 0, loupeDiameter, loupeDiameter);
|
||||
|
||||
// In order to scale the image data, must draw to a tmp canvas first
|
||||
const tmpCanvas = document.createElement('canvas');
|
||||
tmpCanvas.width = LOUPE_RADIUS * 2;
|
||||
tmpCanvas.height = LOUPE_RADIUS * 2;
|
||||
tmpCanvas.width = loupeDiameter;
|
||||
tmpCanvas.height = loupeDiameter;
|
||||
const tmpCtx = tmpCanvas.getContext('2d');
|
||||
const imageData = tmpCtx.createImageData(
|
||||
LOUPE_RADIUS * 2, LOUPE_RADIUS * 2
|
||||
loupeDiameter, loupeDiameter
|
||||
);
|
||||
imageData.data.set(this.props.colorInfo.data);
|
||||
tmpCtx.putImageData(imageData, 0, 0);
|
||||
|
||||
// Scale the loupe canvas and draw the zoomed image
|
||||
ctx.save();
|
||||
ctx.scale(ZOOM_SCALE, ZOOM_SCALE);
|
||||
ctx.drawImage(tmpCanvas, 0, 0, LOUPE_RADIUS * 2, LOUPE_RADIUS * 2);
|
||||
ctx.drawImage(tmpCanvas, 0, 0);
|
||||
|
||||
// Draw an outlined square at the cursor position (cursor is hidden)
|
||||
ctx.lineWidth = boxLineWidth;
|
||||
ctx.strokeStyle = 'black';
|
||||
ctx.fillStyle = `rgba(${color[0]}, ${color[1]}, ${color[2]}, ${color[3]})`;
|
||||
ctx.beginPath();
|
||||
ctx.rect((20) - (boxSize / 2), (20) - (boxSize / 2), boxSize, boxSize);
|
||||
ctx.rect(loupeRadius - (boxSize / 2), loupeRadius - (boxSize / 2), boxSize, boxSize);
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
|
||||
|
@ -60,10 +60,9 @@ class LoupeComponent extends React.Component {
|
|||
ctx.strokeStyle = `rgba(${color[0]}, ${color[1]}, ${color[2]}, ${color[3]})`;
|
||||
ctx.lineWidth = colorRingWidth;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(LOUPE_RADIUS * 2, LOUPE_RADIUS);
|
||||
ctx.arc(LOUPE_RADIUS, LOUPE_RADIUS, LOUPE_RADIUS, 0, 2 * Math.PI);
|
||||
ctx.moveTo(loupeDiameter, loupeDiameter);
|
||||
ctx.arc(loupeRadius, loupeRadius, loupeRadius, 0, 2 * Math.PI);
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
}
|
||||
setCanvas (element) {
|
||||
this.canvas = element;
|
||||
|
@ -74,6 +73,7 @@ class LoupeComponent extends React.Component {
|
|||
pixelRatio,
|
||||
...boxProps
|
||||
} = this.props;
|
||||
const loupeDiameter = ZOOM_SCALE * LOUPE_RADIUS * 2;
|
||||
return (
|
||||
<Box
|
||||
{...boxProps}
|
||||
|
@ -82,10 +82,10 @@ class LoupeComponent extends React.Component {
|
|||
element="canvas"
|
||||
height={LOUPE_RADIUS * 2}
|
||||
style={{
|
||||
top: (colorInfo.y / pixelRatio) - ((ZOOM_SCALE * (LOUPE_RADIUS * 2)) / 2),
|
||||
left: (colorInfo.x / pixelRatio) - ((ZOOM_SCALE * (LOUPE_RADIUS * 2)) / 2),
|
||||
width: (LOUPE_RADIUS * 2) * ZOOM_SCALE,
|
||||
height: (LOUPE_RADIUS * 2) * ZOOM_SCALE
|
||||
top: (colorInfo.y / pixelRatio) - (loupeDiameter / 2),
|
||||
left: (colorInfo.x / pixelRatio) - (loupeDiameter / 2),
|
||||
width: loupeDiameter,
|
||||
height: loupeDiameter
|
||||
}}
|
||||
width={LOUPE_RADIUS * 2}
|
||||
/>
|
||||
|
|
|
@ -121,7 +121,6 @@ $border-radius: 0.25rem;
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.canvas-controls {
|
||||
|
|
|
@ -340,7 +340,8 @@ class PaintEditor extends React.Component {
|
|||
paper.project.view.pixelRatio,
|
||||
paper.view.zoom,
|
||||
paper.project.view.bounds.x,
|
||||
paper.project.view.bounds.y
|
||||
paper.project.view.bounds.y,
|
||||
isBitmap(this.props.format)
|
||||
);
|
||||
this.eyeDropper.pickX = -1;
|
||||
this.eyeDropper.pickY = -1;
|
||||
|
|
|
@ -50,7 +50,7 @@ const getRaster = function () {
|
|||
return _getLayer('isRasterLayer').children[0];
|
||||
};
|
||||
|
||||
const _getBackgroundGuideLayer = function () {
|
||||
const getBackgroundGuideLayer = function () {
|
||||
return _getLayer('isBackgroundGuideLayer');
|
||||
};
|
||||
|
||||
|
@ -75,7 +75,7 @@ const getGuideLayer = function () {
|
|||
* @return {object} an object of the removed layers, which should be passed to showGuideLayers to re-add them.
|
||||
*/
|
||||
const hideGuideLayers = function (includeRaster) {
|
||||
const backgroundGuideLayer = _getBackgroundGuideLayer();
|
||||
const backgroundGuideLayer = getBackgroundGuideLayer();
|
||||
const guideLayer = getGuideLayer();
|
||||
guideLayer.remove();
|
||||
backgroundGuideLayer.remove();
|
||||
|
@ -213,6 +213,7 @@ export {
|
|||
hideGuideLayers,
|
||||
showGuideLayers,
|
||||
getGuideLayer,
|
||||
getBackgroundGuideLayer,
|
||||
clearRaster,
|
||||
getRaster,
|
||||
setupLayers
|
||||
|
|
|
@ -1,11 +1,36 @@
|
|||
import paper from '@scratch/paper';
|
||||
import {createCanvas, getRaster, getBackgroundGuideLayer} from '../layer';
|
||||
|
||||
const LOUPE_RADIUS = 20;
|
||||
const ZOOM_SCALE = 3;
|
||||
|
||||
class EyeDropperTool extends paper.Tool {
|
||||
constructor (canvas, width, height, pixelRatio, zoom, offsetX, offsetY) {
|
||||
constructor (canvas, width, height, pixelRatio, zoom, offsetX, offsetY, isBitmap) {
|
||||
super();
|
||||
|
||||
const layer = isBitmap ? getRaster().layer : paper.project.activeLayer;
|
||||
const contentRaster3x = layer.rasterize(
|
||||
72 * ZOOM_SCALE * paper.view.zoom, false /* insert */, paper.view.bounds);
|
||||
const backgroundRaster3x = getBackgroundGuideLayer().rasterize(
|
||||
72 * ZOOM_SCALE * paper.view.zoom, false /* insert */, paper.view.bounds);
|
||||
|
||||
// Canvas from which loupe is cut, shows art and grid
|
||||
this.bufferCanvas = createCanvas(canvas.width * ZOOM_SCALE, canvas.height * ZOOM_SCALE);
|
||||
const bufferCanvasContext = this.bufferCanvas.getContext('2d');
|
||||
// Canvas to sample colors from; just the art
|
||||
this.colorCanvas = createCanvas(canvas.width * ZOOM_SCALE, canvas.height * ZOOM_SCALE);
|
||||
const colorCanvasContext = this.colorCanvas.getContext('2d');
|
||||
|
||||
backgroundRaster3x.onLoad = () => {
|
||||
bufferCanvasContext.drawImage(backgroundRaster3x.canvas, 0, 0);
|
||||
contentRaster3x.onLoad = () => {
|
||||
colorCanvasContext.drawImage(contentRaster3x.canvas, 0, 0);
|
||||
bufferCanvasContext.drawImage(this.colorCanvas, 0, 0);
|
||||
this.bufferLoaded = true;
|
||||
};
|
||||
if (contentRaster3x.loaded) contentRaster3x.onLoad();
|
||||
};
|
||||
|
||||
this.onMouseDown = this.handleMouseDown;
|
||||
this.onMouseMove = this.handleMouseMove;
|
||||
|
||||
|
@ -21,23 +46,6 @@ class EyeDropperTool extends paper.Tool {
|
|||
this.pickX = -1;
|
||||
this.pickY = -1;
|
||||
this.hideLoupe = true;
|
||||
|
||||
/*
|
||||
Chrome 64 has a bug that makes it impossible to use getImageData directly
|
||||
a 2d canvas. Until that is resolved, copy the canvas to a buffer canvas
|
||||
and read the data from there.
|
||||
https://github.com/LLK/scratch-paint/issues/276
|
||||
*/
|
||||
this.bufferLoaded = false;
|
||||
this.bufferCanvas = document.createElement('canvas');
|
||||
this.bufferCanvas.width = canvas.width;
|
||||
this.bufferCanvas.height = canvas.height;
|
||||
this.bufferImage = new Image();
|
||||
this.bufferImage.onload = () => {
|
||||
this.bufferCanvas.getContext('2d').drawImage(this.bufferImage, 0, 0);
|
||||
this.bufferLoaded = true;
|
||||
};
|
||||
this.bufferImage.src = canvas.toDataURL();
|
||||
}
|
||||
handleMouseMove (event) {
|
||||
// Set the pickX/Y for the color picker loop to pick up
|
||||
|
@ -54,6 +62,11 @@ class EyeDropperTool extends paper.Tool {
|
|||
if (!this.hideLoupe) {
|
||||
const colorInfo = this.getColorInfo(this.pickX, this.pickY, this.hideLoupe);
|
||||
if (!colorInfo) return;
|
||||
if (colorInfo.color[3] === 0) {
|
||||
// Alpha 0
|
||||
this.colorString = null;
|
||||
return;
|
||||
}
|
||||
const r = colorInfo.color[0];
|
||||
const g = colorInfo.color[1];
|
||||
const b = colorInfo.color[2];
|
||||
|
@ -68,18 +81,21 @@ class EyeDropperTool extends paper.Tool {
|
|||
}
|
||||
}
|
||||
getColorInfo (x, y, hideLoupe) {
|
||||
const artX = x / this.pixelRatio;
|
||||
const artY = y / this.pixelRatio;
|
||||
if (!this.bufferLoaded) return null;
|
||||
const ctx = this.bufferCanvas.getContext('2d');
|
||||
const colors = ctx.getImageData(x, y, 1, 1);
|
||||
const colorContext = this.colorCanvas.getContext('2d');
|
||||
const bufferContext = this.bufferCanvas.getContext('2d');
|
||||
const colors = colorContext.getImageData(artX * ZOOM_SCALE, artY * ZOOM_SCALE, 1, 1);
|
||||
return {
|
||||
x: x,
|
||||
y: y,
|
||||
color: colors.data,
|
||||
data: ctx.getImageData(
|
||||
x - LOUPE_RADIUS,
|
||||
y - LOUPE_RADIUS,
|
||||
LOUPE_RADIUS * 2,
|
||||
LOUPE_RADIUS * 2
|
||||
data: bufferContext.getImageData(
|
||||
(artX * ZOOM_SCALE) - (LOUPE_RADIUS * ZOOM_SCALE),
|
||||
(artY * ZOOM_SCALE) - (LOUPE_RADIUS * ZOOM_SCALE),
|
||||
LOUPE_RADIUS * 2 * ZOOM_SCALE,
|
||||
LOUPE_RADIUS * 2 * ZOOM_SCALE
|
||||
).data,
|
||||
hideLoupe: hideLoupe
|
||||
};
|
||||
|
@ -88,5 +104,6 @@ class EyeDropperTool extends paper.Tool {
|
|||
|
||||
export {
|
||||
EyeDropperTool as default,
|
||||
LOUPE_RADIUS
|
||||
LOUPE_RADIUS,
|
||||
ZOOM_SCALE
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue