emmm其实写到这里的话也差不多写完了基本的图形实现。因为先接触的v3.0+版的D3,现在是v5.0+的,写法上差别比较大,加上全是英文的API真的是硬伤。但是实现思路还是差不多的。有写错的请指正。有写的不好的地方请忽略。
对于这个主题河流图,应该这么叫?
我最开始的想法
是和堆栈图联系起来的,想法是使用区域转换器d3.area()
但是报错了,脑子糊了没找出来原因,所以放弃了。
所以有大佬写出来的话,希望可以指导一下。
老规矩,先看图。
import * as d3 from 'd3';
export default function stackRect(id) {
(() => {
d3.select(id)
.selectAll('svg')
.remove();
})();
const height = 800;
const width = 800;
const padding = {
left: 100,
right: 100,
top: 100,
bottom: 100,
};
const axisHeight = height - padding.top - padding.bottom;
const axisWidth = width - padding.left - padding.right;
const z = d3.interpolateCool;
// 画布
const svg = d3
.select(id)
.append('svg')
.attr('width', width)
.attr('height', height);
const n = 10; // 层的总数
const m = 50; // 每层的样本数目
const k = 50; // 每层的颠簸总数
const stack = d3
.stack()
.keys(d3.range(n))
.offset(d3.stackOffsetWiggle);
const layers0 = stack(
d3.transpose(
d3.range(n).map(function() {
return bumps(m, k);
})
)
);
const layers1 = stack(
d3.transpose(
d3.range(n).map(function() {
return bumps(m, k);
})
)
);
// 将layers1和layers0两个矩阵连接起来
const layers = layers0.concat(layers1);
// 定义x轴比例尺
const x = d3
.scaleLinear()
.domain([0, m - 1])
.range([0, axisWidth]);
// 定义y轴比例尺
const y = d3
.scaleLinear()
// 定义定义域
.domain([d3.min(layers, stackMin), d3.max(layers, stackMax)])
// 定义值域
.range([axisHeight, 0]);
const area = d3
.area()
.x(function(d, i) {
return x(i);
})
.y0(function(d) {
return y(d[0]);
})
.y1(function(d) {
return y(d[1]);
});
svg
.selectAll('path')
.data(layers0)
.enter()
.append('path')
.attr('d', area)
.attr('fill', function() {
return z(Math.random());
});
// 获取堆栈数据矩阵的最大值
function stackMax(layer) {
return d3.max(layer, function(d) {
return d[1];
});
}
// 获取堆栈数据矩阵的最小值
function stackMin(layer) {
return d3.min(layer, function(d) {
return d[0];
});
}
}
// 该方法用于生成长度为n的数组,其中通过m次颠簸,即调用dump(a,n)方法来变换a数组,最终返回变换后的a数组
function bumps(n, m) {
const a = [];
let i;
for (i = 0; i < n; i += 1) a[i] = 0;
for (i = 0; i < m; i += 1) bump(a, n);
return a;
}
// 该方法通过一定的随机数的运算来变换数组a的值
function bump(a, n) {
const x = 1 / (0.1 + Math.random());
const y = 2 * Math.random() - 0.5;
const z = 10 / (0.1 + Math.random());
for (let i = 0; i < n; i += 1) {
const w = (i / n - y) * z;
// eslint-disable-next-line no-param-reassign
a[i] += x * Math.exp(-w * w);
}
}