Building a Memo Book Personalization Web App for Field Notes

A Companion App to the New “Vignette” Edition

Image for post
Image for post

Base

Image for post
Image for post
The original prototype

Camera

navigator.mediaDevices.getUserMedia({
audio: false,
video: true
})
.then(stream => {
this.stream = stream
this.$refs.video.srcObject = this.stream
})
<div class="hole">
<video autoplay playsinline></video>
</div>
.hole{
border-radius: 100%;
height: 20em;
overflow: hidden;
width: 20em;
}
.hole video{
height: calc(20em * 1.60);
left: 50%;
object-fit: cover;
position: absolute;
transform: translate(-50%, -50%);
top: 50%;
width: calc(20em * 1.21);
}
let video = this.$refs.videolet canvas = document.createElement('canvas')canvas.height = video.videoHeight
canvas.width = video.videoWidth
let context = canvas.getContext('2d')context.drawImage(this.$refs.video, 0, 0)

Cropping

.hole{
border-radius: 100%;
height: 20em;
overflow: hidden;
width: 20em;
}
.hole .canvas-container{
height: calc(20em * 1.60);
left: 50%;
position: absolute;
transform: translate(-50%, -50%);
top: 50%;
width: calc(20em * 1.21);
}
.hole .canvas-container canvas{
height: 1065px;
left: 0;
transform-origin: left top;
top: 0;
width: 808px;
}
let canvas = new fabric.Canvas(this.$refs.canvas, {
height: 1065,
width: 808
}
let hole = this.$refs.holelet screenTrimHeight = hole.offsetHeight * 1.60
let screenTrimWidth = hole.offsetWidth * 1.21
let printTrimHeight = 1065
let printTrimWidth = 808
let sy = screenTrimHeight / printTrimHeight
let sx = screenTrimWidth / printTrimWidth
let canvases = Array.from(canvas.wrapperEl.children)canvases.forEach(c => {
c.style.cssText = `transform: scale(${sx}, ${sy});`
})

PDF Generation

Image for post
Image for post
let canvas = this.$store.state.imagecanvas.toBlob((blob) => {
let file = new File([blob], 'FN.PNG', {
type: 'image/png'
})
fetch(process.env.FIELD_NOTES_API, {
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data'
},
body: file
})
.then(r => r.blob())
.then(this.openPDF)
})
let pdfBlob = new Blob([blob], {
type: 'application/pdf'
})
const data = window.URL.createObjectURL(pdfBlob)let link = document.createElement('a')link.href = data
link.download = "fn.pdf"
link.click()
service: field-notes-apiprovider:
name: aws
runtime: nodejs12.x
apiGateway:
binaryMediaTypes:
- "*/*"
functions:
hello:
handler: handler.pdf
events:
- http:
path: pdf
method: post
contentHandling: CONVERT_TO_BINARY
plugins:
- serverless-apigw-binary
- serverless-apigwy-binary
- serverless-offline
custom:
apigwBinary:
types:
- "application/pdf"
'use strict';const PDFDocument = require('pdfkit');
const { Base64Encode } = require('base64-stream');
module.exports.pdf = async (event, context) => {
// functionality will go here
}
let image = Buffer.from(event.body, 'base64')
const pdfBuffer = await new Promise(resolve => {
const doc = new PDFDocument()
doc.image(image, 209, 268, {
height: 256,
width: 194
})
doc.image('guide.png', 0, 0, {
height: 792,
width: 612
})
doc.end() const buffers = [] doc.on("data", buffers.push.bind(buffers)) doc.on("end", () => {
const pdfData = Buffer.concat(buffers)
resolve(pdfData)
})
})
return {
headers: {
"Content-Type": "application/pdf",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Credentials": true
},
body: pdfBuffer.toString("base64"),
isBase64Encoded: true,
}

Bonus: Faux 3D Compositing

Image for post
Image for post

Thanks

Make of These What You Will

Written by

I develop websites for rock 'n' roll bands and get paid in sex and drugs. Previously Silva Artist Management, SoundCloud, and Songkick. Currently: Available

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store