Skip to content

Commit

Permalink
feat: tweak andimprove card layout
Browse files Browse the repository at this point in the history
  • Loading branch information
Guilhermeasper committed Sep 2, 2023
1 parent 4714733 commit 8b16b49
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 77 deletions.
3 changes: 2 additions & 1 deletion src/commands/slashCommands/lastfmCharts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ export const lastfmCharts: SlashCommand = {
images,
chartNames,
interaction.user.username,
type.value as string
type.value as string,
period.value as string
);

await interaction.channel.send({
Expand Down
Binary file added src/resources/images/background-card-story.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src/resources/images/card-background.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
203 changes: 127 additions & 76 deletions src/utils/collageBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,36 @@
import axios, { AxiosResponse } from 'axios';
import fs from 'fs';
import sharp from 'sharp';
import { createCanvas, loadImage, registerFont } from 'canvas';
import { Image, createCanvas, loadImage, registerFont } from 'canvas';
import { join } from 'path';
import fontColorContrast from 'font-color-contrast';

const FORMAT_CONFIGS = {
regular: {
width: 1100,
height: 1400,
namePosition: {
x: 45,
y: 90,
},
typePosition: {
x: 45,
y: 150,
},
firstImagePosition: {
x: 550,
y: 40,
},
firstImageWidth: 500,
firstImageHeight: 300,
imageWidth: 300,
imageHeight: 300,
imageBorderWidth: 1,
imageBorderOffset: 10,
imageCounterPosition: {
x: 30,
y: 35,
},
},
instagramFeed: {
width: 1080,
Expand All @@ -25,7 +47,7 @@ export class CollageBuilder {
const resizedImages: Promise<Buffer>[] = [];

for (const imageBuffer of imagesBuffers) {
const size: number = imagesBuffers.length === 0 ? 500 : 300;
const size: number = resizedImages.length === 0 ? 500 : 300;
const resizedImageBuffer = sharp(imageBuffer)
.resize(size, size)
.toBuffer();
Expand Down Expand Up @@ -72,41 +94,7 @@ export class CollageBuilder {
return images;
}

async resizeAndCrop(originalImage: any) {
const canvas = createCanvas(500, 500);
const ctx = canvas.getContext('2d');

// Resize the image to 500x500
ctx.drawImage(originalImage, 0, 0, 500, 500);

// Calculate crop coordinates for center crop (adjust as needed)
const cropX = 0;
const cropY = (500 - 300) / 2; // To vertically center the crop
const cropWidth = 500;
const cropHeight = 300;

// Perform the cropping
const croppedImage = canvas.toDataURL('image/jpeg', 1.0); // Convert to JPEG
const croppedCanvas = createCanvas(cropWidth, cropHeight);
const croppedCtx = croppedCanvas.getContext('2d');
const croppedImageObj = await loadImage(croppedImage);
croppedCtx.drawImage(
croppedImageObj,
cropX,
cropY,
cropWidth,
cropHeight,
0,
0,
cropWidth,
cropHeight
);

// Save the cropped image
return await loadImage(croppedCanvas.toBuffer());
}

async addBorder(image: any, width: number, height: number) {
async addBorder(image: Image, width: number, height: number) {
const canvas = createCanvas(width, height);
const ctx = canvas.getContext('2d');

Expand All @@ -122,63 +110,98 @@ export class CollageBuilder {
return loadImage(canvas.toBuffer());
}

async createCollage(
images: any,
chartNames: string[],
name: string,
type: string
) {
setupCanvas() {
const canvasWidth = 1100;
const canvasHeight = 1400;
const canvasHeight = 1600;
const collageCanvas = createCanvas(canvasWidth, canvasHeight);
const ctx = collageCanvas.getContext('2d');
registerFont(join(__dirname, '../resources/fonts/BebasNeueRegular.ttf'), {
family: "'Bebas Neue', sans-serif",
});
return { ctx, collageCanvas, canvasWidth, canvasHeight };
}

let x = 45;
let y = 385;
let counter = 1;

async drawBackground(ctx: any, canvasWidth: number, canvasHeight: number) {
const background = fs.readFileSync(
join(__dirname, '../resources/images/card-background.png')
);
const backgroundImg = await loadImage(background);
ctx.drawImage(backgroundImg, 0, 0);
const firtsImageX = 550;
const firtsImageY = 40;
const firstImage = await loadImage(images[0]);
const croppedImage = await this.resizeAndCrop(firstImage);
const croppedImageWithBorder = await this.addBorder(croppedImage, 500, 300);
this.drawWithEffect(ctx, croppedImageWithBorder, firtsImageX, firtsImageY);
const firtsImageCounter = `#0${counter++}`;
const firstImageName = chartNames[0];
ctx.font = '50px Bebas Neue'; // Font size and name
let color = await this.getColorInfo(images[0], 30, 35, 80, 80);
}

async drawFirstImage(
ctx: any,
firstImage: Image,
firstImageBuffer: Buffer,
footerText: string,
firtsImageX: number,
firtsImageY: number,
width: number,
height: number
) {
const imageWithBorder = await this.addBorder(firstImage, width, height);
this.drawWithEffect(ctx, imageWithBorder, firtsImageX, firtsImageY);

const firtsImageCounter = '#01';
const firstImageName = footerText;
ctx.font = '80px Bebas Neue'; // Font size and name
let color = await this.getColorInfo(firstImageBuffer, 30, 35, 90, 80);
let textColor = fontColorContrast(color);
ctx.fillStyle = textColor; // Text color
ctx.fillText(firtsImageCounter, firtsImageX + 30, firtsImageY + 35);
ctx.font = '36px Bebas Neue'; // Font size and name
ctx.fillText(firtsImageCounter, firtsImageX + 30, firtsImageY + 70);
ctx.font = '50px Bebas Neue'; // Font size and name

color = await this.getColorInfo(images[0], 30, 250, 250, 50);
color = await this.getColorInfo(
firstImageBuffer,
30,
410,
ctx.measureText(firstImageName).width,
50
);
textColor = fontColorContrast(color);
ctx.fillStyle = textColor; // Text color
this.textEllipsis(
ctx,
firstImageName,
firtsImageX + 30,
firtsImageY + 270,
250
firtsImageY + 450,
470
);
}

async createCollage(
images: Buffer[],
chartNames: string[],
name: string,
type: string,
period: string = 'overall'
) {
const { ctx, collageCanvas, canvasWidth, canvasHeight } =
this.setupCanvas();
await this.drawBackground(ctx, canvasWidth, canvasHeight);
const firstImageBuffer = images.shift();
const firstImage = await loadImage(firstImageBuffer);
const firstFooterText = chartNames.shift();
await this.drawFirstImage(
ctx,
firstImage,
firstImageBuffer,
firstFooterText,
550,
40,
500,
500
);

images.shift();
chartNames.shift();
let x = 45;
let y = 590;
let counter = 1;

for (let index = 0; index < images.length; index++) {
const imageBuffer = images[index];
const imageName = chartNames[index];

const image = await loadImage(imageBuffer);

const imageWithBorder = await this.addBorder(image, 300, 300);
this.drawWithEffect(ctx, imageWithBorder, x, y);
const cardCounter = `#${counter < 10 ? '0' + counter++ : counter++}`;
Expand All @@ -204,24 +227,52 @@ export class CollageBuilder {
}

// Set font properties
const textX = 45; // X-coordinate of the text
const textY = 250; // Y-coordinate of the text
ctx.font = '70px Bebas Neue'; // Font size and name
ctx.fillStyle = '#FFFFFF'; // Text color
const userProfileName = name.toUpperCase();
ctx.fillText(userProfileName, textX, textY);

// Draw the text on the canvas
const text1 = name.toUpperCase();
const text2 = `TOP ${type.toUpperCase()}`;
const textX = 45; // X-coordinate of the text
const textY = 90; // Y-coordinate of the text
ctx.fillText(text1, textX, textY);
const categoryType = `TOP ${type.toUpperCase()}`;
ctx.font = '46px Bebas Neue'; // Font size and name
let xText = textX;
for (const char of text2) {
ctx.fillText(char, xText, textY + 60);
let categoryTypeTextX = textX;
for (const char of categoryType) {
ctx.fillText(char, categoryTypeTextX, textY + 70);
const charWidth = ctx.measureText(char).width + 10;
xText += charWidth;
categoryTypeTextX += charWidth;
}

return await collageCanvas.toBuffer();
const periodMessage = this.parsePeriodMessage(period);
ctx.font = '32px Bebas Neue'; // Font size and name
let periodMessageTextX = textX;
for (const char of periodMessage) {
ctx.fillText(char, periodMessageTextX, textY + 130);
const charWidth = ctx.measureText(char).width + 15;
periodMessageTextX += charWidth;
}

return collageCanvas.toBuffer();
}

parsePeriodMessage(period: string) {
switch (period) {
case '7day':
return 'Last Week';
case '1month':
return 'Last month';
case '3month':
return 'Last 3 months';
case '6month':
return 'Last 6 months';
case '12month':
return 'Last year';
case 'overall':
return 'All time';
default:
return 'All time';
}
}

drawWithEffect(ctx: any, image: any, x: number, y: number) {
Expand Down

0 comments on commit 8b16b49

Please sign in to comment.