2017-07-28 99 views
3

我想正確禁止使用d3的縮放行爲縮放和縮放我的d3圖表。我已經將問題簡化爲以下最小的可運行示例。我想讓用戶無法放大,讓他看到y軸上的0線以下。約束縮放和平移d3

通過將translateExtent設置爲svg的整個高度,樣本可以在非縮放狀態下工作,但當用戶放大一點時,該示例當然會中斷。事實上,你進一步放大,進一步你可以看到負面的地區。

我需要將translateExtent設置爲?

我在每個縮放事件上重繪線條和軸的原因是,通常我使用的是反應來呈現我的svg並僅使用d3進行calculcations - 但我已經刪除了對反應的依賴性以提供更簡潔例。

const data = [ 0, 15, 30, 32, 44, 57, 60, 60, 85]; 
 

 
// set up dimensions and margins 
 
const full = { w: 200, h: 200 }; 
 
const pct = { w: 0.7, h: 0.7 }; 
 
const dims = { w: pct.w * full.w, h: pct.h * full.h }; 
 
const margin = { w: (full.w - dims.w)/2, h: (full.h - dims.h)/2 }; 
 

 
// scales 
 
const x = d3.scaleLinear() 
 
    .rangeRound([0, dims.w]) 
 
    .domain([0, data.length]); 
 
    
 
const y = d3.scaleLinear() 
 
    .rangeRound([dims.h, 0]) 
 
    .domain(d3.extent(data)); 
 

 
// axes 
 
const axes = { 
 
    x: d3.axisBottom().scale(x).tickSize(-dims.w), 
 
    y: d3.axisLeft().scale(y).tickSize(-dims.h) 
 
} 
 

 

 
const g = d3.select('.center'); 
 

 
// actual "charting area" 
 
g 
 
    .attr('transform', `translate(${margin.w}, ${margin.h})`) 
 
    .attr('width', dims.w) 
 
    .attr('height', dims.h) 
 
    
 
// x-axis 
 
g.append('g') 
 
.attr('transform', `translate(0, ${dims.h})`) 
 
.attr('class', 'axis x') 
 
.call(axes.x) 
 

 
// y-axis 
 
g.append('g') 
 
.attr('class', 'axis y') 
 
.call(axes.y) 
 

 

 
// generator for the line 
 
const line = d3.line() 
 
    .x((_, i) => x(i)) 
 
    .y(d => y(d)); 
 

 
// the actual data path 
 
const path = g 
 
    .append('path') 
 
    .attr('class', 'path') 
 
    .attr('d', line(data)) 
 
    .attr('stroke', 'black') 
 
    .attr('fill', 'none') 
 

 
const zoomBehaviour = d3.zoom() 
 
    .scaleExtent([1, 10]) 
 
    .translateExtent([[0,0], [Infinity, full.h]]) 
 
    .on('zoom', zoom); 
 
    
 
d3.select('svg.chart').call(zoomBehaviour); 
 
    
 
function zoom() { 
 
    const t = d3.event.transform; 
 
    
 
    const scaledX = t.rescaleX(x); 
 
    const scaledY = t.rescaleY(y); 
 
    axes.x.scale(scaledX); 
 
    axes.y.scale(scaledY); 
 
    d3.select('.axis.x').call(axes.x); 
 
    d3.select('.axis.y').call(axes.y); 
 
    
 
    line 
 
    .x((_, i) => scaledX(i)) 
 
    .y(d => scaledY(d)); 
 
    
 
    const scaledPath = path.attr('d', line(data)); 
 
}
body { 
 
    width: 200px; 
 
    height: 200px; 
 
} 
 

 
svg.chart { 
 
    width: 200px; 
 
    height: 200px; 
 
    border: 1px solid black; 
 
} 
 

 
.axis line, .axis path { 
 
    stroke: grey; 
 
}
<script src="https://d3js.org/d3.v4.js"></script> 
 
<body> 
 
<svg class="chart"> 
 
    <g class="center"></g> 
 
</svg> 
 
</body>

回答

1

退房的zoomed功能this example by Mike Bostock,我相信你想要做什麼,關鍵部分是t.x和/或t.y突變,如果有違反的約束:

function zoomed() { 
    var t = d3.event.transform; 
    if (t.invertX(0) > x0) t.x = -x0 * t.k; 
    else if (t.invertX(width) < x1) t.x = width - x1 * t.k; 
    if (t.invertY(0) > y0) t.y = -y0 * t.k; 
    else if (t.invertY(height) < y1) t.y = height - y1 * t.k; 
    g.attr("transform", t); 
} 
+0

不幸的是,它產生的行爲與我所需要的不同。在這個例子中,圖像的任何部分都不可見,所以放大實際上是不可能的。如果你在你提供的鏈接中嘗試它,你會注意到你永遠不能放大「進入」樣本圖像。 我希望我的用戶能夠做到這一點,當放大時,他們不應該能夠「放大」圖像,即圖像的一部分必須始終可見。 – DeX3

+0

我的意思並不是將上面的內容作爲你的用例的一個確切的解決方案,更像是在你的appriach中展示了一個額外的步驟,這是't.x'和't.y'的條件變種。也許你可以調整條件,因爲邁克的例子將整個內容限制爲始終可見,而你想限制內容(我想,這是行內容的邊界框)以「包圍」你的視口。只是一個不同的條件(如果我理解你的需求,內容邊界框和視口框與Mike的例子相反)。 –

+0

是的,我能夠使它工作。我會接受你的答案,因爲你最終指出了我的正確方向。如果有人希望看到最終使其工作的代碼,我會將其作爲另一個答案發布。 – DeX3

2

我能夠根據Robert提出的建議使其工作。我不得不根據我的使用情況調整一些條件:

const dims = /* object holding svg width and height */; 
const [xMin, xMax] = this.scales.x.domain().map(d => this.scales.x(d)); 
const [yMin, yMax] = this.scales.y.domain().map(d => this.scales.y(d)); 

if (t.invertX(xMin) < 0) { 
    t.x = -xMin * t.k; 
} 
if (t.invertX(xMax) > dims.width) { 
    t.x = xMax - dims.width * t.k; 
} 
if (t.invertY(yMax) < 0) { 
    t.y = -yMax * t.k; 
} 
if (t.invertY(yMin) > dims.height) { 
    t.y = yMin - dims.height * t.k; 
}