Select Git revision
billboard.js
billboard.js 20.45 KiB
class Billboard extends Template {
description = "Určeno pro tisk na billboardy.";
changeableAttributes = [
"logoImage",
"primaryImage",
"primaryText",
"primaryColorScheme",
"primaryImagePosition",
"underNameText"
];
iconText = "";
underNameText = "";
defaultResolution = 4430;
aspectRatio = 1.86551;
primaryColorSchemes = [
"black-on-white",
"white-on-black"
];
changeableColors = [
"primaryTextColor",
"foregroundColor",
"backgroundColor",
"nameTextColor",
"primaryTextHighlightColor",
"underNameTextColor",
"requesterTextColor"
];
// Canvas
async redrawCanvas() {
if (this.redrawing) {
return;
}
this.redrawing = true;
const backgroundRectangleWidth = Math.ceil(this.canvas.width * 0.25);
const foregroundRectangleOffsetTop = Math.ceil(this.canvas.height * 0.17);
const foregroundRectangleOffsetSide = Math.ceil(this.canvas.height * 0.05);
const foregroundRectangleWidth = Math.ceil(this.canvas.width * 0.4);
const foregroundRectangleHeight = Math.ceil(this.canvas.height * 0.5);
const foregroundRectanglePaddingInnerSides = Math.ceil(foregroundRectangleWidth * 0.075);
const foregroundRectanglePaddingTopBottom = Math.ceil(foregroundRectangleHeight * 0.075);
const foregroundRectangleAngle = Math.ceil(this.canvas.height * 0.0075);
const highlightPaddingSides = Math.ceil(this.canvas.width * 0.01);
const highlightPaddingBottom = Math.ceil(this.canvas.height * 0.0125);
const highlightPaddingTop = Math.ceil(this.canvas.height * -0.01);
let primaryFontSize = Math.ceil(this.canvas.height * 0.105);
let primaryTextMaxLines = 4;
const logoHeight = Math.ceil(this.canvas.height * 0.07) * this.logoImageZoom;
const logoOffsetBottom = Math.ceil(this.canvas.height * 0.195) - (
logoHeight / this.logoImageZoom * (this.logoImageZoom - 1) / 2
);
const nameFontSize = Math.ceil(this.canvas.height * 0.036);
const nameTextOffsetSide = Math.ceil(this.canvas.width * 0.29);
const nameTextOffsetBottom = Math.ceil(this.canvas.height * 0.23);
let secondaryFontSize = Math.ceil(this.canvas.height * 0.029);
const underNameTextOffsetBottom = Math.ceil(this.canvas.height * 0.197);
// Clear the canvas
this.context.clearRect(
0, 0,
this.canvas.width, this.canvas.height
);
// Set image
if (this.primaryImage !== null) {
// https://github.com/DonkeyDushan/piratilol/blob/main/src/js/index.js
// Thanks to DonkeyDushan, the guy who made the joke 2021 campaign generator :D
const imageScaleX = this.canvas.width / this.primaryImage.width;
const imageScaleY = this.canvas.height / this.primaryImage.height;
const imageScale = Math.max(imageScaleX, imageScaleY) * this.primaryImageZoom;
// https://stackoverflow.com/a/8529655
// Thanks to alex!
this.context.setTransform(
imageScale,
0, 0,
imageScale,
(this.canvas.width - this.primaryImage.width * imageScale) / 2 + this.primaryImageX * this.primaryImageZoom,
(this.canvas.height - this.primaryImage.height * imageScale) / 2 + this.primaryImageY * this.primaryImageZoom,
);
this.context.drawImage(
this.primaryImage,
0, 0
);
this.context.setTransform(); // Reset transformation
}
this.context.fillStyle = this.backgroundColor;
this.context.fillRect(
this.canvas.width - backgroundRectangleWidth, 0,
backgroundRectangleWidth, this.canvas.height
);
let currentForegroundRectangleY = foregroundRectangleOffsetTop;
const foregroundRectangleStartingX = this.canvas.width - foregroundRectangleOffsetSide - foregroundRectangleWidth;
const foregroundRectangleEndingX = foregroundRectangleStartingX + foregroundRectangleWidth;
this.context.fillStyle = this.foregroundColor;
this.context.beginPath();
this.context.moveTo(
foregroundRectangleStartingX,
currentForegroundRectangleY + foregroundRectangleAngle
);
this.context.lineTo(
foregroundRectangleEndingX,
currentForegroundRectangleY
);
this.context.lineTo(
foregroundRectangleEndingX,
currentForegroundRectangleY + foregroundRectangleHeight
);
this.context.lineTo(
foregroundRectangleStartingX,
currentForegroundRectangleY + foregroundRectangleAngle + foregroundRectangleHeight
);
this.context.closePath();
this.context.fill();
let primaryLines = [];
const originalHeight = (primaryFontSize * primaryTextMaxLines) - foregroundRectanglePaddingTopBottom;
do {
this.context.font = `${this.primaryFontStyle} ${primaryFontSize}px ${this.primaryFont}`;
primaryLines = splitStringIntoLines(
this.context,
this.primaryText,
foregroundRectangleWidth - 2 * foregroundRectanglePaddingInnerSides,
primaryTextMaxLines,
true
);
if (primaryLines.length > primaryTextMaxLines) {
primaryFontSize -= 2;
}
if (((primaryTextMaxLines + 1) * primaryFontSize) < originalHeight) {
primaryTextMaxLines += 1;
}
} while (primaryLines.length > primaryTextMaxLines);
currentForegroundRectangleY += foregroundRectanglePaddingTopBottom + primaryFontSize;
// Create primary text
this.context.textAlign = "left";
const foregroundRGB = hexToRgb(this.foregroundColor);
const foregroundLightness = (
0.2126 * foregroundRGB.r
+ 0.7152 * foregroundRGB.g
+ 0.0722 * foregroundRGB.b
)
const useLightHighlight = (foregroundLightness > 207);
const primaryLineX = foregroundRectangleEndingX - (foregroundRectangleWidth / 2);
let primaryTextHighlightedColor = null;
const lowercasePrimaryTextHighlightColor = this.primaryTextHighlightColor.toLowerCase();
const hasColorOverride = (
lowercasePrimaryTextHighlightColor === "#209a37" ||
lowercasePrimaryTextHighlightColor === "#e63812"
);
if (hasColorOverride) {
if (useLightHighlight) {
primaryTextHighlightedColor = this.foregroundColor;
} else {
primaryTextHighlightedColor = this.primaryTextColor;
}
} else if (!useLightHighlight) {
primaryTextHighlightedColor = this.foregroundColor;
} else {
primaryTextHighlightedColor = this.primaryTextColor;
}
this.context.fillStyle = this.primaryTextColor;
for (let line of primaryLines) {
let wordPosition = 0;
for (let word of line) {
const previousWords = line.slice(0, wordPosition).join(" ");
const previousWordsWidth = this.context.measureText(
previousWords
+ (
(previousWords.length !== 0) ?
" " : ""
)
).width;
const nextWords = line.slice(wordPosition + 1, line.length).join(" ")
const nextWordsWidth = this.context.measureText(
nextWords
+ (
(nextWords.length !== 0) ?
" " : ""
)
).width;
let currentWordWidth = this.context.measureText(word).width;
for (const word of line.slice(wordPosition + 1, line.length)) {
if (word.isHighlighted) {
currentWordWidth += this.context.measureText(word.toString() + " ").width;
} else {
break;
}
}
if (word.isHighlighted) {
if (
wordPosition === 0 ||
!line[wordPosition - 1].isHighlighted
) {
this.context.fillStyle = this.primaryTextHighlightColor;
this.context.beginPath();
const startingHighlightLineX = (
primaryLineX
+ Math.max(previousWordsWidth / 2)
- Math.max(nextWordsWidth / 2)
- Math.max(this.context.measureText(word).width / 2)
);
this.context.moveTo(
startingHighlightLineX - highlightPaddingSides,
currentForegroundRectangleY + highlightPaddingBottom
);
this.context.lineTo(
(
startingHighlightLineX
+ currentWordWidth
+ highlightPaddingSides
),
(
currentForegroundRectangleY
+ highlightPaddingBottom
- Math.max(
(currentWordWidth * foregroundRectangleAngle)
/ foregroundRectangleWidth
)
)
);
this.context.lineTo(
(
startingHighlightLineX
+ currentWordWidth
+ highlightPaddingSides
),
(
currentForegroundRectangleY
- primaryFontSize
- highlightPaddingTop
- Math.max(
(currentWordWidth * foregroundRectangleAngle)
/ foregroundRectangleWidth
)
)
);
this.context.lineTo(
startingHighlightLineX - highlightPaddingSides,
(
currentForegroundRectangleY
- primaryFontSize
- highlightPaddingTop
)
);
this.context.closePath();
this.context.fill();
}
this.context.fillStyle = primaryTextHighlightedColor;
}
this.context.fillText(
word + " ",
(
primaryLineX
+ Math.ceil(previousWordsWidth / 2)
- Math.ceil(nextWordsWidth / 2)
- Math.ceil(this.context.measureText(word).width / 2)
),
currentForegroundRectangleY
);
wordPosition++;
this.context.fillStyle = this.primaryTextColor;
}
currentForegroundRectangleY += primaryFontSize;
}
this.context.textAlign = "center";
const backgroundRGB = hexToRgb(this.backgroundColor);
const backgroundLightness = (
0.2126 * backgroundRGB.r
+ 0.7152 * backgroundRGB.g
+ 0.0722 * backgroundRGB.b
)
const useLightLogo = (backgroundLightness < 207);
let classRef = this;
function drawLogoImage(image) {
let logoWidth = Math.ceil(image.width * (logoHeight / image.height));
classRef.context.drawImage(
image,
(
classRef.canvas.width - backgroundRectangleWidth
+ (backgroundRectangleWidth - logoWidth) / 2
), classRef.canvas.height - logoHeight - logoOffsetBottom,
logoWidth, logoHeight
);
}
if (this.logoImage === null) {
const logoImageLoadPromise = new Promise(
resolve => {
let logoImage = new Image();
logoImage.onload = function() {
drawLogoImage(this);
resolve();
}
if (useLightLogo) {
logoImage.src = classRef.lightLogoDefaultSource;
} else {
logoImage.src = classRef.darkLogoDefaultSource;
}
}
);
await logoImageLoadPromise;
} else {
drawLogoImage(this.logoImage);
}
if (this.nameText !== "") {
this.context.fillStyle = this.nameBackgroundColor;
this.context.font = `bold ${nameFontSize}px 'Roboto Condensed'`;
this.context.textBaseline = "bottom";
this.context.textAlign = "right";
this.context.fillStyle = this.nameTextColor;
const nameTextWidth = this.context.measureText(this.nameText).width;
this.context.fillText(
this.nameText,
(
this.canvas.width
- nameTextOffsetSide
),
(
this.canvas.height
- nameTextOffsetBottom
)
);
this.context.fillStyle = this.underNameTextColor;
this.context.font = `${secondaryFontSize}px 'Roboto Condensed'`;
while (this.context.measureText(this.underNameText).width > nameTextWidth) {
secondaryFontSize -= 2;
this.context.font = `${secondaryFontSize}px 'Roboto Condensed'`;
}
this.context.fillText(
this.underNameText,
(
this.canvas.width
- nameTextOffsetSide
),
(
this.canvas.height
- underNameTextOffsetBottom
)
);
this.context.textBaseline = "alphabetic";
}
if (this.requesterText !== "") {
// https://newspaint.wordpress.com/2014/05/22/writing-rotated-text-on-a-javascript-canvas/
// Thanks to newspaint!
this.context.save();
this.context.translate(this.canvas.width - 1, 0);
this.context.rotate(3 * Math.PI / 2);
let requesterFontSize = Math.ceil(this.canvas.width * 0.015);
do {
this.context.font = `${this.primaryFontStyle} ${requesterFontSize}px ${this.primaryFont}`;
if (
this.context.measureText(this.requesterText).width
> this.canvas.height * 0.97
) {
requesterFontSize -= 2;
this.context.font = `${this.primaryFontStyle} ${requesterFontSize}px ${this.primaryFont}`;
}
} while (
this.context.measureText(this.requesterText).width
> this.canvas.height * 0.97
);
this.context.fillStyle = this.requesterTextColor;
this.context.textAlign = "left";
this.context.globalAlpha = 0.6;
this.context.fillText(
this.requesterText,
-this.canvas.height * 0.985 + underNameTextOffsetBottom, -this.canvas.width * 0.9925 + requesterFontSize
);
this.context.globalAlpha = 1;
this.context.restore();
}
this.finalDrawHook();
this.stickerDrawHook();
this.redrawing = false;
}
// Text
async setUnderNameText(text, skipRedraw = false) {
this.underNameText = text;
if (!skipRedraw) {
await this.redrawCanvas();
}
}
// Color schemes
async setPrimaryColorScheme(scheme, skipRedraw = false) {
switch (scheme) {
case "black-on-white":
this.primaryTextColor = "#000000";
this.foregroundColor = "#ffffff";
this.backgroundColor = "#000000";
this.nameTextColor = "#ffffff";
this.primaryTextHighlightColor = "#ffcc00";
this.underNameTextColor = "#ffffff";
break;
case "white-on-black":
this.primaryTextColor = "#ffffff";
this.foregroundColor = "#000000";
this.backgroundColor = "#ffffff";
this.nameTextColor = "#ffffff";
// this.primaryTextHighlightColor = "#ffcc00";
this.underNameTextColor = "#ffffff";
break;
case "forum-black-on-white":
this.primaryTextColor = "#000000";
this.foregroundColor = "#ffffff";
this.backgroundColor = "#962a51";
this.nameTextColor = "#ffffff";
this.primaryTextHighlightColor = "#ffcc00";
this.underNameTextColor = "#ffffff";
break;
case "forum-white-on-purple":
this.primaryTextColor = "#ffffff";
this.foregroundColor = "#962a51";
this.backgroundColor = "#ffffff";
this.nameTextColor = "#ffffff";
// this.primaryTextHighlightColor = "#ffcc00";
this.underNameTextColor = "#ffffff";
break;
case "zeleni-volary-bystrc-most-black-on-white":
this.primaryTextColor = "#000000";
this.foregroundColor = "#ffffff";
this.backgroundColor = "#00ad43";
this.nameTextColor = "#ffffff";
this.primaryTextHighlightColor = "#ffcc00";
this.underNameTextColor = "#ffffff";
break;
case "zeleni-volary-bystrc-most-white-on-green":
this.primaryTextColor = "#ffffff";
this.foregroundColor = "#00ad43";
this.backgroundColor = "#ffffff";
this.nameTextColor = "#ffffff";
// this.primaryTextHighlightColor = "#ffcc00";
this.underNameTextColor = "#ffffff";
break;
case "spolecne-s-piraty-black-on-white":
this.primaryTextColor = "#000000";
this.foregroundColor = "#ffffff";
this.backgroundColor = "#21274e";
this.nameTextColor = "#ffffff";
this.primaryTextHighlightColor = "#ffcc00";
this.underNameTextColor = "#ffffff";
break;
case "spolecne-s-piraty-white-on-blue":
this.primaryTextColor = "#ffffff";
this.foregroundColor = "#21274e";
this.backgroundColor = "#ffffff";
this.nameTextColor = "#ffffff";
// this.primaryTextHighlightColor = "#ffcc00";
this.underNameTextColor = "#ffffff";
break;
case "louny-spolecne-black-on-white":
this.primaryTextColor = "#000000";
this.foregroundColor = "#ffffff";
this.backgroundColor = "#3e2a5b";
this.nameTextColor = "#ffffff";
this.primaryTextHighlightColor = "#ffcc00";
this.underNameTextColor = "#ffffff";
break;
case "louny-spolecne-white-on-purple":
this.primaryTextColor = "#ffffff";
this.foregroundColor = "#3e2a5b";
this.backgroundColor = "#ffffff";
this.nameTextColor = "#ffffff";
// this.primaryTextHighlightColor = "#ffcc00";
this.underNameTextColor = "#ffffff";
break;
case "litomerice-blue-on-white":
this.primaryTextColor = "#123172";
this.foregroundColor = "#ffffff";
this.backgroundColor = "#123172";
this.nameTextColor = "#ffffff";
this.primaryTextHighlightColor = "#afe87e";
this.underNameTextColor = "#ffffff";
break;
case "litomerice-white-on-blue":
this.primaryTextColor = "#ffffff";
this.foregroundColor = "#123172";
this.backgroundColor = "#ffffff";
this.nameTextColor = "#ffffff";
// this.primaryTextHighlightColor = "#ffcc00";
this.underNameTextColor = "#ffffff";
break;
case "stranane-gray-on-yellow":
this.primaryTextColor = "#ffffff";
this.foregroundColor = "#4d4d4d";
this.backgroundColor = "#ffd500";
this.nameTextColor = "#ffffff";
// this.primaryTextHighlightColor = "#afe87e";
this.underNameTextColor = "#ffffff";
break;
case "stranane-yellow-on-white":
this.primaryTextColor = "#4d4d4d";
this.foregroundColor = "#ffd500";
this.backgroundColor = "#ffffff";
this.nameTextColor = "#ffffff";
this.primaryTextHighlightColor = "#4d4d4d";
this.underNameTextColor = "#ffffff";
break;
case "stranane-white-on-yellow":
this.primaryTextColor = "#4d4d4d";
this.foregroundColor = "#ffffff";
this.backgroundColor = "#ffd500";
this.nameTextColor = "#ffffff";
this.primaryTextHighlightColor = "#ffd500";
this.underNameTextColor = "#ffffff";
break;
case "prusanky-black-on-yellow":
this.primaryTextColor = "#ffffff";
this.foregroundColor = "#000000";
this.backgroundColor = "#ffd500";
this.nameTextColor = "#ffffff";
// this.primaryTextHighlightColor = "#afe87e";
this.underNameTextColor = "#ffffff";
break;
case "prusanky-yellow-on-white":
this.primaryTextColor = "#000000";
this.foregroundColor = "#ffd500";
this.backgroundColor = "#ffffff";
this.nameTextColor = "#ffffff";
this.primaryTextHighlightColor = "#000000";
this.underNameTextColor = "#ffffff";
break;
case "prusanky-white-on-yellow":
this.primaryTextColor = "#000000";
this.foregroundColor = "#ffffff";
this.backgroundColor = "#ffd500";
this.nameTextColor = "#ffffff";
this.primaryTextHighlightColor = "#ffd500";
this.underNameTextColor = "#ffffff";
break;
case "ujezd-green-on-white":
this.primaryTextColor = "#000000";
this.foregroundColor = "#8ed4a3";
this.backgroundColor = "#ffffff";
this.nameTextColor = "#ffffff";
this.primaryTextHighlightColor = "#ffdd55";
this.underNameTextColor = "#ffffff";
break;
case "ujezd-white-on-green":
this.primaryTextColor = "#000000";
this.foregroundColor = "#ffffff";
this.backgroundColor = "#8ed4a3";
this.nameTextColor = "#ffffff";
this.primaryTextHighlightColor = "#ffdd55";
this.underNameTextColor = "#ffffff";
break;
case "cssd-red-on-black":
this.primaryTextColor = "#ffffff";
this.foregroundColor = "#e63812";
this.backgroundColor = "#000000";
this.nameTextColor = "#ffffff";
this.underNameTextColor = "#ffffff";
break;
case "cssd-black-on-red":
this.primaryTextColor = "#ffffff";
this.foregroundColor = "#000000";
this.backgroundColor = "#e63812";
this.nameTextColor = "#ffffff";
this.underNameTextColor = "#ffffff";
break;
case "jilemnice-purple-on-black":
this.primaryTextColor = "#ffffff";
this.foregroundColor = "#6e1646";
this.backgroundColor = "#000000";
this.nameTextColor = "#ffffff";
this.underNameTextColor = "#ffffff";
break;
case "jilemnice-black-on-purple":
this.primaryTextColor = "#ffffff";
this.foregroundColor = "#000000";
this.backgroundColor = "#6e1646";
this.nameTextColor = "#ffffff";
this.underNameTextColor = "#ffffff";
break;
case "novarole-white-on-green":
this.primaryTextColor = "#000000";
this.primaryTextHighlightColor = "#a9ce2d";
this.foregroundColor = "#ffffff";
this.backgroundColor = "#a9ce2d";
this.nameTextColor = "#ffffff";
this.underNameTextColor = "#ffffff";
break;
case "novarole-green-on-white":
this.primaryTextColor = "#000000";
this.foregroundColor = "#a9ce2d";
this.backgroundColor = "#ffffff";
this.nameTextColor = "#ffffff";
this.underNameTextColor = "#ffffff";
this.primaryTextHighlightColor = "#ffcc00";
break;
case "novarole-green-on-black":
this.primaryTextColor = "#000000";
this.foregroundColor = "#a9ce2d";
this.backgroundColor = "#000000";
this.nameTextColor = "#ffffff";
this.underNameTextColor = "#ffffff";
this.primaryTextHighlightColor = "#ffcc00";
break;
case "zeleni-melnik-yellow-name-rect":
await this.setPrimaryColorScheme("white-on-black", true);
break;
default:
throw new Error("This scheme does not exist.");
break;
}
this.requesterTextColor = this.underNameTextColor;
if (!skipRedraw) {
await this.redrawCanvas();
}
}
}