Published
Edited
Jan 24, 2021
1 star
Insert cell
md`# Horizon area chart draw Refact II (help)`
Insert cell
chart2 = {
const rows = 10
const [h_w, h_h] = [600, 60]
const div = html`<div style="position:relative;>`
const margin = { top: 10, left: 10}
const horizon = new MyHorizon()
const mydata = genData()
d3.select(div)
.style('width', h_w+'px')
.style('height', h_h*rows+'px')
.selectAll('canvas')
.data(d3.range(rows).map(() => genData()))
.enter()
.append(() => DOM.context2d(h_w+margin.left, h_h+margin.top, 1).canvas)
.property('context', function () { return this.getContext('2d') })
.attr('y', (d,i) => i*h_h)
.each(function (data,i){
let ctx = this.getContext('2d')
ctx.translate(margin.left,margin.top)
//this.getContext('2d').translate(10, 50)
horizon.obj(this)
.slices(i+1)
.data(mydata)
.dim(h_w,h_h)
.colors1(colors_blue)
.colors2(colors_red)
.plot()
ctx.fillStyle = 'black'
ctx.moveTo(10,10)
ctx.fillText(`band = ${i+1}`, 10, 10)
})
//const canvas = DOM.canvas(w,h)
//const i = horizon.obj(canvas)
// .data(data3)
// .dim(w,h)
// .colors(colors)
// .plot()
//console.log(horizon._x(200))
return div
}
Insert cell
class MyHorizon {
constructor(template='default'){
// data = [[] (Date), [] (Value)]
// args = {slices: 4, colors: [], band: []}
let x = d3.scaleLinear()
x = d3.scaleTime()
let y = d3.scaleLinear()
let y2 = d3.scaleLinear()
let slices = 4
let colors1 = []
let colors2 = []
let band1 = []
let band2 = []
let extent = []
this._x = x
this._y = y
this._y2 = y2
this._slices = slices
this._colors1 = colors1
this._colors2 = colors2
this._band1 = band1
this._band2 = band2
this._extent = extent
}
setup(){
this.slices = this.colors.length
}
slices(slices){
this._slices = slices
return this
}
obj(canvas){
this._obj = d3.select(canvas)
this._context = canvas.getContext('2d')
return this
}
data(data) {
this._data = data
this._x.domain(d3.extent(data, function(d) { return d.x; }));
this._y.domain(d3.extent(data.filter(d => d.y > 0), function(d) { return d.y; }));
this._y2.domain(d3.extent(data.filter(d => d.y < 0), function(d) { return d.y; }));

this._extent = d3.extent(data, (d) => d.y)
const [minVal, maxVal] = this._extent
const part1 = Math.floor(maxVal/this._slices)
const part2 = Math.floor(minVal/this._slices)

let band1 = [
0,
...[...Array(this._slices-1)].map((d,i) => part1*(i+1)),
this._extent[1]
]
let band2 = [
0,
...[...Array(this._slices-1)].map((d,i) => part2*(i+1)),
this._extent[0]
]
this._band1 = [...band1]
this._band2 = [...band2]
return this
}
colors1(colors){
this._colors1 = colors
return this
}
colors2(colors){
this._colors2 = colors
return this
}
band(band){
this._band = band
return this
}
x(x){
this._x = x
return this
}
y(y){
this._y = y
return this
}
dim(w,h){
this.width(w)
this.height(h)
return this
}
width(width){
this._width = width
this._x.range([0, width])
return this
}
height(height){
console.log('set height', height)
this._height = height
this._y.range([height, 0])
this._y2.range([height, 0])
return this
}
plot() {
const {
_x, _y, _y2, _colors1, _colors2,
_band1, _band2, _extent,
_height, _width,
_context, _data, _slices
} = this
console.log('band1', _band1)
console.log('band2', _band2)
console.log('x.domain', this._x.domain(), this._x.range())
console.log('y.domain', this._y.domain(), this._y.range())
console.log('y2.domain', this._y2.domain(), this._y2.range())
const area1 = d3.area()
.x(function (d) { return _x(d.x); })
.y0(_height)
.y1(function (d) { return _y(d.y); })
.context(_context)
const area2 = d3.area()
.x(function (d) { return _x(d.x); })
.y0(0)
.y1(function (d) { return _y2(d.y); })
.context(_context)
const upper = _data.filter(d =>
d.y > 0
)
const lower = _data.filter(d =>
d.y < 0
)
for (let i = 0; i < _band1.length-1; i++){
console.log('===========================', i)
let tdata = upper.filter(d =>
d.y > _band1[i]
)
console.log(tdata.length, tdata.slice(0,5).map(d => d.y))
_y.domain([_band1[i], _extent[1]])
console.log('y.domain', this._y.domain(), this._y.range())
area1.y1(function (d) { return _y(d.y)})
console.log('y', tdata.slice(0,5).map(d => _y(d.y)))
console.log('x', tdata.slice(0,5).map(d => _x(d.x)))

_context.beginPath();
area1(tdata);
_context.fillStyle = _colors1[i];
_context.fill();
_context.closePath()
}
if (1){
for (let i = 0; i < _band2.length-1; i++){
console.log('+==========================', i)
let tdata = lower.filter(d =>
d.y < _band2[i]
)

console.log(tdata.length, tdata.slice(0,5).map(d => d.y))
_y2.domain([_extent[0], _band2[i]])
console.log('y2.domain', this._y2.domain(), this._y2.range())
area2.y1(function (d) { return _y2(d.y)})
console.log('y', tdata.slice(0,5).map(d => _y2(d.y)))
console.log('x', tdata.slice(0,5).map(d => _x(d.x)))

_context.beginPath();
area2(tdata);
_context.fillStyle = _colors2[i];
_context.fill();
_context.closePath()
}
}
}
}
Insert cell
colors_blue = ['#e6f7ff', '#bae7ff', '#91d5ff', '#69c0ff', '#40a9ff', '#1890ff', '#096dd9', '#0050b3', '#003a8c', '#002766']
Insert cell
colors_red = ['#fff1f0', '#ffccc7', '#ffa39e', '#ff7875', '#ff4d4f', '#f5222d', '#cf1322', '#a8071a', '#820014', '#5c0011']
Insert cell
genData = () => {
var series = [];
for (var i = 0, variance = 0; i < 1500; i++) {
variance += (Math.random() - 0.5) / 10;
series.push({
x: i,
y: (Math.cos(i/100) + variance) *200
})
}
return series
}
Insert cell
d3 = require('d3@6')
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