You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Amcharts自定义图片Bullet显隐及图表边界裁剪问题咨询

AmCharts 4 Line Chart: Fixing Bullet Visibility and Clipping Issues

Alright, let's tackle your two AmCharts 4 line chart problems one by one. I’ve dealt with similar requirements before, so here’s a step-by-step breakdown of solutions that should work for you:


1. Show/Hide Custom Image Bullets in Pairs on Hover

To get your custom image bullets hidden by default, then show both (one for each series) when hovering, and hide them again when the cursor leaves, you’ll need to combine state configuration with event listeners. Here’s how to implement it:

Step 1: Set Bullets to Hide by Default

Instead of manually calling hide(), configure the bullet's default state to be transparent—this aligns better with AmCharts' built-in state management:

// For the first series' bullet
var bullet = series.bullets.push(new am4charts.Bullet());
bullet.defaultState.properties.opacity = 0; // Initial hidden state

// For the second series' bullet
var bullet2 = series2.bullets.push(new am4charts.Bullet());
bullet2.defaultState.properties.opacity = 0;

Step 2: Add Event Listeners for Cursor Interaction

You have two options depending on your desired behavior:

Option A: Show Bullets When Cursor Moves Over Data Points

Use the chart's cursor position change event to sync bullet visibility across both series:

// Listen for cursor position changes
chart.cursor.events.on("cursorpositionchanged", function(event) {
  // Hide all bullets first
  series.bullets.each(b => b.hide());
  series2.bullets.each(b => b.hide());

  // Get the current data point index from the cursor position
  var axisPos = dateAxis.toAxisPosition(chart.cursor.xPosition);
  var dataIndex = dateAxis.positionToIndex(axisPos);

  // Show bullets for the matching index in both series
  if (series.dataItems.getIndex(dataIndex)) {
    series.dataItems.getIndex(dataIndex).bullets.each(b => b.show());
  }
  if (series2.dataItems.getIndex(dataIndex)) {
    series2.dataItems.getIndex(dataIndex).bullets.each(b => b.show());
  }
});

// Hide all bullets when cursor leaves the chart
chart.cursor.events.on("hidden", function() {
  series.bullets.each(b => b.hide());
  series2.bullets.each(b => b.hide());
});

Option B: Show Bullets When Hovering Directly Over a Bullet

If you only want bullets to show when the user hovers over one of them, use the bullet's over and out events:

// Handle hover-in
function showPairedBullets(event) {
  const targetBullet = event.target;
  const dataIndex = targetBullet.dataItem.index;

  // Show current bullet and its pair in the other series
  targetBullet.show();
  series2.dataItems.getIndex(dataIndex).bullets.each(b => b.show());
}

// Handle hover-out
function hidePairedBullets(event) {
  const targetBullet = event.target;
  const dataIndex = targetBullet.dataItem.index;

  // Hide current bullet and its pair
  targetBullet.hide();
  series2.dataItems.getIndex(dataIndex).bullets.each(b => b.hide());
}

// Bind events to both bullets
bullet.events.on("over", showPairedBullets);
bullet.events.on("out", hidePairedBullets);
bullet2.events.on("over", showPairedBullets);
bullet2.events.on("out", hidePairedBullets);

2. Prevent Custom Images from Being Clipped by Chart Bounds

To make sure your images display fully outside the chart's plot area without being cut off, adjust these three key settings:

Step 1: Disable Plot Area Clipping

By default, AmCharts clips any content outside the main plot area. Turn this off with:

chart.plotContainer.clip = false;

Step 2: Increase Bullet Z-Index

Ensure your images appear above other chart elements (like axes or grid lines) by setting a higher z-index:

bullet.zIndex = 100;
bullet2.zIndex = 100;

Step 3: Add Padding or Adjust Image Position

Give your images space to display by either increasing the chart's padding, or offsetting the image position:

// Option 1: Add padding to the chart
chart.padding(50, 20, 50, 20); // Top, right, bottom, left padding

// Option 2: Offset the image position (adjust values as needed)
image1.y = 25; // Move first image down
image2.y = -25; // Move second image up

Full Modified Code

Here’s the complete code integrating both solutions:

