UNPKG

letter-avatar-image

Version:

A simple and lightweight package to generate letter-based avatars with customizable styles and colors, perfect for displaying initials or user profile images in your applications. `letter-avatar-image` is an easy-to-use npm package that allows you to gene

172 lines (147 loc) 8.89 kB
const { createCanvas } = require('canvas'); const allColors = require('./colors.json'); const Avatar = { extractHexColors: () => { return Object.values(allColors).map((color) => color.hex); }, allConditionCheck: async ({ width, height, letter, font, textColor, textAlign, textBaseline, colors }) => { return new Promise(async (resolve, reject) => { try { if (!letter || letter === null || letter === undefined || letter == '') { throw new Error(`Please provide the 'letter' configuration for the specified text. The first letter will be converted into an avatar image.`); } if (!colors?.length || colors == [] || colors === null || colors === undefined || colors === '') { throw new Error(`Invalid input: The colors array cannot be empty. Please provide at least one color in the array.`); } if (!width || width === null || width === undefined || width === '') { throw new Error(`Invalid input: The width field cannot be empty. Please provide a valid width value.`); } if (!height || height === null || height === undefined || height === '') { throw new Error(`Invalid input: The height field cannot be empty. Please provide a valid height value.`); } if (!font || font === null || font === undefined || font === '') { throw new Error(`Invalid input: The font field cannot be empty. Please provide a valid font value.`); } if (!textColor || textColor === null || textColor === undefined || textColor === '') { throw new Error(`Invalid input: The textColor field cannot be empty. Please provide a valid textColor value.`); } if (!textAlign || textAlign === null || textAlign === undefined || textAlign === '') { throw new Error(`Invalid input: The textAlign field cannot be empty. Please provide a valid textAlign value.`); } if (!textBaseline || textBaseline === null || textBaseline === undefined || textBaseline === '') { throw new Error(`Invalid input: The textAlign field cannot be empty. Please provide a valid textAlign value.`); } resolve(true); } catch (error) { reject(error); } }); }, LetterAvatar: async ({ width = 100, height = 100, letter = 'A', font = 'bold 60pt Graphiks', textColor = '#ffffff', textAlign = 'center', textBaseline = 'middle', strokeColor = '#000000', isStroke = false, strokeWidth = 1, isTextStroke = false, textStrokeColor = '#000000', textStrokeWidth = 1, colors = Avatar.extractHexColors(), singleColor = '', isRounded = false, roundedStrokeWidth = 1, isUpperCase = true, textCharacter = 0 }) => { const isNode = typeof window === 'undefined'; return new Promise(async (resolve, reject) => { try { await Avatar.allConditionCheck({ width, height, letter, font, textColor, textAlign, textBaseline, colors }); const character = textCharacter === 0 ? letter?.charAt(textCharacter) : letter?.slice(0, textCharacter); let text = isUpperCase ? character?.toUpperCase() : character; const adjustedFont = isRounded ? font.replace(/\d+pt/, `${Math.floor(parseInt(font.match(/\d+/)[0]) * 0.8)}pt`) : font; const randomColor = singleColor || colors[Math.floor(Math.random() * colors.length)]; if (isNode) { const canvas = createCanvas(width, height); const ctx = canvas.getContext('2d'); if (isRounded) { ctx.beginPath(); ctx.arc(width / 2, height / 2, Math.min(width, height) / 2, 0, Math.PI * 2); ctx.clip(); } ctx.fillStyle = randomColor; ctx.fillRect(0, 0, width, height); ctx.font = adjustedFont; ctx.fillStyle = textColor; ctx.textAlign = textAlign; ctx.textBaseline = textBaseline; if (isStroke) { const roundedStroke = roundedStrokeWidth <= 3 ? roundedStrokeWidth : 1; ctx.strokeStyle = strokeColor; ctx.lineWidth = isRounded ? roundedStroke : strokeWidth; if (isRounded) { ctx.beginPath(); ctx.arc(width / 2, height / 2, Math.min(width, height) / 2 - roundedStroke / 2, 0, Math.PI * 2); ctx.stroke(); } else { ctx.strokeRect(0, 0, width, height); } } const textMetrics = ctx.measureText(text); const textHeight = textMetrics.actualBoundingBoxAscent + textMetrics.actualBoundingBoxDescent; const centerY = height / 2 - textHeight / 2 + textMetrics.actualBoundingBoxAscent; ctx.fillText(text, width / 2, isRounded ? centerY : height / 2); if (isTextStroke) { ctx.strokeStyle = textStrokeColor; ctx.lineWidth = textStrokeWidth; ctx.strokeText(text, width / 2, isRounded ? centerY : height / 2); } const buffer = canvas.toBuffer('image/png'); resolve(buffer); } else { // Browser (Frontend) const canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; const ctx = canvas.getContext('2d'); // Check for rounded shape if (isRounded) { ctx.beginPath(); ctx.arc(width / 2, height / 2, Math.min(width, height) / 2, 0, Math.PI * 2); ctx.clip(); } // Set background color ctx.fillStyle = randomColor; ctx.fillRect(0, 0, width, height); // Adjust font size for rounded shapes ctx.font = adjustedFont; // Set text properties ctx.fillStyle = textColor; ctx.textAlign = textAlign; ctx.textBaseline = textBaseline; // Add border/stroke around the shape if needed if (isStroke) { const roundedStroke = roundedStrokeWidth <= 3 ? roundedStrokeWidth : 1; ctx.strokeStyle = strokeColor; ctx.lineWidth = isRounded ? roundedStroke : strokeWidth; if (isRounded) { ctx.beginPath(); ctx.arc(width / 2, height / 2, Math.min(width, height) / 2 - roundedStroke / 2, 0, Math.PI * 2); ctx.stroke(); } else { ctx.strokeRect(0, 0, width, height); } } // Measure text for vertical alignment const textMetrics = ctx.measureText(text); const textHeight = textMetrics.actualBoundingBoxAscent + textMetrics.actualBoundingBoxDescent; const centerY = height / 2 - textHeight / 2 + textMetrics.actualBoundingBoxAscent; // Draw text ctx.fillText(text, width / 2, isRounded ? centerY : height / 2); // Add stroke to text if required if (isTextStroke) { ctx.strokeStyle = textStrokeColor; ctx.lineWidth = textStrokeWidth; ctx.strokeText(text, width / 2, isRounded ? centerY : height / 2); } // Convert canvas to data URL and resolve const dataURL = canvas.toDataURL('image/png'); resolve(dataURL); } } catch (e) { console.error('error in LetterAvatar =----->> ', e); reject(e); } }); }, }; // (async () => { // const avtar = await Avatar.LetterAvatar({ font: 'bold 60pt Graphiks', letter: "tirth", isRounded: false, isUpperCase: true, isStroke: true }); // console.log('avtar :', avtar.toString('base64')); // })(); module.exports = Avatar;