{
const div = document.createElement('div');
const w = Math.min(width, 500);
const h = Math.min(width, 500);
div.style.width = `${w}px`;
div.style.height = `${h}px`;
var initialData = {
xBot: 0.4,
xTop: 0.6,
y: 0.5
};
const box = {
t: -h * 0.5 + 30,
b: h * 0.5 - 40,
r: w * 0.5 - 40,
l: -w * 0.5 + 50
};
function mix(a, b, t) {
return a + (b - a) * t;
}
function clamp(x, a, b) {
return Math.max(a, Math.min(b, x));
}
var render = function(data, ctx) {
const clampedY = clamp(data.y, 0, 1);
const clampedXBot = clamp(data.xBot, 0, 1);
const clampedXTop = clamp(data.xTop, 0, 1);
const y = mix(box.b, box.t, clampedY);
const xBot = mix(box.l, box.r, clampedXBot);
const xTop = mix(box.l, box.r, clampedXTop);
// but left/width and right/-width draws equivalently but affects whether the
// interactions work correctly or not
ctx.rect(box.l, y, xBot - box.l, box.b - y, {
fill: 'rgba(255, 0, 0, 0.1',
//affects: ['xBot'],
cursor: 'move'
});
ctx.rect(xBot, y, box.r - xBot, box.b - y, {
fill: 'rgba(0, 255, 0, 0.1',
//affects: ['xBot'],
cursor: 'move'
});
ctx.rect(xTop, box.t, box.r - xTop, y - box.t, {
fill: 'rgba(0, 0, 255, 0.1',
//affects: ['xTop'],
cursor: 'move'
});
ctx.rect(box.l, box.t, xTop - box.l, y - box.t, {
fill: 'rgba(255, 0, 255, 0.1',
//affects: ['xTop'],
cursor: 'move'
});
// Horizontal divider
ctx.line(box.l, y, box.r, y, {
'stroke-width': 10,
stroke: 'rgba(0, 0, 0, 0)',
affects: ['y'],
cursor: 'ns-resize'
});
// Bottom vertical divider
ctx.line(xBot, box.b, xBot, y, {
'stroke-width': 10,
stroke: 'rgba(0, 0, 0, 0)',
cursor: 'ew-resize',
affects: ['xBot']
});
// Top vertical divider
ctx.line(xTop, box.t, xTop, y, {
'stroke-width': 10,
stroke: 'rgba(0, 0, 0, 0)',
cursor: 'ew-resize',
affects: ['xTop']
});
// Horizontal dashed line
ctx.line(box.l, y, box.r, y, {
'stroke-width': 2,
'stroke-dasharray': 4,
stroke: '#000',
affects: ['y'],
cursor: 'ns-resize'
});
// Bottom vertical dashed line
ctx.line(xBot, box.b, xBot, y, {
'stroke-width': 2,
'stroke-dasharray': 4,
stroke: '#000',
cursor: 'ew-resize',
affects: ['xBot']
});
// Top vertical dashed line
ctx.line(xTop, box.t, xTop, y, {
'stroke-width': 2,
'stroke-dasharray': 4,
stroke: '#000',
cursor: 'ew-resize',
affects: ['xTop']
});
// Text labels
// Bottom left
ctx.text(
(clampedXBot * clampedY).toFixed(3),
mix(box.l, xBot, 0.5),
mix(box.b, y, 0.5),
{
'alignment-baseline': "middle",
cursor: 'move',
'text-anchor': 'middle'
}
);
// Bottom right
ctx.text(
((1 - clampedXBot) * clampedY).toFixed(3),
mix(box.r, xBot, 0.5),
mix(box.b, y, 0.5),
{
'alignment-baseline': "middle",
cursor: 'move',
'text-anchor': 'middle'
}
);
// Top left
ctx.text(
((1 - clampedY) * clampedXTop).toFixed(3),
mix(box.l, xTop, 0.5),
mix(box.t, y, 0.5),
{
'alignment-baseline': "middle",
cursor: 'move',
'text-anchor': 'middle'
}
);
// Top right
ctx.text(
((1 - clampedY) * (1 - clampedXTop)).toFixed(3),
mix(box.r, xTop, 0.5),
mix(box.t, y, 0.5),
{
'alignment-baseline': "middle",
cursor: 'move',
'text-anchor': 'middle'
}
);
// Y label
ctx.text(clampedY.toFixed(3), box.l - 10, mix(box.b, box.t, clampedY), {
'alignment-baseline': "middle",
cursor: 'ns-resize',
'text-anchor': 'end'
});
// X bottom label
ctx.text(
clampedXBot.toFixed(3),
mix(box.l, box.r, clampedXBot),
box.b + 20,
{
'alignment-baseline': "bottom",
cursor: 'ew-resize',
'text-anchor': 'middle'
}
);
// X top label
ctx.text(
clampedXTop.toFixed(3),
mix(box.l, box.r, clampedXTop),
box.t - 10,
{
'alignment-baseline': "bottom",
cursor: 'ew-resize',
'text-anchor': 'middle'
}
);
// The box
ctx.line(box.l, box.b, box.r, box.b, {
'stroke-width': 2,
stroke: 'black',
'stroke-linecap': 'round'
});
ctx.line(box.l, box.t, box.r, box.t, {
'stroke-width': 2,
stroke: 'black',
'stroke-linecap': 'round'
});
ctx.line(box.l, box.b, box.l, box.t, {
'stroke-width': 2,
stroke: 'black',
'stroke-linecap': 'round'
});
ctx.line(box.r, box.b, box.r, box.t, {
'stroke-width': 2,
stroke: 'black',
'stroke-linecap': 'round'
});
};
const graphics = g9(initialData, render)
.insertInto(div)
.align('center', 'center');
// It seems you can't resize until it's inserted:
requestAnimationFrame(() => {
graphics.node.style.height = `${h}px`;
graphics.node.style.width = `${w}px`;
graphics.resize();
});
return div;
}