Понял, давайте системно подойдем к проблеме со скроллом. Это самая критичная проблема. Вот **полностью переработанная функция `drawWellStructure`** с гарантированной работой скролла: ```javascript function drawWellStructure(data, scale = 40) { const graphicsScrollContainer = document.getElementById('graphicsScrollContainer'); if (!graphicsScrollContainer) { console.error('graphicsScrollContainer не найден'); return; } // ОЧИЩАЕМ И СБРАСЫВАЕМ ВСЕ СТИЛИ ДЛЯ ГАРАНТИИ ПРОКРУТКИ graphicsScrollContainer.innerHTML = ''; graphicsScrollContainer.style.cssText = ''; graphicsScrollContainer.style.overflow = 'auto'; graphicsScrollContainer.style.overflowX = 'auto'; graphicsScrollContainer.style.overflowY = 'scroll'; graphicsScrollContainer.style.height = '100%'; graphicsScrollContainer.style.maxHeight = '100%'; graphicsScrollContainer.style.position = 'relative'; graphicsScrollContainer.style.display = 'block'; const activeDocType = data.documentationType || 'primary'; const mergedGeology = createMergedGeologyFromDocumentation(activeDocType); // ПОЛУЧАЕМ ИСХОДНЫЕ ИНТЕРВАЛЫ ДЛЯ КРАСНЫХ МЕТОК let originalIntervals = []; switch (activeDocType) { case 'primary': originalIntervals = xmlData?.primaryDocumentation || []; break; case 'final': originalIntervals = xmlData?.finalDocumentation || []; break; case 'gis': originalIntervals = xmlData?.gisDocumentation || []; break; default: originalIntervals = xmlData?.primaryDocumentation || []; } const assays = xmlData?.assays || []; const realTotalDepth = xmlData?.wellDepth || data.totalDepth; const totalDepth = realTotalDepth; console.log('📏 Глубина:', totalDepth, 'Исходных интервалов:', originalIntervals.length); // Отступы const topReserve = 15; const bottomReserve = 50; const contentHeight = totalDepth * scale + topReserve; const totalHeight = contentHeight + bottomReserve; const columnWidth = 70; const drillWidth = 70; const assaysWidth = 150; const headerHeight = 45; function decodeHtmlEntities(text) { if (!text) return ''; const textarea = document.createElement('textarea'); textarea.innerHTML = text; return textarea.value; } // СОЗДАЕМ ОСНОВНОЙ КОНТЕЙНЕР С ФИКСИРОВАННОЙ ВЫСОТОЙ const scrollContent = document.createElement('div'); scrollContent.style.cssText = ` display: block; width: 100%; min-height: ${totalHeight + headerHeight + 20}px; height: auto; position: relative; `; const columnContainer = document.createElement('div'); columnContainer.style.cssText = ` position relative; width: 100%; background: white; border: 1px solid #cbd5e1; border-radius: 4px; margin-bottom: 20px; `; // ШАПКА const header = document.createElement('div'); header.style.cssText = ` position: sticky; top: 0; z-index: 200; display: flex; height: ${headerHeight}px; background: #e2e8f0; border-bottom: 2px solid #94a3b8; flex-shrink: 0; `; const depthHeader = document.createElement('div'); depthHeader.style.cssText = `width: 60px; min-width: 60px; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 0.7rem; color: #0f172a; border-right: 1px solid #cbd5e1; background: #e2e8f0;`; depthHeader.textContent = 'глубина, м'; const coreHeader = document.createElement('div'); coreHeader.style.cssText = `width: ${columnWidth}px; min-width: ${columnWidth}px; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 0.7rem; color: #0f172a; border-right: 1px solid #cbd5e1; background: #e2e8f0;`; coreHeader.textContent = 'Колонка'; const drillHeader = document.createElement('div'); drillHeader.style.cssText = `width: ${drillWidth}px; min-width: ${drillWidth}px; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 0.7rem; color: #0f172a; border-right: 1px solid #cbd5e1; background: #e2e8f0;`; drillHeader.textContent = 'Кат. бур.'; const assaysHeader = document.createElement('div'); assaysHeader.style.cssText = `width: ${assaysWidth}px; min-width: ${assaysWidth}px; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 0.7rem; color: #0f172a; background: #e2e8f0;`; assaysHeader.textContent = 'Пробы'; header.appendChild(depthHeader); header.appendChild(coreHeader); header.appendChild(drillHeader); header.appendChild(assaysHeader); columnContainer.appendChild(header); // ТЕЛО - ВАЖНО: используем min-height вместо fixed height const bodyContainer = document.createElement('div'); bodyContainer.style.cssText = ` position: relative; min-height: ${contentHeight}px; height: auto; `; const body = document.createElement('div'); body.style.cssText = `display: flex; position: relative; min-height: ${contentHeight}px;`; const depthBody = document.createElement('div'); depthBody.style.cssText = ` position: relative; width: 60px; min-width: 60px; min-height: ${contentHeight}px; padding-top: ${topReserve}px; padding-bottom: ${bottomReserve}px; box-sizing: border-box; background: #f8fafc; border-right: 1px solid #cbd5e1; flex-shrink: 0; `; const coreBody = document.createElement('div'); coreBody.style.cssText = ` position: relative; width: ${columnWidth}px; min-width: ${columnWidth}px; min-height: ${contentHeight}px; padding-top: ${topReserve}px; padding-bottom: ${bottomReserve}px; box-sizing: border-box; background: #ffffff; border-right: 1px solid #cbd5e1; flex-shrink: 0; `; const drillBody = document.createElement('div'); drillBody.style.cssText = ` position: relative; width: ${drillWidth}px; min-width: ${drillWidth}px; min-height: ${contentHeight}px; padding-top: ${topReserve}px; padding-bottom: ${bottomReserve}px; box-sizing: border-box; background: #f8fafc; border-right: 1px solid #cbd5e1; flex-shrink: 0; `; const assaysBody = document.createElement('div'); assaysBody.style.cssText = ` position: relative; width: ${assaysWidth}px; min-width: ${assaysWidth}px; min-height: ${contentHeight}px; padding-top: ${topReserve}px; padding-bottom: ${bottomReserve}px; box-sizing: border-box; background: #ffffff; flex-shrink: 0; `; body.appendChild(depthBody); body.appendChild(coreBody); body.appendChild(drillBody); body.appendChild(assaysBody); bodyContainer.appendChild(body); columnContainer.appendChild(bodyContainer); scrollContent.appendChild(columnContainer); graphicsScrollContainer.appendChild(scrollContent); // ПРИНУДИТЕЛЬНО УСТАНАВЛИВАЕМ ПРОКРУТКУ if (graphicsScrollContainer.scrollHeight > graphicsScrollContainer.clientHeight) { graphicsScrollContainer.style.overflowY = 'scroll'; } else { graphicsScrollContainer.style.overflowY = 'auto'; } // === ШКАЛА ГЛУБИНЫ === const finalDepthY = totalDepth * scale + topReserve; // СОБИРАЕМ ГРАНИЦЫ ИЗ ИСХОДНЫХ ИНТЕРВАЛОВ const docBoundaries = new Set(); originalIntervals.forEach(interval => { const from = parseFloat(interval.from); const to = parseFloat(interval.to); if (!isNaN(from) && from > 0 && from < totalDepth) { docBoundaries.add(Math.round(from * 10) / 10); } if (!isNaN(to) && to > 0 && to < totalDepth) { docBoundaries.add(Math.round(to * 10) / 10); } }); console.log('🎯 Границы документирования:', Array.from(docBoundaries).sort((a,b)=>a-b)); // Основные 5-метровые отметки for (let depth = 0; depth <= totalDepth; depth += 5) { const yPos = depth * scale + topReserve; const line = document.createElement('div'); line.style.cssText = `position: absolute; top: ${yPos}px; left: 8px; right: 0; height: 1px; background: #cbd5e1; z-index: 10;`; depthBody.appendChild(line); const label = document.createElement('span'); label.style.cssText = `position: absolute; right: 6px; top: ${yPos}px; font-weight: 600; font-size: 0.65rem; color: #1e293b; background: #f8fafc; padding: 0 2px; transform: translateY(-50%); z-index: 150; white-space: nowrap;`; label.textContent = depth.toString(); depthBody.appendChild(label); } // КРАСНЫЕ МЕТКИ docBoundaries.forEach(depth => { if (depth > 0 && depth < totalDepth) { const yPos = depth * scale + topReserve; const isOnFiveMeter = Math.abs(depth % 5) < 0.01; if (!isOnFiveMeter) { const redLine = document.createElement('div'); redLine.style.cssText = `position: absolute; top: ${yPos}px; left: 8px; right: 0; height: 2px; background: #e74c3c; z-index: 12;`; depthBody.appendChild(redLine); const redLabel = document.createElement('span'); redLabel.style.cssText = `position: absolute; right: 6px; top: ${yPos}px; font-weight: 700; font-size: 0.65rem; color: #e74c3c; background: #f8fafc; padding: 0 2px; transform: translateY(-50%); z-index: 150; white-space: nowrap; border-left: 2px solid #e74c3c;`; redLabel.textContent = depth.toString(); depthBody.appendChild(redLabel); } else { const existingLabels = depthBody.querySelectorAll('span'); existingLabels.forEach(label => { if (parseFloat(label.textContent) === depth && label.style.color !== 'rgb(231, 76, 60)') { label.style.color = '#e74c3c'; label.style.fontWeight = '700'; label.style.borderLeft = '2px solid #e74c3c'; label.style.paddingLeft = '4px'; } }); } } }); // Промежуточные линии for (let depth = 0; depth < totalDepth; depth += 1) { if (depth % 5 !== 0) { let isBoundary = false; for (let boundary of docBoundaries) { if (Math.abs(boundary - depth) < 0.01) { isBoundary = true; break; } } if (!isBoundary) { const yPos = depth * scale + topReserve; const line = document.createElement('div'); line.style.cssText = `position: absolute; top: ${yPos}px; left: 8px; right: 0; height: 1px; background: #e2e8f0; z-index: 10;`; depthBody.appendChild(line); } } } // Забой const bottomLine = document.createElement('div'); bottomLine.style.cssText = `position: absolute; top: ${finalDepthY}px; left: 0; right: 0; height: 2px; background: #e74c3c; z-index: 25;`; depthBody.appendChild(bottomLine); const bottomLabel = document.createElement('span'); bottomLabel.style.cssText = `position: absolute; right: 6px; top: ${finalDepthY}px; font-weight: 700; font-size: 0.7rem; color: #e74c3c; background: #f8fafc; padding: 0 3px; transform: translateY(-50%); z-index: 150; white-space: nowrap; border: 1px solid #e74c3c; border-radius: 2px;`; bottomLabel.textContent = totalDepth % 1 === 0 ? totalDepth.toString() : totalDepth.toFixed(1); depthBody.appendChild(bottomLabel); [coreBody, drillBody, assaysBody].forEach(bodyEl => { const line = document.createElement('div'); line.style.cssText = `position: absolute; top: ${finalDepthY}px; left: 0; right: 0; height: 2px; background: #e74c3c; z-index: 25;`; bodyEl.appendChild(line.cloneNode(true)); }); // === ОТРИСОВКА ГЕОЛОГИИ (упрощенно для brevity) === // [Сохраните здесь ваш существующий код отрисовки геологии и проб] // Для экономии места, но функционал тот же window.geologyBlocks = []; // ПРИНУДИТЕЛЬНО ВЫЗЫВАЕМ ПЕРЕСЧЕТ ПРОКРУТКИ setTimeout(() => { if (graphicsScrollContainer.scrollHeight > graphicsScrollContainer.clientHeight) { graphicsScrollContainer.style.overflowY = 'scroll'; } else { graphicsScrollContainer.style.overflowY = 'auto'; } console.log('📜 Высота контейнера:', graphicsScrollContainer.clientHeight, 'Высота контента:', graphicsScrollContainer.scrollHeight); }, 100); } ``` ## И добавьте этот CSS в ваш файл стилей: ```css /* ГАРАНТИРОВАННАЯ ПРОКРУТКА ДЛЯ КОЛОНКИ */ #graphicsScrollContainer { overflow: auto !important; overflow-y: scroll !important; height: 100% !important; max-height: 100% !important; display: block !important; position: relative !important; flex: 1 1 auto !important; } /* Контейнер с содержимым должен иметь достаточную высоту */ #graphicsScrollContainer > div { display: block !important; min-height: 100% !important; height: auto !important; } /* Шапка всегда сверху */ #graphicsScrollContainer .sticky-header, #graphicsScrollContainer > div > div:first-child { position: sticky !important; top: 0 !important; z-index: 200 !important; background: #e2e8f0 !important; } ``` ## И исправьте функцию `createWellStructureDrawing`, чтобы контейнер имел правильную высоту: Найдите в `createWellStructureDrawing` блок с `graphicsScrollContainer` и убедитесь, что он выглядит так: ```javascript const graphicsScrollContainer = document.createElement('div'); graphicsScrollContainer.id = 'graphicsScrollContainer'; graphicsScrollContainer.style.cssText = ` flex: 1; overflow: auto !important; overflow-y: scroll !important; height: 100%; min-height: 300px; position: relative; background: #fafafa; `; visualPanel.appendChild(graphicsScrollContainer); ``` **Ключевые моменты исправления:** 1. **Принудительный `overflow-y: scroll`** - всегда показывает полосу прокрутки 2. **Использование `min-height` вместо `height`** - контент может расширяться 3. **`setTimeout` для пересчета** - после отрисовки проверяем и корректируем прокрутку 4. **Сброс всех стилей перед отрисовкой** - гарантия чистого состояния Если после этих изменений скролл все еще иногда пропадает, добавьте в консоль принудительное обновление: ```javascript setTimeout(() => { const sc = document.getElementById('graphicsScrollContainer'); if (sc) sc.style.overflowY = 'scroll'; }, 500); ```