Skip to main content
0:05est. 22 min

Canva

// https://gitlab.com/sebdeckers/canva-interview
// https://gist.github.com/samcooke98
// https://gist.github.com/samcooke98/1d86da6a588443be9cc9083129f1d777
<script src="https://gist.github.com/samcooke98/1d86da6a588443be9cc9083129f1d777.js"></script>
// Part-1.js
/**
* Fetch data at the given URL. Returns a promise that resolves with the data.
* Simulates random network latency up to 4 seconds.
*/
function fetch(url) {
return new Promise(resolve => {
setTimeout(() => resolve(`dummy data for ${url}`), Math.random() * 4000);
});
}

function fetch(url) {
return new Promise(resolve => {
setTimeout(() => resolve('dymmy data'), Math.random() * 5000);
})
}
// part-three.js
/**
* Fetch data at the given URL. Returns a promise that resolves with the data.
* Simulates random network latency up to 4 seconds.
*/
function fetch(url) {
return new Promise(resolve => {
setTimeout(() => resolve({
designId: 1,
shapes: [
{shapeId: 'basic-shape', color: { r: 55, g: 40, b: 255 }, children: []},
{shapeId: 'duck', color: { r: 255, g: 255, b: 252 }, children: [
{shapeId: 'duck-bill', color: { r: 255, g: 255, b: 255 }, children: []},
{shapeId: 'duck-body', color: { r: 205, g: 255, b: 252 }, children: []},
{shapeId: 'duck-legs', color: { r: 100, g: 255, b: 252 }, children: []},
]},
{shapeId: 'zigzag-polygon', color: { r: 205, g: 255, b: 252 }, children: []},
{shapeId: 'fish', color: { r: 205, g: 255, b: 252 }, children: [
{shapeId: 'fish-eyes', color: { r: 255, g: 255, b: 255 }, children: []},
{shapeId: 'fish-fin', color: { r: 100, g: 66, b: 74 }, children: [
{shapeId: 'fish-fin-part-1', color: { r: 93, g: 54, b: 55 }, children: []},
{shapeId: 'fish-fin-part-2', color: { r: 33, g: 255, b: 255 }, children: []},
{shapeId: 'fish-fin-part-3', color: { r: 128, g: 53, b: 255 }, children: []},
]},
{shapeId: 'fish-tail', color: { r: 255, g: 5, b: 255 }, children: []},
]},
{shapeId: 'duck', color: { r: 255, g: 255, b: 252 }, children: [
{shapeId: 'duck-bill', color: { r: 255, g: 255, b: 255 }, children: []},
{shapeId: 'duck-body', color: { r: 205, g: 255, b: 252 }, children: []},
{shapeId: 'duck-legs', color: { r: 100, g: 255, b: 252 }, children: []},
]},
]
}
), Math.random() * 4000);
});
}
part-two.js
/**
* Fetch data at the given URL. Returns a promise that resolves with the data.
* Simulates random network latency up to 4 seconds.
*/
function fetch(url) {
return new Promise(resolve => {
setTimeout(() => resolve({
designId: 1,
shapes: [
{ shapeId: 'basic-square', color: { r: 255, g: 255, b: 255 }},
{ shapeId: 'basic-circle', color: { r: 255, g: 255, b: 255 }},
{ shapeId: 'basic-diamond', color: { r: 255, g: 0, b: 0 }},
{ shapeId: 'basic-rectangle', color: { r: 0, g: 255, b: 0 }}
]
}
), Math.random() * 4000);
});
}


// --------------------------

/**
* Fetch data at the given URL. Returns a promise that resolves with the data.
* Simulates random network latency up to 4 seconds.
*/
function fetch(url) {
return new Promise(resolve => {
setTimeout(() => resolve(`dummy data for ${url}`), Math.random() * 4000);
});
}
// design/1->10
const BASE_API = 'https://localhost:3001/design/'


// fetch all
// -

// -- oncomplete
// print done

function allImagesCalls() { //pass-in
let itr;

let imageLibraryPromises = [];

for (itr=1;itr<=10;itr++) {

// just save urls in an array

// make call?
// 1. sync fetch 1 -> response 1 --> fetch 2 -> response -> 2 fetch-- upto 10 // print done // problem cause mutiple then catch, pyramid of hell
// 2. async wait for all of them to resolve at once // print done
const url = `${BASE_API}/${itr}`;
// create mutiple promises
imageLibraryPromises.push(
fetch(url)
)
}

return imageLibraryPromises;
}

