canvas2djs
Version:
HTML5 canvas based game engine
288 lines (258 loc) • 9.96 kB
text/typescript
import { Sprite } from '../sprite/Sprite';
import { Texture } from '../Texture';
import { uid, convertColor } from '../Util';
import { TextFragment } from '../measureText';
export class CanvasRenderTarget {
protected options: CanvasFrameBufferOptions;
protected context: CanvasRenderingContext2D;
protected isIdle: boolean;
id: number;
source: HTMLCanvasElement;
hasSomethingToDraw: boolean;
constructor() {
this.id = uid(this);
}
protected init() {
this.options = {
radius: 0,
width: 0,
height: 0,
texture: null,
sourceX: 0,
sourceY: 0,
sourceWidth: null,
sourceHeight: null,
grid: null,
bgColor: null,
borderColor: null,
borderWidth: null,
fragmentsPos: null,
bmfontLines: null,
textLines: null,
fontSize: null,
fontColor: null,
strokeColor: null,
strokeWidth: null,
};
this.isIdle = false;
this.hasSomethingToDraw = false;
}
public update(sprite: Sprite<{}>) {
let needUpdate: boolean;
for (let key in this.options) {
let value = sprite[key];
if (key === 'bgColor' || key === 'borderColor' || key === 'fontColor' || key === 'strokeColor') {
value = convertColor(value);
}
if (key === 'texture') {
let texture = value as Texture;
if (this.options.texture != texture) {
if (!texture || texture.ready) {
this.options.texture = texture;
needUpdate = true;
}
}
}
else if (this.options[key] != value) {
this.options[key] = value;
needUpdate = true;
}
}
if (!needUpdate) {
return false;
}
this.hasSomethingToDraw = true;
this.updateSource();
return true;
}
public updateSource() {
let { width, height, radius, bgColor, borderColor, texture, textLines, bmfontLines } = this.options;
if (((width === 0 || height === 0) && radius === 0) ||
(bgColor == null && borderColor == null && texture == null && textLines == null && bmfontLines == null)) {
return this.hasSomethingToDraw = false;
}
let canvasWidth: number;
let canvasHeight: number;
if (radius > 0) {
canvasWidth = canvasHeight = Math.ceil(radius * 2);
}
else {
canvasWidth = Math.ceil(width);
canvasHeight = Math.ceil(height);
}
if (canvasWidth === 0 || canvasHeight === 0) {
this.hasSomethingToDraw = false;
return;
}
if (!this.source) {
this.createCanvas();
}
let { source, context } = this;
if (source.width != canvasWidth || source.height !== canvasHeight) {
source.width = canvasWidth;
source.height = canvasHeight;
}
else {
context.clearRect(0, 0, source.width, source.height);
}
context.save();
this.drawBgColor();
this.drawTexture();
this.drawText();
this.drawBMFont();
this.drawBorder();
context.restore();
}
public drawBgColor() {
let context = this.context;
let { bgColor, radius, width, height } = this.options;
if (bgColor != null) {
context.fillStyle = bgColor;
context.beginPath();
if (radius > 0) {
context.arc(radius, radius, radius, 0, Math.PI * 2, true);
}
else {
context.rect(0, 0, width, height);
}
context.closePath();
context.fill();
this.hasSomethingToDraw = true;
}
}
public drawBorder() {
let context = this.context;
let { borderColor, borderWidth, radius, width, height } = this.options;
if (borderColor != null) {
context.lineWidth = borderWidth || 1;
context.strokeStyle = borderColor;
context.beginPath();
if (radius > 0) {
context.arc(radius, radius, radius, 0, Math.PI * 2, true);
}
else {
context.rect(0, 0, width, height);
}
context.closePath();
context.stroke();
this.hasSomethingToDraw = true;
}
}
public drawTexture() {
let context = this.context;
let { borderWidth, sourceX, sourceY, sourceWidth, sourceHeight, grid, texture, width, height } = this.options;
if (texture && texture.ready && texture.width !== 0 && texture.height !== 0) {
sourceWidth = sourceWidth == null ? texture.width : sourceWidth;
sourceHeight = sourceHeight == null ? texture.height : sourceHeight;
if (!Array.isArray(grid)) {
context.drawImage(texture.source, sourceX, sourceY, sourceWidth, sourceHeight, 0, 0, width, height);
}
else {
let gridSource = texture.createGridSource(width, height, sourceX, sourceY, sourceWidth, sourceHeight, grid);
context.drawImage(gridSource, 0, 0, width, height);
}
this.hasSomethingToDraw = true;
}
}
public drawText() {
let context = this.context;
let { fontColor, strokeColor, strokeWidth, fragmentsPos, textLines } = this.options;
if (textLines) {
let index = 0;
context.textAlign = "left";
context.textBaseline = 'middle';
context.lineJoin = 'round';
for (let i = 0, l = textLines.length; i < l; i++) {
let line = textLines[i];
for (let i = 0, fragment: TextFragment; fragment = line.fragments[i]; i++) {
if (fragment.text) {
let _fontColor = fragment.fontColor != null ? fragment.fontColor : fontColor;
let _strokeColor = "strokeColor" in fragment ? fragment.strokeColor : strokeColor;
let _strokeWidth = "strokeWidth" in fragment ? fragment.strokeWidth : strokeWidth;
let pos = fragmentsPos[index];
context.font = fragment.fontStyle + ' ' + fragment.fontWeight + ' ' + fragment.fontSize + 'px ' + fragment.fontName;
context.fillStyle = convertColor(_fontColor);
if (_strokeColor != null) {
context.strokeStyle = convertColor(_strokeColor || 0x000);
context.lineWidth = _strokeWidth || 1;
context.strokeText(fragment.text, pos.x, pos.y);
}
context.fillText(fragment.text, pos.x, pos.y);
}
index += 1;
}
}
this.hasSomethingToDraw = true;
}
}
public drawBMFont() {
let context = this.context;
let { bmfontLines, fragmentsPos, fontSize } = this.options;
if (bmfontLines) {
let index = 0;
for (let l = bmfontLines.length, i = 0; i < l; i++) {
let line = bmfontLines[i];
for (let j = 0, n = line.fragments.length; j < n; j++) {
let word = line.fragments[j];
if (word) {
let pos = fragmentsPos[index];
context.drawImage(word.source, 0, 0, word.width, word.height, pos.x, pos.y, fontSize, pos.height);
}
index += 1;
}
}
this.hasSomethingToDraw = true;
}
}
protected createCanvas() {
this.source = document.createElement('canvas');
this.context = this.source.getContext('2d');
}
protected static instanceMap: { [id: number]: CanvasRenderTarget } = {};
protected static instanceList: CanvasRenderTarget[] = [];
public static create(id: number) {
let renderTarget = this.instanceMap[id];
if (!renderTarget) {
for (let i = 0; renderTarget = this.instanceList[i]; i++) {
if (renderTarget.isIdle) {
break;
}
}
if (!renderTarget) {
renderTarget = new this();
this.instanceList.push(renderTarget);
}
renderTarget.init();
this.instanceMap[id] = renderTarget;
}
return renderTarget;
}
public static remove(id: number) {
let spriteTexture = this.instanceMap[id];
if (spriteTexture) {
spriteTexture.isIdle = true;
delete this.instanceMap[id];
}
}
}
export type CanvasFrameBufferOptions = {
grid: number[];
radius: number;
width: number;
height: number;
bgColor: string;
borderColor: string;
borderWidth: number;
texture: Texture;
sourceX: number;
sourceY: number;
sourceWidth: number;
sourceHeight: number;
fontSize: number;
fontColor: string;
strokeColor: string;
strokeWidth: number;
fragmentsPos: { x: number; y: number; height?: number; }[];
textLines: { fragments: TextFragment[]; width: number }[];
bmfontLines: { width: number; fragments: Texture[] }[];
}