iT邦幫忙

1

d3.js scale 放大某個區塊

  • 分享至 

  • xImage

請問d3若數據差異太大時,有沒有比例尺方法可以改變成不是平均分配範圍,像是可能範圍是1~5000的x軸(或Y軸),其中2500~3000的範圍放大顯示,不是zone點及放大圖片。

像是.range([0,2500,3000,5000])之類的方式

圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 個回答

0
ccutmis
iT邦高手 2 級 ‧ 2019-12-20 14:30:09

弄了一個範例給樓主參考,網址如下:
http://www.web3d.url.tw/ITHELP/D3_20191220/

左邊是原圖,右邊是y值介於2500-3000的內容會顯示在y座標中間60%的區域)

源碼:

<!DOCTYPE html>
<html lang="zh-tw">
<head>
	<meta charset="utf-8">
	<title>絕對座標轉相對座標測試</title>
	<script src="https://d3js.org/d3.v5.min.js"></script>
	<link href="https://fonts.googleapis.com/css?family=Audiowide&display=swap" rel="stylesheet">
	<style>*,html{margin:0;padding:0;}body{background:#fff;font-family: 'Audiowide', cursive;}svg{background:#ffd;border:Solid 1px #999;} circle{opacity:0.5;}</style>
</head>
<body onload="draw()">
<svg id="svg1"></svg>
<svg id="svg2"></svg>
<script>
function draw() {
	d3.tsv("demo-data.tsv")
		.then(function (data){
		console.log(data);
		const padding=20;
		const pxX = (window.innerWidth-padding)/2;
		const pxY = window.innerHeight-padding;
		const scX = d3.scaleLinear()
			.domain(d3.extent(data, d => d.x))
			.range([0+padding, pxX-padding]);
		const scY = d3.scaleLinear()
				.domain(d3.extent(data, d => d.y))
				.range([pxY-padding, 0+padding]);
		let scYFix = (yVal) =>{
			const maxValue=5000,minValue=0,valueGap=1000;
			const valueIn90percent=maxValue-valueGap;
			const valueIn10percent=minValue+valueGap;
			if(yVal>2499&&yVal<3001){
				//介於2500至3000之間
				return valueGap+((yVal-2500)/500)*3000;
				
			}else if(yVal>3000){
				//大於3000
				return valueIn90percent+((yVal-3000)/2000)*valueGap;
			}else{
				//小於2500
				return (yVal/2500)*valueGap;
			}
		};
		let svg1=d3.select('#svg1').attr('width',pxX).attr('height',pxY);
		svg1.selectAll('line').data(data).enter().append('line').
			attr('x1',0).attr('x2',pxX).attr('y1',d=>scY(d.y)).attr('y2',d=>scY(d.y))
			.attr('stroke',d=>{return (d.y>2499&&d.y<3001)?'Red':'Green';}).attr('stroke-dasharray','5,5');
		svg1.selectAll('circle').data(data).enter().append('circle')
			.attr("r",d=>{return (d.y>2499&&d.y<3001)?20:5;})
			.attr('fill',d=>{return (d.y>2499&&d.y<3001)?'Red':'Green';})
			.attr('cx',d=>scX(d.x)).attr('cy',d=>scY(d.y));
		svg1.selectAll('text').data(data).enter().append('text')
			.attr('x',d=>scX(d.x>0?d.x-0.5:d.x+0.1)).attr('y',d=>scY(d.y)).text(d=>d.y);
			
		let svg2=d3.select('#svg2').attr('width',pxX).attr('height',pxY);
		svg2.selectAll('line').data(data).enter().append('line')
			.attr('x1',0).attr('x2',pxX).attr('y1',d=>scY(scYFix(d.y)))
			.attr('y2',d=>scY(scYFix(d.y)))
			.attr('stroke',d=>{return (d.y>2499&&d.y<3001)?'Red':'Green';}).attr('stroke-dasharray','5,5');
		svg2.selectAll('circle').data(data).enter().append('circle')
			.attr("r",d=>{return (d.y>2499&&d.y<3001)?20:5;})
			.attr('fill',d=>{return (d.y>2499&&d.y<3001)?'Red':'Green';})
			.attr('cx',d=>scX(d.x)).attr('cy',d=>scY(scYFix(d.y)));
		svg2.selectAll('text').data(data).enter().append('text')
			.attr('x',d=>scX(d.x>0?d.x-0.5:d.x+0.1)).attr('y',d=>scY(scYFix(d.y))).text(d=>d.y);
		})
}
</script>
</body></html>

資料源 demo-data.tsv 內容為:

x	y
0	0
1	350
2	1500
3	2520
4	2990
5	2780
6	3500
7	4350
8	5000

整理一下樓主問的大概是說 假如Y值如果落在2500~3000範圍內的部份要局部放大,
其它的部份(>3000或<2500)則相對的要縮小,但是同時要維持原本的比例。
這個部份我不是在scaleLinear()處理,而是另外寫一個函式去把y依照設定的條件把絕對座標轉換為相對座標,詳情請參閱上面的源碼。

我要發表回答

立即登入回答