rot-js
Version:
A roguelike toolkit in JavaScript
106 lines (105 loc) • 3.87 kB
JavaScript
import Canvas from "./canvas.js";
/**
* @class Rectangular backend
* @private
*/
export default class Rect extends Canvas {
constructor() {
super();
this._spacingX = 0;
this._spacingY = 0;
this._canvasCache = {};
}
setOptions(options) {
super.setOptions(options);
this._canvasCache = {};
}
draw(data, clearBefore) {
if (Rect.cache) {
this._drawWithCache(data);
}
else {
this._drawNoCache(data, clearBefore);
}
}
_drawWithCache(data) {
let [x, y, ch, fg, bg] = data;
let hash = "" + ch + fg + bg;
let canvas;
if (hash in this._canvasCache) {
canvas = this._canvasCache[hash];
}
else {
let b = this._options.border;
canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
canvas.width = this._spacingX;
canvas.height = this._spacingY;
ctx.fillStyle = bg;
ctx.fillRect(b, b, canvas.width - b, canvas.height - b);
if (ch) {
ctx.fillStyle = fg;
ctx.font = this._ctx.font;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
let chars = [].concat(ch);
for (let i = 0; i < chars.length; i++) {
ctx.fillText(chars[i], this._spacingX / 2, Math.ceil(this._spacingY / 2));
}
}
this._canvasCache[hash] = canvas;
}
this._ctx.drawImage(canvas, x * this._spacingX, y * this._spacingY);
}
_drawNoCache(data, clearBefore) {
let [x, y, ch, fg, bg] = data;
if (clearBefore) {
let b = this._options.border;
this._ctx.fillStyle = bg;
this._ctx.fillRect(x * this._spacingX + b, y * this._spacingY + b, this._spacingX - b, this._spacingY - b);
}
if (!ch) {
return;
}
this._ctx.fillStyle = fg;
let chars = [].concat(ch);
for (let i = 0; i < chars.length; i++) {
this._ctx.fillText(chars[i], (x + 0.5) * this._spacingX, Math.ceil((y + 0.5) * this._spacingY));
}
}
computeSize(availWidth, availHeight) {
let width = Math.floor(availWidth / this._spacingX);
let height = Math.floor(availHeight / this._spacingY);
return [width, height];
}
computeFontSize(availWidth, availHeight) {
let boxWidth = Math.floor(availWidth / this._options.width);
let boxHeight = Math.floor(availHeight / this._options.height);
/* compute char ratio */
let oldFont = this._ctx.font;
this._ctx.font = "100px " + this._options.fontFamily;
let width = Math.ceil(this._ctx.measureText("W").width);
this._ctx.font = oldFont;
let ratio = width / 100;
let widthFraction = ratio * boxHeight / boxWidth;
if (widthFraction > 1) { /* too wide with current aspect ratio */
boxHeight = Math.floor(boxHeight / widthFraction);
}
return Math.floor(boxHeight / this._options.spacing);
}
_normalizedEventToPosition(x, y) {
return [Math.floor(x / this._spacingX), Math.floor(y / this._spacingY)];
}
_updateSize() {
const opts = this._options;
const charWidth = Math.ceil(this._ctx.measureText("W").width);
this._spacingX = Math.ceil(opts.spacing * charWidth);
this._spacingY = Math.ceil(opts.spacing * opts.fontSize);
if (opts.forceSquareRatio) {
this._spacingX = this._spacingY = Math.max(this._spacingX, this._spacingY);
}
this._ctx.canvas.width = opts.width * this._spacingX;
this._ctx.canvas.height = opts.height * this._spacingY;
}
}
Rect.cache = false;