Javascript 强制工具提示在图表上的位置

Javascript 强制工具提示在图表上的位置,javascript,tooltip,amcharts4,Javascript,Tooltip,Amcharts4,简而言之,我想强制工具提示始终显示在直线系列上悬停点的上方,即使它超出图表区域。工具提示适用于所有系列。示例如下所示: const {useRef, useState} = React; const CHART_CONTAINER = 'campaign-budget-chart'; const CHART_COLORS = { value1: '#05a8fa', value2: '#ed3434', value3: '#0ec76a', } function getRandom

简而言之,我想强制工具提示始终显示在直线系列上悬停点的上方,即使它超出图表区域。工具提示适用于所有系列。示例如下所示:

const {useRef, useState} = React;
const CHART_CONTAINER = 'campaign-budget-chart';
const CHART_COLORS = {
  value1: '#05a8fa',
  value2: '#ed3434',
  value3: '#0ec76a',
}

function getRandomNumber(max){
  return Math.floor(Math.random() * Math.floor(max))
}

const initialData = [{
  timestamp: new Date(2020, 09, 25),
  value1: 0,
  value2: getRandomNumber(50),
  value3: getRandomNumber(250),
},{
  timestamp: new Date(2020, 09, 26),
  value1: getRandomNumber(100),
  value2: getRandomNumber(50),
  value3: getRandomNumber(250),
},{
  timestamp: new Date(2020, 09, 27),
  value1: getRandomNumber(100),
  value2: getRandomNumber(50),
  value3: getRandomNumber(250),
},
           {
  timestamp: new Date(2020, 09, 28),
  value1: getRandomNumber(100),
  value2: getRandomNumber(50),
  value3: getRandomNumber(250),
}];
let i = 0;

function BudgetChart() {
  const chartRef = useRef(null);
  const [data, setData] = useState(initialData);
  const [cursor, setCursor] = React.useState({ x: 0, y: 0 });
  const [cursorVisible, setCursorVisible] = React.useState(false);

  function createSeries(
    fieldX,
    fieldY,
    name,
    lineColor,
  ) {
    if (!chartRef.current) return;
    console.log('Create series');
    // Init series
    let series = chartRef.current.series.push(new am4charts.LineSeries());
    series.name = name;
    series.dataFields.valueY = fieldY;
    series.dataFields.dateX = fieldX;
    series.strokeWidth = 3;
    series.stroke = am4core.color(lineColor);
    series.tooltip.pointerOrientation = 'down';
    series.tooltip.background.filters.clear(); // remove shadow
    series.tooltip.getFillFromObject = false;
    series.tooltip.background.fill = am4core.color('#2a2b2e');
    series.tooltip.background.stroke = am4core.color('#2a2b2e');
    series.tooltip.label.fontSize = 12;
    series.tooltip.background.pointerLength = 0;
    series.tooltip.dy = -5;
    series.tooltipText = '{valueY}';
    series.tensionX = 0.8;
    series.showOnInit = false;

    // Add bullet for optimization
    let circleBullet = series.bullets.push(new am4charts.CircleBullet());
    circleBullet.circle.radius = 6;
    circleBullet.circle.fill = lineColor;
    circleBullet.circle.stroke = am4core.color('#fff');
    circleBullet.circle.strokeWidth = 3;
    circleBullet.propertyFields.disabled = 'optimizationTooltipDisabled';
      // Set up tooltip
  series.adapter.add("tooltipText", function(ev) {
    var text = "[bold]{dateX}[/]\n"
    chartRef.current.series.each(function(item) {
      text += "[" + item.stroke.hex + "]●[/] " + item.name + ": {" + item.dataFields.valueY + "}\n";
    });
    return text;
  });

    // Bullet shadow
    let shadow = circleBullet.filters.push(new am4core.DropShadowFilter());
    shadow.opacity = 0.1;

  }

  React.useEffect(() => {
    if (!chartRef.current) {
      chartRef.current = am4core.create(CHART_CONTAINER, am4charts.XYChart);

      chartRef.current.paddingLeft = 0;

      // Add date axis
      let dateAxis = chartRef.current.xAxes.push(new am4charts.DateAxis());
      dateAxis.renderer.labels.template.fontSize = 12;
      dateAxis.renderer.labels.template.fill = am4core.color(
        'rgba(183,186,199,0.8)'
      );
      dateAxis.renderer.grid.template.strokeOpacity = 0;

      // Add value axis
      let valueAxis = chartRef.current.yAxes.push(new am4charts.ValueAxis());
      valueAxis.renderer.grid.template.stroke = am4core.color(
        '#f0f2fa'
      );
      valueAxis.renderer.grid.template.strokeOpacity = 1;
      valueAxis.renderer.labels.template.fill = am4core.color(
        'rgba(183,186,199,0.8)'
      );
      valueAxis.renderer.labels.template.fontSize = 12;

      // Add cursor
      chartRef.current.cursor = new am4charts.XYCursor();
      chartRef.current.cursor.maxTooltipDistance = -1;

      // Add legend
      chartRef.current.legend = new am4charts.Legend();
      chartRef.current.legend.position = 'bottom';
      chartRef.current.legend.contentAlign = 'left';
      chartRef.current.legend.paddingTop = 20;

      // Disable axis lines
      chartRef.current.cursor.lineX.disabled = true;
      chartRef.current.cursor.lineY.disabled = true;

      // Disable axis tooltips
      dateAxis.cursorTooltipEnabled = false;
      valueAxis.cursorTooltipEnabled = false;

      // Disable zoom
      chartRef.current.cursor.behavior = 'none';

      chartRef.current.cursor.events.on('cursorpositionchanged', function(ev) {
        let xAxis = ev.target.chart.xAxes.getIndex(0);
        let yAxis = ev.target.chart.yAxes.getIndex(0);
        setCursor({
          x: xAxis.toAxisPosition(ev.target.xPosition),
          y: yAxis.toAxisPosition(ev.target.yPosition),
        });
      });
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);
  
  // Load data into chart
  React.useEffect(() => {
    console.log('data ', data)
    if (chartRef.current) {
      chartRef.current.data = data;
      
      Object.keys(data[0]).forEach(key => {
        if(key === 'timestamp') return;
        createSeries(
        'timestamp',
        key,
        key,
        CHART_COLORS[key]
      );
      })
    }
  }, [data]);

  // Handle component unmounting, dispose chart
  React.useEffect(() => {
    return () => {
      chartRef.current && chartRef.current.dispose();
    };
  }, []);
  
  function handleRemoveSeries(){
    setData(data.map(item => ({timestamp: item.timestamp, value1: item.value1, value2: item.value2})))
  }

  return (
    <>
      <button onClick={handleRemoveSeries}>Remove 3rd series</button>
      <div
        id={CHART_CONTAINER}
        style={{
          width: '100%',
          height: '350px',
          marginBottom: '50px',
        }}
      />
    </>
  );
}

ReactDOM.render(<BudgetChart />, document.getElementById('app'));
const{useRef,useState}=React;
const CHART_CONTAINER='活动预算图';
常量图表颜色={
值1:“#05a8fa”,
值2:“#ed3434”,
值3:“#0ec76a”,
}
函数getRandomNumber(最大值){
返回Math.floor(Math.random()*Math.floor(max))
}
常量初始数据=[{
时间戳:新日期(2020年9月25日),
值1:0,
值2:getRandomNumber(50),
值3:getRandomNumber(250),
},{
时间戳:新日期(2020年9月26日),
值1:getRandomNumber(100),
值2:getRandomNumber(50),
值3:getRandomNumber(250),
},{
时间戳:新日期(2020年9月27日),
值1:getRandomNumber(100),
值2:getRandomNumber(50),
值3:getRandomNumber(250),
},
{
时间戳:新日期(2020年9月28日),
值1:getRandomNumber(100),
值2:getRandomNumber(50),
值3:getRandomNumber(250),
}];
设i=0;
函数BudgetChart(){
const chartRef=useRef(null);
const[data,setData]=使用状态(initialData);
const[cursor,setCursor]=React.useState({x:0,y:0});
const[cursorVisible,setCursorVisible]=React.useState(false);
函数createSeries(
fieldX,
菲尔迪,
名称
线条颜色,
) {
如果(!chartRef.current)返回;
console.log('createseries');
//Init系列
let series=chartRef.current.series.push(新的am4charts.LineSeries());
series.name=名称;
series.dataFields.valueY=fieldY;
series.dataFields.dateX=fieldX;
系列。冲程宽度=3;
series.stroke=am4core.color(lineColor);
series.tooltip.pointerOrientation='down';
series.tooltip.background.filters.clear();//删除阴影
series.tooltip.getFillFromObject=false;
series.tooltip.background.fill=am4core.color(“#2a2b2e”);
series.tooltip.background.stroke=am4core.color(“#2a2b2e”);
series.tooltip.label.fontSize=12;
series.tooltip.background.pointerLength=0;
series.tooltip.dy=-5;
series.tooltipText='{valueY}';
级数.tensionX=0.8;
series.showOnInit=false;
//添加用于优化的项目符号
让circleBullet=series.bullets.push(新的am4charts.circleBullet());
圆圈bullet.circle.radius=6;
circleBullet.circle.fill=线条颜色;
circleBullet.circle.stroke=am4core.color(“#fff”);
circleBullet.circle.strokeWidth=3;
circleBullet.propertyFields.disabled='optimizationTooltipDisabled';
//设置工具提示
series.adapter.add(“tooltipText”,函数(ev){
var text=“[bold]{dateX}[/]\n”
chartRef.当前系列每个(功能(项目){
text+=“[”+item.stroke.hex+”]●[/]“+item.name+”:{“+item.dataFields.valueY+”}\n”;
});
返回文本;
});
//子弹影
让shadow=circleBullet.filters.push(新的am4core.DropShadowFilter());
shadow.opacity=0.1;
}
React.useffect(()=>{
如果(!chartRef.current){
chartRef.current=am4core.create(图表容器,am4charts.XYChart);
chartRef.current.paddingLeft=0;
//添加日期轴
让dateAxis=chartRef.current.xAxes.push(新的am4charts.dateAxis());
dateAxis.renderer.labels.template.fontSize=12;
dateAxis.renderer.labels.template.fill=am4core.color(
‘rgba(183186199,0.8)’
);
dateAxis.renderer.grid.template.strokeOpacity=0;
//增值轴
让valueAxis=chartRef.current.yAxes.push(新的am4charts.valueAxis());
valueAxis.renderer.grid.template.stroke=am4core.color(
“#f0f2fa”
);
valueAxis.renderer.grid.template.strokeOpacity=1;
valueAxis.renderer.labels.template.fill=am4core.color(
‘rgba(183186199,0.8)’
);
valueAxis.renderer.labels.template.fontSize=12;
//添加光标
chartRef.current.cursor=新的am4charts.XYCursor();
chartRef.current.cursor.maxTooltipDistance=-1;
//添加图例
chartRef.current.legend=新的am4charts.legend();
chartRef.current.legend.position='bottom';
chartRef.current.legend.contentAlign='左';
chartRef.current.legend.paddingTop=20;
//禁用轴线
chartRef.current.cursor.lineX.disabled=true;
chartRef.current.cursor.lineY.disabled=true;
//禁用轴工具提示
dateAxis.cursorTooltipEnabled=false;
valueAxis.cursorTooltipEnabled=false;
//禁用缩放
chartRef.current.cursor.behavior='none';
chartRef.current.cursor.events.on('cursorpositionchanged',函数(ev){
设xAxis=ev.target.chart.xAxes.getIndex(0);
设yAxis=ev.target.chart.yAxes.getIndex(0);
设置光标({
x:xAxis.toAxisPosition(ev.target.xPosition),
y:yAxis.toAxisPosition(ev.target.yPosition),
});
});
}
//eslint禁用下一行react HOOK/deps
}, []);
//将数据加载到图表中
React.useffect(()=>{
console.log('data',data)
如果(当前图表参考){
chartRef.current.data=数据;
Object.key(数据[0]).forEach(key=>{
if(key==='timestamp')返回;
创建系列(
“时间戳”,
钥匙
钥匙
图表颜色[键]
);
})
}
},[数据];
//处理组件卸载,处置图表
React.useffect(()=>{
return()=>{
chartRef.current&&chartRef.current.dispose();
};
}, []);
函数handleRemoveSeries(){
setData(data.map(item=>({timestamp:item.timestamp,value1:item.value1,value2:item.value2})))
}
返回(
删除第3个系列
);
}
ReactDOM.render(,document.getElementById('app'));
对于图表顶部附近的所有值,工具提示试图将其自身压缩到图表区域内。根据:

重要提示:在某些情况下,例如将多个工具提示堆叠在一起