kyranet / canvas-constructor

An ES6 utility for canvas with built-in functions and chained methods.
https://canvasconstructor.js.org
MIT License
116 stars 23 forks source link

Cannot read properties of undefined (reading 'animVal') #411

Closed minhcrafters closed 2 years ago

minhcrafters commented 2 years ago

Describe the bug

This error TypeError: Cannot read properties of undefined (reading 'animVal') happens when I try to use function printCircularImage()

To Reproduce

  1. use printCircularImage()
  2. error shown

Expected behavior

It should print the image in the canvas

Screenshots

image

Additional context

using discord.js v12.5.3

kyranet commented 2 years ago

printCircularImage accepts only Image or a Canvas (not canvas-constructor's, you can access to it via .canvas) instance, it doesn't take Buffers or any other source.

minhcrafters commented 2 years ago

i mean i tried to use printCircularImage() on a Canvas instance using canvas-contructor/skia did i do anything wrong? also i'm trying to follow your profile card tutorial

kyranet commented 2 years ago

Chances are high you're using canvas-constructor's Canvas instance, instead of Skia's Canvas instance (accessible via .canvas).

You still need to share the code, though.

minhcrafters commented 2 years ago

well here's my profile.js code:

const { MessageAttachment } = require('discord.js');
const Commando = require('discord.js-commando');
const { Canvas } = require('canvas-constructor/skia');
const { registerFont } = require('canvas-constructor/skia');
const { resolve, join } = require('path');
const fetch = require('node-fetch');

registerFont('Discord', resolve(join(__dirname, '../../discord.otf')));

const imageUrlRegex = /\?size=2048$/g;

module.exports = class Command extends Commando.Command {
    constructor(client) {
        super(client, {
            name: 'profile',
            group: 'info',
            memberName: 'profile',
            ownerOnly: false,
            guildOnly: true,
            description: 'Shows your profile.',
            args: [
                {
                    key: 'user',
                    prompt: 'What user do you want to use with?',
                    type: 'member',
                },
            ],
        });
    }

    async run(message, { user }) {
        async function profile(member, key) {
            const { level, points } = message.client.points.get(key);

            try {
                const result = await fetch(member.user.displayAvatarURL().replace(imageUrlRegex, '?size=128'));
                if (!result.ok) throw new Error('Failed to get the avatar.');
                const avatar = await result.buffer();

                const name = member.displayName.length > 20 ? member.displayName.substring(0, 17) + '...' : member.displayName;

                return new Canvas(400, 180)
                    .setColor('#7289DA')
                    .printRectangle(84, 0, 316, 180)
                    .setColor('#2C2F33')
                    .printRectangle(0, 0, 84, 180)
                    .printRectangle(169, 26, 231, 46)
                    .printRectangle(224, 108, 176, 46)
                    .setShadowColor('rgba(22, 22, 22, 1)')
                    .setShadowOffsetY(5)
                    .setShadowBlur(10)
                    .printCircle(84, 90, 62)
                    .printCircularImage(avatar, 20, 26, 64)
                    .save()
                    .createRoundedClip(20, 138, 128, 32, 5)
                    .setColor('#23272A')
                    .fill()
                    .restore()
                    .setTextAlign('center')
                    .setTextFont('10pt Discord')
                    .setColor('#FFFFFF')
                    .printText(name, 285, 54)
                    .printText(`Level: ${level.toLocaleString()}`, 84, 159)
                    .setTextAlign('left')
                    .printText(`XP: ${points.toLocaleString()}`, 241, 136)
                    .toBuffer();
            } catch (error) {
                message.channel.send(`Something happened: \`${error.message}\``);
                console.error(error);
            }
        }

        const key = `${message.guild.id}-${user.user.id}`;

        message.client.points.ensure(`${message.guild.id}-${user.user.id}`, {
            user: user.user.id,
            guild: message.guild.id,
            points: 0,
            level: 1,
        });

        const buffer = await profile(user, key);
        const filename = `profile-${user.user.id}.jpg`;
        const attachment = new MessageAttachment(buffer, filename);

        message.channel.send({ files: [attachment] });
    }
};
kyranet commented 2 years ago

The culprit is in those lines:

const result = await fetch(member.user.displayAvatarURL().replace(imageUrlRegex, '?size=128'));
if (!result.ok) throw new Error('Failed to get the avatar.');
const avatar = await result.buffer();
// ...
    .printCircularImage(avatar, 20, 26, 64)
// ...

avatar must be an Image or Canvas instance. Here, it's a Buffer instance.

Please use skia.resolveImage, that'll solve your issue.

minhcrafters commented 2 years ago

now it's returning another error: image

kyranet commented 2 years ago

Please join the official server at https://discord.gg/taNgb9d or share the code.

minhcrafters commented 2 years ago

here

const url = member.user.displayAvatarURL().replace('.webp', '.png').replace(imageUrlRegex, '?size=128');
console.log(url);
const result = await fetch(url);
if (!result.ok) throw new Error('Failed to get the avatar.');
console.log(result);
const buffer = await result.buffer();
console.log(buffer);
const avatar = await resolveImage(buffer);
// ...
    .printCircularImage(avatar, 20, 26, 64)
// ...
kyranet commented 2 years ago

You don't need node-fetch, resolveImage already fetches images for you:

const url = member.user.displayAvatarURL().replace('.webp', '.png').replace(imageUrlRegex, '?size=128');
const avatar = await resolveImage(url);
// ...
    .printCircularImage(avatar, 20, 26, 64)
// ...
minhcrafters commented 2 years ago

oh ok, thanks for the help