富文本

自 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
    }
    // ...
  }
};

注意:只有在指定了 rich 时,widthheight 才有效。

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

可以为文本设置基本字体样式: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 指定,尽管它们很少被设置。 文本片段的 边框盒子大小 通过添加 边框盒子大小padding 计算得出。

只有 '\n' 是换行符,它会断开一行。

一行中存在多个文本片段。 行的高度由文本片段中最大的 lineHeight 决定。 可以在 rich 中或在 rich 的父级中指定文本片段的 lineHeight,否则使用文本片段的 盒子大小

确定 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