viewof focus = {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, focusHeight])
.style("display", "block");
svg.append("g")
.call(xAxis, x, focusHeight);
svg.append("path")
.datum(data)
.attr("fill", "steelblue")
.attr("d", area(x, y.copy().range([focusHeight - margin.bottom, 4])));
/**
*
* 刷选
*
*/
// 创建一个 X 轴刷选器
const brush = d3.brushX()
// 设置可刷选区域,即缩略图的面积形状区域(将 svg 减去留白的区域)
// 刷选器会在该区域创建一个 <rect class="overlay" ...> 元素作为覆盖层,响应用户的刷选操作
.extent([[margin.left, 0.5], [width - margin.right, focusHeight - margin.bottom + 0.5]])
// 监听刷选过程中(如鼠标移动操作)所触发的事件,触发回调函数 brushed
.on("brush", brushed)
// 监听刷选结束时(如松开按键操作)所触发的事件,触发回调函数 brushended
.on("end", brushended);
// 设置默认的选区
// 使用 d3.utcYear 创建一个以年为间距的 interval,通过 interval.offset(date, step) 对入参的时间 date 进行偏移处理
// 关于时距器的介绍可以参考官方文档 https://d3js.org/d3-time#interval_offset
// 或这一篇笔记 https://datavis-note.benbinbin.com/article/d3/core-concept/d3-concept-data-process#时间修约
// 这里入参的时间是 x.domain()[-1] 横坐标轴比例尺的定义域的上界(数据集中的最后一年,即 2012 年)
// 然后 step=-1 表示向前调整一年,即 2011 年,再使用比例尺 x 进行映射,计算出选区的左侧端点
// 而选区的右侧端点,采用比例尺 x 的值域上界 x.range()[1],即横坐标轴的最右端
// 所以默认选区是横跨最后一年
const defaultSelection = [x(d3.utcYear.offset(x.domain()[1], -1)), x.range()[1]];
// 创建一个容器
const gb = svg.append("g")
.call(brush) // 将前面所创建的刷选器绑定到容器上
.call(brush.move, defaultSelection); // 操作刷选区,设置为默认选区
// 刷选发生时(选区发生改变)所触发的回调函数
// 从入参的刷选事件对象中解构出 selection 选区属性
function brushed({selection}) {
// 如果用户创建了选区
if (selection) {
// 选区 selection 是一个二元数组,其形式为 [x0, x1],其中 x0, x1 分别表示选区两端的横坐标值
// 然后使用 JS 原生方法 array.map(callbackFn, thisArg) 对数组的元素进行转换
// 通过 continue.invert(value) 将给定的值域的值 value(像素),反过来得到定义域的值(日期)
// 💡 基于选区位置反过来求出的日期并不正好是一天的开始,但是原始数据集中日期都是按天计算的,可以进行修约处理(其实也没有必要 ❓ 由于面积图是连续型的)
// 再使用 d3.utcDay 创建一个以天为间隔的 interval,通过 interval.round 对日期进行修约
// 为 svg 选择集中的元素(只包含一个 <svg> 元素)添加名为 value 的属性,如果选区为空,则该属性值为 [] 空数组;如果创建了选区,则该属性值为一个二元数组,表示选区两端所对应的日期
svg.property("value", selection.map(x.invert, x).map(d3.utcDay.round));
// 并分发一个 `input` 事件(Observable 会监听,并响应式地改变上一个代码块的值)
svg.dispatch("input");
}
}
// 刷选结束时所触发的回调函数
// 从入参的刷选事件对象中解构出 selection 选区属性
function brushended({selection}) {
// 如果用户没有创建选区,例如单击(而不是刷选)缩略图
if (!selection) {
// 则将选区设置回默认选区
gb.call(brush.move, defaultSelection);
}
}
return svg.node();
}