Improve responsive chart scaling and hide center label earlier

- Scale chart layers on mobile (≤500px) to fill container while
  keeping same hole size as desktop (20%)
- Add resize handler to dynamically update proportions when crossing
  500px threshold
- Hide center label at 850px (stacked layout) instead of 500px to
  prevent overlap with chart
- Center chart at 50% on mobile since details box is below

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Anton Volnuhin 2026-02-01 15:57:16 +03:00
parent 2b45a42b49
commit d76e80dc02
2 changed files with 84 additions and 28 deletions

88
app.js
View File

@ -470,11 +470,25 @@ function renderChart(data) {
// Gradual transition between 640-1000px
const transitionProgress = (screenWidth - 640) / 360; // 0 to 1
centerPosition = 40 + (transitionProgress * 10); // 40 to 50
} else if (screenWidth <= 500) {
// Mobile: center the chart since details box is below
centerPosition = 50;
} else {
// For smaller screens
// For smaller screens (500-640px)
centerPosition = 40;
}
// Mobile: scale chart to fill container, hide outside labels
const isMobile = screenWidth <= 500;
// Mobile: extend layers to fill container, keeping same hole size as desktop
// Hole stays at 20%, layers scaled to fill remaining 80% (vs 55% on desktop)
const level1Inner = '20%';
const level1Outer = isMobile ? '56%' : '45%';
const level2Outer = isMobile ? '93%' : '70%';
const level3Outer = isMobile ? '100%' : '75%';
const outerRadius = isMobile ? '100%' : '95%';
option = {
backgroundColor: '#fff',
grid: {
@ -489,7 +503,7 @@ function renderChart(data) {
//animationEasingUpdate: 'cubicInOut',
series: {
type: 'sunburst',
radius: [0, '95%'],
radius: [0, outerRadius],
center: [`${centerPosition}%`, '50%'],
startAngle: 0,
nodeClick: false,
@ -518,8 +532,8 @@ function renderChart(data) {
{},
{
// First level - Categories
r0: '20%',
r: '45%',
r0: level1Inner,
r: level1Outer,
label: {
show: true,
rotate: 'radial',
@ -538,8 +552,8 @@ function renderChart(data) {
},
{
// Second level - Subcategories
r0: '45%',
r: '70%',
r0: level1Outer,
r: level2Outer,
label: {
show: function(param) {
// Show labels for sectors that are at least 5% of the total
@ -617,16 +631,17 @@ function renderChart(data) {
}
},
{
// Third level - Microcategories - a bit wider than before
r0: '70%',
r: '75%',
// Third level - Microcategories
r0: level2Outer,
r: level3Outer,
label: {
// Only show labels conditionally based on segment size
show: function(param) {
// On mobile, hide outside labels to maximize chart size
show: isMobile ? false : function(param) {
// Show label if segment is wide enough (>1%)
return param.percent > 0.000;
},
position: 'outside',
position: isMobile ? 'inside' : 'outside',
padding: 3,
minAngle: 3, // Add this - default is 5, reducing it will show more labels
silent: false,
@ -1756,16 +1771,51 @@ function adjustChartSize() {
if (!option) return;
const screenWidth = window.innerWidth;
const detailsBox = document.getElementById('details-box');
const detailsWidth = detailsBox.offsetWidth;
const isMobile = screenWidth <= 500;
// Calculate center position with a smooth transition
// Mobile: extend layers to fill container, keeping same hole size as desktop
// Hole stays at 20%, layers scaled to fill remaining 80% (vs 55% on desktop)
const level1Inner = '20%';
const level1Outer = isMobile ? '56%' : '45%';
const level2Outer = isMobile ? '93%' : '70%';
const level3Outer = isMobile ? '100%' : '75%';
const outerRadius = isMobile ? '100%' : '95%';
// Update layer proportions
option.series.levels[1].r0 = level1Inner;
option.series.levels[1].r = level1Outer;
option.series.levels[2].r0 = level1Outer;
option.series.levels[2].r = level2Outer;
option.series.levels[3].r0 = level2Outer;
option.series.levels[3].r = level3Outer;
option.series.radius = [0, outerRadius];
// Update level 3 labels: hide on mobile, show on desktop (with conditions)
if (isMobile) {
option.series.levels[3].label.show = false;
option.series.levels[3].label.position = 'inside';
} else if (screenWidth < 950) {
option.series.levels[3].label.show = false;
option.series.levels[3].label.position = 'outside';
} else {
option.series.levels[3].label.show = function(param) {
return param.percent > 0.000;
};
option.series.levels[3].label.position = 'outside';
}
// Calculate center position
let centerPosition;
if (screenWidth < 950)
option.series.levels[3].label.show=false;
else option.series.levels[3].label.show=true;
centerPosition = 50;
if (screenWidth >= 1000) {
centerPosition = 50;
} else if (screenWidth >= 640) {
const transitionProgress = (screenWidth - 640) / 360;
centerPosition = 40 + (transitionProgress * 10);
} else if (isMobile) {
centerPosition = 50;
} else {
centerPosition = 40;
}
// Update chart center position
option.series.center = [`${centerPosition}%`, '50%'];

View File

@ -359,6 +359,10 @@ body {
.top-item-amount {
min-width: 80px;
}
.center-label {
display: none;
}
}
@media (max-width: 500px) {
@ -394,10 +398,6 @@ body {
gap: 4px;
}
.center-label {
display: none;
}
.container {
padding: 15px 10px;
}
@ -405,6 +405,12 @@ body {
.chart-wrapper {
height: 95vw;
}
#chart-container {
background-color: transparent;
box-shadow: none;
border-radius: 0;
}
}
@media (max-width: 400px) {
@ -432,7 +438,7 @@ body {
}
.chart-wrapper {
height: 98vw;
height: 100vw;
}
.details-header {