cdf
Version:
A library for creating oldschool demo-like animations with JavaScript
263 lines (241 loc) • 8.55 kB
JavaScript
var utils = require('utils');
var defaults = require('defaults');
var total_canvas = 0;
var createCanvasElement = function(){
var canvas = document.createElement('canvas');
canvas.style.className = 'cdf_canvas cdf_canvas'+total_canvas++;
return canvas;
}
/**
* Constructor for cdf canvas element
* @param width Canvas width in pixels
* @param height Canvas height in pixels
* @param {Node|0} parent Parent element, to which the canvas will be assigned. If nothing is passed - the canvas will
* be "virtual" - it will exist only in memory. Be careful creating multiple canvas - it may eat all the memory quickly.
* @constructor
*/
var Canvas = function (width, height, parent){
if(width.length){
parent = height;
height = width[1];
width = width[1];
}
this.element = createCanvasElement();
this.ctx = this.element.getContext('2d');
this.style = this.element.style;
this
.attach(parent)
.size(width,height)
.handle(0,0);
}
Canvas.prototype = {
/**
* Attaches the canvas to DOM element
* @param {Node} element
* @returns {Canvas}
*/
attach: function(element){
if(element === 0) element = document.body;
if(element === undefined || element === false) return this;
element.appendChild(this.element);
this.parent = element;
return this;
},
/**
* Sets canvas size. Note that this will clear the canvas.
* @param width Width in pixels
* @param height Height in pixels
* @returns {Canvas} Returns itself
*/
size: function(width, height){
if(width && width.length){
height = width[1];
width = width[0];
}
this.width = this.w = width;
this.height = this.h = height;
this.cx = width/2;
this.cy = width/2;
this.center = [this.cx, this.cy];
this.ss = width>height?height:width;
this.element.setAttribute('width', width);
this.element.setAttribute('height', height);
return this.trigger('resize',[width, height]);
},
/**
* Sets canvas handle. Canvas are rotated around the handle and are placed at this candle on other canvas. If you
* Sets canvas handle. Canvas are rotated around the handle and are placed at this candle on other canvas. If you
* pass no parameters the handle will be centered;
* @param x Coordinate in pixels
* @param y Coordinate in pixels
* @returns {Canvas}
*/
handle: function(xyPair){
this.__handle = utils.coordPair(xyPair, this.center);
return this;
},
/**
* Clears canvas transparent. Uses clearRect
* @return {Canvas}
*/
clear: function () {
this.ctx.clearRect(0, 0, this.w, this.h);
return this;
},
/**
* Fills canvas with fillStyle. Uses fillRect.
* @param fillStyle
* @return {Canvas}
*/
fill: function (fillStyle) {
this.ctx.fillStyle = fillStyle;
this.ctx.fillRect(0, 0, this.w, this.h);
return this;
},
/**
* Draws a polygon on canvas.
* @param {ArrayLike|Points} dots An array of arrays of coordinates, like [[x,y], [x,y]].
* You can also use Points class.
* @param fillStyle Style to fill the polygon
* @param lineWidth Width of the stroke
* @param strokeStyle Style of the stroke
* @param closePath Set true to close the path
* @param {string} lineCap A line cap. "butt", "round" or "square".
* @return {Canvas}
*/
poly: function(dots,fillStyle,lineWidth,strokeStyle,closePath,lineCap){
if(lineCap)this.ctx.lineCap=lineCap;
this.ctx.beginPath();
this.ctx.moveTo(dots[0][0], dots[0][1]);
for(var i = 1; i<dots.length; i++){
this.ctx.lineTo(dots[i][0], dots[i][1]);
}
utils.strokeFill(this,fillStyle,lineWidth,strokeStyle,closePath);
return this;
},
/**
* Just a shorthand for ctx.fillRect with option to set a color.
* @param xyPair Pair of coordinates, as array
* @param whPair Pair of width and height, as array
* @param color fillStyle for rectangle
* @return {Canvas}
*/
rect: function(xyPair, whPair, color){
this.ctx.fillStyle = color;
xyPair = utils.coordPair(xyPair);
whPair = utils.coordPair(whPair);
this.ctx.fillRect(xyPair[0], xyPair[1], whPair[0], whPair[1]);
return this;
},
/**
* A shorthand to draw a line through two coulpes of coordinates
* @param {Array} p1
* @param {Array} p2
* @param strokeSize
* @param strokeStyle
* @return {*|Canvas}
*/
line: function (p1, p2, strokeSize, strokeStyle) {
return this.poly([utils.coordPair(p1),utils.coordPair(p2)],undefined,strokeSize,strokeStyle);
},
/**
* Just a shorthand for ctx.arc with stroke and fill. The arc will not be closed.
* @param {Array} xyPair
* @param {number} r Radius
* @param fillStyle Fill style
* @param lineWidth Line width
* @param strokeStyle Stroke style
* @param sa Starting angle
* @param ea Ending angle
* @return {Canvas}
*/
circle: function(xyPair,r,fillStyle,lineWidth,strokeStyle,sa,ea){
xyPair = utils.coordPair(xyPair);
this.ctx.beginPath();
this.ctx.arc(xyPair[0],xyPair[1],r,sa||0,ea===undefined?360:+ea);
utils.strokeFill(this,fillStyle,lineWidth,strokeStyle,false);
return this;
},
/**
* Shorthand to place a point at coordinates. The point is actually a fillRect. Not the fastest option: if you need
* thousands of points you probably should use Buffer class.
* @param {ArrayLike} xyPair An array of [x,y]
* @param color fillStyle for the point
* @param width Width of a dot. Default will be just a point of 1 px at coordinates.
* @return {*}
*/
plot: function (xyPair, color, width) {
if(color)this.ctx.fillStyle = color;
var wc = width === 1? 0 : width/2;
this.ctx.fillRect(xyPair[0]-wc, xyPair[1]-wc, width, width);
return this;
},
/**
* Converts canvas to base64 url. toDataUrl with image/png is used
* @param quality conversion quality. Default is 0.92
* @param type type. Default is image/png
* @return {*}
*/
toBase64: function(quality, type){
return this.element.toDataURL(type || 'image/png',quality || 0.92);
},
/**
* Draws canvas onto another canvas at handle
* @param {HTMLCanvasElement|Canvas} dest
* @param {Array} [pos]
* @param {Number} [rot]
* @param {Array} [scale]
* @param {Number} [alpha]
* @param {Array} [partPos] Position of the canvas to start drawing from
* @param {Array} [partSize] Width of the canvas area to be drawn
* // TODO: Make reasonable handling of handle on this
* @return {Canvas}
*/
draw: function(dest, pos, rot,scale,alpha, partPos, partSize){
if(dest.ctx)dest = dest.ctx;
dest.save();
pos = utils.coordPair(pos);
partPos = utils.coordPair(partPos);
partSize = utils.coordPair(partSize,[this.w-partPos[0], this.h-partPos[1]]);
if(alpha!==undefined) dest.globalAlpha = alpha;
dest.translate(pos[0],pos[1]);
if(rot!==undefined)dest.rotate(rot*utils.DEG_TO_RAD);
scale = utils.coordPair(scale, [1,1]);
dest.scale(scale[0],scale[1]);
dest.translate(-this.__handle[0], -this.__handle[1]);
dest.drawImage(this.element, partPos[0], partPos[1], partSize[0], partSize[1],0,0,partSize[0], partSize[1]);
dest.restore();
return this;
},
/**
* Draws text on canvas. The text does not have word wrapping.
* @param {string} text Text to draw
* @param {Array} xyPair Coordinates where to draw text. Text is drawn from upper left corner by default.
* @param fillStyle Fillstyle for text. Or color.
* @param {string} font Font for text. Default is '12px sans-serif'
* @param {string} align Text align. Values are: start,end,left,right,center,justify,match-parent,justify-all
* @param {string} baseline Text baseline. Values are: alphabetic,top,hanging,middle,ideographic,bottom
* @return {Canvas}
*/
text: function (text, xyPair, fillStyle, font, align, baseline) {
xyPair = xyPair===undefined?defaults.fontPos:utils.coordPair(xyPair);
if(fillStyle!==undefined) this.ctx.fillStyle = fillStyle;
this.ctx.textAlign = align || defaults.fontAlign;
this.ctx.textBaseline = baseline || defaults.fontBaseline;
this.ctx.font = font || defaults.font;
this.ctx.fillText(text, xyPair[0], xyPair[1]);
return this;
},
/**
* Measure text using measuretext.
* @param text Text to measure
* @param font Font
* @return {*}
*/
textWidth: function(text,font){
this.ctx.font = font || defaults.font;
return this.ctx.measureText(text);
}
};
utils.eventer(Canvas.prototype);
module.exports = Canvas;