Amcharts自定义图片Bullet显隐及图表边界裁剪问题咨询
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

