在以前版本的d3fc中,我的代码使用fc.util.seriesPointSnapXOnly来抓取十字线。
这似乎在d3fc的最新版本中消失了(或者我在一个独立的包中遗漏了它?)。
我使用的是画布实现(annotationCanvasCrosshair),它似乎也缺少了以前像这样使用的"snap“函数:
fc.tool.crosshair()
.snap(fc.util.seriesPointSnapXOnly(line, series))此外,"on“也不可用,所以我不能附加诸如trackingstart、trackingend等事件。
我现在怎么才能实现一条交叉剪线呢?组件的画布版本非常缺乏示例。有没有人有一个通过画布呈现显示d3fc最新版本中的交叉头发的例子?
到目前为止,https://codepen.io/parliament718/pen/xxbQGgp的情况如下
发布于 2020-02-11 09:53:52
我知道您已经向d3fc github提出了这个问题,因此我假设您知道util/snap.js 不受欢迎。
由于这个功能现在不受支持,因此似乎唯一可行的方法就是实现您自己的功能。
我以您的钢笔和原始snap.js码为起点,并应用了文档中简单的横发例子中概述的方法。
最后,我不得不逐字添加缺少的函数及其依赖项(当然,您可以重构并打包到一个单独的模块中):
function defined() {
var outerArguments = arguments;
return function(d, i) {
for (var c = 0, j = outerArguments.length; c < j; c++) {
if (outerArguments[c](d, i) == null) {
return false;
}
}
return true;
};
}
function minimum(data, accessor) {
return data.map(function(dataPoint, index) {
return [accessor(dataPoint, index), dataPoint, index];
}).reduce(function(accumulator, dataPoint) {
return accumulator[0] > dataPoint[0] ? dataPoint : accumulator;
}, [Number.MAX_VALUE, null, -1]);
}
function pointSnap(xScale, yScale, xValue, yValue, data, objectiveFunction) {
// a default function that computes the distance between two points
objectiveFunction = objectiveFunction || function(x, y, cx, cy) {
var dx = x - cx,
dy = y - cy;
return dx * dx + dy * dy;
};
return function(point) {
var filtered = data.filter(function(d, i) {
return defined(xValue, yValue)(d, i);
});
var nearest = minimum(filtered, function(d) {
return objectiveFunction(point.x, point.y, xScale(xValue(d)), yScale(yValue(d)));
})[1];
return [{
datum: nearest,
x: nearest ? xScale(xValue(nearest)) : point.x,
y: nearest ? yScale(yValue(nearest)) : point.y
}];
};
}
function seriesPointSnap(series, data, objectiveFunction) {
return function(point) {
var xScale = series.xScale(),
yScale = series.yScale(),
xValue = series.crossValue(),
yValue = (series.openValue).call(series);
return pointSnap(xScale, yScale, xValue, yValue, data, objectiveFunction)(point);
};
};
function seriesPointSnapXOnly(series, data) {
function objectiveFunction(x, y, cx, cy) {
var dx = x - cx;
return Math.abs(dx);
}
return seriesPointSnap(series, data, objectiveFunction);
}工作的最终结果可以在这里看到:kh/pen/YzXXOOG。我基本上定义了两个系列,并使用pointer组件更新第二个系列数据并触发重呈现:
const data = {
series: stream.take(50), // your candle stick chart
crosshair: [] // second series to hold the crosshair position
};
.............
const crosshair = fc.annotationCanvasCrosshair() // define your crosshair
const multichart = fc.seriesCanvasMulti()
.series([candlesticks, crosshair]) // we've got two series now
.mapping((data, index, series) => {
switch(series[index]) {
case candlesticks:
return data.series;
case crosshair:
return data.crosshair;
}
});
.............
function render() {
d3.select('#zoom-chart')
.datum(data)
.call(chart);
// add the pointer component to the plot-area, re-rendering each time the event fires.
var pointer = fc.pointer()
.on('point', (event) => {
data.crosshair = seriesPointSnapXOnly(candlesticks, data.series)(event[0]);// and when we update the crosshair position - we snap it to the other series using the old library code.
render();
});
d3.select('#zoom-chart .plot-area')
.call(pointer);
}UPD:功能可以简化如下,我还更新了笔:
function minimum(data, accessor) {
return data.map(function(dataPoint, index) {
return [accessor(dataPoint, index), dataPoint, index];
}).reduce(function(accumulator, dataPoint) {
return accumulator[0] > dataPoint[0] ? dataPoint : accumulator;
}, [Number.MAX_VALUE, null, -1]);
}
function seriesPointSnapXOnly(series, data, point) {
if (point == undefined) return []; // short circuit if data point was empty
var xScale = series.xScale(),
xValue = series.crossValue();
var filtered = data.filter((d) => (xValue(d) != null));
var nearest = minimum(filtered, (d) => Math.abs(point.x - xScale(xValue(d))))[1];
return [{
x: xScale(xValue(nearest)),
y: point.y
}];
};这还远远不够完善,但我希望它传达的是一般的想法。
https://stackoverflow.com/questions/59832117
复制相似问题