许多地方有现成的鹰眼图,但是我需要在d3.js中实现,想不到一个简单的需求居然也花了一两天的时间。
总结:
- 用一个 textarea来显示x,y,k,还是很省事的,比用console.log()强太多
- event.transform的 x,y,k x,y 是图层平移的坐标
-
初始时:
Translate X: 0, Translate Y: 0, Scale: 1
-
向右平移 100 像素,向下平移 50 像素:
Translate X: 100, Translate Y: 50, Scale: 1
-
缩放比例变为 2:
Translate X: 100, Translate Y: 50, Scale: 2
- 这只是普通的一个点图,如果要用在我想用的复杂topo图上,这个缩略图应该如何绘制?
- 这图缩放到最大时还有问题,恢复不回去
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3.js Minimap Example with Consistent Viewport</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
<style>
.main-view {
border: 1px solid #ccc;
width: 800px;
height: 400px;
position: relative;
overflow: hidden;
}
.minimap {
border: 1px solid #ccc;
width: 200px;
height: 100px;
position: relative;
overflow: hidden;
margin-top: 10px;
}
.main-view circle, .minimap circle {
fill: steelblue;
}
.viewport {
fill: none;
stroke: red;
stroke-width: 1;
}
</style>
</head>
<body>
<div class="main-view"></div>
<div class="minimap"></div>
<div class="txt"></div>
<div id="textarea-container"></div>
<script>
const content = "这是一个示例文本内容。";
// 选择容器并添加一个文本区域
const container = d3.select("#textarea-container");
const textarea = container.append("textarea")
.attr("rows", 4)
.attr("cols", 50)
.text(content); // 设置文本内容
// 如果需要,可以为文本区域添加一些样式
textarea.style("margin", "20px")
.style("padding", "10px")
.style("font-size", "16px");
const mainView = d3.select(".main-view").append("svg")
.attr("width", 800)
.attr("height", 400);
const minimap = d3.select(".minimap").append("svg")
.attr("width", 200)
.attr("height", 100);
const xMin=0;
const xMax=800;
const yMin=0;
const yMax=400;
// 生成一些随机数据
const data = d3.range(50).map(() => [Math.random() * 800, Math.random() * 400]);
// 主视图的节点
const mainNodes = mainView.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("cx", d => d[0])
.attr("cy", d => d[1])
.attr("r", 5);
// Minimap 的节点
const minimapNodes = minimap.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("cx", d => d[0] * (200 / 800))
.attr("cy", d => d[1] * (100 / 400))
.attr("r", 2);
// 主视图的缩放和移动
const zoom = d3.zoom()
.scaleExtent([1, 5]) // 设置最小缩放比例为 1,最大缩放比例为 10
.on("zoom", (event) => {
let { x, y, k } = event.transform;
// 限制平移范围
console.log(x,y,k)
//x = Math.min(x - 800 / k, Math.max(xMin, x));
//y = Math.min(y - 400 / k, Math.max(yMin, y));
//console.log(x,y,k);
// 不被允许的平移包括:
// x最小是0,最大是 -width*(k-1) 注意这方向,反的,反常识
if (x<-xMax*(k-1)){
x=-xMax*(k-1)
}
if (x>0){
x=0
}
// y最小为0,最大是-height*(k-1)
if (y<-yMax*(k-1)){
y=-yMax*(k-1)
}
if (y>0){
y=0
}
textarea.text(x+"\n"+y+"\n"+k);
mainView.selectAll("circle")
.attr("transform", `translate(${x}, ${y}) scale(${k})`);
const viewport = minimap.select(".viewport");
if (k === 1) {
// 如果是全图,禁用平移
console.log("k = 1 , 禁用移动");
mainView.selectAll("circle")
.attr("transform", `translate(0, 0) scale(1)`);
minimap.select(".viewport").attr("transform", `translate(0, 0) scale(1)`);
} else {
mainView.selectAll("circle")
.attr("transform", `translate(${x}, ${y}) scale(${k})`);
viewport.attr("x", -x / k * (200 / 800))
.attr("y", -y / k * (100 / 400))
.attr("width", 200 / k)
.attr("height", 100 / k);
}
// 更新 minimap 的视图
});
mainView.call(zoom);
// 在 minimap 上添加一个矩形,表示主视图的可视区域
const viewport = minimap.append("rect")
.attr("class", "viewport")
.attr("x", 0)
.attr("y", 0)
.attr("width", 200)
.attr("height", 100);
</script>
</body>
</html>