Last active
May 20, 2021 13:07
-
-
Save socmov/746316b6483386e0173d98abdaf9e0bc to your computer and use it in GitHub Desktop.
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 fs = require('fs') | |
const { createCanvas, loadImage } = require('canvas') | |
const { drawImage } = require('canvas-object-fit') | |
const createConferenceMetaImage = async (conf, img = './stage.jpg') => { | |
try { | |
const width = 1200 | |
const height = 600 | |
const hexWidth = 750 | |
const confColours = { | |
apac: ['#EF4444', '#DC2626'], | |
usa: ['#F97316', '#EA580C'], | |
emea: ['#14B8A6', '#0D9488'], | |
community: ['#06B6D4', '#0891B2'] | |
} | |
const uxdx = '#6366F1' | |
const uxdx1 = '#0EA5E9' | |
const uxdx2 = '#0369A1' | |
console.log('creating canvas') | |
const canvas = createCanvas(width, height) | |
const context = canvas.getContext('2d') | |
console.log('loading background image') | |
const background = await loadImage(img) | |
drawImage(context, background, 0, 0, width, height, { objectFit: 'cover' }) | |
context.fillStyle = 'rgba(0, 0, 0, 0.4)' | |
context.fillRect(0, 0, width, height) | |
/// ////////////////////////////////////// | |
/// HEXAGON ///////////////////////// | |
/// /////////////////////////////////////// | |
var grd = context.createLinearGradient(width / 3, 0, width / 1.5, height) | |
grd.addColorStop(0, uxdx) | |
// grd.addColorStop(1, uxdx) | |
const stop1 = conf ? confColours[conf][0] : uxdx1 | |
const stop2 = conf ? confColours[conf][1] : uxdx2 | |
console.log(stop1, stop2) | |
grd.addColorStop(0.8, stop1) | |
grd.addColorStop(1, stop2) | |
context.beginPath() | |
const points = [ | |
{ x: -1130 + hexWidth, y: 420 }, | |
{ x: -850 + hexWidth, y: -110 }, | |
{ x: -260 + hexWidth, y: -140 }, | |
{ x: 70 + hexWidth, y: 370 }, | |
{ x: -210 + hexWidth, y: 910 }, | |
{ x: -800 + hexWidth, y: 940 } | |
] | |
roundedPoly(context, points, 100) | |
context.strokeStyle = 'white' | |
context.lineWidth = 15 | |
context.fillStyle = grd | |
context.stroke() | |
context.fill() | |
const logo = await loadImage('../src/images/uxdx-logo.svg') | |
context.drawImage(logo, 40, 35, 150, 150) | |
context.font = 'bold 40pt Lato' | |
context.textAlign = 'left' | |
context.textBaseline = 'top' | |
context.fillStyle = 'white' | |
const logoText = 'UXDX APAC' | |
context.fillText(logoText, 190, 55) | |
context.font = 'normal 25pt Lato' | |
context.textAlign = 'left' | |
context.textBaseline = 'top' | |
context.fillStyle = 'white' | |
const date = '4 - 5 March 2021' | |
context.fillText(date, 200, 115) | |
context.font = 'italic 35pt Lato' | |
context.textAlign = 'left' | |
context.textBaseline = 'top' | |
context.fillStyle = 'white' | |
const line1 = 'The conference to shift' | |
context.fillText(line1, 60, 235) | |
context.font = '100 55pt Lato' | |
context.textAlign = 'left' | |
context.textBaseline = 'top' | |
context.fillStyle = 'white' | |
const line2 = 'FROM' | |
context.fillText(line2, 60, 295) | |
context.font = '600 65pt Lato' | |
context.textAlign = 'left' | |
context.textBaseline = 'top' | |
context.fillStyle = 'white' | |
const line22 = 'PROJECTS' | |
context.fillText(line22, 280, 285) | |
context.font = '100 55pt Lato' | |
context.textAlign = 'left' | |
context.textBaseline = 'top' | |
context.fillStyle = 'white' | |
const line3 = 'TO' | |
context.fillText(line3, 60, 385) | |
context.font = '900 65pt Lato' | |
context.textAlign = 'left' | |
context.textBaseline = 'top' | |
context.fillStyle = 'white' | |
const line33 = 'PRODUCTS' | |
context.fillText(line33, 180, 375) | |
context.font = '500 20pt Lato' | |
context.textAlign = 'left' | |
context.textBaseline = 'top' | |
context.fillStyle = 'white' | |
const line4 = 'PRODUCT | UX | DESIGN | DEV' | |
context.fillText(line4, 60, 530) | |
const buffer = canvas.toBuffer('image/png') | |
fs.writeFileSync('./test.png', buffer) | |
return canvas | |
} catch (e) { | |
console.log(e) | |
} | |
} | |
createConferenceMetaImage('apac') | |
function roundedPoly (ctx, points, radiusAll) { | |
var i, x, y, len, p1, p2, p3, v1, v2, sinA, sinA90, radDirection, drawDirection, angle, halfAngle, cRadius, lenOut, radius | |
// convert 2 points into vector form, polar form, and normalised | |
var asVec = function (p, pp, v) { | |
v.x = pp.x - p.x | |
v.y = pp.y - p.y | |
v.len = Math.sqrt(v.x * v.x + v.y * v.y) | |
v.nx = v.x / v.len | |
v.ny = v.y / v.len | |
v.ang = Math.atan2(v.ny, v.nx) | |
} | |
radius = radiusAll | |
v1 = {} | |
v2 = {} | |
len = points.length | |
p1 = points[len - 1] | |
// for each point | |
for (i = 0; i < len; i++) { | |
p2 = points[(i) % len] | |
p3 = points[(i + 1) % len] | |
// ----------------------------------------- | |
// Part 1 | |
asVec(p2, p1, v1) | |
asVec(p2, p3, v2) | |
sinA = v1.nx * v2.ny - v1.ny * v2.nx | |
sinA90 = v1.nx * v2.nx - v1.ny * -v2.ny | |
angle = Math.asin(sinA < -1 ? -1 : sinA > 1 ? 1 : sinA) | |
// ----------------------------------------- | |
radDirection = 1 | |
drawDirection = false | |
if (sinA90 < 0) { | |
if (angle < 0) { | |
angle = Math.PI + angle | |
} else { | |
angle = Math.PI - angle | |
radDirection = -1 | |
drawDirection = true | |
} | |
} else { | |
if (angle > 0) { | |
radDirection = -1 | |
drawDirection = true | |
} | |
} | |
if (p2.radius !== undefined) { | |
radius = p2.radius | |
} else { | |
radius = radiusAll | |
} | |
// ----------------------------------------- | |
// Part 2 | |
halfAngle = angle / 2 | |
// ----------------------------------------- | |
// ----------------------------------------- | |
// Part 3 | |
lenOut = Math.abs(Math.cos(halfAngle) * radius / Math.sin(halfAngle)) | |
// ----------------------------------------- | |
// ----------------------------------------- | |
// Special part A | |
if (lenOut > Math.min(v1.len / 2, v2.len / 2)) { | |
lenOut = Math.min(v1.len / 2, v2.len / 2) | |
cRadius = Math.abs(lenOut * Math.sin(halfAngle) / Math.cos(halfAngle)) | |
} else { | |
cRadius = radius | |
} | |
// ----------------------------------------- | |
// Part 4 | |
x = p2.x + v2.nx * lenOut | |
y = p2.y + v2.ny * lenOut | |
// ----------------------------------------- | |
// Part 5 | |
x += -v2.ny * cRadius * radDirection | |
y += v2.nx * cRadius * radDirection | |
// ----------------------------------------- | |
// Part 6 | |
ctx.arc(x, y, cRadius, v1.ang + Math.PI / 2 * radDirection, v2.ang - Math.PI / 2 * radDirection, drawDirection) | |
// ----------------------------------------- | |
p1 = p2 | |
p2 = p3 | |
} | |
ctx.closePath() | |
} | |
function desaturate (amount, ctx, w, h) { | |
const imgData = ctx.getImageData(0, 0, w, h) | |
let x; let y; let i | |
let grey | |
for (y = 0; y < h; y++) { | |
for (x = 0; x < w; x++) { | |
i = (y * w + x) * 4 | |
grey = parseInt(0.2125 * imgData.data[i] + 0.7154 * imgData.data[i + 1] + 0.0721 * imgData.data[i + 2], 10) | |
imgData.data[i] += amount * (grey - imgData.data[i]) | |
imgData.data[i + 1] += amount * (grey - imgData.data[i + 1]) | |
imgData.data[i + 2] += amount * (grey - imgData.data[i + 2]) | |
} | |
} | |
return imgData | |
} | |
const shrinkwrap = (ctx, text, x, y, fontWeight, fontSize, fontFace, maxWidth, maxLines, originalFontSize) => { | |
if (!originalFontSize) originalFontSize = fontSize | |
const words = text.split(' ') | |
const line = [] | |
let lineCount = 1 | |
line[lineCount] = '' | |
const lineHeight = fontSize * 1.5 // a good approx for 10-18px sizes | |
ctx.font = fontWeight + ' ' + fontSize + 'pt ' + fontFace | |
ctx.textBaseline = 'top' | |
for (var n = 0; n < words.length; n++) { | |
var testLine = line[lineCount] + words[n] + ' ' | |
var metrics = ctx.measureText(testLine) | |
var testWidth = metrics.width | |
if (testWidth > maxWidth) { | |
if (n < words.length) { | |
lineCount++ | |
line[lineCount] = words[n] + ' ' | |
} | |
} else { | |
line[lineCount] = testLine | |
} | |
} | |
if (lineCount > maxLines) return shrinkwrap(ctx, text, x, y, fontWeight, fontSize - 1, fontFace, maxWidth, maxLines, originalFontSize) | |
// if the font size is too small we'll need to push down a little | |
const diff = originalFontSize - fontSize | |
y = y + diff * 2 | |
for (let i = 1; i <= lineCount; i++) { | |
if (lineCount === 1) y += lineHeight * (maxLines / 2) | |
if (lineCount === 2 && i === 1) y += lineHeight / (maxLines / 2) | |
ctx.fillText(line[i], x, y + lineHeight * (i - 1)) | |
ctx.strokeText(line[i], x, y + lineHeight * (i - 1)) | |
} | |
// if there is only one line then push it down a little | |
// // print the lines | |
// if (lineCount === 2) ctx.fillText(line[2], x, y + lineHeight) | |
// ctx.strokeText(line[1], x, y) | |
// if (lineCount === 2) ctx.strokeText(line[2], x, y + lineHeight) | |
} | |
const startAndEndDate = (start, end) => { | |
if (!start || !end) return null | |
const startDate = start.split('-') | |
const endDate = end.split('-') | |
// get start day | |
const startDay = parseInt(startDate[2]) | |
const startMonth = parseInt(startDate[1]) | |
// get end day | |
const endDay = parseInt(endDate[2]) | |
const endMonth = parseInt(endDate[1]) | |
const endYear = endDate[0] | |
const sameDay = startDay === endDay | |
const sameMonth = startMonth === endMonth | |
let formattedString = startDay + ' ' | |
if (!sameMonth) formattedString += months[startMonth - 1] + ' ' | |
if (!sameDay) formattedString += '- ' + endDay + ' ' | |
formattedString += months[endMonth - 1] + ' ' + endYear | |
return formattedString | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment