富文本

从 v3.7 开始,Apache EChartsTM 的系列、坐标轴或其他组件的标签支持使用富文本。其中包括:

  • 可以指定文本块的盒模型样式(背景、边框、阴影等)、旋转和位置。
  • 文本片段的样式(颜色、字体、宽高、背景、阴影等)和对齐方式可以自定义。
  • 可以在文本中使用图片作为图标或背景。
  • 结合这些配置,可以制作出一些特殊效果,如简单的表格、水平分割线 (hr)。

在开始之前,需要先明确下面将要使用的两个术语的含义:

  • 文本块 (Text Block):整个标签文本块。
  • 文本片段 (Text fragment):文本块中的一段文本。

例如:

关于文本的配置项

echarts 提供了丰富的文本配置项,包括:

  • 基础字体样式:fontStyle, fontWeight, fontSize, fontFamily
  • 文本填充:color
  • 文本描边:textBorderColor, textBorderWidth
  • 文本阴影:textShadowColor, textShadowBlur, textShadowOffsetX, textShadowOffsetY
  • 文本块或文本片段的盒尺寸:lineHeight, width, height, padding
  • 文本块或文本片段的对齐方式:align, verticalAlign
  • 文本块或文本片段的边框、背景(颜色或图片):backgroundColor, borderColor, borderWidth, borderRadius
  • 文本块或文本片段的阴影:shadowColor, shadowBlur, shadowOffsetX, shadowOffsetY
  • 文本块的位置和旋转:position, distance, rotate

用户可以在 rich 属性中为文本片段定义样式。例如,series-bar.label.rich

例如:

labelOption = {
  // Styles defined in 'rich' can be applied to some fragments
  // of text by adding some markers to those fragment, like
  // `{styleName|text content text content}`.
  // `'\n'` is the newline character.
  formatter: [
    '{a|Style "a" is applied to this fragment}',
    '{b|Style "b" is applied to this fragment}This fragment use default style{x|use style "x"}'
  ].join('\n'),

  // Styles for the whole text block are defined here:
  color: '#333',
  fontSize: 5,
  fontFamily: 'Arial',
  borderWidth: 3,
  backgroundColor: '#984455',
  padding: [3, 10, 10, 5],
  lineHeight: 20,

  // Styles for text fragments are defined here:
  rich: {
    a: {
      color: 'red',
      lineHeight: 10
    },
    b: {
      backgroundColor: {
        image: 'xxx/xxx.jpg'
      },
      height: 40
    },
    x: {
      fontSize: 18,
      fontFamily: 'Microsoft YaHei',
      borderColor: '#449933',
      borderRadius: 4
    }
    // ...
  }
};

注意:widthheight 仅在指定了 rich 时有效。

文本、文本块和文本片段的基础样式

可以为文本设置基础字体样式:fontStyle, fontWeight, fontSize, fontFamily

可以为文本设置填充颜色和描边颜色:color, textBorderColor, textBorderWidth

可以为文本块设置边框样式和背景样式:borderColor, borderWidth, backgroundColor, padding

也可以为文本片段设置边框样式和背景样式:borderColor, borderWidth, backgroundColor, padding

例如:

option = {
  series: [
    {
      type: 'scatter',
      symbolSize: 1,
      data: [
        {
          value: [0, 0],
          label: {
            show: true,
            formatter: [
              'Plain text',
              '{textBorder|textBorderColor + textBorderWidth}',
              '{textShadow|textShadowColor + textShadowBlur + textShadowOffsetX + textShadowOffsetY}',
              '{bg|backgroundColor + borderRadius + padding}',
              '{border|borderColor + borderWidth + borderRadius + padding}',
              '{shadow|shadowColor + shadowBlur + shadowOffsetX + shadowOffsetY}'
            ].join('\n'),
            backgroundColor: '#eee',
            borderColor: '#333',
            borderWidth: 2,
            borderRadius: 5,
            padding: 10,
            color: '#000',
            fontSize: 14,
            shadowBlur: 3,
            shadowColor: '#888',
            shadowOffsetX: 0,
            shadowOffsetY: 3,
            lineHeight: 30,
            rich: {
              textBorder: {
                fontSize: 20,
                textBorderColor: '#000',
                textBorderWidth: 3,
                color: '#fff'
              },
              textShadow: {
                fontSize: 16,
                textShadowBlur: 5,
                textShadowColor: '#000',
                textShadowOffsetX: 3,
                textShadowOffsetY: 3,
                color: '#fff'
              },
              bg: {
                backgroundColor: '#339911',
                color: '#fff',
                borderRadius: 15,
                padding: 5
              },
              border: {
                color: '#000',
                borderColor: '#449911',
                borderWidth: 1,
                borderRadius: 3,
                padding: 5
              },
              shadow: {
                backgroundColor: '#992233',
                padding: 5,
                color: '#fff',
                shadowBlur: 5,
                shadowColor: '#336699',
                shadowOffsetX: 6,
                shadowOffsetY: 6
              }
            }
          }
        }
      ]
    }
  ],
  xAxis: {
    show: false,
    min: -1,
    max: 1
  },
  yAxis: {
    show: false,
    min: -1,
    max: 1
  }
};
在线示例

标签位置

label 配置项可用于 bar(柱状图)、line(折线图)、scatter(散点图)等图表。标签的位置可以通过 label.positionlabel.distance 来指定。

尝试在下面的示例中修改 positiondistance 配置项

option = {
  series: [
    {
      type: 'scatter',
      symbolSize: 160,
      symbol: 'roundRect',
      data: [[1, 1]],
      label: {
        // Options: 'left', 'right', 'top', 'bottom', 'inside', 'insideTop', 'insideLeft', 'insideRight', 'insideBottom', 'insideTopLeft', 'insideTopRight', 'insideBottomLeft', 'insideBottomRight'
        position: 'top',
        distance: 10,

        show: true,
        formatter: ['Label Text'].join('\n'),
        backgroundColor: '#eee',
        borderColor: '#555',
        borderWidth: 2,
        borderRadius: 5,
        padding: 10,
        fontSize: 18,
        shadowBlur: 3,
        shadowColor: '#888',
        shadowOffsetX: 0,
        shadowOffsetY: 3,
        textBorderColor: '#000',
        textBorderWidth: 3,
        color: '#fff'
      }
    }
  ],
  xAxis: {
    max: 2
  },
  yAxis: {
    max: 2
  }
};
在线示例

注意,不同的图表类型,position 的可选值也不同。并且并非所有图表都支持 distance。更多详细信息可以查看配置项文档

标签旋转

有时需要旋转标签。例如:

const labelOption = {
  show: true,
  rotate: 90,
  formatter: '{c}  {name|{a}}',
  fontSize: 16,
  rich: {
    name: {}
  }
};

option = {
  xAxis: [
    {
      type: 'category',
      data: ['2012', '2013', '2014', '2015', '2016']
    }
  ],
  yAxis: [
    {
      type: 'value'
    }
  ],
  series: [
    {
      name: 'Forest',
      type: 'bar',
      barGap: 0,
      label: labelOption,
      emphasis: {
        focus: 'series'
      },
      data: [320, 332, 301, 334, 390]
    },
    {
      name: 'Steppe',
      type: 'bar',
      label: labelOption,
      emphasis: {
        focus: 'series'
      },
      data: [220, 182, 191, 234, 290]
    }
  ]
};
在线示例

在这种场景下,可以使用 alignverticalAlign 来调整标签的位置。

注意,先应用 alignverticalAlign,然后再进行旋转。

文本片段的布局和对齐

为了理解布局规则,可以将每个文本片段想象成 CSS 中的 inline-block 元素。

默认情况下,文本片段的 内容盒尺寸 (content box size) 由其字体大小决定。也可以通过 widthheight 直接指定,尽管很少这样设置。文本片段的 边框盒尺寸 (border box size) 是通过将 边框盒尺寸padding 相加计算得出的。

只有 '\n' 是换行符,用于换行。

一行中存在多个文本片段。行的高度由文本片段中最大的 lineHeight 决定。文本片段的 lineHeight 可以在 rich 中指定,也可以在 rich 的父级指定,否则将使用文本片段的 盒尺寸 (box size)

确定了 lineHeight 后,文本片段的垂直位置可以通过 verticalAlign 来确定(这与 CSS 中的规则略有不同):

  • 'bottom':文本片段的底边紧贴行的底边。
  • 'top':文本片段的顶边紧贴行的顶边。
  • 'middle':居于行中间。

文本块的宽度可以通过 width 指定,否则由最长的一行决定。确定了宽度后,文本片段可以放置在每一行中,其中文本片段的水平位置可以通过其 align 决定。

  • 首先,将 align'left' 的文本片段从左到右连续放置。
  • 其次,将 align'right' 的文本片段从右到左连续放置。
  • 最后,剩余的文本片段将紧贴并放置在剩余空间的中心。

文本片段中文字的位置

  • 如果 align'center',文字在文本片段盒的中心对齐。
  • 如果 align'left',文字在文本片段盒的左侧对齐。
  • 如果 align'right',文字在文本片段盒的右侧对齐。

