Published
Edited
Oct 10, 2020
1 fork
Importers
Insert cell
Insert cell
chart = {
const wrapper = d3.create('div')
.style('height', '353px')
.style('width', '355px')
.style('padding', '5px')
.style('display', 'flex')
.style('flex-wrap', 'wrap')
.style('justify-content', 'left')
.style('background-color', 'lightgrey')
.style('border', '1px solid darkgrey')
let colorSelected = null,
colorPrev = null,
hexSelected = null;
const updateSelection = () => {
updateColor();
colorSelected.style('background-color', selected.hex());
hexSelected.property('value', selected.hex().toString());
}
const updateFromHex = function() {
const maybeHex = this.value;
if (/^#([0-9A-F]{3}){1,2}$/i.test(maybeHex)) {
const newColor = d3.hsl(maybeHex);
selected.h = newColor.h, selected.s = newColor.s, selected.l = newColor.l;
channels.h = selected.h
colorSelected.style('background-color', selected.hex());
picker.call(renderPicker);
}
}
const colorDrag = function() {
const newCoords = d3.mouse(this);
coords[0] = Math.max(Math.min(newCoords[0], len), 0),
coords[1] = Math.max(Math.min(newCoords[1], len), 0);
updateSelection();
}
const spectrumDrag = function() {
const y = d3.mouse(this)[1];
channels.h = scales.h(y);
picker.call(renderPicker);
updateSelection();
}
const revert = () => {
const c = colorPrev.style('background-color');
const newColor = d3.hsl(c);
selected.h = newColor.h, selected.s = newColor.s, selected.l = newColor.l;
channels.h = selected.h
colorSelected.style('background-color', selected.hex());
hexSelected.property('value', selected.hex().toString());
picker.call(renderPicker);
}
const commit = () => {
previous.h = selected.h, previous.s = selected.s, previous.l = selected.l;
colorPrev.style('background-color', previous.hex());
}
const picker = wrapper
.append('div')
.style('width', `${len}px`)
.style('height', `${len}px`)
.style('margin', '5px')
.style('cursor', 'crosshair')
.style('border', '1px solid darkgrey')
.style('padding', '1px')
.append('canvas')
.attr('width', len)
.attr('height', len)
.call(renderPicker)
.call(d3.drag()
.on('start', colorDrag)
.on('drag', colorDrag));
const spectrum = wrapper
.append('div')
.style('width', '25px')
.style('height', `${len}px`)
.style('margin', '5px')
.style('cursor', 'crosshair')
.style('border', '1px solid darkgrey')
.style('padding', '1px')
.append('canvas')
.attr('width', 1)
.attr('height', `${len}`)
.style('width', '25px')
.style('height', `${len}px`)
.call(renderSpectrum)
.call(d3.drag()
.on('start', spectrumDrag)
.on('drag', spectrumDrag));
const swatches = wrapper
.append('div')
.style('margin', '5px')
.style('padding', '1px')
.style('display', 'flex')
.style('border', '1px solid darkgrey');
colorSelected = swatches
.append('div')
.style('width', '50px')
.style('height', '25px')
.style('background-color', selected.hex());
colorPrev = swatches
.append('div')
.style('width', '50px')
.style('height', '25px')
.style('background-color', previous.hex())
.style('cursor', 'pointer')
.on('click', revert);
hexSelected = wrapper
.append('input')
.style('width', '55px')
.style('height', '22px')
.style('margin', '5px')
.property('value', selected.hex().toString())
.on('input', updateFromHex);
const okInput = wrapper
.append('button')
.text('OK')
.style('width', '70px')
.style('height', '28px')
.style('margin', '5px')
.style('flex-grow', 1)
.on('click', commit);
const cancelInput = wrapper
.append('button')
.text('Cancel')
.style('width', '70px')
.style('height', '28px')
.style('margin', '5px')
.style('flex-grow', 1)
.on('click', revert);
return Object.assign(wrapper.node());
}
Insert cell
updateColor = () => {
selected.h = channels.h;
selected.s = scales.s(coords[0]);
selected.l = scales.l1(coords[0]) * scales.l2(coords[1]);
}
Insert cell
channels = ({ h: 210, s: 1, l: 0.5 });
Insert cell
coords = [len, 0];
Insert cell
selected = d3.hsl(channels.h, channels.s, channels.l);
Insert cell
Insert cell
scales = ({
h: d3.scaleLinear().domain([0, len]).range([360, 0]).clamp(true),
l2: d3.scaleLinear().domain([0, len]).range([1, 0]),
l1: d3.scaleLinear().domain([0, len]).range([1, 0.5]),
s: d3.scaleLinear().domain([0, len]).range([0, 1])
})
Insert cell
renderPicker = (g) => {
let i = -1;

const current = d3.hsl(channels.h, channels.s, channels.l);
const ctx = g.node().getContext('2d')
const imageData = ctx.createImageData(len, len);
for (let y = 0; y < len; ++y) {
for (let x = 0, c; x < len; ++x) {
current.s = scales.s(x);
current.l = scales.l1(x) * scales.l2(y);
c = current.rgb();
imageData.data[++i] = c.r;
imageData.data[++i] = c.g;
imageData.data[++i] = c.b;
imageData.data[++i] = 255;
}
}

ctx.putImageData(imageData, 0, 0);
}
Insert cell
renderSpectrum = (g) => {
let i = -1;
const current = d3.hsl(channels.h, channels.s, channels.l);
const ctx = g.node().getContext('2d')
const imageData = ctx.createImageData(1, len);

for (let y = 0, c; y <= len; ++y) {
current.h = scales.h(y);
c = current.rgb();
imageData.data[++i] = c.r;
imageData.data[++i] = c.g;
imageData.data[++i] = c.b;
imageData.data[++i] = 255;
}

ctx.putImageData(imageData, 0, 0);
}
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more