富文本

自 v3.7 起,富文本可用于 Apache EChartsTM 中的系列、坐标轴或其他组件的标签。它支持

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

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

  • 文本块:标签文本的整体块。
  • 文本片段:文本块中的一段文本。

例如

文本选项

echarts 提供了丰富的文本选项,包括

  • 基本的字体样式:fontStylefontWeightfontSizefontFamily
  • 文本的填充:color
  • 文本的描边:textBorderColortextBorderWidth
  • 文本的阴影:textShadowColortextShadowBlurtextShadowOffsetXtextShadowOffsetY
  • 文本块或文本片段的盒子尺寸:lineHeightwidthheightpadding
  • 文本块或文本片段的对齐方式:alignverticalAlign
  • 文本块或文本片段的边框、背景(颜色或图片):backgroundColorborderColorborderWidthborderRadius
  • 文本块或文本片段的阴影:shadowColorshadowBlurshadowOffsetXshadowOffsetY
  • 文本块的位置和旋转:positiondistancerotate

用户可以在 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 时才有效。

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

可以为文本设置基本的字体样式:fontStylefontWeightfontSizefontFamily

可以为文本设置填充颜色和描边颜色:colortextBorderColortextBorderWidth

可以为文本块设置边框样式和背景样式:borderColorborderWidthbackgroundColorpadding

也可以为文本片段设置边框样式和背景样式:borderColorborderWidthbackgroundColorpadding

例如

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 选项可用于 barlinescatter 等图表中。标签的位置可以通过 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 dom 元素。

文本片段的内容框大小默认由其字体大小决定。它也可以通过 widthheight 直接指定,尽管它们很少被设置。文本片段的边框框大小是通过添加 边框框大小内边距 计算得出的。

只有 '\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 plainheartTSinChen TSinChenpissang pissang