普通换行模式效果
换行均匀分布模式效果
逻辑介绍
- x坐标计算:如图例数据为数组,数组下标为0的x坐标默认为0(按实际开发来设置初始坐标),到数组下标为1的时候获取上一个兄弟元素的x坐标 + 上一个兄弟元素的宽度 = 当前元素的x坐标,后面的循环依此类推
- x坐标超出重置逻辑:上一个兄弟元素的宽度 + 上一个兄弟元素的x坐标 + 当前元素的宽度 如大于 父级宽度(按实际开发来设置宽度)表示当前循环到的元素已经超出了父级的宽度,当前循环到的元素x坐标重置为0。
- y坐标换行计算:在attr('y')的循环中判断当前循环元素的x坐标是否小于等于0,如果小于等于0表示可以对y进行累计
实际使用中需要把rect.node().getBBox().width替换为自己设定的宽度
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<button onclick="defaultMode()">普通换行模式</button>
<button onclick="equallyMode()">换行均匀分布模式</button>
</div>
<script src="https://d3js.org/d3.v6.min.js"></script>
<script>
let data = [
{
num: 10,
label: '三星S'
}, {
num: 10,
label: '三星Note'
}, {
num: 10,
label: '华为Nova'
}, {
num: 10,
label: '华为Mate'
}, {
num: 10,
label: '三星W'
}, {
num: 10,
label: '小米Note'
}, {
num: 10,
label: '小米Mix'
}, {
num: 10,
label: '小米Max'
}, {
num: 10,
label: '小米Redmi'
}, {
num: 10,
label: 'gbxvxs32'
}, {
num: 10,
label: '989898'
}, {
num: 10,
label: '32323232'
}, {
num: 10,
label: 'efegfsgds'
}, {
num: 10,
label: 'vfxcvffv'
}, {
num: 10,
label: 'nnnnnnnnn'
}
]
let setW = 450
let svg = d3.select('div')
.append('svg')
.attr('width', setW)
.attr('height', 300)
let rect = svg.append('rect')
.attr('width', setW)
.attr('height', 300)
.attr('fill', 'black')
let textG = svg.selectAll('text')
.data(data)
.enter()
.append('text')
.attr('fill', '#fff')
.text((d) => d.label)
let arr = []
let paddingLeft = 10
defaultMode()
/**
* 普通换行模式
**/
function defaultMode() {
arr = []
let w = 0
let x = 0
let xCount = 0 // 累计每一行有几个元素
let yCount = 1
textG.attr('x', function (d, i) {
// 超出设定父级宽度,x重置为0
// 略过0,因为0的上一个兄弟元素是rect
if (x + this.getBBox().width + this.previousElementSibling.getBBox().width + paddingLeft > rect.node().getBBox().width || i === 0) {
if (w > 0) {
arr.push({
w: w, // 收集每一行的宽度
count: xCount // 累计会比实际元素个数少一个,因为换行后的第一个元素不需要计算间隔距离
})
}
w = this.getBBox().width
x = 0
xCount = 0
return x
} else {
xCount++
w += this.getBBox().width
// 移动距离 = 上一个兄弟元素的x坐标 + 上一个兄弟元素的宽度 + 间隔
x = this.previousElementSibling.getBBox().x + this.previousElementSibling.getBBox().width + paddingLeft
return x
}
}) // 换行
.attr('y', function (d, i) {
// this.getBBox().x < 0 用于兼容chrome,chrome有时会出现-1的情况
// 略过0,因为0的上一个兄弟元素是rect
// this.getBBox().x <= 0 表示为换行后的第一个元素。直到碰到下一个this.getBBox().x <= 0才会加一
if (this.getBBox().x <= 0 && i > 0) {
yCount++
}
return yCount * 21
})
// 最后一行少于设定长度不添加进数组
if(w > rect.node().getBBox().width / 1.5){
arr.push({
w: w, // 收集最后一行的宽度
count: xCount // 收集最后一行有几个元素
})
}
}
/**
* 换行均匀分布模式
**/
function equallyMode() {
let index = 0
let space = 0
if (arr.length) {
textG.attr('x', function () {
if (this.getBBox().x <= 0) {
// arr[index]为false间隔将保持为paddingLeft
space = paddingLeft
if (arr[index]) {
// rect宽度 - 文字所占宽度 = 空余的空间
// 空余的空间 / 元素个数 = 每个元素之间的间隔距离
space = (rect.node().getBBox().width - arr[index].w) / arr[index].count
index++
}
return 0
} else {
// x轴移动距离 = 上一个兄弟元素的宽度 + 上一个兄弟元素的x坐标 + 间隔
return this.previousElementSibling.getBBox().width + this.previousElementSibling.getBBox().x + space
}
})
}
}
</script>
</body>
</html>