Skip to content

Instantly share code, notes, and snippets.

@socmov
Last active May 20, 2021 13:07
Show Gist options
  • Save socmov/746316b6483386e0173d98abdaf9e0bc to your computer and use it in GitHub Desktop.
Save socmov/746316b6483386e0173d98abdaf9e0bc to your computer and use it in GitHub Desktop.
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