canvafy
Version:
Make configurable canvas easily with Canvafy
215 lines (181 loc) • 6.64 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const { createCanvas, loadImage, GlobalFonts } = require('@napi-rs/canvas');
const path = require('path');
const fetch = (...args) => import('node-fetch').then(({ default: fetch }) => fetch(...args));
/**
* @typedef {object} Tweet
* @see {Tweet}
* @example const tweetCard = await new canvafy.Tweet()
* @type {Class}
*/
module.exports = class Tweet {
constructor(options) {
this.font = { name: options?.font?.name ?? "Chirp", path: options?.font?.path };
this.avatar = "https://cdn.discordapp.com/avatars/928259219038302258/299ebac2bc13f5a8f44d2dd1f0c9f856.png?size=1024";
this.comment = "This is a tweet card. You can customize it as you wish. Enjoy! #Canvafy";
this.verified = false;
this.client = null;
this.theme = "light";
this.user = { displayName: "Beş", username: "fivesobes" };
}
/**
* .setAvatar
* @param {string} image Set User Avatar URL
* @returns {Tweet}
* @example setAvatar("https://someone-image.png")
*/
setAvatar(image) {
this.avatar = image;
return this;
};
/**
* .setUser
* @param {object} user {displayName: "string", username: "string"}
* @returns {Tweet}
* @example setUser({displayName: "Beş", username: "fivesobes"})
*/
setUser({ displayName, username }) {
this.user = { displayName, username };
return this;
};
/**
* .setComment
* @param {string} text Set User Comment
* @returns {Tweet}
* @example setComment("This is a tweet card. You can customize it as you wish. Enjoy! #Canvafy")
*/
setComment(text) {
this.comment = text;
return this;
};
/**
* .setTheme
* @param {string} theme Set Theme
* @returns {Tweet}
* @example setTheme("dark")
* @example setTheme("light")
* @example setTheme("dim")
* @throws {Error} Invalid theme
*/
setTheme(theme) {
if (!["dark", "light", "dim"].some(e => e == theme)) throw new Error("Invalid theme");
this.theme = theme;
return this;
};
/**
* .setVerified
* @param {boolean} verified Set Verified
* @returns {Tweet}
* @example setVerified(true)
* @example setVerified(false)
* @throws {Error} Verified must be a boolean
*/
setVerified(verified) {
if (typeof verified !== "boolean") throw new Error("Verified must be a boolean");
this.verified = verified;
return this;
};
async build() {
if (this.font.path) GlobalFonts.registerFromPath(this.font.path, this.font.name);
var canvas = createCanvas(968, 343);
var ctx = canvas.getContext("2d");
var totalHeight = calculateHeight(ctx,this.comment);
canvas = createCanvas(968, 343 + totalHeight);
ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.fillStyle = this.theme === "dim" ? "#15202b" : this.theme == "light" ? "#fff" : "#000";
ctx.fillRect(10, 10, canvas.width - 20, canvas.height - 20)
ctx.fillStyle = this.theme === "dim" ? "#fff" : this.theme == "light" ? "#000" : "#fff";
ctx.textAlign = "left";
ctx.font = "25px Chirp";
ctx.fillText(this.user.displayName, 130, 70);
ctx.fillStyle = this.theme === "dim" ? "#8493a2" : this.theme == "light" ? "#000" : "#8493a2";
ctx.textAlign = "left";
ctx.font = "25px Chirp";
ctx.fillText("@" + this.user.username, 130, 100);
if (this.verified === true) {
var textLength = ctx.measureText(this.user.displayName).width;
ctx.drawImage(await loadImage(`${__dirname}/../assets/images/twitter-verified.png`), (textLength + 140), 48, 30, 30);
}
writeComment(ctx,this.comment,this.theme);
try {
ctx.drawImage(await loadImage(`${__dirname}/../assets/images/reply.png`), 186.6, canvas.height - 68, 45, 45);
ctx.drawImage(await loadImage(`${__dirname}/../assets/images/retweet.png`), 384, canvas.height - 68, 45, 45);
ctx.drawImage(await loadImage(`${__dirname}/../assets/images/like.png`), 577.8, canvas.height - 68, 45, 45);
ctx.drawImage(await loadImage(`${__dirname}/../assets/images/share.png`), 771, canvas.height - 68, 45, 45);
ctx.drawImage(await loadImage(`${__dirname}/../assets/images/other.png`), 900, 40, 35, 35);
} catch (err) {
console.log(err);
};
ctx.strokeStyle = "#8493a2";
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(50, canvas.height - 88);
ctx.lineTo(918, canvas.height - 88);
ctx.stroke();
ctx.beginPath();
ctx.arc(80, 75, 40, 0, Math.PI * 2);
ctx.closePath();
ctx.clip();
try {
ctx.drawImage(await loadImage(this.avatar), 93 - 58, 28, 90, 90);
} catch {
throw new Error("The image given in the argument of the setAvatar method is not valid or you are not connected to the internet.");
}
return canvas.toBuffer('image/png');
}
};
function writeComment(ctx,comment,theme){
comment = comment.length > 2490 ? comment.slice(0, 2490) + "..." : comment;
if(!comment.includes(" ")) {
comment.length > 57 ? comment = comment.slice(0, 57) + "..." : comment;
ctx.fillStyle = theme == "light" ? "#000" : "#fff";
ctx.font = "25px Chirp";
ctx.fillText(comment, 85, 170);
return;
}
var words = comment.split(' ');
var line = '';
var lineHeight = 40;
var x = 85;
var y = 170;
for (var i = 0; i < words.length; i++) {
var testLine = line + words[i] + ' ';
var metrics = ctx.measureText(testLine).width;
if (metrics > 800) {
ctx.fillStyle = theme == "light" ? "#000" : "#fff";
ctx.font = "25px Chirp";
ctx.fillText(line, x, y);
line = words[i] + ' ';
y += lineHeight;
} else {
line = testLine;
}
}
ctx.fillStyle = theme == "light" ? "#000" : "#fff";
ctx.fillText(line, x, y);
};
function calculateHeight(ctx,comment){
comment = comment.length > 2490 ? comment.slice(0, 2490) + "..." : comment;
if(!comment.includes(" ")) {
comment.length > 57 ? comment = comment.slice(0, 57) + "..." : comment;
return 40;
}
var words = comment.split(' ');
var line = '';
var lineHeight = 40;
var totalHeight = 0;
for (var i = 0; i < words.length; i++) {
var testLine = line + words[i] + ' ';
var metrics = ctx.measureText(testLine).width;
if (metrics > 800) {
line = words[i] + ' ';
totalHeight += lineHeight * 2.2;
} else {
line = testLine;
}
}
totalHeight += lineHeight;
return totalHeight;
}