scratch-paint/src/helper/bit-tools/brush-tool.js

122 lines
4.1 KiB
JavaScript
Raw Normal View History

2018-04-04 17:37:11 -04:00
import paper from '@scratch/paper';
import {getRaster} from '../layer';
2018-04-26 14:54:44 -04:00
import {forEachLinePoint, getBrushMark} from '../bitmap';
2018-04-13 11:33:47 -04:00
import {getGuideLayer} from '../layer';
2018-04-04 17:37:11 -04:00
/**
2018-06-14 10:35:02 -04:00
* Tool for drawing with the bitmap brush and eraser
2018-04-04 17:37:11 -04:00
*/
class BrushTool extends paper.Tool {
/**
* @param {!function} onUpdateImage A callback to call when the image visibly changes
2018-06-14 10:35:02 -04:00
* @param {boolean} isEraser True if brush should erase
2018-04-04 17:37:11 -04:00
*/
2018-06-14 10:35:02 -04:00
constructor (onUpdateImage, isEraser) {
2018-04-04 17:37:11 -04:00
super();
this.onUpdateImage = onUpdateImage;
2018-06-14 10:35:02 -04:00
this.isEraser = isEraser;
2018-04-04 17:37:11 -04:00
// We have to set these functions instead of just declaring them because
// paper.js tools hook up the listeners in the setter functions.
2018-04-13 11:33:47 -04:00
this.onMouseMove = this.handleMouseMove;
2018-04-04 17:37:11 -04:00
this.onMouseDown = this.handleMouseDown;
this.onMouseDrag = this.handleMouseDrag;
this.onMouseUp = this.handleMouseUp;
this.colorState = null;
this.active = false;
this.lastPoint = null;
2018-04-13 11:33:47 -04:00
this.cursorPreview = null;
2018-04-04 17:37:11 -04:00
}
setColor (color) {
this.color = color;
this.tmpCanvas = getBrushMark(this.size, this.color, this.isEraser);
}
setBrushSize (size) {
// For performance, make sure this is an integer
this.size = Math.max(1, ~~size);
this.tmpCanvas = getBrushMark(this.size, this.color, this.isEraser);
}
// Draw a brush mark at the given point
draw (x, y) {
2018-04-20 10:57:10 -04:00
const roundedUpRadius = Math.ceil(this.size / 2);
2018-06-14 10:35:02 -04:00
const context = getRaster().getContext('2d');
if (this.isEraser) {
context.globalCompositeOperation = 'destination-out';
}
getRaster().drawImage(this.tmpCanvas, new paper.Point(~~x - roundedUpRadius, ~~y - roundedUpRadius));
2018-06-14 10:35:02 -04:00
if (this.isEraser) {
context.globalCompositeOperation = 'source-over';
}
2018-04-13 11:33:47 -04:00
}
updateCursorIfNeeded () {
if (!this.size) {
2018-04-13 11:33:47 -04:00
return;
}
2018-04-26 14:54:44 -04:00
2018-04-13 11:33:47 -04:00
// The cursor preview was unattached from the view by an outside process,
// such as changing costumes or undo.
if (this.cursorPreview && !this.cursorPreview.parent) {
this.cursorPreview = null;
}
if (!this.cursorPreview || !(this.lastSize === this.size && this.lastColor === this.color)) {
2018-04-13 11:33:47 -04:00
if (this.cursorPreview) {
this.cursorPreview.remove();
}
2018-06-14 10:35:02 -04:00
this.tmpCanvas = getBrushMark(this.size, this.color, this.isEraser);
2018-04-13 11:33:47 -04:00
this.cursorPreview = new paper.Raster(this.tmpCanvas);
this.cursorPreview.guide = true;
this.cursorPreview.parent = getGuideLayer();
this.cursorPreview.data.isHelperItem = true;
}
2018-04-26 14:54:44 -04:00
this.lastSize = this.size;
2018-04-13 11:33:47 -04:00
this.lastColor = this.color;
}
handleMouseMove (event) {
this.updateCursorIfNeeded();
this.cursorPreview.position = new paper.Point(~~event.point.x, ~~event.point.y);
}
2018-04-04 17:37:11 -04:00
handleMouseDown (event) {
if (event.event.button > 0) return; // only first mouse button
this.active = true;
2018-04-13 11:33:47 -04:00
2018-05-23 16:39:18 -04:00
if (this.cursorPreview) {
this.cursorPreview.remove();
}
2018-04-13 11:33:47 -04:00
this.draw(event.point.x, event.point.y);
this.lastPoint = event.point;
2018-04-04 17:37:11 -04:00
}
handleMouseDrag (event) {
if (event.event.button > 0 || !this.active) return; // only first mouse button
2018-04-20 10:57:10 -04:00
forEachLinePoint(this.lastPoint, event.point, this.draw.bind(this));
this.lastPoint = event.point;
2018-04-04 17:37:11 -04:00
}
handleMouseUp (event) {
if (event.event.button > 0 || !this.active) return; // only first mouse button
2018-04-20 10:57:10 -04:00
forEachLinePoint(this.lastPoint, event.point, this.draw.bind(this));
this.onUpdateImage();
this.lastPoint = null;
2018-04-04 17:37:11 -04:00
this.active = false;
2018-04-13 11:33:47 -04:00
this.updateCursorIfNeeded();
this.cursorPreview.position = new paper.Point(~~event.point.x, ~~event.point.y);
2018-04-04 17:37:11 -04:00
}
deactivateTool () {
2018-04-13 11:33:47 -04:00
this.active = false;
this.tmpCanvas = null;
if (this.cursorPreview) {
this.cursorPreview.remove();
this.cursorPreview = null;
}
2018-04-04 17:37:11 -04:00
}
}
export default BrushTool;