canvacard
Version:
Powerful image manipulation package for beginners.
306 lines (290 loc) • 9.29 kB
JavaScript
const { createCanvas, loadImage } = require("@napi-rs/canvas");
const APIError = require("./utils/error.utils");
const shorten = require("./utils/shorten.utils");
/**
* @kind class
* @description Welcome or Leave card creator
* <details open>
* <summary>PREVIEW</summary>
* <br>
* <a>
* <img src="https://raw.githubusercontent.com/SrGobi/canvacard/refs/heads/test/welcome_1.png" alt="Welcome Card Preview 1">
* </a>
* <a>
* <img src="https://raw.githubusercontent.com/SrGobi/canvacard/refs/heads/test/welcome_2.png" alt="Welcome Card Preview 2">
* </a>
* <a>
* <img src="https://raw.githubusercontent.com/SrGobi/canvacard/refs/heads/test/welcome_3.png" alt="Welcome Card Preview 3">
* </a>
* </details>
*
* @example
* ```js
const welcome = new canvacard.WelcomeLeave()
.setAvatar(data.avatarURL)
.setBackground('COLOR', '#000000')
.setTitulo("Titulo de la Tarjeta👋", '#FFFFFF')
.setSubtitulo("Subtitulo de la Tarjeta 👋", '#FFFFFF')
.setOpacityOverlay(1)
.setColorCircle('#FFFFFF')
.setColorOverlay('#5865F2')
.setTypeOverlay('ROUNDED');
const welcomeImage = await welcome.build("Suravaram");
canvacard.write(welcomeImage, "./welcomer.png");
* ```
*/
class WelcomeLeave {
constructor() {
/**
* Card background
* @property {object} backgroundGlobal Card background
* @property {"IMAGE"|"COLOR"} [backgroundGlobal.type="color"] Type of fund
* @private
*/
this.backgroundGlobal = { type: "COLOR", image: "#23272A" };
/**
* Avatar of card
* @property {string}
* @private
*/
this.avatar = `${__dirname}/../assets/images/default-avatar.png`;
/**
* Title
* @property {string}
* @private
*/
this.titulo = "Custom Title!";
/**
* Subtitle
* @property {string}
* @private
*/
this.subtitulo = "Custom Subtitle!";
/**
* Color of the Title
* @property {string}
* @private
*/
this.colorTitulo = "#FFFFFF";
/**
* Color of the Subtitle
* @property {string}
* @private
*/
this.colorSubtitulo = "#5865f2";
/**
* Color of the circle
* @property {string}
* @private
*/
this.colorCircle = "#FFFFFF";
/**
* Color of the overlay
* @property {string}
* @private
*/
this.colorOverlay = "#000000";
/**
* Opacity of the overlay
* @property {number}
* @private
*/
this.opacityOverlay = 0.4;
/**
* Type of overlay
* @property {object} typeOverlay Type of overlay
* @property {"RECTANGLE"|"ROUNDED"} [typeOverlay.type="ROUNDED"] Type of overlay
* @private
*/
this.typeOverlay = { type: "ROUNDED" };
}
/**
* @method setAvatar
* @name setAvatar
* @description Set the avatar of the card
* @param {string|Buffer} value Avatar URL or Buffer
* @returns {WelcomeLeave} The current instance of WelcomeLeave
* @throws {APIError} Missing field: avatar
*/
setAvatar(value) {
if (!value) throw new APIError("Falta campo: avatar");
this.avatar = value;
return this;
}
/**
* @method setTitulo
* @name setTitulo
* @description Set the title of the card
* @param {string} value Title value
* @param {string} color HTML5 color code "#000000"
* @returns {WelcomeLeave} The current instance of WelcomeLeave
* @throws {APIError} The title must be a string
*/
setTitulo(value, color) {
if (typeof value !== 'string') throw new APIError("The title must be a string");
this.titulo = value;
if (typeof color !== 'string') throw new APIError("The color must be a string");
this.colorTitulo = color;
return this;
}
/**
* @method setSubtitulo
* @name setSubtitulo
* @description Set the subtitle of the card
* @param {string} value Subtitle value
* @param {string} color HTML5 color code "#000000"
* @returns {WelcomeLeave} The current instance of WelcomeLeave
* @throws {APIError} The subtitle must be a string
*/
setSubtitulo(value, color) {
if (typeof value !== 'string') throw new APIError("The subtitle must be a string");
this.subtitulo = value;
if (typeof color !== 'string') throw new APIError("The color must be a string");
this.colorSubtitulo = color;
return this;
}
/**
* @method setColorCircle
* @name setColorCircle
* @description Set the color of the circle
* @param {string} value HTML5 color code "#000000"
* @returns {WelcomeLeave} The current instance of WelcomeLeave
* @throws {APIError} The color must be a string
*/
setColorCircle(value) {
if (typeof value !== 'string') throw new APIError("The color must be a string");
this.colorCircle = value;
return this;
}
/**
* @method setColorOverlay
* @name setColorOverlay
* @description Set the color of the overlay
* @param {string} value HTML5 color code "#000000"
* @returns {WelcomeLeave} The current instance of WelcomeLeave
* @throws {APIError} The color must be a string
*/
setColorOverlay(value) {
if (typeof value !== 'string') throw new APIError("The color must be a string");
this.colorOverlay = value;
return this;
}
/**
* @method setOpacityOverlay
* @name setOpacityOverlay
* @description Set the opacity of the overlay
* @param {number} value Opacity value (0 to 1)
* @returns {WelcomeLeave} The current instance of WelcomeLeave
* @throws {APIError} The opacity must be a number
*/
setOpacityOverlay(value) {
if (isNaN(value)) throw new APIError("The opacity must be a number");
this.opacityOverlay = value;
return this;
}
/**
* @method setBackground
* @name setBackground
* @description Set background image/color of the card
* @param {"COLOR"|"IMAGE"} type Type of background
* @param {string} data Image URL or HTML color code
* @returns {WelcomeLeave} The current instance of WelcomeLeave
* @throws {APIError} Missing field: data
*/
setBackground(type, data) {
if (!data) throw new APIError("Missing field: data");
if (type === "COLOR") {
this.backgroundGlobal.type = "color";
this.backgroundGlobal.image = typeof data === "string" ? data : "#23272A";
} else if (type === "IMAGE") {
this.backgroundGlobal.type = "image";
this.backgroundGlobal.image = data;
} else {
throw new APIError(`Background type not supported "${type}"`);
}
return this;
}
/**
* @method setTypeOverlay
* @name setTypeOverlay
* @description Set the type of overlay
* @param {"RECTANGLE"|"ROUNDED"} type Type of overlay
* @returns {WelcomeLeave} The current instance of WelcomeLeave
* @throws {APIError} Missing field: type
*/
setTypeOverlay(type) {
if (!type) throw new APIError("Missing field: type");
switch (type) {
case "RECTANGLE":
this.typeOverlay.type = "RECTANGLE";
break;
case "ROUNDED":
this.typeOverlay.type = "ROUNDED";
break;
default:
throw new APIError(`Overlay type not supported "${type}"`);
}
return this;
}
/**
* @method build
* @name build
* @description Build the card
* @param {string} [font=Arial] Font to use in the card
* @returns {Promise<Buffer>} Card image in buffer format
* @throws {APIError} Error loading background image
*/
async build(font = "Arial") {
const canvas = createCanvas(1100, 500);
const ctx = canvas.getContext("2d");
let bg = null;
try {
if (this.backgroundGlobal.type === "image") {
bg = await loadImage(this.backgroundGlobal.image);
ctx.drawImage(bg, 0, 0, canvas.width, canvas.height);
} else {
ctx.fillStyle = this.backgroundGlobal.image;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
} catch (error) {
throw new APIError("Error loading background image:", error);
}
// Draw Overlay
ctx.fillStyle = this.colorOverlay;
ctx.globalAlpha = this.opacityOverlay;
if (this.typeOverlay.type === "RECTANGLE") {
ctx.rect(55, 25, canvas.width - 110, canvas.height - 50);
} else if (this.typeOverlay.type === "ROUNDED") {
ctx.roundRect(55, 25, canvas.width - 110, canvas.height - 50, 10);
}
ctx.shadowBlur = 10;
ctx.shadowColor = this.colorOverlay;
ctx.fill();
ctx.globalAlpha = 1;
// Draw Title
ctx.shadowBlur = 10;
ctx.shadowColor = "black";
ctx.fillStyle = this.colorTitulo;
ctx.textAlign = "center";
ctx.font = `60px ${font}`;
ctx.fillText(shorten(this.titulo, 30), canvas.width - 550, canvas.height - 120);
// Draw Subtitle
ctx.fillStyle = this.colorSubtitulo;
ctx.font = `30px ${font}`;
ctx.fillText(shorten(this.subtitulo, 50), canvas.width - 550, canvas.height - 70);
// Draw Circle
ctx.shadowBlur = 0;
ctx.beginPath();
ctx.lineWidth = 10;
ctx.strokeStyle = this.colorCircle;
ctx.arc(canvas.width - 550, 190, 125, 0, Math.PI * 2, true);
ctx.stroke();
ctx.closePath();
ctx.clip();
// Draw Avatar
const avatar = await loadImage(this.avatar);
ctx.drawImage(avatar, canvas.width - 675, 65, 250, 250);
return canvas.toBuffer("image/png");
}
}
module.exports = WelcomeLeave;