Skip to content

Instantly share code, notes, and snippets.

@xqsit94
Created September 4, 2023 17:24
Show Gist options
  • Save xqsit94/2c41085edb3f5030dfcdb21e80f49303 to your computer and use it in GitHub Desktop.
Save xqsit94/2c41085edb3f5030dfcdb21e80f49303 to your computer and use it in GitHub Desktop.
Blog Featured Image Generator
const imageWidth = 1280;
const imageHeight = 720;
const coverTitle = "The quick brown fox jumped over the lazy dog";
const predefinedColoursLight = ["#559BFF", "#FFD948", "#CD1FFF", "#41FFA7"];
const predefinedColoursDark = [
"#281140",
"#023373",
"#044035",
"#131827",
"#012340",
];
export {
imageWidth,
imageHeight,
coverTitle,
predefinedColoursDark,
predefinedColoursLight,
};
import axios from "axios";
import tinycolor from "tinycolor2";
/**
* Fetches an icon from the Iconify API.
* @param {string} iconName The name of the icon to fetch.
* @param {string} color The color of the icon.
* @returns {string} The SVG icon.
*/
const fetchIcon = async (iconName, color) => {
const height = 16 * 16;
const url = `https://api.iconify.design/${iconName}.svg?color=${encodeURIComponent(
color
)}&height=${height}`;
const response = await axios.get(url);
return response.data;
};
/**
* Generates a random hex colour.
* @returns {string} A random hex colour.
*/
const generateRandomColour = () => {
const hexCharacters = "0123456789abcdef";
let hexCode = "#";
for (let i = 0; i < 6; i++) {
hexCode += hexCharacters[Math.floor(Math.random() * hexCharacters.length)];
}
return hexCode;
};
/**
* Picks a random colour from an array.
* @param {string[]} colours An array of colours.
* @returns {string} A random colour from the array.
*/
const pickRandomColourFromArray = (colours) => {
return colours[Math.floor(Math.random() * colours.length)];
};
/**
* Generates a gradient from a colour.
* @param {string} colour The colour to generate the gradient from.
* @returns {string} The gradient.
*/
const determineFontSize = (title) => {
if (title.length > 30) return "3rem";
if (title.length > 24) return "4rem";
return "4.5rem";
};
/**
* Generates a gradient from a colour.
* @param {string} mainColour The colour to generate the gradient from.
* @returns {string} The gradient.
*/
const generateGradient = (mainColour) => {
const firstColour = tinycolor(mainColour).darken(25).toString();
const firstColourRGB = tinycolor(firstColour).toRgbString();
const secondColour = tinycolor(mainColour).spin(50).toString();
const secondColourRGB = tinycolor(secondColour).toRgbString();
const angle = Math.floor(Math.random() * 180);
return `linear-gradient(-${angle}deg, ${firstColourRGB}, ${secondColourRGB})`;
};
export {
fetchIcon,
generateRandomColour,
pickRandomColourFromArray,
determineFontSize,
generateGradient,
};
import tinycolor from "tinycolor2";
import { coverTitle, imageWidth, imageHeight } from "./config.js";
import { generateGradient, determineFontSize } from "./functions.js";
const generateHtml = (mainColour, icon) => {
console.log("Generating gradient");
const gradient = generateGradient(mainColour);
console.log("Gradient: " + gradient);
console.log("Generating title colours");
const titleBG = tinycolor(mainColour).setAlpha(0.6).toHexString();
const titleText = tinycolor(titleBG).isLight() ? "#000000" : "#ffffff";
console.log("Calculating font size for title");
const titleFontSize = determineFontSize(coverTitle);
console.log("Font Size: " + titleFontSize);
// generate subtitle color
const subtitleBG = tinycolor(mainColour).darken(10).toString();
const subtitleText = tinycolor(subtitleBG).isLight() ? "#000000" : "#ffffff";
return `
<html>
<head>
<style>
*,
::before,
::after {
box-sizing: border-box;
border-width: 0;
border-style: solid;
border-color: #e5e7eb;
}
::before,
::after {
--tw-content: '';
}
html {
line-height: 1.5;
-moz-tab-size: 4;
tab-size: 4;
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
font-feature-settings: normal;
font-variation-settings: normal;
}
body {
margin: 0;
padding: 0;
line-height: inherit;
width: ${imageWidth}px;
height: ${imageHeight}px;
}
.image-container {
background: ${gradient};
width: ${imageWidth}px;
height: ${imageHeight}px;
display: flex;
justify-content: center;
align-items: center;
}
.post-image {
background-size: cover;
background-position: center;
border-radius: 2rem;
}
.post-container {
width: 80%;
height: 70%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: row;
/* background-color: ${titleBG}; */
border-radius: 2rem;
padding: 2rem;
}
.post-details {
color: ${titleText};
margin: 0 2rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
}
.post-category {
font-size: 1.5rem;
margin: 0;
text-transform: uppercase;
}
.post-title {
font-size: ${titleFontSize};
margin: 0;
text-transform: capitalize;
}
.post-subtitle {
background-color: ${subtitleBG};
border-radius: 1rem;
margin-top: 1rem;
}
.post-subtitle p {
color: ${subtitleText};
font-size: 1.8rem;
margin: 0;
padding: 0.6rem 1.2rem;
}
</style>
</head>
<body>
<div class="image-container">
<div class="post-container">
<div class="post-image">
${icon}
</div>
<div class="post-details">
<h6 class="post-category"># AdonisJS 5</h6>
<h1 class="post-title">${coverTitle}</h1>
<div class="post-subtitle">
<p>${coverTitle}</p>
</div>
</div>
</div>
</div>
</body>
</html>
`;
};
export default generateHtml;
import createImage from "node-html-to-image";
import { predefinedColoursDark, predefinedColoursLight } from "./config.js";
import {
fetchIcon,
pickRandomColourFromArray,
generateRandomColour,
} from "./functions.js";
import generateHtml from "./html-generator.js";
// Fetch icon
const icon = await fetchIcon("logos:laravel", "#000000");
// Main Color Selection
const mainColourDark = predefinedColoursDark
? pickRandomColourFromArray(predefinedColoursDark)
: generateRandomColour();
const mainColourLight = predefinedColoursLight
? pickRandomColourFromArray(predefinedColoursLight)
: generateRandomColour();
const htmlDark = generateHtml(mainColourDark, icon);
const htmlLight = generateHtml(mainColourLight, icon);
await createImage({
output: "./image.png",
html: htmlDark,
});
await createImage({
output: "./image-light.png",
html: htmlLight,
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment