富文字

自 v3.7 起,Apache EChartsTM 的系列、座標軸或其他元件的標籤開始支援富文字。富文字支援:

  • 可以指定一個文字塊的盒模型樣式(背景、邊框、陰影等)、旋轉和位置。
  • 可以對文字片段自定義樣式(顏色、字型、寬度/高度、背景、陰影等)和對齊方式。
  • 可以在文字中使用圖片作為圖示或背景。
  • 結合這些配置,可以製作出一些特殊效果,例如簡單的表格、水平分割線(hr)。

首先,需要明確下文將用到的兩個術語的含義:

  • 文字塊(Text Block):整個標籤文字的區塊。
  • 文字片段(Text fragment):文字塊中的某一段文字。

例如:

文字相關配置項

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 配置項可用於如柱狀圖折線圖散點圖等圖表中。標籤的位置可以透過 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 元素。

一個文字片段的內容盒大小(content box size)預設由其字型大小決定。也可以透過 widthheight 直接指定,儘管很少這樣設定。一個文字片段的邊框盒大小(border box size)是透過將內容盒大小padding 相加計算得出的。

只有 '\n' 是換行符,用於換行。

一行中可以存在多個文字片段。一行的高度由文字片段中最大的 lineHeight 決定。文字片段的 lineHeight 可以在 rich 中指定,或者在 rich 的父級中指定,否則使用文字片段的盒大小

確定了 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.apache.tw/examples/data/asset/img/weather/sunny_128.png'
            }
          },
          cloudy: {
            height: 30,
            align: 'left',
            backgroundColor: {
              image:
                'https://echarts.apache.tw/examples/data/asset/img/weather/cloudy_128.png'
            }
          },
          showers: {
            height: 30,
            align: 'left',
            backgroundColor: {
              image:
                'https://echarts.apache.tw/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 上編輯此頁

plainheartTSinChenpissang