am4core.ready(function() { 
  // Themes begin 
  am4core.useTheme(am4themes_animated); 
  // Themes end 
  // Create chart 
  var chart = am4core.create("chartdiv", am4charts.XYChart); 
  chart.padding(50, 20, 50, 20); // Add padding for images
  chart.plotContainer.clip = false; // Disable clipping

  var colorSale = "#75af26"; 
  var colorBuy = "#269cb5"; 
  // Load external data 
  var data = []; 
  var price1 = 100; 
  var price2 = 150; 
  var quantity = 1000; 
  for (var i = 15; i < 30; i++) { 
    price1 += Math.round((Math.random() < 0.5 ? 1 : -1) * Math.random() * 100); 
    price2 += Math.round((Math.random() < 0.5 ? 1 : -1) * Math.random() * 100); 
    if (price1 < 100) { 
      price1 = 100; 
    } 
    if (price2 < 100) { 
      price2 = 100; 
    } 
    quantity += Math.round((Math.random() < 0.5 ? 1 : -1) * Math.random() * 500); 
    if (quantity < 0) { 
      quantity *= -1; 
    } 
    data.push({ 
      date: new Date(2000, 0, i), 
      price1: price1, 
      price2:price2, 
      quantity: quantity 
    }); 
  } 
  chart.data = data; 
  // Vertical axis arrangement
  chart.leftAxesContainer.layout = "vertical"; 
  var dateAxis = chart.xAxes.push(new am4charts.DateAxis()); 
  dateAxis.renderer.grid.template.location = -0.001; 
  dateAxis.renderer.ticks.template.length = 8; 
  dateAxis.renderer.ticks.template.strokeOpacity = 0.2; 
  dateAxis.renderer.minLabelPosition = 0.01; 
  dateAxis.renderer.maxLabelPosition = 0.99; 
  dateAxis.keepSelection = true; 
  dateAxis.renderer.fontSize = "13px"; 
  dateAxis.renderer.labels.template.fill = am4core.color("#767676"); 
  dateAxis.renderer.grid.template.fill = am4core.color("#d8d8d8"); 
  dateAxis.minHeight = 30; 
  dateAxis.groupData = true; 
  dateAxis.maxZoomLevel = 0; 
  var valueAxis = chart.yAxes.push(new am4charts.ValueAxis()); 
  valueAxis.tooltip.disabled = true; 
  valueAxis.zIndex = 1; 
  valueAxis.renderer.baseGrid.disabled = true; 
  valueAxis.height = am4core.percent(65); 
  valueAxis.renderer.gridContainer.background.fillOpacity = 0.05; 
  valueAxis.renderer.inside = true; 
  valueAxis.renderer.labels.template.verticalCenter = "bottom"; 
  valueAxis.renderer.labels.template.padding(2, 2, 2, 2); 
  valueAxis.renderer.labels.template.fill = am4core.color("#767676"); 
  valueAxis.renderer.grid.template.fill = am4core.color("#d8d8d8"); 
  valueAxis.renderer.fontSize = "13px" 

  var series = chart.series.push(new am4charts.LineSeries()); 
  series.dataFields.dateX = "date"; 
  series.dataFields.valueY = "price1"; 
  series.tooltipText = "{valueY.value}"; 
  series.name = "MSFT: Value"; 
  series.strokeWidth = 3; 
  series.stroke = am4core.color(colorSale); 
  series.tensionX = 0.77; 
  series.defaultState.transitionDuration = 0; 

  var series2 = chart.series.push(new am4charts.LineSeries()); 
  series2.dataFields.dateX = "date"; 
  series2.dataFields.valueY = "price2"; 
  series2.tooltipText = "{valueY.value}"; 
  series2.name = "MSFT: Value"; 
  series2.strokeWidth = 3; 
  series2.stroke = am4core.color(colorBuy); 
  series2.tensionX = 0.77; 
  series2.defaultState.transitionDuration = 0; 

  chart.cursor = new am4charts.XYCursor(); 
  chart.cursor.behavior = "none";

  // First series bullet setup
  var bullet = series.bullets.push(new am4charts.Bullet());
  bullet.zIndex = 100;
  bullet.defaultState.properties.opacity = 0;
  var image1 = bullet.createChild(am4core.Image);
  image1.href = "https://upload.wikimedia.org/wikipedia/commons/thumb/a/ac/Approve_icon.svg/620px-Approve_icon.svg.png";
  image1.width = 50;
  image1.height = 50;
  image1.horizontalCenter = "middle";
  image1.verticalCenter = "middle";
  image1.y = 25;

  // Second series bullet setup
  var bullet2 = series2.bullets.push(new am4charts.Bullet());
  bullet2.zIndex = 100;
  bullet2.defaultState.properties.opacity = 0;
  var image2 = bullet2.createChild(am4core.Image);
  image2.href = "https://upload.wikimedia.org/wikipedia/commons/thumb/a/ac/Approve_icon.svg/620px-Approve_icon.svg.png";
  image2.width = 50;
  image2.height = 50;
  image2.horizontalCenter = "middle";
  image2.verticalCenter = "middle";
  image2.y = -25;

  // Cursor event to show/hide bullets
  chart.cursor.events.on("cursorpositionchanged", function(event) {
    series.bullets.each(b => b.hide());
    series2.bullets.each(b => b.hide());

    var axisPos = dateAxis.toAxisPosition(chart.cursor.xPosition);
    var dataIndex = dateAxis.positionToIndex(axisPos);

    if (series.dataItems.getIndex(dataIndex)) {
      series.dataItems.getIndex(dataIndex).bullets.each(b => b.show());
    }
    if (series2.dataItems.getIndex(dataIndex)) {
      series2.dataItems.getIndex(dataIndex).bullets.each(b => b.show());
    }
  });

  chart.cursor.events.on("hidden", function() {
    series.bullets.each(b => b.hide());
    series2.bullets.each(b => b.hide());
  });
}); // end am4core.ready() 
#chartdiv{ height: 600px }
<script src="https://www.amcharts.com/lib/4/core.js"></script> 
<script src="https://www.amcharts.com/lib/4/charts.js"></script> 
<script src="https://www.amcharts.com/lib/4/themes/animated.js"></script> 
<div id="chartdiv"></div>

内容的提问来源于stack exchange,提问作者Dpace Space

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 09:57:36