Created
September 10, 2020 07:40
-
-
Save noudadrichem/2e058505d5e181fcbf37a8b79d636b56 to your computer and use it in GitHub Desktop.
This file contains 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 Logger from "../lib/logger"; | |
import axios from "axios"; | |
import { createCanvas, loadImage, registerFont } from "canvas"; | |
import path from "path"; | |
import ColorThief from "colorthief"; | |
import { uploadImage } from "../resolvers/imgResolver"; | |
import { KeycapsetModel } from "../models"; | |
registerFont(__dirname + "../../../static/Rubik-SemiBold.ttf", { | |
family: "Rubik Semi", | |
}); | |
// registerFont("/static/Rubik-Regular.ttf", { family: "Rubik" }); | |
class MetaService { | |
log: any; | |
colors: any; | |
constructor() { | |
this.log = Logger; | |
this.colors = { | |
blue: "#539BFB", | |
blueLight: "#D4E4FA", | |
whiteLight: "#f8f9fb", | |
blackLight: "#1B1B1B", | |
black: "#000000", | |
white: "#ffffff", | |
}; | |
} | |
public drawImageProp( | |
ctx: any, | |
img: any, | |
x: number, | |
y: number, | |
w: number, | |
h: number, | |
offsetX: number = 0.5, | |
offsetY: number = 0.5 | |
) { | |
if (arguments.length === 2) { | |
x = y = 0; | |
w = ctx.canvas.width; | |
h = ctx.canvas.height; | |
} | |
// keep bounds [0.0, 1.0] | |
if (offsetX < 0) offsetX = 0; | |
if (offsetY < 0) offsetY = 0; | |
if (offsetX > 1) offsetX = 1; | |
if (offsetY > 1) offsetY = 1; | |
var iw = img.width, | |
ih = img.height, | |
r = Math.min(w / iw, h / ih), | |
nw = iw * r, // new prop. width | |
nh = ih * r, // new prop. height | |
cx, | |
cy, | |
cw, | |
ch, | |
ar = 1; | |
// decide which gap to fill | |
if (nw < w) ar = w / nw; | |
if (Math.abs(ar - 1) < 1e-14 && nh < h) ar = h / nh; // updated | |
nw *= ar; | |
nh *= ar; | |
// calc source rectangle | |
cw = iw / (nw / w); | |
ch = ih / (nh / h); | |
cx = (iw - cw) * offsetX; | |
cy = (ih - ch) * offsetY; | |
// make sure source rectangle is valid | |
if (cx < 0) cx = 0; | |
if (cy < 0) cy = 0; | |
if (cw > iw) cw = iw; | |
if (ch > ih) ch = ih; | |
// fill image in dest. rectangle | |
ctx.drawImage(img, cx, cy, cw, ch, x, y, w, h); | |
} | |
public isColourDarkOrLight(rgb: number[]): "light" | "dark" { | |
// RGB to HSP equation: http://alienryderflex.com/hsp.html | |
const [r, g, b] = rgb; | |
const hsp = Math.sqrt( | |
0.299 * (r * r) + 0.587 * (g * g) + 0.114 * (b * b) | |
); | |
return hsp > 117.5 ? "light" : "dark"; | |
} | |
public async getColoursFromImage(url: string) { | |
console.log("MetaService.getColoursFromImage()"); | |
const pallete = await ColorThief.getPalette(url); | |
const dominant = await ColorThief.getColor(url); | |
const type = this.isColourDarkOrLight(dominant); | |
return { dominant, pallete, type }; | |
} | |
public async generateMeta(keycapset: any) { | |
this.log.info(keycapset, "Generate meta image..."); | |
const width = 1200; | |
const height = 630; | |
const logoPath = path.join("./static/logo-dark.png"); | |
try { | |
const canvas = createCanvas(width, height); | |
const context = canvas.getContext("2d"); | |
this.log.info("Loading logo img..."); | |
const logo = await loadImage(logoPath); | |
this.log.info("Loading cover img..."); | |
const coverImg = await loadImage(keycapset.coverImageUrl); | |
const { dominant, pallete, type } = await this.getColoursFromImage( | |
keycapset.coverImageUrl | |
); | |
context.fillStyle = "rgba(255, 255, 255, 1)"; | |
context.fillRect(0, 0, width, height); | |
this.drawImageProp(context, coverImg, 0, 0, 880, height); | |
const gradient = context.createLinearGradient(185, 315, 721, 380); | |
gradient.addColorStop(0, `rgba(${dominant.join(",")}, 0)`); | |
gradient.addColorStop(0.8, `rgba(${dominant.join(",")}, 1)`); | |
context.fillStyle = gradient; | |
context.fillRect(0, 0, width, height); | |
context.drawImage(logo, 820, 162, 180, 71); | |
context.font = "bold 35pt Rubik Semi"; | |
context.textAlign = "center"; | |
context.textBaseline = "top"; | |
context.fillStyle = | |
type === "light" | |
? this.colors.blackLight | |
: this.colors.whiteLight; | |
const title = `${ | |
keycapset.brand === "gmk" | |
? "GMK" | |
: keycapset.brand === "epbt" | |
? "EPBT" | |
: keycapset.type.toUpperCase() | |
} ${keycapset.name}`; | |
context.fillText(title, 920, 262); | |
context.fillStyle = | |
type === "light" ? this.colors.blue : this.colors.blueLight; | |
context.fillRect(757, 360, 326, 64); | |
context.font = "bold 14pt Rubik Semi"; | |
context.textAlign = "center"; | |
context.textBaseline = "top"; | |
context.fillStyle = | |
type === "light" ? this.colors.white : this.colors.blue; | |
context.fillText("See on keycapsets.com", 920, 380); | |
const buffer = canvas.toBuffer("image/png"); | |
// fs.writeFileSync( | |
// `./tmp/meta-${keycapset._id}-${keycapset.name}.png`, | |
// buffer | |
// ); | |
this.log.info("DONE"); | |
return buffer; | |
} catch (err) { | |
this.log.error(err, "Error while generating meta..."); | |
} | |
} | |
async setMetaURLonSet(keycapset) { | |
const metaImgBuffer = await this.generateMeta(keycapset); | |
console.log(metaImgBuffer); | |
// const metaGenURL = `https://api-testing.keycapsets.com/meta/${keycapset._id}`; | |
if (metaImgBuffer) { | |
const newImgUrl = await uploadImage( | |
metaImgBuffer.toString("base64") | |
); | |
const newKeycapset: any = await KeycapsetModel.findOneAndUpdate( | |
{ _id: keycapset._id }, | |
{ metaUrl: newImgUrl.url }, | |
{ new: true } | |
); | |
return newKeycapset; | |
} | |
return keycapset; | |
} | |
} | |
export default new MetaService(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment