2017-07-28 26 views
3

d3のズーム動作を使用して、d3チャートでズーミングとスケーリングを正しく抑制しようとしています。私は問題を次の最小限の実行可能な例に減らしました。ユーザーがy軸上の0行の下を見ることができないようにズームできないようにします。d3でズームとパンを制限する

サンプルはズームされていない状態で、translateExtentをsvgの完全な高さに設定すると動作しますが、ユーザーが少しズームするとすぐに途切れます。実際、ズームインするほど、負の領域をさらに見ることができます。

translateExtentを設定するにはどうすればよいですか?

各ズームイベントで線と軸を再描画しているのは、通常、私はsvgをレンダリングしてd3を計算に使用するためです。しかし、私はもっと簡潔に例。

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

私は何をしたいん信じるthis example by Mike Bostockzoomed機能をチェック制約の違反がある場合、重要な部分は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

私はあなたのユースケースの正確な解決策として上記を意味するものではなく、単にあなたのアパレルには存在しない余分なステップを示すようなものであり、これは 't.x'と' t.y'の条件付きの突然変異です。 Mikeの例では、コンテンツ全体を常に可視に制限しながら、内容を制限しながら(ビュー内のコンテンツの境界ボックス)、ビューポートを「囲む」ために、条件を調整することができます。ちょうど別の条件(コンテンツバウンディングボックスとビューポートボックスは、あなたのニーズを理解すれば、マイクの例とは逆の関係にあります)。 –

+0

うん、私はそれを動作させることができました。私は正しい方向に私を指摘した最後にあなたの答えの理由を受け入れるでしょう。誰かが最終的に動作させるようなコードを見たいと思ったら、別の答えとして投稿します。 – DeX3

2

ロバートの提案に基づいて動作させることができました。

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; 
} 
関連する問題