Guide discord.js
Sujets populaires

Canvas

Configuration de @napi-rs/canvas

@napi-rs/canvas est un outil de manipulation d'images qui vous permet de modifier des images avec du code. Nous allons explorer comment utiliser ce module dans une commande slash pour créer une commande de profil.

Ce guide a été testé en dernier avec @napi-rs/canvas^0.1.25, assurez-vous d'avoir cette version ou une version similaire après l'installation.

Assurez-vous d'être familier avec des choses comme async/await et destruction d'objets avant de continuer, car nous allons utiliser ces fonctionnalités dans les sections à venir.

Installation du package

Exécutez la commande suivante dans votre terminal :

npm install @napi-rs/canvas

Commencer

Voici le code de base que vous utiliserez pour commencer :

index.js
const { AttachmentBuilder, Client, Events, GatewayIntentBits } = require('discord.js');
const Canvas = require('@napi-rs/canvas');

const client = new Client({ intents: [GatewayIntentBits.Guilds] });

client.once(Events.ClientReady, (readyClient) => {
	console.log(`Ready! Logged in as ${readyClient.user.tag}`);
});

client.on(Events.InteractionCreate, (interaction) => {
	if (!interaction.isChatInputCommand()) return;

	if (interaction.commandName === 'profile') {
		// ...
	}
});

client.login('your-token-goes-here');

Si vous avez suivi la section gestionnaire d'événements de ce guide, envisagez de mettre cela dans votre gestionnaire d'interactions à la place !

N'oubliez pas d'enregistrer les commandes slash avant de continuer cette section du guide. Vous pouvez voir comment faire cela ici.

Chargement d'images de base

L'objectif final sera d'afficher l'avatar et le surnom de l'utilisateur.

Après avoir importé le module @napi-rs/canvas et l'avoir initialisé, vous devez charger les images. Avec @napi-rs/canvas, vous devez d'abord spécifier d'où vient l'image, naturellement, puis spécifier comment elle est chargée dans le Canvas réel en utilisant context, que vous utiliserez pour interagir avec Canvas.

@napi-rs/canvas fonctionne presque de manière identique à HTML5 Canvas. Vous pouvez lire les tutoriels HTML5 Canvas sur w3Schools et MDN pour plus d'informations plus tard !

index.js
client.on(Events.InteractionCreate, async (interaction) => {
	if (!interaction.isChatInputCommand()) return;

	if (interaction.commandName === 'profile') {
		// Créez un canvas de 700x250 pixels et obtenez son contexte
		// Le contexte sera utilisé pour modifier le canvas
		const canvas = Canvas.createCanvas(700, 250);
		const context = canvas.getContext('2d');
		// ...
	}
});

Maintenant, vous devez charger l'image que vous souhaitez utiliser dans Canvas.

Nous allons utiliser cette image comme arrière-plan dans l'image de bienvenue, mais vous pouvez utiliser ce que vous voulez. Assurez-vous de télécharger le fichier, de le nommer wallpaper.jpg et de le sauvegarder dans le même répertoire que votre fichier bot principal.

index.js
client.on(Events.InteractionCreate, async (interaction) => {
	const canvas = Canvas.createCanvas(700, 250);
	const context = canvas.getContext('2d');
	const background = await Canvas.loadImage('./wallpaper.jpg');

	// Ceci utilise les dimensions du canvas pour étirer l'image sur tout le canvas
	context.drawImage(background, 0, 0, canvas.width, canvas.height);

	// Utilisez la structure de classe Attachment utile pour traiter le fichier pour vous
	const attachment = new AttachmentBuilder(await canvas.encode('png'), { name: 'profile-image.png' });

	interaction.reply({ files: [attachment] });
});

Si vous obtenez une erreur comme Error: ENOENT: no such file or directory, le chemin du fichier fourni était incorrect.

Manipulation d'images

Ensuite, plaçons une bordure autour de l'image à titre de démonstration.

client.on(Events.InteractionCreate, async (interaction) => {
	// ...
	context.drawImage(background, 0, 0, canvas.width, canvas.height);

	// Définir la couleur du trait
	context.strokeStyle = '#0099ff';

	// Dessinez un rectangle avec les dimensions de tout le canvas
	context.strokeRect(0, 0, canvas.width, canvas.height);
	// ...
});

Un peu simple, non ? N'ayez pas peur, vous avez encore un peu plus à faire jusqu'à ce que vous terminiez. Puisque l'objectif de cette page de guide est axé davantage sur le code réel que sur la conception, plaçons un avatar carré de base pour l'instant sur le côté gauche de l'image. Par souci de complétude, vous le transformerez également en cercle par la suite.

const { request } = require('undici'); 

client.on(Events.InteractionCreate, async (interaction) => {
	// ...
	context.strokeRect(0, 0, canvas.width, canvas.height);

	// Utilisation de undici pour effectuer des requêtes HTTP pour de meilleures performances
	const { body } = await request(interaction.user.displayAvatarURL({ extension: 'jpg' }));
	const avatar = await Canvas.loadImage(await body.arrayBuffer());

	// Si vous ne vous souciez pas de la performance des requêtes HTTP, vous pouvez instead charger l'avatar en utilisant
	// const avatar = await Canvas.loadImage(interaction.user.displayAvatarURL({ extension: 'jpg' }));

	// Dessinez une forme sur le canvas principal
	context.drawImage(avatar, 25, 0, 200, canvas.height);
	context.drawImage(background, 0, 0, canvas.width, canvas.height); 
	// ...
});

