Published
Edited
Sep 26, 2019
Importers
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const svg = new SimpleSVG(500, 100, {
border: '2px dotted DimGrey',
});
svg.addStaticLabel('This is a simple test!', {
fontWeight: 'bold',
});
const label = svg.addLabel('Performance');
label.value = 2;
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9];
svg.addStaticCircles(data, {
p: d => [d * 50, 50],
r: 20,
color: (d, i) => d3.interpolateSpectral(i / data.length),
opacity: d => Math.sqrt(1 / d),
});
return svg.node;
}
Insert cell
Insert cell
Insert cell
class SimpleSVG {
constructor(width, height, styles = {}) {
this.root = d3.create('div');
this.pre = this.root.append('div'); // Comes before
this.post = this.root.append('div'); // Comes after
// Svg
const svg = this.post.append('svg')
.style('width', `${width}px`)
.style('height', `${height}px`)
for (const key of Object.keys(styles)) { // Set svg styles
svg.style(
key.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`), // fontWeight -> font-weight
styles[key],
)
}
this.svg = svg;
}
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// Add static text
addStaticLabel(text, styles = {}) {
const node = this.pre.append('div')
.style('height', '25px')
.text(text);
// Set styles
for (const key of Object.keys(styles)) {
node.style(
key.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`),
styles[key],
)
}
}
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// Add dynamic text
addLabel(name, styles = {}){
const node = this.pre.append('div')
.style('height', '25px')
.text(`${name}: ---`);
for (const key of Object.keys(styles)) {
node.style(
key.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`),
styles[key],
)
}
return new Label(name, node);
}
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// Add circle to svg
addStaticCircle(attributes = {}) {
// Attributes with shortcuts
let x = attributes.cx || attributes.x || 0;
delete attributes.cx; delete attributes.x; // Attempt delete
let y = attributes.cy || attributes.y || 0;
delete attributes.cy; delete attributes.y; // Attempt delete
const r = attributes.r || attributes.rad || attributes.radius || 2;
delete attributes.r; delete attributes.rad; delete attributes.radius; // Attempt delete
const fill = attributes.fill || attributes.color || 'Black';
delete attributes.fill; delete attributes.color; // Attempt delete
// Special attribute for setting x and y together
const p = attributes.p; delete attributes.p;
if(p != undefined) {
const d = Get2DData(p);
x = d.x; y = d.y;
}
// Append circle
const circle = this.svg.append('circle')
.attr('cx', x)
.attr('cy', y)
.attr('r', r)
.attr('fill', fill);
// Add remaining
for (const key of Object.keys(attributes)) {
circle.attr(
key.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`),
attributes[key],
)
}
}
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// Add circles to svg
addStaticCircles(data, attributes = {}) {
// Attributes with shortcuts
let x = attributes.cx || attributes.x || 0;
delete attributes.cx; delete attributes.x; // Attempt delete
let y = attributes.cy || attributes.y || 0;
delete attributes.cy; delete attributes.y; // Attempt delete
const r = attributes.r || attributes.rad || attributes.radius || 2;
delete attributes.r; delete attributes.rad; delete attributes.radius; // Attempt delete
const fill = attributes.fill || attributes.color || 'Black';
delete attributes.fill; delete attributes.color; // Attempt delete
let compoundFunction = false;
// Special attribute for setting x and y together
const p = attributes.p; delete attributes.p;
if(p != undefined) {
if (typeof p === 'function') compoundFunction = true; // Use special compound function
else {
const d = Get2DData(p);
x = d.x; y = d.y;
}
}
// Fix keys
const attributeKeys = Object.keys(attributes).map(d => {
return {
original: d,
processed: d.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`),
}
})
for (let i = 0; i < data.length; i++) {
// Append circle
const circle = this.svg.append('circle')
.attr('r', typeof r === 'function' ? r(data[i], i) : r)
.attr('fill', typeof fill === 'function' ? fill(data[i], i) : fill);
if(compoundFunction) {
const d = Get2DData(p(data[i], i));
circle.attr('cx', d.x).attr('cy', d.y);
} else {
circle
.attr('cx', typeof x === 'function' ? x(data[i], i) : x)
.attr('cy', typeof y === 'function' ? y(data[i], i) : y)
}
// Add remaining
attributeKeys.forEach(d => {
const a = attributes[d.original];
circle.attr(d.processed, typeof a === 'function' ? a(data[i], i) : a);
});
}
}
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// Add single line
addStaticLine(attributes = {}) {
// Attributes with shortcuts
let x1 = attributes.x1 || 0;
delete attributes.x1; // Attempt delete
let y1 = attributes.y1 || 0;
delete attributes.y1; // Attempt delete
let x2 = attributes.x2 || 10;
delete attributes.x2; // Attempt delete
let y2 = attributes.y2 || 10;
delete attributes.y2; // Attempt delete
const stroke = attributes.stroke || attributes.color || 'Black';
delete attributes.stroke; delete attributes.color; // Attempt delete
const strokeWidth = attributes.strokeWidth || 1;
delete attributes.strokeWidth;// Attempt delete
// Special attribute for setting x1 and y1 together
const p1 = attributes.p1; delete attributes.p1;
if(p1 != undefined) {
const d = Get2DData(p1);
x1 = d.x; y1 = d.y;
}
// Special attribute for setting x1 and y1 together
const p2 = attributes.p2; delete attributes.p2;
if(p2 != undefined) {
const d = Get2DData(p2);
x2 = d.x; y2 = d.y;
}
// Append line
const line = this.svg.append('line')
.attr('x1', x1)
.attr('y1', y1)
.attr('x2', x2)
.attr('y2', y2)
.attr('stroke', stroke)
.attr('stroke-width', strokeWidth);
// Add remaining
for (const key of Object.keys(attributes)) {
line.attr(
key.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`),
attributes[key],
)
}
}
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
get node() {
return this.root.node();
}
}
Insert cell
class Label {
constructor(name, node) {
this.name = name;
this.node = node;
}
set value(v) {
this._value = v;
this.node.text(`${this.name}: ${v}`);
}
get value() {
return this._value;
}
}
Insert cell
Insert cell
Insert cell
Insert cell
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