我正在嘗試設定最小畫筆大小,但沒有“翻轉”,如下圖所示。我發現資料“跳躍”導致翻轉在視覺上令人困惑,并且如果用戶繼續拖影片筆手柄,我希望什么都不會發生。
我見過一些設定最小畫筆大小的例子(例如這個問題的答案:
uj5u.com熱心網友回復:
d3.event.selection對于brush處理程式正在回傳當前畫筆選擇:
回傳指定節點的當前畫筆選擇。... 對于一個二維畫筆,它是 [[x0, y0], [x1, y1]],其中 x0 是最小 x 值,y0 是最小 y 值,x1 是最大 x 值, y1 是最大 y 值。對于 x-brush,它是 [x0, x1];...
因此,在您的影片中 - 當“右側”變為“左側”時,為 BrushX 選擇回傳的基礎陣列進行調整,使得最小值始終為索引 0,最大值始終為索引 1。
您可以通過以下方式檢測到這一點:
const flipped = (s[1] === previousS0) || (s[0] === previousS1)
您可以在下面的片段中看到這一點 - 在第一個畫筆中,在畫筆的右側邊緣越過左側(然后變成左側邊緣)的點緩慢移動 -flipped當這種情況發生時,輸出將暫時為真.
在第二個畫筆中,您可以通過檢查畫筆寬度百分比是否小于閾值或flipped變數是否為來防止它發生true。
顯示代碼片段
const width = 400;
const height = 32;
const margin = {top: 20, bottom: 40, left: 20, right: 20}
let previousS0_1;
let previousS1_1;
let previousS0_2;
let previousS1_2;
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
const svg1 = d3.select("#svg1")
.attr("width", width margin.left margin.right)
.attr("height", height margin.top margin.bottom)
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
const svg2 = d3.select("#svg2")
.attr("width", width margin.left margin.right)
.attr("height", height margin.top margin.bottom)
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
const xScale = d3.scaleLinear()
.range([0, width])
.domain(d3.extent(data));
const xAxis1 = svg1.append("g")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(xScale));
const xAxis2 = svg2.append("g")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(xScale));
const brush1 = d3.brushX()
.extent([[0, 0], [width, height]])
.on("brush end", brushed1);
const brush2 = d3.brushX()
.extent([[0, 0], [width, height]])
.on("brush end", brushed2);
const context1 = svg1.append("g")
const context2 = svg2.append("g")
const brushGroup1 = context1.append("g")
.call(brush1)
.call(brush1.move, [xScale.range()[0] 80, xScale.range()[1] - 80]);
const brushGroup2 = context2.append("g")
.call(brush2)
.call(brush2.move, [xScale.range()[0] 80, xScale.range()[1] - 80]);
function brushed1() {
const s = d3.event.selection || xScale.range();
const brushPc = (((s[1] - s[0]) / width) * 100);
const flipped = (s[1] === previousS0_1) || (s[0] === previousS1_1)
let str = "";
str = `prevS: ${JSON.stringify([previousS0_1, previousS1_1])}`;
str = `s: ${JSON.stringify(s)}`;
str = ` flip: ${flipped}`;
d3.select("#output1").html(`<pre>s: ${str}</pre>`);
previousS0_1 = s[0];
previousS1_1 = s[1];
}
function brushed2() {
const s = d3.event.selection || xScale.range();
const brushPc = (((s[1] - s[0]) / width) * 100);
const flipped = (s[1] === previousS0_2) || (s[0] === previousS1_2)
let str = "";
str = `prevS: ${JSON.stringify([previousS0_2, previousS1_2])}`;
str = ` s: ${JSON.stringify(s)}`;
str = ` pc: ${JSON.stringify(brushPc)}`;
d3.select("#output2").html(`<pre>${str}</pre>`);
if (brushPc < 10 || flipped) {
brushGroup2.call(brush1.move, [previousS0_2, previousS1_2]);
return;
}
previousS0_2 = s[0];
previousS1_2 = s[1];
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<div>
<div id="output1"></div>
<svg id="svg1"></svg>
</div>
<div>
<div id="output2"></div>
<svg id="svg2"></svg>
</div>
brushed您也可以在原始 bl.ocks 示例的情況下添加此邏輯- 見下文:
function brushed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
var s = d3.event.selection || x2.range();
var brushPc = (((s[1] - s[0])/width)*100);
var flipped = (s[1] === previousS0) || (s[0] === previousS1); // <-- add 'flipped' check
if(brushPc < 10 || flipped){ // <-- consider along with brush percentage
brushGroup.call(brush.move, [previousS0, previousS1]);
return;
};
previousS0 = s[0];
previousS1 = s[1];
x.domain(s.map(x2.invert, x2));
focus.select(".area").attr("d", area);
focus.select(".axis--x").call(xAxis);
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
}
作業示例(根據@Gerardo-Furtado原始答案):
顯示代碼片段
let previousS0, previousS1, brushGroup;
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 100, left: 40},
margin2 = {top: 120, right: 20, bottom: 30, left: 40},
width = svg.attr("width") - margin.left - margin.right,
height = svg.attr("height") - margin.top - margin.bottom,
height2 = svg.attr("height") - margin2.top - margin2.bottom;
var parseDate = d3.timeParse("%b %Y");
var x = d3.scaleTime().range([0, width]),
x2 = d3.scaleTime().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
y2 = d3.scaleLinear().range([height2, 0]);
var xAxis = d3.axisBottom(x),
xAxis2 = d3.axisBottom(x2),
yAxis = d3.axisLeft(y);
var brush = d3.brushX()
.extent([[0, 0], [width, height2]])
.on("brush end", brushed);
var zoom = d3.zoom()
.scaleExtent([1, Infinity])
.translateExtent([[0, 0], [width, height]])
.extent([[0, 0], [width, height]])
.on("zoom", zoomed);
var area = d3.area()
.curve(d3.curveMonotoneX)
.x(function(d) { return x(d.date); })
.y0(height)
.y1(function(d) { return y(d.price); });
var area2 = d3.area()
.curve(d3.curveMonotoneX)
.x(function(d) { return x2(d.date); })
.y0(height2)
.y1(function(d) { return y2(d.price); });
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" margin.left "," margin.top ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" margin2.left "," margin2.top ")");
// fake data
var data = [];
for (var ix=0; ix<600; ix ) {
var yr = 2000 Math.floor(ix / 12) "";
var mth = ((ix % 12) 1);
mth = (mth < 10 ? "0" : "") mth;
var dt = new Date(`${yr}-${mth}-01`);
var price = Math.floor(Math.random() * 5) 1
data.push({
date: dt,
price: price
});
}
//d3.csv("sp500.csv", type, function(error, data) {
//if (error) throw error;
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.price; })]);
x2.domain(x.domain());
y2.domain(y.domain());
focus.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
focus.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," height ")")
.call(xAxis);
focus.append("g")
.attr("class", "axis axis--y")
.call(yAxis);
context.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area2);
context.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," height2 ")")
.call(xAxis2);
brushGroup = context.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, x.range());
svg.append("rect")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" margin.left "," margin.top ")")
.call(zoom);
//});
function brushed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
var s = d3.event.selection || x2.range();
var brushPc = (((s[1] - s[0])/width)*100);
var flipped = (s[1] === previousS0) || (s[0] === previousS1);
if(brushPc < 10 || flipped){
brushGroup.call(brush.move, [previousS0, previousS1]);
return;
};
previousS0 = s[0];
previousS1 = s[1];
x.domain(s.map(x2.invert, x2));
focus.select(".area").attr("d", area);
focus.select(".axis--x").call(xAxis);
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
}
function zoomed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush
var t = d3.event.transform;
x.domain(t.rescaleX(x2).domain());
focus.select(".area").attr("d", area);
focus.select(".axis--x").call(xAxis);
context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
}
function type(d) {
d.date = parseDate(d.date);
d.price = d.price;
return d;
};
.area {
fill: steelblue;
clip-path: url(#clip);
}
.zoom {
cursor: move;
fill: none;
pointer-events: all;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<svg width="400" height="200"></svg>
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/452627.html
