一、坐标轴
连续性坐标轴
const width = 800
const height = 600
const x = d3.scaleLinear().range([0, width])
const scale = x.domain([0, 10])
// x.domain([d3.max(data, (d) => d.value),d3.min(data, (d) => d.value)])
// x.domain(d3.extent(data,(d) => d.value))
// d3.axisTop、d3.axisBottom() 控制坐标轴显示 上方或者下方
const axis = svg.append('g')
.attr('class', 'axis')
.attr('transform', `translate(0, ${height})`)
.call(d3.axisBottom(scale))
连续性时间型坐标轴
const width = 800
const height = 600
const x = d3.scaleTime().range([0, width])
const scale = x.domain([new Date(2022, 1), new Date(2022, 8)])
const axis = svg.append('g')
.attr('class', 'axis')
.attr('transform', `translate(0, ${height})`)
.call(d3.axisBottom(scale))
不从零开始的非连续性坐标轴
const width = 800
const height = 600
const x = d3.scaleBand().range([0, width])
const scale = x.domain(['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日'])
const axis = svg.append('g')
.attr('class', 'axis')
.attr('transform', `translate(0, ${height})`)
.call(d3.axisBottom(scale))
从零开始的非连续性坐标轴
const width = 800
const height = 600
const x = d3.scaleOrdinal().range([150, 300, 450, 600, 750, 900, 1050]);
const scale = x.domain(['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日'])
const axis = svg.append('g')
.attr('class', 'axis')
.attr('transform', `translate(0, ${height})`)
.call(d3.axisBottom(xScale))
其他设置
• axis.scale – 为坐标轴设置比例尺
• axis.ticks – 定义坐标轴刻度生成方式(默认值为)
• axis.tickArguments – 定义坐标轴刻度参数
• axis.tickValues – 设置有特殊需求的刻度值(默认值为null)
• axis.tickFormat – 设置有特殊需求的刻度格式(默认值为null)
• axis.tickSize – 设置刻度的大小
• axis.tickSizeInner – 设置除两端外所有刻度的长度(默认值为6)
• axis.tickSizeOuter - 设置两端刻度的长度(默认值为6)
• axis.tickPadding – 设置刻度与文字之间距离(默认值为3)
二、柱状图
<svg id="bar"></svg>
const data = [
{ name: '星期一', value: 6 },
{ name: '星期二', value: 9 },
{ name: '星期三', value: 16 },
{ name: '星期四', value: 6 },
{ name: '星期五', value: 14 },
{ name: '星期六', value: 12 },
{ name: '星期日', value: 18 },
];
const margin = {
top: 50,
left: 50,
bottom: 50,
right: 50,
};
const width = 700;
const height = 500;
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
const initBar = () => {
const svg = d3
.selectAll('#bar')
.attr('width', width)
.attr('height', height);
xScale = d3
.scaleBand()
.padding(0.5)
.range([0, innerWidth])
.domain(data.map((n) => n.name));
yScale = d3
.scaleLinear()
.domain([d3.max(data, (d) => d.value), 0])
.range([0, innerHeight]);
const g = svg
.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
svg
.append('g') // 输出标题
.attr('class', 'bar--title')
.append('text')
.attr('fill', '#000')
.attr('font-size', '16px')
.attr('font-weight', '700')
.attr('text-anchor', 'middle')
.attr('x', '50%')
.attr('y', 40)
.text('本周酒店房间空置率');
g.append('g')
.attr('class', 'y-title')
.attr('transform', `translate(-30,-10)`)
.append('text')
.text('空置率 (%)');
// 添加x轴
g.append('g')
.attr('class', 'axis axis--x')
.attr('transform', `translate(0,${innerHeight})`)
.call(d3.axisBottom(xScale));
// 添加y轴
g.append('g')
.attr('class', 'axis axis--y')
.call(d3.axisLeft(yScale).tickFormat((n) => n + '%'));
// 设置tip
let tip = d3Tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function (d) {
const _data = d.target.__data__;
return (
'<strong>' +
_data.name +
"<br>空置率:</strong> <span style='color:#ffeb3b'>" +
_data.value +
'%</span>'
);
});
svg.call(tip);
g.selectAll('.bar')
.data(data, (n) => n.name) // (n) => n.name name作为更新数据的key
.enter()
.append('rect')
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
.attr('class', 'bar')
.attr('fill', '#409eff')
.attr('x', function (n) {
return xScale(n.name);
})
.attr('y', function (d) {
return yScale(d.value);
})
.attr('width', xScale.bandwidth())
.attr('height', function (d) {
return innerHeight - yScale(d.value);
});
};
三、折线图
<svg id="line"></svg>
const data = [
{ name: '星期一', value: 6 },
{ name: '星期二', value: 9 },
{ name: '星期三', value: 16 },
{ name: '星期四', value: 6 },
{ name: '星期五', value: 14 },
{ name: '星期六', value: 12 },
{ name: '星期日', value: 18 },
];
const margin = {
top: 50,
left: 50,
bottom: 50,
right: 50,
};
const width = 700;
const height = 500;
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
const initLine = () => {
const svg = d3
.selectAll('#line')
.attr('width', width)
.attr('height', height);
// 设置tip
const tip = d3Tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function (d, i) {
console.log(d, i, 'd');
const _data = d.target.__data__;
return (
'<strong>' +
_data.name +
"<br>空置率:</strong> <span style='color:#ffeb3b'>" +
_data.value +
'%</span>'
);
});
svg.call(tip);
const xAxis = d3
.scaleBand()
.range([0, innerWidth])
.domain(data.map((n) => n.name));
const yAxis = d3
.scaleLinear()
.domain([d3.max(data, (d) => d.value), 0])
.range([0, innerHeight]);
const g = svg
.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
g.append('g')
.attr('class', 'axis axis--x')
.attr('transform', `translate(0,${innerHeight})`)
.call(d3.axisBottom(xAxis));
g.append('g').attr('class', 'axis axis--y').call(d3.axisLeft(yAxis));
const line = d3
.line()
.x((d) => {
return xAxis(d.name) + xAxis.bandwidth() / 2;
})
.y((d) => yAxis(d.value))
.curve(d3.curveCatmullRom);
g.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr('cx', function (d) {
return xAxis(d.name) + xAxis.bandwidth() / 2;
})
.attr('cy', function (d) {
return yAxis(d.value);
})
.attr('r', 5)
.attr('fill', function (d, i) {
return colors[i];
})
.attr('style', `cursor: pointer;`)
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
g.append('path')
.datum(data)
.attr('stroke', 'green')
.attr('stroke-width', 2)
.attr('fill', 'none')
.attr('d', line);
};
四、折线区域
<svg id="area"></svg>
const data = [
{ name: '星期一', value: 6 },
{ name: '星期二', value: 9 },
{ name: '星期三', value: 16 },
{ name: '星期四', value: 6 },
{ name: '星期五', value: 14 },
{ name: '星期六', value: 12 },
{ name: '星期日', value: 18 },
];
const margin = {
top: 50,
left: 50,
bottom: 50,
right: 50,
};
const width = 700;
const height = 500;
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
const initArea = () => {
const svg = d3
.selectAll('#area')
.attr('width', width)
.attr('height', height);
xScale = d3
.scalePoint()
.range([0, innerWidth])
.domain(data.map((n) => n.name));
yScale = d3
.scaleLinear()
.domain([d3.max(data, (d) => d.value), 0])
.range([0, innerHeight]);
const g = svg
.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
g.append('g')
.attr('class', 'axis axis--x')
.attr('transform', `translate(0,${innerHeight})`)
.call(d3.axisBottom(xScale));
// 添加y轴
g.append('g')
.attr('class', 'axis axis--y')
.call(d3.axisLeft(yScale).tickFormat((n) => n + '%'));
const line = d3
.line()
.x((d) => xScale(d.name))
.y((d) => yScale(d.value));
const area = d3
.area()
.x((d) => xScale(d.name))
.y1((d) => yScale(d.value))
.y0(yScale(0));
g.append('g')
.append('path')
.style('fill', 'none')
.style('fill', 'green')
.style('stroke', 'rgb(51, 209, 243)')
.style('stroke-width', 1)
.datum(data)
.attr('d', area);
};
五、饼图、玫瑰图
<svg id="pie"></svg>
const margin = {
top: 50,
left: 50,
bottom: 50,
right: 50,
};
const width = 700;
const height = 500;
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
const initPie = () => {
const data = [
{ name: '外包', value: 3000 },
{ name: '金融', value: 4754 },
{ name: '制造', value: 1120 },
{ name: '咨询', value: 4032 },
];
const svg = d3
.selectAll('#pie')
.attr('width', width)
.attr('height', height);
const pieData = d3.pie().value((d) => d.value)(data);
// const allCount = data.reduce((pre, cur) => pre + cur.value, 0);
const arc = d3
.arc()
.innerRadius(0)
.outerRadius((d) => {
return 200;
// return (d.value / allCount) * 100 + 100; 玫瑰图
});
const color = d3.scaleOrdinal(d3.schemeCategory10);
const arcGroup = svg
.append('g')
.attr('transform', 'translate(300, 300)')
.selectAll('path')
.data(pieData);
arcGroup
.join('path')
.attr('fill', (d, i) => color(i))
.attr('d', arc);
arcGroup
.join('text')
.text((d) => d.data.name)
.attr('transform', function (d) {
const angle = (d.endAngle * 180) / Math.PI;
const dAngle = ((d.endAngle - d.startAngle) * 180) / Math.PI;
let tAngle = angle - dAngle / 2;
let tx = 40;
if (tAngle > 270) {
tAngle = tAngle - 275;
tx = -70;
} else {
tAngle = tAngle - 85;
}
return `rotate(${tAngle})
translate(${tx})`;
});
};