Ça fonctionne bien, mais l'image d'avatar elle-même semble un peu étirée. Réparez cela.

client.on(Events.InteractionCreate, async (interaction) => {
	// ...
	const { body } = await request(interaction.user.displayAvatarURL({ extension: 'jpg' }));
	const avatar = await Canvas.loadImage(await body.arrayBuffer());

	context.drawImage(avatar, 25, 0, 200, canvas.height); 
	// Déplacez l'image vers le bas verticalement et limitez sa hauteur à 200, pour qu'elle soit carrée
	context.drawImage(avatar, 25, 25, 200, 200);
	// ...
});

Le but de cette petite section est de démontrer que travailler avec Canvas est essentiellement un flux de travail par essai et erreur où vous modifiez les propriétés jusqu'à ce qu'elles fonctionnent correctement.

Puisque nous avons couvert comment charger des images externes et corriger les dimensions, transformons l'avatar en cercle pour améliorer le style général de l'image.

client.on(Events.InteractionCreate, async (interaction) => {
	// ...
	context.strokeRect(0, 0, canvas.width, canvas.height);

	// Soulevez le stylo
	context.beginPath();

	// Commencez l'arc pour former un cercle
	context.arc(125, 125, 100, 0, Math.PI * 2, true);

	// Posez le stylo
	context.closePath();

	// Coupez la région que vous avez dessinée
	context.clip();
	// ...
});

Vous pouvez en savoir plus sur context.arc() sur w3schools ou MDN.

Ajout de texte

Maintenant, parlons rapidement d'ajouter du texte à votre image. Cela aidera à rendre l'objectif de cette image évident car actuellement, c'est juste un avatar flottant sur un arrière-plan étoilé qui apparait de nulle part.

client.on(Events.InteractionCreate, async (interaction) => {
	// ...
	context.strokeRect(0, 0, canvas.width, canvas.height);

	// Sélectionnez la taille et le type de police parmi l'une des polices disponibles en natif
	context.font = '60px sans-serif';

	// Sélectionnez le style qui sera utilisé pour remplir le texte
	context.fillStyle = '#ffffff';

	// Remplissez réellement le texte avec une couleur unie
	context.fillText(interaction.member.displayName, canvas.width / 2.5, canvas.height / 1.8);
	// ...
});

Si vous obtenez une erreur comme Fontconfig error: Cannot load default config file, cela signifie que vous n'avez pas de polices installées sur votre système. Sur Linux, vous pouvez exécuter la commande suivante pour résoudre ce problème : sudo apt-get install fontconfig. Ceci peut également avoir besoin d'être installé si vous voyez des boîtes où le texte devrait être. Quant à Windows, vous devrez trouver un moyen d'installer des polices.

Vous avez peut-être remarqué ou considéré que si le nom d'utilisateur d'un membre est trop long, la sortie ne sera pas très agréable. C'est parce que le texte dépasse du canvas, et vous n'avez aucune mesure en place pour cela. Prenons soin de ce problème !

// Passez l'objet Canvas entier car vous aurez besoin d'accéder à sa largeur et son contexte
const applyText = (canvas, text) => {
	const context = canvas.getContext('2d');

	// Déclarez une taille de base de la police
	let fontSize = 70;

	do {
		// Attribuez la police au contexte et décrémentez-la pour qu'elle puisse être mesurée à nouveau
		context.font = `${(fontSize -= 10)}px sans-serif`;
		// Comparez la largeur en pixels du texte au canvas moins la taille approximative de l'avatar
	} while (context.measureText(text).width > canvas.width - 300);

	// Retournez le résultat à utiliser dans le canvas réel
	return context.font;
};

client.on(Events.InteractionCreate, async (interaction) => {
	// ...
	context.strokeRect(0, 0, canvas.width, canvas.height);

	context.font = '60px sans-serif'; 
	context.font = applyText(canvas, interaction.member.displayName); 
	context.fillStyle = '#ffffff';
	context.fillText(interaction.member.displayName, canvas.width / 2.5, canvas.height / 1.8);
	// ...
});

Avant l'ajustement :

Après l'ajustement :

Déplaçons le texte de bienvenue à l'intérieur de l'image elle-même au lieu de l'ajouter à l'extérieur comme touche finale agréable.

client.on(Events.InteractionCreate, async (interaction) => {
	// ...
	context.strokeRect(0, 0, canvas.width, canvas.height);

	// Texte légèrement plus petit placé au-dessus du nom d'affichage du membre
	context.font = '28px sans-serif';
	context.fillStyle = '#ffffff';
	context.fillText('Profil', canvas.width / 2.5, canvas.height / 3.5);

	// Ajoutez un point d'exclamation ici et ci-dessous
	context.font = applyText(canvas, `${interaction.member.displayName}!`);
	context.font = applyText(canvas, interaction.member.displayName); 
	context.fillStyle = '#ffffff';
	context.fillText(`${interaction.member.displayName}!`, canvas.width / 2.5, canvas.height / 1.8); 
	context.fillText(interaction.member.displayName, canvas.width / 2.5, canvas.height / 1.8); 
	// ...
});

Et voilà ! Nous avons couvert les bases de la manipulation d'images, de la génération de texte et du chargement à partir d'une source distante.