function getAllImages(imageLibraryPromises) {
Promise.all(imageLibraryPromises).then((response) => {
// [ {response1} , {response2}, {respo---}]
console.log(response);
return response;
}).then(() => {
console.log('Done');
}).catch((e) => {
throw new Error("Failed to fetch")
})
}

const data = allImagesCalls();
// console.log(data);
getAllImages(allImagesCalls())

// ----


/**
* Fetch data at the given URL. Returns a promise that resolves with the data.
* Simulates random network latency up to 4 seconds.
*/
function fetch() {
return new Promise(resolve => {
setTimeout(() => resolve({
designId: 1,
shapes: [
{ shapeId: 'basic-square', color: { r: 255, g: 255, b: 255 }},
{ shapeId: 'basic-circle', color: { r: 255, g: 255, b: 255 }},
{ shapeId: 'basic-diamond', color: { r: 255, g: 0, b: 0 }},
{ shapeId: 'basic-rectangle', color: { r: 0, g: 255, b: 0 }}
]
}
), Math.random() * 4000);
});
}


// =-------------------------------

/**
* Fetch data at the given URL. Returns a promise that resolves with the data.
* Simulates random network latency up to 4 seconds.
*/
function fetch() {
return new Promise(resolve => {
setTimeout(() => resolve({
designId: 1,
shapes: [
{ shapeId: 'basic-square', color: { r: 255, g: 255, b: 255 }},
{ shapeId: 'basic-circle', color: { r: 255, g: 255, b: 255 }},
{ shapeId: 'basic-diamond', color: { r: 255, g: 0, b: 0 }},
{ shapeId: 'basic-rectangle', color: { r: 0, g: 255, b: 0 }}
]
}
), Math.random() * 4000);
});
}


// Design 1: { r: 191.25 g: 191.25 b: 127.5 } //vg r, g, b
// (255 + 255 + 255 + 0)/4
// s-> sq -> colors -> r
// s-> c -> colors -> r
// Design 2: { r: 191.25 g: 191.25 b: 127.5 }

const arrayOfPromisesShapes = [fetch()]; // use loop to create

const prob1= [
{
designId: 1,
shapes: [
{ shapeId: 'basic-square', color: { r: 255, g: 255, b: 255 }},
{ shapeId: 'basic-circle', color: { r: 255, g: 255, b: 255 }},
{ shapeId: 'basic-diamond', color: { r: 255, g: 0, b: 0 }},
{ shapeId: 'basic-rectangle', color: { r: 0, g: 255, b: 0 }}
]
},
{
designId: 2,
shapes: [
{ shapeId: 'basic-square', color: { r: 255, g: 255, b: 255 }},
{ shapeId: 'basic-circle', color: { r: 255, g: 255, b: 255 }},
{ shapeId: 'basic-diamond', color: { r: 255, g: 0, b: 0 }},
{ shapeId: 'basic-rectangle', color: { r: 0, g: 255, b: 0 }}
]
}
]

function getAvgs(arrayOfPromisesShapes) {
// p1 -> comp
// p1,p2 ->
arrayOfPromisesShapes.map((p) => {
return p
}).then((response) => {
return response;
}).then(getAvgsOfResponse).then((result) => {
console.log(result);
}).catch((e) => {
throw new Error("Failed to fetch")
})

// done
}

const result = [];

function getAvgsOfResponse(arrayOfResponses, itr) { // (x*x)
return new Promise((resolve, reject) => {
result.push(arrayOfResponses.map((design) => {
let sumR=0;
let sumG=0;
let sumB=0;
const totalShapes = design.shapes.length
// AVG
design.shapes.map((shape) => {
sumR+=shape.color.r
sumG+=shape.color.g
sumB+=shape.color.b
})
return {
r: sumR/totalShapes,
g: sumG/totalShapes,
b: sumB/totalShapes,
}
}))
if (itr==10) { // magic
resolve(result);
}
})
}

getAvgs(arrayOfPromisesShapes) // return

// excute me

// https://stackoverflow.com/questions/59513451/more-efficient-way-to-calculate-the-average-of-a-rgb-color-block-in-javascript
const colorBlock = [{ r: 234, g: 250, b: 0 }, { r: 234, g: 250, b: 0 }, { r: 234, g: 250, b: 0 }]

const arrLength = colorBlock.length

let redArr = colorBlock.map(obj => obj.r)
let greenArr = colorBlock.map(obj => obj.g)
let blueArr = colorBlock.map(obj => obj.b)

const add = (total, num) => total + num;

const totalR = redArr.reduce(add, 0);
const totalG = greenArr.reduce(add, 0);
const totalB = blueArr.reduce(add, 0);

