Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

仪表图分区标签防重叠方案 #3784

Open
skie1997 opened this issue Mar 6, 2025 · 0 comments
Open

仪表图分区标签防重叠方案 #3784

skie1997 opened this issue Mar 6, 2025 · 0 comments
Assignees

Comments

@skie1997
Copy link
Contributor

skie1997 commented Mar 6, 2025

大屏仪表图配置区间后,区间标签由extensionMark构造,无防重叠效果,寻求优化方案。

Image

可能的方案:

  1. 底层抽象出标签防重叠接口,支持图表库在生成extensionMark后调用
  2. 用轴代替区间标签,但轴要支持自定义tick

Spec

const minValue = 0
const maxValue = 1
const startAngle = -180
const endAngle = -0
const innerRadius = 0.75
const outerRadius = 0.9
const offsetLayoutRadius = 0.2
const offsetRadius = 5

const degreeToRadian = degree => (degree / 180) * Math.PI

const getAngle = (value, min, max, startAngle, endAngle) =>
  degreeToRadian(((value - min) / (max - min)) * (endAngle - startAngle) + startAngle)


const getLayoutRadius = vchart => {
  const region = vchart.getChart().getRegionsInIndex(0)[0]
  const { width, height } = region.getLayoutRect()
  return Math.min(width / 2, height / 2)
}

const computeTick = (value, ctx) => {
  const { vchart } = ctx
  // customMark无法直接获取center, 需要从轴上获取center, 用于tick计算
  const getCenter = () => ctx.vchart.getChart().getAllSeries()[0].angleAxisHelper.center()

  const layoutRadius = getLayoutRadius(vchart)
  const center = getCenter()
  const angle = getAngle(value, minValue, maxValue, startAngle, endAngle)
  const radius = (innerRadius + offsetLayoutRadius) * layoutRadius + offsetRadius // 后续可以增加配置来判断label放在外侧还是内侧, 目前放在外侧
  const x = center.x + radius * Math.cos(angle)
  const y = center.y + radius * Math.sin(angle)
  return [x, y, angle]
} 

