D3.js 범위 선택(d3.brush) 터치 패널 사용법
D3의 범위 선택의 터치 패널 사용법을 소개한다.
범위 선택의 기본적인 사용법은 여기에서 소개했는데, 터치 패널에 대응하는 경우는 이 샘플과 같이 변경하면 사용하기 쉬워진다.
예제 프로그램
그래프를 터치한다. 여기 예제과 달리 터치한 곳으로 범위 선택이 이동한다.
예제 코드
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3 Brush Touch</title>
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<!-- 1. 선택 시 스타일 설정 -->
<style>
.selected {
fill: red;
stroke: brown;
}
</style>
<script>
// 2. 산점도 표시
var width = 800; // 그래프 넓이
var height = 600; // 그래프 높이
var margin = { "top": 30, "bottom": 30, "right": 30, "left": 30 };
var randomX = d3.randomUniform(0.5, 10);
var randomY = d3.randomNormal(0.5, 0.12);
var data = d3.range(500).map(function () { return [randomX(), randomY()]; });
var svg = d3.select("body").append("svg").attr("width", width).attr("height", height);
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var xScale = d3.scaleLinear()
.domain([0, 10])
.range([0, width - margin.right - margin.left]);
var yScale = d3.scaleLinear()
.domain([0, 1])
.range([height - margin.bottom - margin.top, 0]);
var dot = g.append("g")
.attr("fill-opacity", 0.2)
.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", function (d) { return xScale(d[0]) })
.attr("cy", function (d) { return yScale(d[1]) })
.attr("r", 5);
svg.append("g")
.attr("transform", "translate(" + margin.left + "," + (height - margin.bottom) + ")")
.call(d3.axisBottom(xScale));
svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(d3.axisLeft(yScale));
// 3. brush 설정
var brush = d3.brush()
.extent([
[0, 0],
[width - margin.left - margin.right, height - margin.top - margin.bottom]
])
.on("start brush", brushed)
var g2 = g.append("g")
.call(brush)
.call(brush.move, [
[xScale(2), yScale(0.8)],
[xScale(5), yScale(0.3)]
])
// 여기부터 중요 -----------------------------
.selectAll(".overlay")
.each(function (d) { d.type = "selection"; })
.on("mousedown touchstart", brushcentered);
// 여기까지 중요 -----------------------------
function brushed() {
var x0 = xScale.invert(d3.event.selection[0][0]);
var y1 = yScale.invert(d3.event.selection[0][1]);
var x1 = xScale.invert(d3.event.selection[1][0]);
var y0 = yScale.invert(d3.event.selection[1][1]);
dot.classed("selected",
function (d) {
return (x0 <= d[0] && d[0] <= x1) && (y0 <= d[1] && d[1] <= y1);
}
);
}
// 여기부터 중요 -----------------------------
function brushcentered() {
var mouse = d3.mouse(this);
var dx = xScale(2) - xScale(0);
var dy = yScale(0.2) - yScale(0);
var x0 = mouse[0] - dx / 2;
var x1 = mouse[0] + dx / 2;
var y0 = mouse[1] - dy / 2;
var y1 = mouse[1] + dy / 2; //
var xMax = xScale.range()[1];
var yMax = yScale.range()[0];
var x = x1 > xMax ? [xMax - dx, xMax] : x0 < 0 ? [0, dx] : [x0, x1];
var y = y1 > yMax ? [yMax - dy, yMax] : y0 < 0 ? [0, dy] : [y0, y1];
d3.select(this.parentNode).call(brush.move, [
[x[0], y[1]],
[x[1], y[0]]
]);
}
// 여기까지 중요 -----------------------------
</script>
</body>
<html>
코드 설명
기본 프로그램은 이 범위 선택과 동일하다. 중요하다고 표시한 부분을 설명한다.
클릭, 터치 시 이벤트 설정
var g2 = g.append("g")
.call(brush)
.call(brush.move, [
[xScale(2), yScale(0.8)],
[xScale(5), yScale(0.3)]
])
.selectAll(".overlay")
.each(function(d) { d.type = "selection"; })
.on("mousedown touchstart", brushcentered);
call
메소드에서 brush
를 설정하면 다음 SVG 요소가 설정된다.
<g class="brush" fill="none" pointer-events="all" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);">
<rect class="overlay" pointer-events="all" cursor="crosshair" x="0" y="0" width="960" height="500"></rect>
<rect class="selection" cursor="move" fill="#777" fill-opacity="0.3" stroke="#fff" shape-rendering="crispEdges" x="112" y="194" width="182" height="83"></rect>
<rect class="handle handle--n" cursor="ns-resize" x="107" y="189" width="192" height="10"></rect>
<rect class="handle handle--e" cursor="ew-resize" x="289" y="189" width="10" height="93"></rect>
<rect class="handle handle--s" cursor="ns-resize" x="107" y="272" width="192" height="10"></rect>
<rect class="handle handle--w" cursor="ew-resize" x="107" y="189" width="10" height="93"></rect>
<rect class="handle handle--nw" cursor="nwse-resize" x="107" y="189" width="10" height="10"></rect>
<rect class="handle handle--ne" cursor="nesw-resize" x="289" y="189" width="10" height="10"></rect>
<rect class="handle handle--se" cursor="nwse-resize" x="289" y="272" width="10" height="10"></rect>
<rect class="handle handle--sw" cursor="nesw-resize" x="107" y="272" width="10" height="10"></rect>
</g>
선택 가능 범위를 나타내는 "overlay"
를 class
속성을 가지는 rect
요소에 이벤트 핸들러(brushcenterd
)를 설정한다. 그 때, "overlay"
의 type을 selection
으로 변경해 두면, move
메소드로 선택 범위를 이동시킬 수 있게 된다.
클릭, 터치시 이벤트 핸들러
이벤트 핸들러 (이벤트 발생시 호출되는 함수)를 설정한다.
function brushcentered() {
var mouse = d3.mouse(this);
var dx = xScale(2) - xScale(0);
var dy = yScale(0.2) - yScale(0);
var x0 = mouse[0] - dx / 2;
var x1 = mouse[0] + dx / 2;
var y0 = mouse[1] - dy / 2;
var y1 = mouse[1] + dy / 2; //
var xMax = xScale.range()[1];
var yMax = yScale.range()[0];
var x = x1 > xMax ? [xMax - dx, xMax] : x0 < 0 ? [0, dx] : [x0, x1];
var y = y1 > yMax ? [yMax - dy, yMax] : y0 < 0 ? [0, dy] : [y0, y1];
d3.select(this.parentNode).call(brush.move, [
[x[0], y[1]],
[x[1], y[0]]
]);
}
d3.mouse()
는 마우스의 위치를 받아오는 메소드로 인수내의 요소에 대한 마우스 좌표를 [x, y]
의 형태로 가져온다. dx
, dy
는 터치시 선택 범위의 크기이다. this.parentNode
를 선택하고 brush
를 설정한 "overlay"
의 부모 요소를 참조한다.
마무리
터치로 이동한 선택 영역은 네 모퉁이를 드래그하여 선택 범위를 변경할 수 있다.
터치 조작에서는 드래그 조작과 같이 범위 선택하기 어려워, 이 페이지에서 소개한 방법으로 가능하고, 화면이 스크롤 하지 않는 경우 등은 D3.js 범위 선택(d3.brush) 사용법의 방법으로 가능하다.
최종 수정 : 2024-03-10