const averageR = parseInt(totalR / arrLength)
const averageG = parseInt(totalG / arrLength)
const averageB = parseInt(totalB / arrLength)

// ----------------------- OR ---------------- //

let totals=colorBlock.reduce(
(totals,current)=>{
totals[0]+=current.r;
totals[1]+=current.g;
totals[2]+=current.b;
return totals;
},[0,0,0]);
let averageR=totals[0]/totals.length;
let averageG=totals[1]/totals.length;
let averageB=totals[2]/totals.length;

// Scratchpad repo for interview rounds with Canva.
// This repo does not contain a complete solutions. It also does not contain the intermediate steps. This is just the final, non-functioning source code I end up with, and my thoughts about the process in this document.
// The aim of this repo is to share my feedback with Canva and record my own experience. Not to give away answers or provide future candidates with unfair advantages.

// Round 0
// Introduced via email by an old friend who currently works at Canva.

// Round 1
// Voice call with in-house recruiter.
// Relocation to Sydney required. Acceptable timeframe is generous: up to end of year (from January).

// Round 2
// Live-coding challenges using Hacker Trail IDE web app and a video/voice call.
// Tested knowledge of JSON, fetch, and abilisty to recursively process a node list to "average" RGB values.
// Retrospective:


// Highlighted issues with the Hacker Trail IDE by pointed out the exact version of Node.js it runs on, and compared my accustomed coding style (async/await) with the supported older style (then/catch). Hope I didn't sound arrogant/entitled; just curious about how systems work.


// Recursion trips me up. My brain goes into ultra slow-and-careful mode. Due to time constraint we did one final iteration of the problem purely as a thought-exercise. Refactoring the code would be a low value-add exercise at that point.



// Round 3
// https://gomakethings.com/dom-diffing-with-vanilla-js-part-1/
// https://gomakethings.com/dom-diffing-with-vanilla-js-part-2/
// - https://programming.vip/docs/realization-and-analysis-of-virtual-dom-diff-algorithm.html (https://github.com/Lin-Kyle/virtualdom-diff, https://github.com/Lin-Kyle/virtualdom-diff/blob/master/src/patch.js)
// - https://github.com/EcutDavid/data-tree-view
// The interview was designed to examine "theoretical reasoning" skills. The aim is not just working code, though I was told that would be nice to have.
// The challenge involved some tree data structure design & tree diff algorithm.
// I got stuck on the recursion logic. Turns out this is a typical Google-esque interview question that CS grads know by heart and the standard response would be to use a queue or stack to avoid the iteration complexity. Oh well... "theoretical" 🙄
// I believe my strenths were:

// reasoning about, and demonstrating my knowledge of, the DOM APIs (e.g. DOM4 remove/append, TreeWalker)
// considering the performance implications of different design trade-offs
// identifying parts of the problem that can be isolated
// approaching the problem from different angles: resursion vs flattening, direct manipulation vs generating instructions, processing 3 vs 2 trees, etc.
// questioning interviewer assumptions and incremental goals
// minimalist data/test-first approach to avoid inflating the problem
// using various supporting tools: standardjs/eslint, http2 server, localhost tls, es modules with cors, webkit debugger, etc.


// Round 4
// Briefing: Emphasis on actual coding. Implement a lightweight Canva UI. Given some SVG assets, something about drag-and-drop, involves coding a game (misheard?), etc.
// Potential relevant experience to review:

// Drywall (2014-2015) heavily uses SVG and JS/CSS animation with GSAP,and Backbone.js

// Demo: https://drywall.cf.sg

// Code: https://github.com/drywallio



// CSSConf.Asia 2014 talk about SVG

https://www.youtube.com/watch?v=VQ7_MwjPqKs&index=2&list=PL37ZVnwpeshHFbT0mLTNMtMGO1mo6yPRX



// https://github.com/EcutDavid/police-and-treasure
// https://github.com/EcutDavid/frontend-test
// https://github.com/EcutDavid/data-tree-view
// https://github.com/yining1023/algorithm-questions
// https://github.com/DoxasticFox/canvas-experiment
// https://github.com/h5bp/Front-end-Developer-Interview-Questions
// https://github.com/tdegrunt/jsonschema
// http://jonobr1.com
// https://github.com/tcorral/javascript-challenges-book
// https://github.com/choojs/nanomorph
// https://gomakethings.com/dom-diffing-with-vanilla-js-part-1/


// ------------------------------ IMPORTANT AS HELL -------------------------------------------- //
// https://programming.vip/docs/realization-and-analysis-of-virtual-dom-diff-algorithm.html
// ------------------------------ IMPORTANT AS HELL -------------------------------------------- //