const spec = {
  "type": "gauge",
  "theme": {},
  "padding": 15,
  "categoryField": "name",
  "valueField": "measureValue",
  "startAngle": -180,
  "endAngle": 0,
  "outerRadius": 0.9,
  "innerRadius": 0.75,
  "data": [
    {
      "id": "data",
      "values": [
        {
          "current": 0.8138999999999998,
          "name": "Sales Proportion",
          "emptyName": "",
          "percent": "-",
          "measurePercent": 0.8138999999999998,
          "measureValue": 0.8138999999999998,
          "prevMeasureValue": null
        }
      ]
    },
    {
      "id": "sections",
      "values": [
        {
          "gaugeColorKey": 0,
          "current": 0.33,
          "name": "",
          "color": "#006EFF",
          "measureValue": 0.33,
          "prevMeasureValue": 0
        },
        {
          "gaugeColorKey": 1,
          "current": 0.37,
          "name": "",
          "color": "#f5a623",
          "measureValue": 0.37,
          "prevMeasureValue": 0.33
        },
        {
          "gaugeColorKey": 2,
          "current": 0.6,
          "name": "",
          "color": "rgb(32,228,228)",
          "measureValue": 0.6,
          "prevMeasureValue": 0.37
        },
        {
          "gaugeColorKey": 3,
          "current": 1,
          "name": "",
          "color": "rgb(45,226,226)",
          "measureValue": 1,
          "prevMeasureValue": 0.6
        }
      ]
    }
  ],
  "axes": [
    {
      "visible": true,
      "type": "linear",
      "orient": "angle",
      "inside": true,
      "min": 0,
      "max": 1,
      "innerRadius": 0.9,
      "interactive": false,
      "label": {
        "visible": true,
        "inside": true,
        "space": 8,
        "style": {
          "fill": "rgba(255,255,255,0.5)",
          "fontSize": 12,
          "fontFamily": "D-DIN",
          "fontWeight": "normal",
          "fillOpacity": 0
        },
        "autoHideSeparation": 4
      },
      "tick": {
        "visible": false,
        "tickSize": 11.700000000000001,
        "style": {
          "stroke": "rgba(255,255,255,0.3)",
          "lineWidth": 1
        }
      },
      "subTick": {
        "visible": false,
        "tickSize": 4,
        "tickCount": 6,
        "style": {
          "stroke": "rgba(255,255,255,0.3)"
        }
      },
      "grid": {
        "visible": false
      },
      "subGrid": {
        "visible": false
      },
      "ticks": false
    }
  ],
  "color": {
    "type": "ordinal",
    "range": [
      "#006EFF",
      "#f5a623",
      "rgb(32,228,228)",
      "rgb(45,226,226)"
    ],
    "domain": [
      0,
      1,
      2,
      3
    ],
    "field": "gaugeColorKey"
  },
  "gauge": {
    "type": "gauge",
    "seriesField": "gaugeColorKey",
    "categoryField": "gaugeColorKey",
    "valueField": "current",
    "dataIndex": 1,
    "progress": {
      "style": {
        "cornerRadius": 0
      }
    }
  },
  "pointer": {
    "visible": true,
    "interactive": false,
    "zIndex": 9999,
    "style": {
      "fill": "rgb(230,247,255)"
    }
  },
  "pin": {
    "visible": false,
    "interactive": false,
    "width": 0.025,
    "height": 0.025,
    "style": {
      "fill": "rgba(230,247,255,0.3)"
    }
  },
  "pinBackground": {
    "visible": true,
    "width": 0.08,
    "height": 0.08,
    "style": {
      "fill": "rgba(230,247,255,0.3)"
    }
  },

  "extensionMark": [
    {
      "type": "text",
      "visible": true,
      "style": {
        "fill": "rgb(255,255,255)",
        "fontSize": 12,
        "fontFamily": "D-DIN",
        "fontWeight": "normal",
        x: (datum, ctx) => {
          console.log('ctx', ctx, computeTick(0.33, ctx))
          return computeTick(0.33, ctx)[0]
        },
        y: (datum, ctx) => {
          return computeTick(0.33, ctx)[1]
        },
        text: 0.33
      }
    },
    {
      "type": "text",
      "visible": true,
      "dataIndex": 1,
      "style": {
        "fill": "rgba(255,255,255,0.5)",
        "fontSize": 12,
        "fontFamily": "D-DIN",
        "fontWeight": "normal",
        x: (datum, ctx) => {
          console.log('ctx', ctx, computeTick(0.33, ctx))
          return computeTick(0.37, ctx)[0]
        },
        y: (datum, ctx) => {
          return computeTick(0.37, ctx)[1]
        },
        text: 0.37
      }
    }
  ],
  "legends": [
    {
      "type": "discrete",
      "visible": false,
      "select": false,
      "hover": false,
      "zIndex": 500.3568476877166
    }
  ],
  "tooltip": {
    "visible": true,
    "renderMode": "canvas",
    "mark": {
      "visible": true
    },
    "style": {
      "panel": {
        "padding": {
          "top": 5,
          "bottom": 10,
          "left": 10,
          "right": 10
        },
        "backgroundColor": "rgba(8, 28, 48, 0.95)",
        "border": {
          "color": "#CFCFCF",
          "width": 0,
          "radius": 2
        },
        "shadow": {
          "x": 0,
          "y": 4,
          "blur": 12,
          "spread": 0,
          "color": "rgba(0, 0, 0, 0.2)"
        }
      },
      "titleLabel": {
        "fontSize": 14,
        "fontColor": "#FFF",
        "fontWeight": "bold",
        "fontFamily": "D-DIN",
        "align": "left",
        "lineHeight": 18
      },
      "keyLabel": {
        "fontSize": 12,
        "fontColor": "rgba(255,255,255,0.65)",
        "fontWeight": "normal",
        "fontFamily": "SourceHanSansCN-Normal",
        "align": "left",
        "lineHeight": 18
      },
      "valueLabel": {
        "fontSize": 12,
        "fontColor": "#FFF",
        "fontWeight": "normal",
        "fontFamily": "D-DIN",
        "align": "right",
        "lineHeight": 18
      },
      "shape": {
        "size": 10,
        "spacing": 10,
        "shapeLineWidth": 0
      },
      "spaceRow": 10
    },
    "dimension": {
      "visible": true
    }
  },
  "background": "rgba(0, 0, 0, 1)",
  "crosshair": {
    "xField": {
      "line": {
        "style": {
          "fillOpacity": 1,
          "fill": "rgba(80,156,255,0.1)"
        }
      },
      "visible": false
    },
    "yField": {
      "line": {
        "style": {
          "fillOpacity": 1,
          "fill": "rgba(80,156,255,0.1)"
        }
      },
      "visible": false
    }
  },
  "morph": {
    "enable": false
  },
  "plotLayout": {
    "clip": false
  },
  "select": {
    "enable": true
  },
  "series": [
    {
      "type": "gauge",
      "angleField": "measureValue",
      "outerRadius": 0.9,
      "innerRadius": 0.75,
      "padAngle": 0,
      "seriesField": "gaugeColorKey",
      "categoryField": "gaugeColorKey",
      "dataIndex": 1,
      "segment": {
        "style": {
          "cornerRadius": 0
        }
      },
      "animation": false,
      "animationAppear": {
        "segment": {
          "loop": false,
          "duration": 1000,
          "easing": "circInOut"
        }
      },
      "animationNormal": {
        "segment": {
          "loop": true,
          "startTime": 5000,
          "delayAfter": 5000,
          "duration": 1000,
          "easing": "circInOut",
          "controlOptions": {
            "immediatelyApply": false
          },
          "channel": {
            "startAngle": {},
            "endAngle": {}
          }
        }
      },
      "animationEnter": {
        "duration": 1000,
        "easing": "circInOut"
      },
      "animationUpdate": {
        "duration": 1000,
        "easing": "circInOut"
      },
      "animationExit": {}
    },
    {
      "type": "gaugePointer",
      "valueField": "measureValue",
      "innerRadius": 0,
      "pin": {
        "visible": false,
        "interactive": false,
        "width": 0.025,
        "height": 0.025,
        "style": {
          "fill": "rgba(230,247,255,0.3)"
        }
      },
      "pinBackground": {
        "visible": true,
        "width": 0.08,
        "height": 0.08,
        "style": {
          "fill": "rgba(230,247,255,0.3)"
        }
      },
      "pointer": {
        "visible": true,
        "interactive": false,
        "zIndex": 9999,
        "style": {
          "fill": "rgb(230,247,255)"
        },
        "width": 0.4,
        "height": 0.3
      },
      "animation": false,
      "animationAppear": {
        "segment": {
          "loop": false,
          "duration": 1000,
          "easing": "circInOut"
        }
      },
      "animationNormal": {
        "segment": {
          "loop": true,
          "startTime": 5000,
          "delayAfter": 5000,
          "duration": 1000,
          "easing": "circInOut",
          "controlOptions": {
            "immediatelyApply": false
          },
          "channel": {
            "startAngle": {},
            "endAngle": {}
          }
        }
      },
      "animationEnter": {
        "duration": 1000,
        "easing": "circInOut"
      },
      "animationUpdate": {
        "duration": 1000,
        "easing": "circInOut"
      },
      "animationExit": {}
    }
  ],
  "hash": "77536063193c2899c95726c3d8c2490f",
  "width": 400,
  "height": 186,
  "animation": false
}

const vchart = new VChart(spec, { dom: CONTAINER_ID });
vchart.renderSync();

// Just for the convenience of console debugging, DO NOT COPY!
window['vchart'] = vchart;
@skie1997 skie1997 self-assigned this Mar 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant