GT2/GT2-iOS/node_modules/art/modes/canvas/base.js

187 lines
5.8 KiB
JavaScript

var Class = require('../../core/class');
var Color = require('../../core/color');
var Transform = require('../../core/transform');
var Node = require('./node');
var genericCanvas = typeof document !== 'undefined' && document.createElement('canvas'),
genericContext = genericCanvas && genericCanvas.getContext && genericCanvas.getContext('2d');
function recolorImage(img, color1, color2){
// TODO: Fix this experimental implementation
color1 = Color.detach(color1);
color2 = Color.detach(color2);
var canvas = document.createElement('canvas'),
context = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;
context.fillStyle = color2[0];
context.fillRect(0, 0, img.width, img.height);
context.globalCompositeOperation = 'lighter';
context.drawImage(img, 0, 0);
return canvas;
}
var Base = Class(Node, {
initialize: function(){
this._fill = null;
this._pendingFill = null;
this._fillTransform = null;
this._stroke = null;
this._strokeCap = null;
this._strokeDash = null;
this._strokeJoin = null;
this._strokeWidth = null;
},
/* styles */
_addColors: function(gradient, stops){
// Enumerate stops, assumes offsets are enumerated in order
// TODO: Sort. Chrome doesn't always enumerate in expected order but requires stops to be specified in order.
if ('length' in stops) for (var i = 0, l = stops.length - 1; i <= l; i++)
gradient.addColorStop(i / l, new Color(stops[i]).toString());
else for (var offset in stops)
gradient.addColorStop(offset, new Color(stops[offset]).toString());
return gradient;
},
fill: function(color){
if (arguments.length > 1) return this.fillLinear(arguments);
if (this._pendingFill) this._pendingFill();
this._fill = color ? new Color(color).toString() : null;
return this.invalidate();
},
fillRadial: function(stops, focusX, focusY, radiusX, radiusY, centerX, centerY){
if (focusX == null) focusX = (this.left || 0) + (this.width || 0) * 0.5;
if (focusY == null) focusY = (this.top || 0) + (this.height || 0) * 0.5;
if (radiusY == null) radiusY = radiusX || (this.height * 0.5) || 0;
if (radiusX == null) radiusX = (this.width || 0) * 0.5;
if (centerX == null) centerX = focusX;
if (centerY == null) centerY = focusY;
centerX += centerX - focusX;
centerY += centerY - focusY;
if (radiusX === 0 || radiusX === '0') return this.fillLinear(stops);
var ys = radiusY / radiusX;
if (this._pendingFill) this._pendingFill();
var gradient = genericContext.createRadialGradient(focusX, focusY / ys, 0, centerX, centerY / ys, radiusX * 2);
// Double fill radius to simulate repeating gradient
if ('length' in stops) for (var i = 0, l = stops.length - 1; i <= l; i++){
gradient.addColorStop(i / l / 2, new Color(stops[i]).toString());
gradient.addColorStop(1 - i / l / 2, new Color(stops[i]).toString());
} else for (var offset in stops){
gradient.addColorStop(offset / 2, new Color(stops[offset]).toString());
gradient.addColorStop(1- offset / 2, new Color(stops[offset]).toString());
}
this._fill = gradient;
this._fillTransform = new Transform(1, 0, 0, ys);
return this.invalidate();
},
fillLinear: function(stops, x1, y1, x2, y2){
if (arguments.length < 5){
var angle = ((x1 == null) ? 270 : x1) * Math.PI / 180;
var x = Math.cos(angle), y = -Math.sin(angle),
l = (Math.abs(x) + Math.abs(y)) / 2,
w = this.width || 1, h = this.height || 1;
x *= l; y *= l;
x1 = 0.5 - x;
x2 = 0.5 + x;
y1 = 0.5 - y;
y2 = 0.5 + y;
this._fillTransform = new Transform(w, 0, 0, h);
} else {
this._fillTransform = null;
}
if (this._pendingFill) this._pendingFill();
var gradient = genericContext.createLinearGradient(x1, y1, x2, y2);
this._addColors(gradient, stops);
this._fill = gradient;
return this.invalidate();
},
fillImage: function(url, width, height, left, top, color1, color2){
if (this._pendingFill) this._pendingFill();
var img = url;
if (!(img instanceof Image)){
img = new Image();
img.src = url;
}
if (img.width && img.height){
return this._fillImage(img, width, height, left || 0, top || 0, color1, color2);
}
// Not yet loaded
this._fill = null;
var self = this,
callback = function(){
cancel();
self._fillImage(img, width, height, left || 0, top || 0, color1, color2);
},
cancel = function(){
img.removeEventListener('load', callback, false);
self._pendingFill = null;
};
this._pendingFill = cancel;
img.addEventListener('load', callback, false);
return this;
},
_fillImage: function(img, width, height, left, top, color1, color2){
var w = width ? width / img.width : 1,
h = height ? height / img.height : 1;
if (color1 != null) img = recolorImage(img, color1, color2);
this._fill = genericContext.createPattern(img, 'repeat');
this._fillTransform = new Transform(w, 0, 0, h, left || 0, top || 0);
return this.invalidate();
},
stroke: function(color, width, cap, join, dash){
this._stroke = color ? new Color(color).toString() : null;
this._strokeWidth = (width != null) ? width : 1;
this._strokeCap = (cap != null) ? cap : 'round';
this._strokeJoin = (join != null) ? join : 'round';
this._strokeDash = dash;
return this.invalidate();
},
// Rendering
element_renderTo: Node.prototype.renderTo,
renderTo: function(context, xx, yx, xy, yy, x, y){
var opacity = this._opacity;
if (opacity == null || opacity >= 1){
return this.renderLayerTo(context, xx, yx, xy, yy, x, y);
}
if (this._fill && this._stroke){
return this.element_renderTo(context, xx, yx, xy, yy, x, y);
}
context.globalAlpha = opacity;
var r = this.renderLayerTo(context, xx, yx, xy, yy, x, y);
context.globalAlpha = 1;
return r;
},
renderLayerTo: function(context, xx, yx, xy, yy, x, y){
context.setTransform(xx, yx, xy, yy, x, y);
this.renderShapeTo(context);
}
});
Base._genericContext = genericContext;
module.exports = Base;