// === MAIN PROGRAM ===
function main() {
const sourceEl = document.querySelector('#source');
const sourceParagraphEl = sourceEl.querySelector('p');
const canvasEl = document.querySelector('#surface');
const ctx = canvasEl.getContext('2d');
const styles = getStyle(sourceEl, [
'width',
'height',
'paddingBlockStart',
'paddingInlineStart',
'backgroundColor',
'color',
'fontFamily',
'fontSize',
'lineHeight'
]);
drawElement(ctx, styles, sourceParagraphEl);
}
main();
// === UTILITY FUNCTIONS ===
// type StyleKey = String
// type StyleVal = String | Number
// type StyleDetails = { [StyleKey]: StyleVal }
// drawElement :: (CanvasContext2D, StyleDetails, String) -> CanvasContext2D
function drawElement(ctx, styles, textEl) {
ctx.fillStyle = styles.backgroundColor;
ctx.fillRect(0, 0, parseFloat(styles.width), parseFloat(styles.height));
ctx.fillStyle = styles.color;
ctx.font = `${styles.fontSize}/${styles.lineHeight} normal ${styles.fontFamily}`;
const text = getText(textEl, ctx);
let textX = parseFloat(styles.paddingInlineStart);
let textY = 2 * parseFloat(styles.paddingBlockStart);
let textL = parseFloat(styles.lineHeight);
let i = 0;
while (i < text.length) {
let line = text[i].join(' ');
ctx.fillText(line, textX, textY);
textY += textL;
i += 1;
}
return ctx;
}
// getText :: (HTMLElement, CanvasContext2D) -> [[String]]
function getText(el, ctx) {
const content = el.textContent.trim().split(/\s+/g);
const maxWidth = el.offsetWidth;
const lines = [];
let line = [];
let lineW = 0;
for (let i = 0; i < content.length; i += 1) {
let word = content[i];
let dim = ctx.measureText(word);
if (lineW + dim.width > maxWidth) {
lines.push(line);
line = [word];
lineW = 0;
} else {
line.push(word);
lineW += dim.width;
}
}
lines.push(line);
return lines;
}
// getStyle :: (HTMLElement, [StyleKey]) -> StyleDetails
function getStyle(el, styleKeys) {
return styleKeys.reduce(
(acc, key) => {
let style = el.style[key];
if (style === '') {
style = window.getComputedStyle(el)[key];
}
return Object.assign(acc, { [key]: style });
},
Object.create(null)
);
}
*, *::before, *::after {
box-sizing: border-box;
}
#surface {
border: 1px dotted black;
}
#source {
width: 100px;
height: auto;
padding: 1rem;
background-color: red;
color: white;
font-size: 1rem;
line-height: 1.5;
}
#source p {
margin: 0;
}
<canvas id="surface" width="200" height="200"></canvas>
<div id="source">
<p>lorem ipsum dolor sit amet...</p>
</div>