Name card almost done
This commit is contained in:
BIN
namecard.png
BIN
namecard.png
Binary file not shown.
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.1 MiB |
@@ -29,13 +29,19 @@ export class NameCardCreator {
|
|||||||
|
|
||||||
const template = this.loadTemplate();
|
const template = this.loadTemplate();
|
||||||
|
|
||||||
const avatar = await this.handleAvatar(avatarPath);
|
const avatarSize = 450;
|
||||||
const messageBuffer = await this.getMessageBuffer(`Hello ${name}!`);
|
const avatar = await this.handleAvatar(avatarPath, avatarSize);
|
||||||
|
const avatarPos = { x : 1500-(avatarSize/2), y : 100}
|
||||||
|
|
||||||
|
const messageWidth = 1500;
|
||||||
|
const messageHeight = 200;
|
||||||
|
const messageBuffer = await this.getMessageBuffer(`Hello ${name}!`, messageWidth, messageHeight);
|
||||||
|
const messagePos = { x : 1500-(messageWidth/2), y : avatarPos.y + 550}
|
||||||
|
|
||||||
const result = await template
|
const result = await template
|
||||||
.composite([
|
.composite([
|
||||||
{ input: avatar, top: 215, left: 275 },
|
{ input: avatar, top: avatarPos.y, left: avatarPos.x },
|
||||||
{ input: messageBuffer, top: 400, left: 1300 }
|
{ input: messageBuffer, top: messagePos.y, left: messagePos.x }
|
||||||
])
|
])
|
||||||
.toFile("namecard.png")
|
.toFile("namecard.png")
|
||||||
|
|
||||||
@@ -50,69 +56,71 @@ export class NameCardCreator {
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param avatarPath
|
* @param avatarPath
|
||||||
|
* @param outputSize
|
||||||
* @returns {Promise<Buffer<ArrayBufferLike>>}
|
* @returns {Promise<Buffer<ArrayBufferLike>>}
|
||||||
*/
|
*/
|
||||||
async handleAvatar(avatarPath) {
|
async handleAvatar(avatarPath, outputSize) {
|
||||||
const avatarSize = 670;
|
const avatarSize = outputSize;
|
||||||
const borderSize = 8;
|
const borderSize = 8;
|
||||||
const radius = avatarSize / 2;
|
|
||||||
const totalSize = avatarSize + borderSize * 2;
|
const totalSize = avatarSize + borderSize * 2;
|
||||||
|
const avatarRadius = avatarSize / 2;
|
||||||
|
|
||||||
|
const maskBuffer = Buffer.from(`
|
||||||
|
<svg width="${avatarSize}" height="${avatarSize}" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle cx="${avatarRadius}" cy="${avatarRadius}" r="${avatarRadius}" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
`);
|
||||||
|
|
||||||
const avatarBuffer = await sharp(avatarPath)
|
const avatarBuffer = await sharp(avatarPath)
|
||||||
.resize(avatarSize, avatarSize, { fit: "cover" })
|
.resize(avatarSize, avatarSize, { fit: "cover" })
|
||||||
.png()
|
.png()
|
||||||
.toBuffer();
|
.toBuffer();
|
||||||
|
|
||||||
const maskSvg = Buffer.from(`
|
const roundedAvatarBuffer = await sharp(avatarBuffer)
|
||||||
<svg width="${avatarSize}" height="${avatarSize}" xmlns="http://www.w3.org/2000/svg">
|
.composite([{ input: maskBuffer, blend: "dest-in" }])
|
||||||
<circle cx="${radius}" cy="${radius}" r="${radius}" fill="white"/>
|
|
||||||
</svg>
|
|
||||||
`);
|
|
||||||
|
|
||||||
const roundedAvatar = await sharp(avatarBuffer)
|
|
||||||
.composite([{ input: maskSvg, blend: "dest-in" }])
|
|
||||||
.png()
|
.png()
|
||||||
.toBuffer();
|
.toBuffer();
|
||||||
|
|
||||||
const roundedBorder = Buffer.from(`
|
const backgroundBuffer = Buffer.from(`
|
||||||
<svg width="${totalSize}" height="${totalSize}" xmlns="http://www.w3.org/2000/svg">
|
<svg width="${totalSize}" height="${totalSize}" xmlns="http://www.w3.org/2000/svg">
|
||||||
<circle
|
<circle
|
||||||
cx="${totalSize / 2}"
|
cx="${totalSize / 2}"
|
||||||
cy="${totalSize / 2}"
|
cy="${totalSize / 2}"
|
||||||
r="${radius + borderSize / 2}"
|
r="${avatarRadius}"
|
||||||
stroke="#ffffff"
|
stroke="#ffffff"
|
||||||
stroke-width="${borderSize}"
|
stroke-width="${borderSize}"
|
||||||
fill="none"
|
fill="white"/>
|
||||||
/>
|
|
||||||
</svg>
|
</svg>
|
||||||
`)
|
`);
|
||||||
|
|
||||||
return await sharp({
|
return await sharp({
|
||||||
create: {
|
create: {
|
||||||
width: totalSize,
|
width: totalSize,
|
||||||
height: totalSize,
|
height: totalSize,
|
||||||
channels: 4,
|
channels: 4,
|
||||||
background: "#0000"
|
background: "#0000",
|
||||||
}
|
}})
|
||||||
}).composite([
|
.composite([
|
||||||
{ input: roundedAvatar, top: 0, left: 0 },
|
{ input: backgroundBuffer, top: 0, left: 0 },
|
||||||
{ input: roundedBorder, top: borderSize, left: borderSize }
|
{ input: roundedAvatarBuffer, top: borderSize, left: borderSize }
|
||||||
])
|
])
|
||||||
.png()
|
.png()
|
||||||
.toBuffer();
|
.toBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
async getMessageBuffer(message){
|
async getMessageBuffer(message, outputWidth, outputHeight){
|
||||||
const messageSvg = this.getMessageSvg(message);
|
const messageSvg = this.getMessageSvg(message, outputWidth, outputHeight);
|
||||||
return Buffer.from(messageSvg, "utf-8");
|
return Buffer.from(messageSvg, "utf-8");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param message {string}
|
* @param message {string}
|
||||||
|
* @param outputWidth
|
||||||
|
* @param outputHeight
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
getMessageSvg(message) {
|
getMessageSvg(message, outputWidth, outputHeight) {
|
||||||
const safeMessage =
|
const safeMessage =
|
||||||
message
|
message
|
||||||
.replace(/&/g, "&")
|
.replace(/&/g, "&")
|
||||||
@@ -120,7 +128,7 @@ export class NameCardCreator {
|
|||||||
.replace(/>/g, ">");
|
.replace(/>/g, ">");
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<svg width="1500" height="200" xmlns="http://www.w3.org/2000/svg">
|
<svg width="${outputWidth}" height="${outputHeight}" xmlns="http://www.w3.org/2000/svg">
|
||||||
<style>
|
<style>
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Fredoka';
|
font-family: 'Fredoka';
|
||||||
@@ -129,7 +137,7 @@ export class NameCardCreator {
|
|||||||
.title {
|
.title {
|
||||||
font-family: 'Fredoka', sans-serif;
|
font-family: 'Fredoka', sans-serif;
|
||||||
fill: #ede6e6;
|
fill: #ede6e6;
|
||||||
font-size: 80px;
|
font-size: ${outputHeight/2}px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
dominant-baseline: middle;
|
dominant-baseline: middle;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user