什么焦点定位效果不知道长什么样? 就是搜索然后选中最后把焦点聚集再画布中心(效果图如下)。 焦点定位就是搜索加zoom到中心点。
效果图:
基础代码准备如下(如果有不明白的可以去翻翻zoom篇),包含选中和zoom效果。 基础代码效果图:
code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#container {
width: 500px;
margin: 50px auto 0;
}
</style>
</head>
<body>
<div id="container">
<input type="text" id="ipt" onchange="onInputChange()">
</div>
</body>
</html>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
const selected = [];
let transform = {
x: 0,
y: 0,
k: 1,
};
let zoom = null;
const zoomMin = 0.3;
const zoomMax = 5;
const width = 500;
const height = 500;
const svg = d3.select('#container')
.append('svg')
.attr('width', width)
.attr('height', width);
const container = svg.append('g')
.attr(
'transform',
`translate(${transform.x},${transform.y} ) scale(${transform.k})`,
);
const data = [
{ id: 1, fill: 'black', x: 10, y: 10 },
{ id: 2, fill: 'black', x: 50, y: 50 },
{ id: 3, fill: 'black', x: 100, y: 70 },
{ id: 4, fill: 'black', x: 20, y: 100 }
];
draw(); // 绘制
bindZoom(); // 绘制
function draw() {
const update = container.selectAll('rect')
.data(data, d => d.id);
//修改层
update.attr('x', (d, idx) => d.x)
.attr('y', (d, idx) => d.y)
.attr('fill', (d) => d.fill)
//渲染层
const enter = update.enter();
//删除层
const exit = update.exit();
enter.append('rect')
.attr('width', 20)
.attr('height', 20)
.attr('id', d => d.id)
.attr('x', (d, idx) => d.x)
.attr('y', (d, idx) => d.y)
.attr('fill', (d) => d.fill)
.attr('stroke', 'blue')
.attr('strokeWidth', 1)
exit.remove()
}
function transformZoomBy([x, y] = [], trans) { //转化成zoom后的坐标
return {
transformX: (x * trans.k) + trans.x,
transformY: (y * trans.k) + trans.y,
}
};
bindEvent();
function bindEvent() { // 绑定点击事件(看不懂的去前面d3event事件绑定去看看)
svg.selectAll('rect').on('click', (d) => {
const index = selected.indexOf(d.id);
if (index !== -1) {
selected.splice(index, 1);
} else {
selected.push(d.id);
}
d3.event.preventDefault();
updateColor();
})
};
function updateColor() { // 更新color
data.forEach((item) => {
if (selected.includes(item.id)) {
item.fill = 'red';
}
});
draw();
};
function bindZoom() { // 设置zoom
zoom = d3.zoom()
.scaleExtent([zoomMin, zoomMax])
.on('zoom', function () {
transform = d3.zoomTransform(this);
container.attr('transform', transform);
console.log(transform, 'transform...')
})
svg.call(zoom).on('dblclick.zoom', null);
};
function stopZoom() {
svg.on('mousedown.zoom', null);
svg.on('mousemove.zoom', null);
svg.on('dblclick.zoom', null);
svg.on('touchstart.zoom', null);
}
function onInputChange() {
const dom = document.querySelector('#ipt');
console.log(dom.value, 'dom', dom);
}
</script>
大家如果看到过zoom篇应该会有印象有一个功能是居中,其实焦点定位是把局部(搜索到的)作为一个块居中,那么怎么把搜索到的作为一个块呢?隐式绘制一份图形(搜索到的数据),再把整个块居中后,把居中的transform和scale获取到移植到咱们的画布zoom 就可以了。
实现效果图:
core code
function drawCloneGroup(item) { // draw clone group
const cloneGroup = svg.append('g')
.attr('class', 'clone-group')
.attr(
'transform',
`translate(${transform.x},${transform.y} ) scale(${transform.k})`,
);
cloneGroup // 绘制搜索到的数据
.append('rect')
.datum(item)
.attr('width', 20)
.attr('height', 20)
.attr('id', d => d.id)
.attr('x', (d, idx) => d.x)
.attr('y', (d, idx) => d.y);
return cloneGroup;
}
function zoomCenter(targetGroupBBox = { width: 0, height: 0, x: 0, y: 0 }) { // 跟之前居中一样
const scaleX = width / targetGroupBBox.width;
const scaleY = height / targetGroupBBox.height;
let k = Math.min(scaleX, scaleY) * 0.7;
k = Math.max(k, zoomMin);
k = Math.min(k, zoomMax); // 用画布大小 / 当前svg得大小 获取到比例值
// 算出居中得x、y坐标(往俩盒子 一个大盒子(画布)和另外一个盒子(图形撑起来得))怎么让图形撑起来得居中呢!!!
// 用画布自身得一半 减去 gropu得一半 * 缩放 K, 再减去gropu得translate
const translateByX = width / 2 - (targetGroupBBox.width / 2) * k - (targetGroupBBox.x * k);
const translateByY = height / 2 - (targetGroupBBox.height / 2) * k - (targetGroupBBox.y * k);
const transform = d3.zoomIdentity
.translate(translateByX, translateByY)
.scale(k); // 获取到目标 transform
svg.transition() // 过渡效果
.duration(500) // 100ms
.call(zoom.transform, transform);
}
function onInputChange() { // 暂时用id进行搜索
const dom = document.querySelector('#ipt');
const value = dom?.value; // get input value
const item = data.find(d => d.id === +value);
if (item) {
const cloneGroup = drawCloneGroup(item); // 绘制并返回group 留作删除绘制的图形用。cloneGroup.remove()
const cloneGroupBBox = document.querySelector('.clone-group').getBBox(); // 使用getBBox 能获取到该的盒子的一些属性
selected.push(item.id);
updateColor(); //选中该节点
zoomCenter(cloneGroupBBox); // 居中
cloneGroup.remove(); //删除clone group
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#container {
width: 500px;
margin: 50px auto 0;
}
</style>
</head>
<body>
<div id="container">
<input type="text" id="ipt" placeholder="请输入id(1,2,3,4)" onchange="onInputChange()">
</div>
</body>
</html>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
const selected = [];
let transform = {
x: 0,
y: 0,
k: 1,
};
let zoom = null;
const zoomMin = 0.3;
const zoomMax = 5;
const width = 500;
const height = 500;
const svg = d3.select('#container')
.append('svg')
.attr('width', width)
.attr('height', width);
const container = svg.append('g')
.attr(
'transform',
`translate(${transform.x},${transform.y} ) scale(${transform.k})`,
);
const data = [
{ id: 1, fill: 'black', x: 10, y: 10 },
{ id: 2, fill: 'black', x: 50, y: 50 },
{ id: 3, fill: 'black', x: 100, y: 70 },
{ id: 4, fill: 'black', x: 20, y: 100 }
];
draw(); // 绘制
bindZoom(); // 绘制
function draw() {
const update = container.selectAll('rect')
.data(data, d => d.id);
//修改层
update.attr('x', (d, idx) => d.x)
.attr('y', (d, idx) => d.y)
.attr('fill', (d) => d.fill)
//渲染层
const enter = update.enter();
//删除层
const exit = update.exit();
enter.append('rect')
.attr('width', 20)
.attr('height', 20)
.attr('id', d => d.id)
.attr('x', (d, idx) => d.x)
.attr('y', (d, idx) => d.y)
.attr('fill', (d) => d.fill)
.attr('stroke', 'blue')
.attr('strokeWidth', 1)
exit.remove()
}
function transformZoomBy([x, y] = [], trans) { //转化成zoom后的坐标
return {
transformX: (x * trans.k) + trans.x,
transformY: (y * trans.k) + trans.y,
}
};
bindEvent();
function bindEvent() { // 绑定点击事件(看不懂的去前面d3event事件绑定去看看)
svg.selectAll('rect').on('click', (d) => {
const index = selected.indexOf(d.id);
if (index !== -1) {
selected.splice(index, 1);
} else {
selected.push(d.id);
}
d3.event.preventDefault();
updateColor();
})
};
function updateColor() { // 更新color
data.forEach((item) => {
if (selected.includes(item.id)) {
item.fill = 'red';
}
});
draw();
};
function bindZoom() { // 设置zoom
zoom = d3.zoom()
.scaleExtent([zoomMin, zoomMax])
.on('zoom', function () {
transform = d3.zoomTransform(this);
container.attr('transform', transform);
console.log(transform, 'transform...')
})
svg.call(zoom).on('dblclick.zoom', null);
};
function stopZoom() {
svg.on('mousedown.zoom', null);
svg.on('mousemove.zoom', null);
svg.on('dblclick.zoom', null);
svg.on('touchstart.zoom', null);
}
function drawCloneGroup(item) { // draw clone group
const cloneGroup = svg.append('g')
.attr('class', 'clone-group')
.attr(
'transform',
`translate(${transform.x},${transform.y} ) scale(${transform.k})`,
);
cloneGroup // 绘制搜索到的数据
.append('rect')
.datum(item)
.attr('width', 20)
.attr('height', 20)
.attr('id', d => d.id)
.attr('x', (d, idx) => d.x)
.attr('y', (d, idx) => d.y);
return cloneGroup;
}
/**
* @params { Object } targetGroupBBox
*/
function zoomCenter(targetGroupBBox = { width: 0, height: 0, x: 0, y: 0 }) { // 跟之前居中一样
const scaleX = width / targetGroupBBox.width;
const scaleY = height / targetGroupBBox.height;
let k = Math.min(scaleX, scaleY) * 0.7;
k = Math.max(k, zoomMin);
k = Math.min(k, zoomMax); // 用画布大小 / 当前svg得大小 获取到比例值
// 算出居中得x、y坐标(往俩盒子 一个大盒子(画布)和另外一个盒子(图形撑起来得))怎么让图形撑起来得居中呢!!!
// 用画布自身得一半 减去 gropu得一半 * 缩放 K, 再减去gropu得translate
const translateByX = width / 2 - (targetGroupBBox.width / 2) * k - (targetGroupBBox.x * k);
const translateByY = height / 2 - (targetGroupBBox.height / 2) * k - (targetGroupBBox.y * k);
const transform = d3.zoomIdentity
.translate(translateByX, translateByY)
.scale(k); // 获取到目标 transform
svg.transition() // 过渡效果
.duration(500) // 100ms
.call(zoom.transform, transform);
}
function onInputChange() { // 暂时用id进行搜索
const dom = document.querySelector('#ipt');
const value = dom?.value; // get input value
const item = data.find(d => d.id === +value);
if (item) {
const cloneGroup = drawCloneGroup(item); // 绘制并返回group 留作删除绘制的图形用。cloneGroup.remove()
const cloneGroupBBox = document.querySelector('.clone-group').getBBox(); // 使用getBBox 能获取到该的盒子的一些属性
selected.push(item.id);
updateColor(); //选中该节点
zoomCenter(cloneGroupBBox); // 居中
cloneGroup.remove(); //删除clone group
}
}
</script>
如果你看过前面zoom篇是不是觉得焦点定位特简单,就是画了一个搜索到的数据重新画了一个盒子,然后用算法居中获取到目标的transform和scale,再给画布做移动就好了。
阅读量:407
点赞量:0
收藏量:0