Created
September 4, 2023 17:24
-
-
Save xqsit94/2c41085edb3f5030dfcdb21e80f49303 to your computer and use it in GitHub Desktop.
Blog Featured Image Generator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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