比例尺的使用
- 
- 一、比例尺是什么?
- 二、常用比例尺类型
- 
- [1. 线性比例尺 (scaleLinear)](#1. 线性比例尺 (scaleLinear))
- [2. 序数比例尺 (scaleOrdinal)](#2. 序数比例尺 (scaleOrdinal))
- [3. 其他常用比例尺类型:](#3. 其他常用比例尺类型:)
 
- 三、实际应用
- 四、比例尺的高级用法
- 
- [1. 颜色比例尺](#1. 颜色比例尺)
- [2. 时间比例尺](#2. 时间比例尺)
- [3. 分段比例尺](#3. 分段比例尺)
 
- 小结
- 下章预告:结合比例尺使用坐标轴,创建更专业的图表
 
比例尺是 D3.js 中非常重要的概念,它能帮助我们优雅地将数据从定义域映射到可视化的值域中。下一章我们将学习坐标轴的使用,它经常与比例尺配合使用。
一、比例尺是什么?
比例尺是数据可视化的核心工具,它能将"一个区间"的数据(通常是原始数据)映射到"另一个区间"(通常是像素值或颜色)。这种映射关系在数据可视化中极为常见。
典型示例:
- 数值映射:[0, 1] 对应到 [0, 300],当输入 0.5 时,输出 150
- 离散映射:[0, 1, 2] 对应到 ["red", "green", "blue"],当输入 2 时,输出 "blue"
在比例尺中,有三个关键概念:
- 定义域 (domain):原始数据的取值范围,如 [0, 1] 或 [0, 1, 2]
- 值域 (range):映射后的目标取值范围,如 [0, 300] 或 ["red", "green", "blue"]
- 对应法则:定义域和值域之间的映射规则
理解这三个概念是掌握比例尺的基础。D3.js 提供了多种比例尺类型,适用于不同的数据映射场景。
二、常用比例尺类型
1. 线性比例尺 (scaleLinear)
线性比例尺是最常用的比例尺类型,它将连续的定义域线性地映射到连续的值域。
            
            
              html
              
              
            
          
          <!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>比例尺的使用</title>
  <script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body></body>
<script>
  // 创建线性比例尺
  var scaleLinear = d3.scaleLinear()
      .domain([0, 3])    // 定义域:输入范围是0到3
      .range([0, 300]);  // 值域:输出范围是0到300像素
  // 测试比例尺
  console.log(scaleLinear(0));  // 输出 0
  console.log(scaleLinear(1));  // 输出 100
  console.log(scaleLinear(2));  // 输出 200
  console.log(scaleLinear(3));  // 输出 300
</script>
</html>特性说明:
- 输入值在定义域外时,默认会进行外推计算
- 可以通过 .clamp(true)方法限制输出不超过值域范围
- 支持反向映射 .invert(),可以从输出值反推输入值
2. 序数比例尺 (scaleOrdinal)
序数比例尺用于离散数据的映射,它将离散的定义域映射到离散的值域。
            
            
              js
              
              
            
          
          var colors = ['blue', 'red', 'green', 'pink'];
// 创建序数比例尺
var scaleOrdinal = d3.scaleOrdinal()
  .domain([0, 1, 2, 3])  // 定义域:离散值
  .range(colors);        // 值域:颜色数组
// 应用比例尺创建彩色文本
for (var i = 0; i < colors.length; i++) {
  d3.select('body')
    .append('div')
    .text('请看我的颜色')
    .style('color', scaleOrdinal(i));
}特性说明:
- 当输入值不在定义域中时,比例尺会循环使用值域中的值
- 可以通过 .unknown(value)方法设置未知输入的默认返回值
- 常用于类别数据到颜色、符号等的映射
3. 其他常用比例尺类型:
- 
scaleBand(): 用于条形图的波段比例尺
- 
scaleTime(): 专门用于时间数据的线性比例尺
- 
scaleQuantize(): 将连续数据离散化到多个区间
- 
scaleSqrt(): 基于平方根的比例尺,适合面积映射
三、实际应用
👇 示例:让我们用比例尺改进上一章的柱状图示例:
            
            
              html
              
              
            
          
          <!DOCTYPE html>
<html>
<head>
  <script src="https://d3js.org/d3.v7.min.js"></script>
  <style>
    .bar {
      transition: all 0.3s;
    }
    .bar:hover {
      opacity: 0.8;
    }
    .bar {
      rx: 3px;
      /* 圆角 */
      filter: drop-shadow(2px 2px 2px rgba(0, 0, 0, 0.1));
    }
    .label {
      font-family: Arial;
      dominant-baseline: middle;
      /* 垂直居中 */
    }
  </style>
</head>
<body>
  <svg width="600" height="300"></svg>
  <script>
    // 模拟数据
    const dataset = [730, 530, 330, 230, 130];
    // 1. 选择容器
    const svg = d3.select("svg");
    // 2. 创建图表区域
    const chart = svg.append("g")
      .attr("transform", "translate(50, 30)");
    // 3. 创建比例尺
    const xScale = d3.scaleLinear()
      .domain([0, d3.max(dataset)])  // 定义域从0到数据最大值
      .range([0, 500]);              // 值域映射到0-500像素
    // 4. 数据绑定与图形创建(使用比例尺)
    chart.selectAll(".bar")
      .data(dataset)
      .enter()
      .append("rect")
      .attr("class", "bar")
      .attr("x", 0)
      .attr("y", (d, i) => i * 35)  // 每个柱形间隔35px
      .attr("width", d => xScale(d)) // 使用比例尺计算宽度
      .attr("height", 25)           // 固定高度
      .attr("fill", "#4CAF50");     // 初始颜色
    // X轴(使用比例尺的最大值)
    chart.append("line")
      .attr("x1", 0)
      .attr("y1", dataset.length * 35)
      .attr("x2", xScale(d3.max(dataset))) // 使用比例尺
      .attr("y2", dataset.length * 35)
      .attr("stroke", "#333")
      .attr("stroke-width", 1);
    // 数据标签(使用比例尺)
    chart.selectAll(".label")
      .data(dataset)
      .enter()
      .append("text")
      .attr("class", "label")
      .attr("x", d => xScale(d) + 5)  // 在柱形右侧显示(使用比例尺)
      .attr("y", (d, i) => i * 35 + 18)
      .text(d => d)
      .attr("font-size", "12px")
      .attr("fill", "#666");
    // 交互效果(保持不变)
    d3.selectAll(".bar")
      .on("mouseover", function () {
        d3.select(this)
          .attr("fill", "#FF5722");
      })
      .on("mouseout", function () {
        d3.select(this)
          .attr("fill", "#4CAF50");
      });
    // 添加提示框(保持不变)
    const tooltip = d3.select("body")
      .append("div")
      .style("position", "absolute")
      .style("visibility", "hidden")
      .style("background", "#fff")
      .style("padding", "5px 10px")
      .style("border-radius", "4px")
      .style("box-shadow", "0 2px 4px rgba(0,0,0,0.2)");
    // 交互逻辑(保持不变)
    d3.selectAll(".bar")
      .on("mouseover", function (event, d) {
        tooltip
          .style("visibility", "visible")
          .html(`数值:${d}`);
      })
      .on("mousemove", function (event) {
        tooltip
          .style("left", `${event.pageX + 10}px`)
          .style("top", `${event.pageY - 20}px`);
      })
      .on("mouseout", function () {
        tooltip.style("visibility", "hidden");
      });
  </script>
</body>
</html>👇 效果如下

主要改进点说明
- 
添加了线性比例尺: jsconst xScale = d3.scaleLinear() .domain([0, d3.max(dataset)]) .range([0, 500]);
- 
柱状图宽度使用比例尺: js.attr("width", d => xScale(d)) // 替换原来的直接使用数据值
- 
X轴长度使用比例尺: js.attr("x2", xScale(d3.max(dataset))) // 替换原来的d3.max(dataset)
- 
数据标签位置使用比例尺: js.attr("x", d => xScale(d) + 5) // 替换原来的d + 5
优势说明:
- 
自动适配数据范围: 当数据集变化时,比例尺会自动调整映射关系 
- 
更好的控制: 可以通过调整range值来控制图表的最大宽度 
- 
可维护性: 如果需要调整图表尺寸,只需修改比例尺的range值 
- 
一致性: 所有基于数据的图形元素使用相同的比例尺,保证视觉一致性 
四、比例尺的高级用法
1. 颜色比例尺
D3提供了专门处理颜色渐变的比例尺:
            
            
              js
              
              
            
          
          // 创建颜色比例尺
var colorScale = d3.scaleLinear()
  .domain([0, 10])
  .range(["white", "steelblue"]);
// 使用
colorScale(5);  // 返回中间色2. 时间比例尺
处理时间数据的专用比例尺:
            
            
              js
              
              
            
          
          var timeScale = d3.scaleTime()
  .domain([new Date(2020, 0, 1), new Date(2020, 11, 31)])
  .range([0, 800]);
timeScale(new Date(2020, 5, 15));  // 返回年中位置3. 分段比例尺
将连续数据分段映射:
            
            
              js
              
              
            
          
          var quantizeScale = d3.scaleQuantize()
  .domain([0, 100])
  .range(["low", "medium", "high"]);
quantizeScale(30);  // 返回"low"
quantizeScale(60);  // 返回"medium"小结
- 合理设置定义域: 使用 .domain([d3.min(data), d3.max(data)])自动适应数据范围
- 考虑边距: 在设置值域时预留边距空间
- 处理异常值: 使用 .clamp(true)防止超出范围的值
- 保持比例尺的可读性: 给比例尺变量起有意义的名称
- 重用比例尺: 在多个可视化中共享相同的比例尺保证一致性