Files
aida_front/public/sketch_bar_chart.html
2025-11-20 10:11:52 +08:00

427 lines
13 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<title>style category</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
<style>
body {
margin: 0;
padding: 0;
font-family: sans-serif;
box-sizing: border-box;
background-color: #f9f9f9;
}
.viewport {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow: auto;
background-color: #f9f9f9;
cursor: grab;
scrollbar-width: thick;
scrollbar-color: #666 #eee;
}
.viewport::-webkit-scrollbar {
height: 16px;
width: 16px;
}
.viewport::-webkit-scrollbar-track {
background: #eee;
border-radius: 8px;
}
.viewport::-webkit-scrollbar-thumb {
background: #666;
border-radius: 8px;
border: 3px solid #eee;
}
.viewport::-webkit-scrollbar-thumb:hover {
background: #333;
}
.content-size {
position: relative;
width: 100%;
height: 100%;
}
.content {
padding: 30px;
transform-origin: top left;
width: max-content;
min-width: 100%;
}
h1 {
text-align: center;
font-size: 100px;
margin: 40px 0 60px;
font-weight: bold;
color: #333;
}
.chart-section {
margin-bottom: 80px;
background: #fff;
border-radius: 16px;
padding: 30px;
box-shadow: 0 6px 20px rgba(0,0,0,0.08);
}
h2 {
text-align: center;
font-size: 80px;
margin: 0 0 40px;
font-weight: bold;
color: #444;
}
.chart-container {
overflow-x: auto;
width: 100%;
min-width: 8000px; /* 适配20倍组间空隙大幅扩大容器宽度 */
min-height: 1900px;
scrollbar-width: thick;
scrollbar-color: #666 #eee;
padding-bottom: 100px;
}
/* 自定义滚动条 */
.chart-container::-webkit-scrollbar {
height: 16px;
}
.chart-container::-webkit-scrollbar-track {
background: #eee;
border-radius: 8px;
}
.chart-container::-webkit-scrollbar-thumb {
background: #666;
border-radius: 8px;
border: 3px solid #eee;
}
.chart-container::-webkit-scrollbar-thumb:hover {
background: #333;
}
canvas {
width: 100%;
height: 1800px !important;
background: #fff;
border: 2px solid #ddd;
border-radius: 12px;
box-shadow: 0 4px 16px rgba(0,0,0,0.1);
}
/* 响应式调整保持默认缩放30%,不额外调整 */
@media (max-width: 1200px) {
.chart-container {
min-height: 1900px;
}
canvas {
height: 1800px !important;
}
h1 {
font-size: 48px;
}
h2 {
font-size: 36px;
}
}
</style>
<!-- 禁止页面缩放的meta标签 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
</head>
<body>
<div id="viewport" class="viewport">
<div id="content-size" class="content-size">
<div id="content" class="content">
<h1>style category</h1>
<div class="chart-section">
<h2>1. ALL </h2>
<div class="chart-container">
<canvas id="chartAll"></canvas>
</div>
</div>
<div class="chart-section">
<h2>2. Male </h2>
<div class="chart-container">
<canvas id="chartMale"></canvas>
</div>
</div>
<div class="chart-section">
<h2>3. Female </h2>
<div class="chart-container">
<canvas id="chartFemale"></canvas>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const viewport = document.getElementById('viewport');
const content = document.getElementById('content');
const contentSize = document.getElementById('content-size');
let MIN_SCALE = 1;
const MAX_SCALE = 3;
let scale = 1;
function computeMinScale() {
const cw = content.offsetWidth;
const vw = viewport.clientWidth;
if (!cw || !vw) return 1;
return Math.min(MAX_SCALE, Math.max(0.2, vw / cw));
}
function updateWrapperSize() {
const w = content.offsetWidth * scale;
const h = content.offsetHeight * scale;
contentSize.style.width = w + 'px';
contentSize.style.height = h + 'px';
}
function applyScale(newScale, ax, ay) {
const prev = scale;
const target = Math.max(MIN_SCALE, Math.min(MAX_SCALE, newScale));
if (target === prev) return;
const ratio = target / prev;
const rect = viewport.getBoundingClientRect();
const anchorX = ax != null ? ax : viewport.scrollLeft + viewport.clientWidth / 2;
const anchorY = ay != null ? ay : viewport.scrollTop + viewport.clientHeight / 2;
scale = target;
content.style.transform = 'scale(' + scale + ')';
updateWrapperSize();
let newLeft = anchorX * ratio - viewport.clientWidth / 2;
let newTop = anchorY * ratio - viewport.clientHeight / 2;
const maxLeft = Math.max(0, contentSize.scrollWidth - viewport.clientWidth);
const maxTop = Math.max(0, contentSize.scrollHeight - viewport.clientHeight);
viewport.scrollLeft = Math.max(0, Math.min(maxLeft, newLeft));
viewport.scrollTop = Math.max(0, Math.min(maxTop, newTop));
}
function initScale() {
MIN_SCALE = computeMinScale();
scale = MIN_SCALE;
content.style.transform = 'scale(' + scale + ')';
updateWrapperSize();
}
initScale();
let dragging = false;
let sx = 0, sy = 0, sl = 0, st = 0;
viewport.addEventListener('mousedown', function(e) {
if (e.button !== 0) return;
dragging = true;
viewport.style.cursor = 'grabbing';
sx = e.clientX;
sy = e.clientY;
sl = viewport.scrollLeft;
st = viewport.scrollTop;
});
window.addEventListener('mousemove', function(e) {
if (!dragging) return;
viewport.scrollLeft = sl - (e.clientX - sx);
viewport.scrollTop = st - (e.clientY - sy);
});
window.addEventListener('mouseup', function() {
dragging = false;
viewport.style.cursor = '';
});
viewport.addEventListener('wheel', function(e) {
if (e.ctrlKey || e.metaKey) {
e.preventDefault();
const factor = e.deltaY < 0 ? 1.1 : 0.9;
const rect = viewport.getBoundingClientRect();
const ax = viewport.scrollLeft + (e.clientX - rect.left);
const ay = viewport.scrollTop + (e.clientY - rect.top);
const next = scale * factor;
applyScale(next < MIN_SCALE ? MIN_SCALE : next, ax, ay);
}
}, { passive: false });
window.addEventListener('resize', function() {
const prevMin = MIN_SCALE;
MIN_SCALE = computeMinScale();
if (scale < MIN_SCALE) {
applyScale(MIN_SCALE);
} else {
updateWrapperSize();
}
});
window.addEventListener('load', function() {
initScale();
});
});
// 共用配置 - 保持原有标签处理逻辑(下划线换行)
const labels = [
'ACADEMIC','BUSINESS','CASUAL','COUNTRY_STYLE','DOPAMINE','ETHNIC','FUTURISM',
'GOTHIC','LOLITA','MERLAD','MINIMALISM','NEW_CHINESE','OUTDOOR_FUNCTIONAL',
'POST_APOCALYPTIC','PREPPY','ROCK','ROMANTIC','SEXY','SWEET','WABI_SABI','Y2K','民族风'
].map(label => label.replace('_', '\n'));
// 所有数据(保持更新后的数值)
const allData = {
male_tops: [0,686,638,491,128,11,366,35,0,20,313,126,359,166,385,187,6,3,3,219,282,0],
male_bottoms: [0,280,199,141,132,1,43,49,0,13,178,93,279,10,96,25,0,5,0,163,239,0],
male_outwear: [0,2442,2147,1245,161,19,671,216,0,58,471,168,1001,319,803,367,11,1,1,278,631,0],
female_dress: [260,1030,4322,1158,256,0,397,340,488,279,1104,312,215,170,0,223,990,607,1438,293,292,159],
female_skirt: [408,662,1177,633,214,0,381,280,278,308,358,200,297,209,0,231,584,233,714,246,385,95],
female_blouse: [618,1275,3786,710,385,0,533,412,454,453,616,221,852,181,0,437,869,533,899,336,991,169],
female_outwear: [267,1255,740,453,178,0,418,160,101,301,273,172,712,172,0,245,198,13,159,280,500,76],
female_trousers: [171,782,1899,391,232,0,267,64,137,293,351,141,865,101,0,189,654,137,319,299,427,96]
};
// 颜色配置(保持原有颜色)
const colors = {
male_tops: '#1f77b4',
male_bottoms: '#ff7f0e',
male_outwear: '#2ca02c',
female_dress: '#d62728',
female_skirt: '#9467bd',
female_blouse: '#8c564b',
female_outwear: '#e377c2',
female_trousers: '#7f7f7f'
};
// 图表基础配置 - 核心修改categoryPercentage设为0.04原0.8的1/20扩大组间空隙20倍
const baseChartConfig = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'top',
labels: {
font: { size: 60, weight: 'bold' },
padding: 60,
boxWidth: 60
},
padding: { bottom: 60 }
},
tooltip: {
mode: 'index',
intersect: false,
padding: 30,
titleFont: { size: 60 },
bodyFont: { size: 42 },
boxPadding: 20,
cornerRadius: 12
}
},
scales: {
x: {
stacked: false,
title: {
display: true,
text: '',
font: { size: 1, weight: 'bold' },
padding: { top: 1, bottom: 1 }
},
ticks: {
maxRotation: 0, // 保持原有斜放角度向左下45度
minRotation: 0, // 强制固定角度
autoSkip: false,
font: { size: 28, weight: 'bold' },
padding: 150,
color: '#333',
lineHeight: 1.3,
align: 'right' // 斜放标签右对齐,提升可读性
},
grid: { display: false },
border: { width: 3 },
categoryPercentage: 0.5, // 核心修改原0.8 → 0.04组间空隙扩大20倍
barPercentage: 0.8, // 保持组内柱子宽度不变
},
y: {
beginAtZero: true,
title: {
display: true,
text: '',
font: { size: 1, weight: 'bold' },
padding: { right: 1 }
},
ticks: {
font: { size: 33, weight: 'bold' },
padding: 30,
color: '#333',
stepSize: 500 // 适配更大数值范围
},
grid: { color: '#e0e0e0', lineWidth: 3 },
border: { width: 3 }
}
},
layout: {
padding: { top: 60, right: 90, bottom: 100, left: 90 }
},
animation: { duration: 1500, easing: 'easeOutQuart' },
barThickness: 'flex',
maxBarThickness: 70,
minBarLength: 5
};
// 1. 创建综合图表
new Chart(document.getElementById('chartAll'), {
type: 'bar',
data: {
labels: labels,
datasets: Object.keys(allData).map(key => ({
label: key,
data: allData[key],
backgroundColor: colors[key]
}))
},
options: { ...baseChartConfig }
});
// 2. 创建男性分类图表
new Chart(document.getElementById('chartMale'), {
type: 'bar',
data: {
labels: labels,
datasets: [
{ label: 'male_tops', data: allData.male_tops, backgroundColor: colors.male_tops },
{ label: 'male_bottoms', data: allData.male_bottoms, backgroundColor: colors.male_bottoms },
{ label: 'male_outwear', data: allData.male_outwear, backgroundColor: colors.male_outwear }
]
},
options: {
...baseChartConfig,
scales: {
...baseChartConfig.scales,
x: {
...baseChartConfig.scales.x,
categoryPercentage: 0.35
},
y: {
...baseChartConfig.scales.y,
stepSize: 800 // 适配男性外套最大值2442
}
}
}
});
// 3. 创建女性分类图表
new Chart(document.getElementById('chartFemale'), {
type: 'bar',
data: {
labels: labels,
datasets: [
{ label: 'female_dress', data: allData.female_dress, backgroundColor: colors.female_dress },
{ label: 'female_skirt', data: allData.female_skirt, backgroundColor: colors.female_skirt },
{ label: 'female_blouse', data: allData.female_blouse, backgroundColor: colors.female_blouse },
{ label: 'female_outwear', data: allData.female_outwear, backgroundColor: colors.female_outwear },
{ label: 'female_trousers', data: allData.female_trousers, backgroundColor: colors.female_trousers }
]
},
options: {
...baseChartConfig,
scales: {
...baseChartConfig.scales,
x: {
...baseChartConfig.scales.x,
categoryPercentage: 0.28
},
y: {
...baseChartConfig.scales.y,
stepSize: 1000 // 适配女性连衣裙最大值4322
}
}
}
});
</script>
</body>
</html>