fabric.js/src/brushes/spray_brush.class.js
2014-07-18 11:16:23 +02:00

205 lines
4.6 KiB
JavaScript

/**
* SprayBrush class
* @class fabric.SprayBrush
*/
fabric.SprayBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabric.SprayBrush.prototype */ {
/**
* Width of a spray
* @type Number
* @default
*/
width: 10,
/**
* Density of a spray (number of dots per chunk)
* @type Number
* @default
*/
density: 20,
/**
* Width of spray dots
* @type Number
* @default
*/
dotWidth: 1,
/**
* Width variance of spray dots
* @type Number
* @default
*/
dotWidthVariance: 1,
/**
* Whether opacity of a dot should be random
* @type Boolean
* @default
*/
randomOpacity: false,
/**
* Whether overlapping dots (rectangles) should be removed (for performance reasons)
* @type Boolean
* @default
*/
optimizeOverlapping: true,
/**
* Constructor
* @param {fabric.Canvas} canvas
* @return {fabric.SprayBrush} Instance of a spray brush
*/
initialize: function(canvas) {
this.canvas = canvas;
this.sprayChunks = [ ];
},
/**
* Invoked on mouse down
* @param {Object} pointer
*/
onMouseDown: function(pointer) {
this.sprayChunks.length = 0;
this.canvas.clearContext(this.canvas.contextTop);
this._setShadow();
this.addSprayChunk(pointer);
this.render();
},
/**
* Invoked on mouse move
* @param {Object} pointer
*/
onMouseMove: function(pointer) {
this.addSprayChunk(pointer);
this.render();
},
/**
* Invoked on mouse up
*/
onMouseUp: function() {
var originalRenderOnAddRemove = this.canvas.renderOnAddRemove;
this.canvas.renderOnAddRemove = false;
var rects = [ ];
for (var i = 0, ilen = this.sprayChunks.length; i < ilen; i++) {
var sprayChunk = this.sprayChunks[i];
for (var j = 0, jlen = sprayChunk.length; j < jlen; j++) {
var rect = new fabric.Rect({
width: sprayChunk[j].width,
height: sprayChunk[j].width,
left: sprayChunk[j].x + 1,
top: sprayChunk[j].y + 1,
originX: 'center',
originY: 'center',
fill: this.color
});
this.shadow && rect.setShadow(this.shadow);
rects.push(rect);
}
}
if (this.optimizeOverlapping) {
rects = this._getOptimizedRects(rects);
}
var group = new fabric.Group(rects, { originX: 'center', originY: 'center' });
group.canvas = this.canvas;
this.canvas.add(group);
this.canvas.fire('path:created', { path: group });
this.canvas.clearContext(this.canvas.contextTop);
this._resetShadow();
this.canvas.renderOnAddRemove = originalRenderOnAddRemove;
this.canvas.renderAll();
},
/**
* @private
* @param {Array} rects
*/
_getOptimizedRects: function(rects) {
// avoid creating duplicate rects at the same coordinates
var uniqueRects = { }, key;
for (var i = 0, len = rects.length; i < len; i++) {
key = rects[i].left + '' + rects[i].top;
if (!uniqueRects[key]) {
uniqueRects[key] = rects[i];
}
}
var uniqueRectsArray = [ ];
for (key in uniqueRects) {
uniqueRectsArray.push(uniqueRects[key]);
}
return uniqueRectsArray;
},
/**
* Renders brush
*/
render: function() {
var ctx = this.canvas.contextTop;
ctx.fillStyle = this.color;
var v = this.canvas.viewportTransform;
ctx.save();
ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
for (var i = 0, len = this.sprayChunkPoints.length; i < len; i++) {
var point = this.sprayChunkPoints[i];
if (typeof point.opacity !== 'undefined') {
ctx.globalAlpha = point.opacity;
}
ctx.fillRect(point.x, point.y, point.width, point.width);
}
ctx.restore();
},
/**
* @param {Object} pointer
*/
addSprayChunk: function(pointer) {
this.sprayChunkPoints = [ ];
var x, y, width, radius = this.width / 2;
for (var i = 0; i < this.density; i++) {
x = fabric.util.getRandomInt(pointer.x - radius, pointer.x + radius);
y = fabric.util.getRandomInt(pointer.y - radius, pointer.y + radius);
if (this.dotWidthVariance) {
width = fabric.util.getRandomInt(
// bottom clamp width to 1
Math.max(1, this.dotWidth - this.dotWidthVariance),
this.dotWidth + this.dotWidthVariance);
}
else {
width = this.dotWidth;
}
var point = new fabric.Point(x, y);
point.width = width;
if (this.randomOpacity) {
point.opacity = fabric.util.getRandomInt(0, 100) / 100;
}
this.sprayChunkPoints.push(point);
}
this.sprayChunks.push(this.sprayChunkPoints);
}
});