editor = (pxls) => {
const svg = html`<svg>`;
const color_picker = color();
const borders_checkbox = checkbox(["Borders"]);
const action_radio = radio({options:["Draw","Pick","Alpha"],value:"Draw"});
const alpha_field = number({placeholder:"opacity",value:1,min:0,max:1,step:0.1});
const undo_button = button("Undo");
const element = html`
${svg}
${color_picker}
${alpha_field}
${action_radio}
${borders_checkbox}
${undo_button}
`;
function update() {
const data = Uint8ClampedArray.from({length: w * h * 4});
const context = DOM.context2d(w, h, 1);
pxls.forEach( p => {
const c = d3.color(p.color);
data[4 * (p.y*64 + p.x) + 0] = c.r;
data[4 * (p.y*64 + p.x) + 1] = c.g;
data[4 * (p.y*64 + p.x) + 2] = c.b;
data[4 * (p.y*64 + p.x) + 3] = p.opacity*255;
})
context.canvas.value = new ImageData(data, w, h);
context.putImageData(context.canvas.value, 0, 0);
element.value = context.canvas;
element.dispatchEvent(new CustomEvent("input"));
};
const heigth = d3.min([500,(width/w)*h]);
d3.select(svg)
.attr("width",width)
.attr("height",heigth);
let rects = d3.select(svg).selectAll("rect");
let active = false;
let previous_colors = [];
const l = d3.min([heigth/h, width/w]);
function render(){
rects = rects.data(pxls)
.join(
enter => enter.append("rect")
.attr("height", l)
.attr("width", l)
.attr("stroke-width","1px")
.attr("x", p => p.x * l)
.attr("y", p => p.y * l)
.on("click", (p,i) => {
if(action_radio.value === "Draw"){
p.color = color_picker.value;
render();
update();
}
else if(action_radio.value === "Alpha"){
p.opacity = alpha_field.value;
render();
update();
}
else if (action_radio.value === "Pick") {
color_picker.reset()
color_picker.value = p.color;
alpha_field.value = p.opacity;
}
})
.on("mouseover", (p,i) => {
if(active && action_radio.value === "Draw") {
p.color = color_picker.value;
render();
}
else if(active && action_radio.value === "Alpha") {
p.opacity = alpha_field.value;
render();
}
})
,
update => update
.attr("fill", p => p.color )
.attr("fill-opacity", p => p.opacity )
.attr("stroke", p => borders_checkbox.value? "black": p.color )
.attr("stroke-opacity", p => borders_checkbox.value? "black": p.opacity ),
exit => exit.remove()
);
}
svg.onmousedown = () => {
active = true;
previous_colors.push(pxls.map( p => p.color ));
};
svg.onmouseup = () => {
active = false;
update();
};
borders_checkbox.oninput = () => render();
undo_button.onclick = () => {
const colors = previous_colors.pop();
pxls.forEach( (p,i) => {
p.color = colors[i];
})
render();
update();
}
render(); // create view
render(); // update view
update(); // update value
return element;
}