echarts pie中心偏移以后,如何让title始终相对饼图居中

图片描述

如图所示,pie的,根据需要,pie需要偏移:

center: ['30%', '50%']

这种情况下title如何才能保证相对饼图居中呢,echarts的宽度是自适应的.

阅读 19.6k
7 个回答
新手上路,请多包涵

我也遇到了这个问题,最后自己想出来了,可以设置padding为负值(应该是标题宽度的一半)。
left的偏移量应该和饼图的中心点位置一致。
image.pngimage.png

加textAlign: "center", 让其水平对其,然后在调节left:xx%,

image

    graphic:{ 
      type:"text",
      left:"center",
      top:"center",
      style:{
        text:'236',
        textAlign:"center",
        fill:"#fff",
        fontSize:16
      }
    },
新手上路,请多包涵

这么写下试下呢 我这边可以实现自适应

graphic: [{
                type: 'group',
                left: '15%',
                top: '35%',
                bounding: 'raw',
                children: [{
                    type: 'text',
                    style: {
                        text:data.value,
                        fontSize: 8,
                        fill: "#27D9C8",
                        textVerticalAlign: 'middle',
                        textAlign: 'center'
                    }
                }]
            }
        ],

不建议这样做,我这种方式label是一个叠一个的,不推荐,建议html直接定位做

目的就是在饼图中间加一个说明文本,想实现一个自适应水平居中的效果,不一定要用title来实现,可以选择使用label,同时设置hover时,不隐藏label即可
pie设置偏移可以用left 50%这样来实现,label设置多行,可以用富文本来做
参考代码

series: [
          {
            name: 'hello test',
            left: '-50%',
            type: 'pie',
            radius: ['50%', '70%'],
            center: ['50%', '50%'],
            avoidLabelOverlap: false,
            label: {
              show: true,
              position: 'center',
              formatter: () => '{count|4425}\n{name|hello}',
              rich: {
                count: {
                  fontSize: 24,
                  padding: [0, 0, 5, 0],
                  color: '#49DEFF',
                  textVerticalAlign: 'middle'
                },
                name: {
                  fontSize: 12,
                }
              }
            },
            emphasis: {
              label: {
                show: true
              }
            },
            labelLine: {
              show: false
            },
            data
          }
        ]
、、、

我觉得非常有必要分享下解决这个问题的方法,搜了不少文章,但都没能真正解决。

  1. 普通的top, left配置,在pie偏移后,无法真正做到居中,一旦缩放,就会出现明显偏差;
  2. 左右布局(左pie,右legend)的方式最头疼,因为pie不在容器中心;
  3. 依赖series.label的方式,虽然可以实现标题居中(其实那是label),但仅在position=center的情况可用,当position=outside,完全不能用了。而且一旦鼠标移动到第一个piepiece,此时标题就会自动响应,仿佛它只是第一个piepiese的title(它确实是)

好了说了一堆屁话,以下是组件

import { useState, useEffect, useCallback, useRef } from "react";
import * as echarts from 'echarts';
import './index.less';
export type RingChartPropsType = {
    id: string,
    style: React.CSSProperties,
    color: string[],
    title?: titlePropsType | undefined,
    legend?: echarts.LegendComponentOption | echarts.LegendComponentOption[],
    series?: echarts.SeriesOption | echarts.SeriesOption[],
    titleText?:{text?:string, textStyle?:React.CSSProperties, subtext?:string, subtextStyle?:React.CSSProperties};
};

type titlePropsType = {
    text: string,
    top: string,
    left: string,
};

type EChartsOption = echarts.EChartsOption;

const RingChart = (props: RingChartPropsType) => {
    const { id, style, color, title, legend, series } = props;
    const [titleStyle, setTitleStyle] = useState({});
    const contRef = useRef<HTMLDivElement>(null);

    const handleEcharts = () => {
        const dom = document.getElementById(id)!;
        const charts = echarts.init(dom);
        return charts;
    }
    const option: EChartsOption = {
        color: color,
        title: title,
        legend: legend,
        series: series,
    };

    useEffect(() => {
        const myChart = handleEcharts();
        myChart.setOption(option);
        setLayout();
    }, [props]);

    const setLayout = useCallback(() => {
        const cont = contRef.current;
        if (!cont) return;
        const width = cont.offsetWidth - (parseFloat(props.style?.paddingRight ?? 0));
        const height = cont.offsetHeight - (parseFloat(props.style?.paddingBottom ?? 0));
        const typeSeries = props.series as echarts.SeriesOption;
        const minSize = Math.min(width, height);

        //默认不能更改
        const [centerX, centerY] = typeSeries.center || ['50%', '50%'];

        const [iRadius] = typeSeries.radius || ['40%', '70%'];
        const fRadius = typeof iRadius === 'string' ? parseFloat(iRadius) / 100 * minSize : iRadius;
        const titleSize = fRadius;
        const titleStyle = {
            textAlign:'center',
            width: titleSize,
            height: titleSize,
            top: '50%',
            marginTop:-titleSize/2,
            left: '50%',
            marginLeft:-titleSize/2,
        };

        //left优先于right
        if(typeSeries.left){
            if (typeof typeSeries.left === 'string') {
                const left = 0.5 * width * parseFloat(typeSeries.left) / 100;
                titleStyle.marginLeft += left;
            }
            if (typeof typeSeries.left === 'number') {
                titleStyle.marginLeft += 0.5 * typeSeries.left;
            }
        }else{
            if (typeof typeSeries.right === 'string') {
                const left = 0.5 * width * parseFloat(typeSeries.right) / 100;
                titleStyle.marginLeft -= left;
            }
            if (typeof typeSeries.right === 'number') {
                const left = 0.5  * typeSeries.right;
                titleStyle.marginLeft -= left;
            }
        }

        //top优先于bottom
        if(typeSeries.top){
            if (typeof typeSeries.top === 'string') {
                const top = 0.5 * height * parseFloat(typeSeries.top) / 100;
                titleStyle.marginTop += top;
            }
            if (typeof typeSeries.top === 'number') {
                titleStyle.marginTop += 0.5 * typeSeries.top;
            }
        }else{
            if (typeof typeSeries.bottom === 'string') {
                const bottom = 0.5 * height * parseFloat(typeSeries.bottom) / 100;
                titleStyle.marginTop -= bottom;
            }
            if (typeof typeSeries.bottom === 'number') {
                const bottom = 0.5  * typeSeries.bottom;
                titleStyle.marginTop -= bottom;
            }
        }
        setTitleStyle({ ...titleStyle });
    }, [props]);


    return <div style={ { ...style } } ref={ contRef }>
        <div style={{position:'relative', height: '100%', width: '100%'}}>
            {props.titleText && <div style={ { position: 'absolute', ...titleStyle } }>
                <div style={{width:'100%', height:'100%', display:'flex', justifyContent:'center', alignItems:'center', flexDirection:'column'}}>
                    <span style={{fontSize:16, color:'#000', ...props.titleText.textStyle}}>{props.titleText.text}</span>
                    <div style={{fontSize:24, lineHeight:'20px', fontWeight:'bold', color:'#000', ...props.titleText.subtextStyle}}>{props.titleText.subtext}</div>
                </div>
            </div>}
            <div id={ id } style={ { height: '100%', width: '100%'} }></div>
        </div>
    </div>
}
export default RingChart;

使用

    const pieOption:RingChartPropsType = {
        id:'pie',
        style:{ height: '40%', width: '100%' },
        color:['#f59a23', '#70b603'],
        legend:{
            bottom: '2%',
            left: 'center',
            icon: 'circle',
        },
        titleText:{
            text:'已存储量',
            subtext:transforByte(dataAssets.assetsEnd+dataAssets.assetsSource)
        },
        series:{
            type:'pie',
            radius:['46%', '75%'],
            label:{
                show:true,
                position:'inside',
                formatter:'{percent|{d}%}',
                rich:{
                    percent:{
                        fontSize: 14,
                        color:'#fffffffe'
                    },
                }
            },
            data:[
                { value: dataAssets.assetsEnd, name: `已发布数据   ${transforByte(dataAssets.assetsEnd)}` },
                { value: dataAssets.assetsSource, name: `原始数据   ${transforByte(dataAssets.assetsSource)}` },
            ],
        }

仅配置titleText即可,可以兼容series[left|right|top|bottom]标量与百分比。
主要思路就是利用传统html元素居中的实现方式,前提要知道pie的中心点。

因为电脑截图会被加密,所以图片就不上传了。
希望对大家有帮助。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进