Public
Edited
May 30, 2024
Insert cell
Insert cell
Insert cell
qrCode = qr(url)
Insert cell
Insert cell
Insert cell
// className gives a CSS "class" to the parent HTML element, which can be used to visually style it
function renderWithCallback(qrCode, renderCellCallback, className) {
let renderRow = renderRowWith(renderCellCallback)
let allRows = qrCode.map(renderRow)

return html`<div style="display: flex; flex-direction: column; padding-bottom: 5px; padding-top: 30px;" class="${className ?? ''}">${allRows}</div>`
}
Insert cell
Insert cell
function renderRowWith(renderCellCallback) {
return function renderRow(row, rowIndex) {
function renderCellCallbackWithCoordinates(bool, columnIndex) {
return renderCellCallback(bool, columnIndex, rowIndex)
}
return html`<div style="display: flex;">${row.map(renderCellCallbackWithCoordinates)}</div>`
}
}
Insert cell
Insert cell
function toDot(bool) {
return bool ? '.' : ' '
}
Insert cell
function toHanZi(bool) {
return bool ? '繁' : ' '
}
Insert cell
function toEmoji(bool) {
return bool ? '🟩' : '🟨'
}
Insert cell
function toHtmlCell(bool) {
let color = bool ? 'black' : 'white'
return html`<span style="width: 10px; height: 10px; background: ${color}; display: inline-flex;"></span>`
}
Insert cell
Insert cell
function toHtmlCellWithHsl(getHslCallback) {
return function toHtmlCell(bool, columnIndex, rowIndex) {
// hue, saturation, luminosity
// https://en.wikipedia.org/wiki/HSL_and_HSV
let [hue, saturation, luminosity] = getHslCallback(bool, columnIndex, rowIndex)
let color = `hsl(${hue}deg ${saturation}% ${luminosity}%)`
return html`<span style="width: 10px; height: 10px; background: ${color}; display: inline-flex;"></span>`
}
}
Insert cell
function randomColor(bool) {
return [Math.random() * 360, 100, bool ? 25 : 85]
}
Insert cell
function rainbow(bool, columnIndex, rowIndex) {
return [12 * (columnIndex + rowIndex), 100, bool ? 25 : 85]
}
Insert cell
Insert cell
function colorFromImage(ctx) {
return function colorFromCell(bool, columnIndex, rowIndex) {
let hue = 0
let saturation = 0
let lightness = bool ? 0 : 100
if (ctx) {
let [r, g, b] = ctx.getImageData(columnIndex, rowIndex, 1, 1).data
let [_hue, _saturation, _lightness] = rgbToHue(r, g, b)
if (Number.isNaN(_hue)) {
hue = 0
} else {
hue = _hue
saturation = Math.min(_saturation * 500, 100)
lightness = bool ? 25 : 85
}
}
return [hue, saturation, lightness]
}
}
Insert cell
Insert cell
renderWithCallback(qrCode, toDot, 'dots')
Insert cell
renderWithCallback(qrCode, toHanZi, 'hanzis')
Insert cell
renderWithCallback(qrCode, toEmoji, 'emojis')
Insert cell
renderWithCallback(qrCode, toHtmlCell)
Insert cell
renderWithCallback(qrCode, toHtmlCellWithHsl(rainbow))
Insert cell
renderWithCallback(qrCode, toHtmlCellWithHsl(randomColor))
Insert cell
renderWithCallback(qrCode, toHtmlCellWithHsl(colorFromImage(ctx)))
Insert cell
Insert cell
function rgbToHue(r, g, b) {
// If all of r, g, b are equal, this function will return NaN (Not a Number)!
// That's because (g - b) will be 0, and (max - min) will also be 0, so you get
// 0 divided by 0.
r /= 255
g /= 255
b /= 255
let max = Math.max(r, g, b)
let min = Math.min(r, g, b)

let luminosity = max - min

let saturation = luminosity < 0.5
? luminosity / (max + min)
: luminosity / (2 - luminosity)
let _hue = max === r
? (g - b) / luminosity
: max === g
? 2 + (b - r) / luminosity
: 4 + (r - g) / luminosity

// NaN can be useful, because any further calculations you do with it still result
// in NaN (e.g. NaN * 60 is still NaN). You don't need to catch errors separately,
// as long as you remember to handle the case where the function returns NaN!
return [_hue * 60, saturation, luminosity]
}
Insert cell
Insert cell
Insert cell
Insert cell
ctx = c.ctx
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more