效果:图标、水平分割线、标题块、简单表格

查看示例

option = {
  series: [
    {
      type: 'scatter',
      data: [
        {
          value: [0, 0],
          label: {
            formatter: [
              '{tc|Center Title}{titleBg|}',
              '  Content text xxxxxxxx {sunny|} xxxxxxxx {cloudy|}  ',
              '{hr|}',
              '  xxxxx {showers|} xxxxxxxx  xxxxxxxxx  '
            ].join('\n'),
            rich: {
              titleBg: {
                align: 'right'
              }
            }
          }
        },
        {
          value: [0, 1],
          label: {
            formatter: [
              '{titleBg|Left Title}',
              '  Content text xxxxxxxx {sunny|} xxxxxxxx {cloudy|}  ',
              '{hr|}',
              '  xxxxx {showers|} xxxxxxxx  xxxxxxxxx  '
            ].join('\n')
          }
        },
        {
          value: [0, 2],
          label: {
            formatter: [
              '{titleBg|Right Title}',
              '  Content text xxxxxxxx {sunny|} xxxxxxxx {cloudy|}  ',
              '{hr|}',
              '  xxxxx {showers|} xxxxxxxx  xxxxxxxxx  '
            ].join('\n'),
            rich: {
              titleBg: {
                align: 'right'
              }
            }
          }
        }
      ],
      symbolSize: 1,
      label: {
        show: true,
        backgroundColor: '#ddd',
        borderColor: '#555',
        borderWidth: 1,
        borderRadius: 5,
        color: '#000',
        fontSize: 14,
        rich: {
          titleBg: {
            backgroundColor: '#000',
            height: 30,
            borderRadius: [5, 5, 0, 0],
            padding: [0, 10, 0, 10],
            width: '100%',
            color: '#eee'
          },
          tc: {
            align: 'center',
            color: '#eee'
          },
          hr: {
            borderColor: '#777',
            width: '100%',
            borderWidth: 0.5,
            height: 0
          },
          sunny: {
            height: 30,
            align: 'left',
            backgroundColor: {
              image:
                'https://echarts.org.cn/examples/data/asset/img/weather/sunny_128.png'
            }
          },
          cloudy: {
            height: 30,
            align: 'left',
            backgroundColor: {
              image:
                'https://echarts.org.cn/examples/data/asset/img/weather/cloudy_128.png'
            }
          },
          showers: {
            height: 30,
            align: 'left',
            backgroundColor: {
              image:
                'https://echarts.org.cn/examples/data/asset/img/weather/showers_128.png'
            }
          }
        }
      }
    }
  ],
  xAxis: {
    show: false,
    min: -1,
    max: 1
  },
  yAxis: {
    show: false,
    min: 0,
    max: 2,
    inverse: true
  }
};
在线示例

图标是通过在 backgroundColor 中使用图片来实现的。

rich: {
    Sunny: {
        backgroundColor: {
            image: './data/asset/img/weather/sunny_128.png'
        },
        // Can only height specified, but leave width auto obtained
        // from the image, where the aspect ratio kept.
        height: 30
    }
}

水平分割线(类似 HTML 的 <hr> 标签)可以通过边框实现。

rich: {
    hr: {
        borderColor: '#777',
        // width is set as '100%' to fullfill the text block.
        // Notice, the percentage is based on the content box, without
        // padding. Although it is a little different from CSS rule,
        // it is convinent in most cases.
        width: '100%',
        borderWidth: 0.5,
        height: 0
    }
}

标题块可以通过 backgroundColor 实现。

// Title is at left.
formatter: '{titleBg|Left Title}',
rich: {
    titleBg: {
        backgroundColor: '#000',
        height: 30,
        borderRadius: [5, 5, 0, 0],
        padding: [0, 10, 0, 10],
        width: '100%',
        color: '#eee'
    }
}

// Title is in the center of the line.
// This implementation is a little tricky, but is works
// without more complicated layout mechanism involved.
formatter: '{tc|Center Title}{titleBg|}',
rich: {
    titleBg: {
        align: 'right',
        backgroundColor: '#000',
        height: 30,
        borderRadius: [5, 5, 0, 0],
        padding: [0, 10, 0, 10],
        width: '100%',
        color: '#eee'
    }
}

简单表格可以通过给不同行中处于同一列的文本片段指定相同的宽度来实现。参见示例

贡献者 在 GitHub 上编辑此页

plainheart plainheart TSinChenpissang pissang