d3数据绑定
TIP
d3js
数据绑定的实现是一大亮点,必须且掌握数据绑定。 等d3js
数据绑定可以在用select
获取到的元素找到__data__
,这个就是绑定到的数据。d3js
数据绑定又可以把数据分为新增层、修改层、删除层,往下看。
data篇
TIP
- 1、data是绑定一个数组的形势
data(data)
这个样默认是绑定的data的索引,也可以这个样data(data, (d,idx) => d)
指定绑定某个字段,这个绑定很重要后面会说到 - 2、绘制多个rect 然后给他们分享绑定data, 大家可以观察下最后的
console.log()
会输出对应绑定的1、2、3、4、5、6、7
html
<!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>
</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 = [1,2,3,4,5,6,7];
data.forEach((item => {
svg.append('rect')
.attr('width', 20)
.attr('height', 20)
.attr('x', 20 * item)
.attr('y', 20 * item)
.attr('fill', 'red')
}))
svg.selectAll('rect')
.data(data)
.attr('stroke',(d,idx) => {
console.log(d,'d')
})
</script>
效果呈现:
datum
TIP
datum 跟data的作用是一样的都是绑定数据,他是绑定单个数据
- 用法示例
javascript
svg.select('rect')
.datum(1)
.attr('stroke',(d,idx) => {
console.log(d,'d')
})
data、datum获取数据
TIP
上面讲到绑定数据,那绑定的数据一样可以获取了; datum
跟 data
用法一致不过他获取到的是个单个数据代码最后一行console.log()
输出:[1,2,3,4,5,6,7]
- 用法示例:
html
<!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>
</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 = [1,2,3,4,5,6,7];
data.forEach((item => {
svg.append('rect')
.attr('width', 20)
.attr('height', 20)
.attr('x', 20 * item)
.attr('y', 20 * item)
.attr('fill', 'red')
}))
svg.selectAll('rect')
.data(data)
.attr('stroke',(d,idx) => {
console.log(d,'d')
})
console.log(svg.selectAll('rect').data(),'data')
</script>
enter
TIP
大家应该记得上面我是用forEach创建的rect 是不是挺费劲的,d3也有自己的方式结合data实现多个元素创建。enter
是把不足的给补充了,例如下面代码是选中多个rect绑定然后调用enter
,之后再append
就是不足data的用append方法给添加上去。enter用法示例:
html
<!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>
</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 = [1,2,3,4,5,6,7];
svg.selectAll('rect')
.data(data)
.enter()
.append('rect')
.attr('width', 20)
.attr('height', 20)
//d代表绑定的数据,idx代表索引,再d3中每个方法都可以用回调函数
.attr('x', (d,idx) => 20 * d)
.attr('y', (d,idx) => 20 * d)
.attr('fill', 'red')
.attr('stroke', 'blue')
.attr('strokeWidth',1)
</script>
呈现效果:
exit
TIP
exit
是当数据少,选中的多的时候不自动填充,可以搭配remove使用把多余的元素给去掉
用法示例:
html
<!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>
</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 = [1,2,3,4,5,6,7];
[1,2,3,4,5,6,7,8,9,10].forEach((item => {
svg.append('rect')
.attr('width', 20)
.attr('height', 20)
.attr('x', 20 * item)
.attr('y', 20 * item)
.attr('fill', 'red')
}))
svg.selectAll('rect')
.data(data)
.exit()
.remove()
console.log(svg.selectAll('rect').data(),'data')
</script>
如何实现一个数据驱动的方法呢?
TIP
1、渲染层;2、修改层;3、删除层;
- 1、渲染层:
enter()
(数据绑定会根据你绑定的值区分下次调用有咩有进入enter层) - 2、修改层:
data()
(数据绑定会根据你绑定的数据绑判断你有没有修改数据) - 3、删除层:
exit()
(数据绑定会根据你绑定的数据检测到你删除了那些数据)
html
<!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>
</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}];
//修改层
const update = svg.selectAll('rect')
.data(data)
//渲染层
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()
</script>
呈现效果:
封装成一个数据驱动的函数
TIP
把上面的那些层次放进一个函数里面,实现数据驱动。在每次数据更新,咱们重新render该方法即可帮咱们绘制出新的图形。
代码示例:
html
<!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);
let 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)
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: 120, y: 120});
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>
效果呈现:可能你会发现点击最后一个按钮没反映尼(问题在于数据绑定的.data(data)
)要改成让他识别id.data(data, d => d.id)
正确的数据驱动函数
html
<!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);
let 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>