mirror of
https://github.com/konvajs/konva.git
synced 2026-03-03 16:58:33 +08:00
introduced new Context class. I've bumped up the next release to v4.7.0 because this is a relatively big mind shift in how the framework works, and it's a big enough API change to warrant a minor update. This is the first step towards enabling context tracing for stellar unit testing
This commit is contained in:
399
src/Canvas.js
399
src/Canvas.js
@@ -34,25 +34,28 @@
|
||||
init: function(config) {
|
||||
config = config || {};
|
||||
|
||||
var width = config.width || 0,
|
||||
height = config.height || 0,
|
||||
pixelRatio = config.pixelRatio || _pixelRatio,
|
||||
contextType = config.contextType || '2d';
|
||||
var pixelRatio = config.pixelRatio || _pixelRatio;
|
||||
|
||||
this.pixelRatio = pixelRatio;
|
||||
this.element = document.createElement('canvas');
|
||||
this._canvas = document.createElement('canvas');
|
||||
|
||||
// set inline styles
|
||||
this.element.style.padding = 0;
|
||||
this.element.style.margin = 0;
|
||||
this.element.style.border = 0;
|
||||
this.element.style.background = 'transparent';
|
||||
this.element.style.position = 'absolute';
|
||||
this.element.style.top = 0;
|
||||
this.element.style.left = 0;
|
||||
|
||||
this.context = this.element.getContext(contextType);
|
||||
this.setSize(width, height);
|
||||
this._canvas.style.padding = 0;
|
||||
this._canvas.style.margin = 0;
|
||||
this._canvas.style.border = 0;
|
||||
this._canvas.style.background = 'transparent';
|
||||
this._canvas.style.position = 'absolute';
|
||||
this._canvas.style.top = 0;
|
||||
this._canvas.style.left = 0;
|
||||
},
|
||||
/**
|
||||
* get canvas context
|
||||
* @method
|
||||
* @memberof Kinetic.Canvas.prototype
|
||||
* @returns {CanvasContext} context
|
||||
*/
|
||||
getContext: function() {
|
||||
return this.context;
|
||||
},
|
||||
/**
|
||||
* get pixel ratio
|
||||
@@ -79,32 +82,6 @@
|
||||
this.pixelRatio = pixelRatio;
|
||||
this.setSize(this.getWidth(), this.getHeight());
|
||||
},
|
||||
/**
|
||||
* reset canvas context transform
|
||||
* @method
|
||||
* @memberof Kinetic.Canvas.prototype
|
||||
*/
|
||||
reset: function() {
|
||||
this.getContext().setTransform(1 * this.pixelRatio, 0, 0, 1 * this.pixelRatio, 0, 0);
|
||||
},
|
||||
/**
|
||||
* get canvas element
|
||||
* @method
|
||||
* @memberof Kinetic.Canvas.prototype
|
||||
* @returns {DomElement} canvas element
|
||||
*/
|
||||
getElement: function() {
|
||||
return this.element;
|
||||
},
|
||||
/**
|
||||
* get canvas context
|
||||
* @method
|
||||
* @memberof Kinetic.Canvas.prototype
|
||||
* @returns {CanvasContext} context
|
||||
*/
|
||||
getContext: function() {
|
||||
return this.context;
|
||||
},
|
||||
/**
|
||||
* set width
|
||||
* @method
|
||||
@@ -113,8 +90,8 @@
|
||||
*/
|
||||
setWidth: function(width) {
|
||||
// take into account pixel ratio
|
||||
this.width = this.element.width = width * this.pixelRatio;
|
||||
this.element.style.width = width + 'px';
|
||||
this.width = this._canvas.width = width * this.pixelRatio;
|
||||
this._canvas.style.width = width + 'px';
|
||||
},
|
||||
/**
|
||||
* set height
|
||||
@@ -124,8 +101,8 @@
|
||||
*/
|
||||
setHeight: function(height) {
|
||||
// take into account pixel ratio
|
||||
this.height = this.element.height = height * this.pixelRatio;
|
||||
this.element.style.height = height + 'px';
|
||||
this.height = this._canvas.height = height * this.pixelRatio;
|
||||
this._canvas.style.height = height + 'px';
|
||||
},
|
||||
/**
|
||||
* get width
|
||||
@@ -156,24 +133,6 @@
|
||||
this.setWidth(width);
|
||||
this.setHeight(height);
|
||||
},
|
||||
/**
|
||||
* clear canvas
|
||||
* @method
|
||||
* @memberof Kinetic.Canvas.prototype
|
||||
*/
|
||||
clear: function(clip) {
|
||||
var context = this.getContext(),
|
||||
pos, size;
|
||||
|
||||
if (clip) {
|
||||
pos = Kinetic.Util._getXY(clip);
|
||||
size = Kinetic.Util._getSize(clip);
|
||||
context.clearRect(pos.x || 0, pos.y || 0, size.width, size.height);
|
||||
}
|
||||
else {
|
||||
context.clearRect(0, 0, this.getWidth(), this.getHeight());
|
||||
}
|
||||
},
|
||||
/**
|
||||
* to data url
|
||||
* @method
|
||||
@@ -186,326 +145,56 @@
|
||||
try {
|
||||
// If this call fails (due to browser bug, like in Firefox 3.6),
|
||||
// then revert to previous no-parameter image/png behavior
|
||||
return this.element.toDataURL(mimeType, quality);
|
||||
return this._canvas.toDataURL(mimeType, quality);
|
||||
}
|
||||
catch(e) {
|
||||
try {
|
||||
return this.element.toDataURL();
|
||||
return this._canvas.toDataURL();
|
||||
}
|
||||
catch(err) {
|
||||
Kinetic.Util.warn('Unable to get data URL. ' + err.message);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* fill shape
|
||||
* @method
|
||||
* @memberof Kinetic.Canvas.prototype
|
||||
* @param {Kinetic.Shape} shape
|
||||
*/
|
||||
fill: function(shape) {
|
||||
if(shape.getFillEnabled()) {
|
||||
this._fill(shape);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* stroke shape
|
||||
* @method
|
||||
* @memberof Kinetic.Canvas.prototype
|
||||
* @param {Kinetic.Shape} shape
|
||||
*/
|
||||
stroke: function(shape) {
|
||||
if(shape.getStrokeEnabled()) {
|
||||
this._stroke(shape);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* fill, stroke, and apply shadows
|
||||
* will only be applied to either the fill or stroke. Fill
|
||||
* is given priority over stroke.
|
||||
* @method
|
||||
* @memberof Kinetic.Canvas.prototype
|
||||
* @param {Kinetic.Shape} shape
|
||||
*/
|
||||
fillStroke: function(shape) {
|
||||
var fillEnabled = shape.getFillEnabled();
|
||||
if(fillEnabled) {
|
||||
this._fill(shape);
|
||||
}
|
||||
|
||||
if(shape.getStrokeEnabled()) {
|
||||
this._stroke(shape, shape.hasShadow() && shape.hasFill() && fillEnabled);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* apply shadow
|
||||
* @method
|
||||
* @memberof Kinetic.Canvas.prototype
|
||||
* @param {Kinetic.Shape} shape
|
||||
* @param {Function} drawFunc
|
||||
*/
|
||||
applyShadow: function(shape, drawFunc) {
|
||||
var context = this.context;
|
||||
context.save();
|
||||
this._applyShadow(shape);
|
||||
drawFunc();
|
||||
context.restore();
|
||||
drawFunc();
|
||||
},
|
||||
_applyLineCap: function(shape) {
|
||||
var lineCap = shape.getLineCap();
|
||||
if(lineCap) {
|
||||
this.context.lineCap = lineCap;
|
||||
}
|
||||
},
|
||||
_applyOpacity: function(shape) {
|
||||
var absOpacity = shape.getAbsoluteOpacity();
|
||||
if(absOpacity !== 1) {
|
||||
this.context.globalAlpha = absOpacity;
|
||||
}
|
||||
},
|
||||
_applyLineJoin: function(shape) {
|
||||
var lineJoin = shape.getLineJoin();
|
||||
if(lineJoin) {
|
||||
this.context.lineJoin = lineJoin;
|
||||
}
|
||||
},
|
||||
_applyAncestorTransforms: function(node) {
|
||||
var m = node.getAbsoluteTransform().getMatrix();
|
||||
this.context.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
|
||||
},
|
||||
_clip: function(container) {
|
||||
var context = this.getContext(),
|
||||
clipX = container.getClipX() || 0,
|
||||
clipY = container.getClipY() || 0,
|
||||
clipWidth = container.getClipWidth(),
|
||||
clipHeight = container.getClipHeight();
|
||||
|
||||
context.save();
|
||||
this._applyAncestorTransforms(container);
|
||||
context.beginPath();
|
||||
context.rect(clipX, clipY, clipWidth, clipHeight);
|
||||
context.clip();
|
||||
this.reset();
|
||||
container._drawChildren(this);
|
||||
context.restore();
|
||||
}
|
||||
};
|
||||
|
||||
Kinetic.SceneCanvas = function(config) {
|
||||
config = config || {};
|
||||
var width = config.width || 0,
|
||||
height = config.height || 0;
|
||||
|
||||
Kinetic.Canvas.call(this, config);
|
||||
this.context = new Kinetic.SceneContext(this);
|
||||
this.setSize(width, height);
|
||||
};
|
||||
|
||||
Kinetic.SceneCanvas.prototype = {
|
||||
setWidth: function(width) {
|
||||
var pixelRatio = this.pixelRatio;
|
||||
var pixelRatio = this.pixelRatio,
|
||||
_context = this.getContext()._context;
|
||||
|
||||
Kinetic.Canvas.prototype.setWidth.call(this, width);
|
||||
this.context.scale(pixelRatio, pixelRatio);
|
||||
_context.scale(pixelRatio, pixelRatio);
|
||||
},
|
||||
setHeight: function(height) {
|
||||
var pixelRatio = this.pixelRatio;
|
||||
var pixelRatio = this.pixelRatio,
|
||||
_context = this.getContext()._context;
|
||||
|
||||
Kinetic.Canvas.prototype.setHeight.call(this, height);
|
||||
this.context.scale(pixelRatio, pixelRatio);
|
||||
},
|
||||
_fillColor: function(shape) {
|
||||
var context = this.context, fill = shape.getFill();
|
||||
context.fillStyle = fill;
|
||||
shape._fillFunc(context);
|
||||
},
|
||||
_fillPattern: function(shape) {
|
||||
var context = this.context,
|
||||
fillPatternImage = shape.getFillPatternImage(),
|
||||
fillPatternX = shape.getFillPatternX(),
|
||||
fillPatternY = shape.getFillPatternY(),
|
||||
fillPatternScale = shape.getFillPatternScale(),
|
||||
fillPatternRotation = shape.getFillPatternRotation(),
|
||||
fillPatternOffset = shape.getFillPatternOffset(),
|
||||
fillPatternRepeat = shape.getFillPatternRepeat();
|
||||
|
||||
if(fillPatternX || fillPatternY) {
|
||||
context.translate(fillPatternX || 0, fillPatternY || 0);
|
||||
}
|
||||
if(fillPatternRotation) {
|
||||
context.rotate(fillPatternRotation);
|
||||
}
|
||||
if(fillPatternScale) {
|
||||
context.scale(fillPatternScale.x, fillPatternScale.y);
|
||||
}
|
||||
if(fillPatternOffset) {
|
||||
context.translate(-1 * fillPatternOffset.x, -1 * fillPatternOffset.y);
|
||||
}
|
||||
|
||||
context.fillStyle = context.createPattern(fillPatternImage, fillPatternRepeat || 'repeat');
|
||||
context.fill();
|
||||
},
|
||||
_fillLinearGradient: function(shape) {
|
||||
var context = this.context,
|
||||
start = shape.getFillLinearGradientStartPoint(),
|
||||
end = shape.getFillLinearGradientEndPoint(),
|
||||
colorStops = shape.getFillLinearGradientColorStops(),
|
||||
grd = context.createLinearGradient(start.x, start.y, end.x, end.y);
|
||||
|
||||
if (colorStops) {
|
||||
// build color stops
|
||||
for(var n = 0; n < colorStops.length; n += 2) {
|
||||
grd.addColorStop(colorStops[n], colorStops[n + 1]);
|
||||
}
|
||||
context.fillStyle = grd;
|
||||
context.fill();
|
||||
}
|
||||
},
|
||||
_fillRadialGradient: function(shape) {
|
||||
var context = this.context,
|
||||
start = shape.getFillRadialGradientStartPoint(),
|
||||
end = shape.getFillRadialGradientEndPoint(),
|
||||
startRadius = shape.getFillRadialGradientStartRadius(),
|
||||
endRadius = shape.getFillRadialGradientEndRadius(),
|
||||
colorStops = shape.getFillRadialGradientColorStops(),
|
||||
grd = context.createRadialGradient(start.x, start.y, startRadius, end.x, end.y, endRadius);
|
||||
|
||||
// build color stops
|
||||
for(var n = 0; n < colorStops.length; n += 2) {
|
||||
grd.addColorStop(colorStops[n], colorStops[n + 1]);
|
||||
}
|
||||
context.fillStyle = grd;
|
||||
context.fill();
|
||||
},
|
||||
_fill: function(shape, skipShadow) {
|
||||
var context = this.context,
|
||||
hasColor = shape.getFill(),
|
||||
hasPattern = shape.getFillPatternImage(),
|
||||
hasLinearGradient = shape.getFillLinearGradientColorStops(),
|
||||
hasRadialGradient = shape.getFillRadialGradientColorStops(),
|
||||
fillPriority = shape.getFillPriority();
|
||||
|
||||
context.save();
|
||||
|
||||
if(!skipShadow && shape.hasShadow()) {
|
||||
this._applyShadow(shape);
|
||||
}
|
||||
|
||||
// priority fills
|
||||
if(hasColor && fillPriority === 'color') {
|
||||
this._fillColor(shape);
|
||||
}
|
||||
else if(hasPattern && fillPriority === 'pattern') {
|
||||
this._fillPattern(shape);
|
||||
}
|
||||
else if(hasLinearGradient && fillPriority === 'linear-gradient') {
|
||||
this._fillLinearGradient(shape);
|
||||
}
|
||||
else if(hasRadialGradient && fillPriority === 'radial-gradient') {
|
||||
this._fillRadialGradient(shape);
|
||||
}
|
||||
// now just try and fill with whatever is available
|
||||
else if(hasColor) {
|
||||
this._fillColor(shape);
|
||||
}
|
||||
else if(hasPattern) {
|
||||
this._fillPattern(shape);
|
||||
}
|
||||
else if(hasLinearGradient) {
|
||||
this._fillLinearGradient(shape);
|
||||
}
|
||||
else if(hasRadialGradient) {
|
||||
this._fillRadialGradient(shape);
|
||||
}
|
||||
context.restore();
|
||||
|
||||
if(!skipShadow && shape.hasShadow()) {
|
||||
this._fill(shape, true);
|
||||
}
|
||||
},
|
||||
_stroke: function(shape, skipShadow) {
|
||||
var context = this.context,
|
||||
stroke = shape.getStroke(),
|
||||
strokeWidth = shape.getStrokeWidth(),
|
||||
dashArray = shape.getDashArray();
|
||||
|
||||
if(stroke || strokeWidth) {
|
||||
context.save();
|
||||
if (!shape.getStrokeScaleEnabled()) {
|
||||
|
||||
context.setTransform(1, 0, 0, 1, 0, 0);
|
||||
}
|
||||
this._applyLineCap(shape);
|
||||
if(dashArray && shape.getDashArrayEnabled()) {
|
||||
if(context.setLineDash) {
|
||||
context.setLineDash(dashArray);
|
||||
}
|
||||
else if('mozDash' in context) {
|
||||
context.mozDash = dashArray;
|
||||
}
|
||||
else if('webkitLineDash' in context) {
|
||||
context.webkitLineDash = dashArray;
|
||||
}
|
||||
}
|
||||
if(!skipShadow && shape.hasShadow()) {
|
||||
this._applyShadow(shape);
|
||||
}
|
||||
context.lineWidth = strokeWidth || 2;
|
||||
context.strokeStyle = stroke || 'black';
|
||||
shape._strokeFunc(context);
|
||||
context.restore();
|
||||
|
||||
if(!skipShadow && shape.hasShadow()) {
|
||||
this._stroke(shape, true);
|
||||
}
|
||||
}
|
||||
},
|
||||
_applyShadow: function(shape) {
|
||||
var context = this.context,
|
||||
util, absOpacity, color, blur, offset, shadowOpacity;
|
||||
|
||||
if(shape.hasShadow() && shape.getShadowEnabled()) {
|
||||
util = Kinetic.Util;
|
||||
absOpacity = shape.getAbsoluteOpacity();
|
||||
color = util.get(shape.getShadowColor(), 'black');
|
||||
blur = util.get(shape.getShadowBlur(), 5);
|
||||
shadowOpacity = util.get(shape.getShadowOpacity(), 0);
|
||||
offset = util.get(shape.getShadowOffset(), {
|
||||
x: 0,
|
||||
y: 0
|
||||
});
|
||||
|
||||
if(shadowOpacity) {
|
||||
context.globalAlpha = shadowOpacity * absOpacity;
|
||||
}
|
||||
|
||||
context.shadowColor = color;
|
||||
context.shadowBlur = blur;
|
||||
context.shadowOffsetX = offset.x;
|
||||
context.shadowOffsetY = offset.y;
|
||||
}
|
||||
_context.scale(pixelRatio, pixelRatio);
|
||||
}
|
||||
};
|
||||
Kinetic.Util.extend(Kinetic.SceneCanvas, Kinetic.Canvas);
|
||||
|
||||
Kinetic.HitCanvas = function(config) {
|
||||
config = config || {};
|
||||
var width = config.width || 0,
|
||||
height = config.height || 0;
|
||||
|
||||
Kinetic.Canvas.call(this, config);
|
||||
};
|
||||
|
||||
Kinetic.HitCanvas.prototype = {
|
||||
_fill: function(shape) {
|
||||
var context = this.context;
|
||||
context.save();
|
||||
context.fillStyle = shape.colorKey;
|
||||
shape._fillFuncHit(context);
|
||||
context.restore();
|
||||
},
|
||||
_stroke: function(shape) {
|
||||
var context = this.context,
|
||||
stroke = shape.getStroke(),
|
||||
strokeWidth = shape.getStrokeWidth();
|
||||
|
||||
if(stroke || strokeWidth) {
|
||||
this._applyLineCap(shape);
|
||||
context.lineWidth = strokeWidth || 2;
|
||||
context.strokeStyle = shape.colorKey;
|
||||
shape._strokeFuncHit(context);
|
||||
}
|
||||
}
|
||||
this.context = new Kinetic.HitContext(this);
|
||||
this.setSize(width, height);
|
||||
};
|
||||
Kinetic.Util.extend(Kinetic.HitCanvas, Kinetic.Canvas);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user