d3js 数据绑定是d3js的一大优点, 尼写好一个d3js的数据绑定函数后,再有数据更新(新增、修改、删除)后再调用该函数就会给你更新数据了。
再大数据量1w+, 甚至更多数据绘制的时候。会发现经过咱们的数据绑定函数重新reload会时间略久。我这边也查看了下d3js数据绑定的源码 data源码链接, 是用m*n的算法时间复杂度,也就是for 嵌套for 做diff分层(unpdate == data()、remove == exit() 、add == enter())。
data 数据绑定可以分为3层:d3js数据绑定
enter()
(数据绑定会根据你绑定的值区分下次调用有咩有进入enter层)data()
(数据绑定会根据你绑定的数据绑判断你有没有修改数据)一个简易的数据绑定函数如下:
<!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>
</head>
<body>
<div>
<button onclick="remove()">删除一条数据</button>
<button onclick="add()">新增一条数据</button>
<button onclick="exit()">修改一条数据</button>
<button onclick="all()">新增一条数据,并修改一条数据,并删除一条数据</button>
</div>
</body>
</html>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
const svg = d3.select('body')
.append('svg')
.attr('width', 500)
.attr('height', 500);
const data = [{id: 1, fill: 'red', x: 20, y: 20}, {id: 2, fill: 'blue', x: 40, y: 40}, {id: 3,fill: 'yellow',x: 60, y: 60}, {id: 4, fill: 'black',x: 80, y: 80}];
draw()
function draw() {
const update = svg.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('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 remove() {
data.pop();
draw();
}
function add() {
data.push({id: Math.random() * 200, fill: 'violet', x: Math.random() * 200, y: Math.random() * 200});
draw();
}
function exit() {
data[0].fill = 'orange';
draw();
}
function all() {
data.shift();
data.push({id: Math.random() * 200, fill: 'green', x: 150, y: 150});
data[0].fill = 'pink';
console.log(data,'data')
draw();
}
</script>
其实d3js数据绑定做的就是分层(update、remove、add),也就是用上一次给的数据跟这次给的数据做对比。
diff逻辑
diff code
import { isEqual } from 'lodash';
// 视图层数据处理 以id作为key 做diff
// add 新增绘制层
// remove 删除层
// update 修改层
export function diffLayeredBy(newData, lastData, key = 'id') {
const newDataMap = new Map();
const lastDataMap = new Map();
const update = [];
lastData.forEach((item) => lastDataMap.set(item[key], item));
const add = newData.filter((item) => { // add
newDataMap.set(item[key], item);
const last = lastDataMap.get(item[key]);
if (!last) {
return item;
}
// update
if (!isEqual(last, item)) { // 没有引入loadsh的 可以简单用JSON.stringify(last) !== JSON.stringify(item) 做对比
update.push(item);
}
return false;
});
const remove = lastData.filter((item) => !newDataMap.get(item[key])); //remove
return { add, remove, update };
}
总结:把d3js的m*n的时间算法复杂度
改成m+n, 先准备好需要的数据转化成map,完事再做数据分层。
效果图:
源码:
<!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>
</head>
<body>
<div>
<button onclick="remove()">删除一条数据</button>
<button onclick="add()">新增一条数据</button>
<button onclick="exit()">修改一条数据</button>
<button onclick="all()">新增一条数据,并修改一条数据,并删除一条数据</button>
</div>
</body>
</html>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
function diffLayeredBy(newData, lastData, key = 'id') {
const newDataMap = new Map();
const lastDataMap = new Map();
const update = [];
lastData.forEach((item) => lastDataMap.set(item[key], item));
const add = newData.filter((item) => { // 新增 add
newDataMap.set(item[key], item);
const last = lastDataMap.get(item[key]);
if (!last) {
return item;
}
if (JSON.stringify(last) !== JSON.stringify) { // 没有引入loadsh的 可以简单用JSON.stringify(last) !== JSON.stringify(item) 做对比
update.push(item); // 修改 update
}
return false;
});
const remove = lastData.filter((item) => !newDataMap.get(item[key])); // remove 删除
return { add, remove, update };
}
</script>
<script>
const ids = {};
const getIds = () => {
let currentId = (Math.random() * 20000).toFixed(0);
if (!ids[currentId]) {
ids[currentId] = currentId;
} else {
currentId = getIds();
}
return currentId;
};
const svg = d3.select('body')
.append('svg')
.attr('width', 500)
.attr('height', 500);
let lastData = [];
const data = [{ id: 1, fill: 'red', x: 20, y: 20 }, { id: 2, fill: 'blue', x: 40, y: 40 }, { id: 3, fill: 'yellow', x: 60, y: 60 }, { id: 4, fill: 'black', x: 80, y: 80 }];
draw()
function draw() {
const { add, update, remove } = diffLayeredBy(data, lastData);
console.log(add, update, remove, 'add, update, remove')
add.forEach(item => { // 新增层
svg.append('rect')
.datum(item) // 单个数据绑定
.attr('width', 20)
.attr('id', (d) => `node-${d.id}`)
.attr('height', 20)
.attr('x', (d, idx) => d.x)
.attr('y', (d, idx) => d.y)
.attr('fill', (d) => d.fill)
.attr('stroke', 'blue')
.attr('strokeWidth', 1)
});
update.forEach((item) => { // 修改层(重新绑定新数据)把修改的值重新赋值
d3.select(`#node-${item.id}`)
.datum(item) // 重新绑定新值
.attr('x', (d, idx) => d.x)
.attr('y', (d, idx) => d.y)
.attr('fill', (d) => d.fill)
});
remove.forEach((item) => { // remove 层(直接获取到删除)
d3.select(`#node-${item.id}`).remove();
});
lastData = JSON.parse(JSON.stringify(data)); // 我就用简单方法实现cloneDeep了, 一定要深克隆
}
function remove() {
data.pop();
draw();
}
function add() {
data.push({ id: getIds(), fill: 'violet', x: Math.random() * 200, y: Math.random() * 200 });
draw();
}
function exit() {
data[0].fill = 'orange';
draw();
}
function all() {
data.shift();
data.push({ id: getIds(), fill: 'green', x: 150, y: 150 });
data[0].fill = 'pink';
console.log(data, 'data')
draw();
}
</script>
阅读量:461
点赞量:0
收藏量:0