ESG 스크롤 복귀와 기술 역량 이미지 정렬·지도 표시 개선

- ESG 푸터에서 위로 스크롤하면 문서 영역이 먼저 복귀하도록 처리
- 섹션 전환 중 문서 내부 스크롤을 막아 상단 잘림 방지
- 기술 역량 이미지의 상단 여백과 가용 높이를 본문 기준으로 정렬
- 이미지 진입 애니메이션을 고려해 텍스트 겹침과 화면 이탈 방지
- 기술 역량 프로젝트 순서·설명 및 개별 이미지 배치 설정 정리
- 기술 역량 데이터에 맞춰 검색용 정적 HTML 동기화
- 지도 검색용 도로명 주소와 층·호수 안내를 분리해 건물 위치 핀 표시
- 지도 기본 확대 수준을 16으로 설정하고 한국어 표시 적용
- 회사 소개·조직도와 개인정보 동의 거부 안내 문구 정리

검증:
- JavaScript 구문 검사 및 git diff --check 통과
- ESG 세 문서의 푸터 복귀·내부 스크롤·키보드 이동 확인
- 기술 역량 29개 슬라이드의 데스크톱 이미지 정렬 및 모바일 이동 확인
- 본사·지사 지도 위치 핀, 상세 주소, 외부 지도 링크 확인
This commit is contained in:
2026-09-30 17:55:10 +09:00
parent 51932269c7
commit 239f61c506
7 changed files with 73 additions and 102 deletions

View File

@@ -176,6 +176,10 @@
function canScrollPolicy(target, direction) {
const panel = target.closest(".esg-policy-panel");
if (!panel || panel.hidden) return false;
const workspace = panel.closest(".esg-workspace");
// Restore the whole reader below the header before scrolling its document.
if (workspace && Math.abs(window.scrollY - sectionStop(workspace)) > 4) return false;
if (performance.now() < transitionUntil) return false;
const remaining = panel.scrollHeight - panel.clientHeight - panel.scrollTop;
return direction > 0 ? remaining > 2 : panel.scrollTop > 2;
}
@@ -238,10 +242,8 @@
<p>수집된 개인정보는 문의 처리 완료 후 1년간 보관하며, 보유 기간이 지나면 지체 없이 파기합니다.</p>
<h3>4. 정보 제공 및 처리 위탁</h3>
<p>제공된 정보는 문의 응대 목적으로 사용합니다. 외부 서비스에 처리를 위탁하는 경우 해당 서비스와 처리 범위를 운영 방침에 명시합니다.</p>
<h3>5. 동의 거부 및 정보 확인</h3>
<p>개인정보 수집 및 이용에 동의하지 않을 수 있습니다. 동의하지 않으면 온라인 문의를 전송할 수 없으며, 고객센터로 직접 연락할 수 있습니다. 제공한 정보의 확인, 수정, 삭제 요청도 고객센터로 문의할 수 있습니다.</p>
<h3>6. 개인정보 관련 문의</h3>
<p>주식회사 모비핀테크놀러지 · 고객센터 <a href="tel:0532140592">053-214-0592</a><br />평일 09:00 ~ 18:00</p>`,
<h3>5. 동의 거부</h3>
<p>개인정보 수집 및 이용에 동의하지 않을 수 있습니다. 다만, 동의하지 않을 경우 온라인 문의 서비스를 이용할 수 없습니다.</p>`,
},
sitemap: {
title: "사이트맵",
@@ -350,10 +352,11 @@
trigger.addEventListener("click", () => {
const office = trigger.dataset.mapTitle;
const address = trigger.dataset.mapAddress;
const query = encodeURIComponent(address);
// Search for the building; floor/unit details can prevent a map pin from resolving.
const query = encodeURIComponent(trigger.dataset.mapQuery || address);
title.textContent = `${office} 오시는 길`;
frame.title = `${office} 위치 지도`;
frame.src = `https://maps.google.com/maps?q=${query}&z=15&output=embed`;
frame.src = `https://maps.google.com/maps?q=${query}&z=16&hl=ko&output=embed`;
addressLabel.textContent = address;
mapLink.href = `https://www.google.com/maps/search/?api=1&query=${query}`;
dialog.showModal();
@@ -752,29 +755,43 @@
const titleBottom = featureTitle.getBoundingClientRect().bottom - stageBounds.top;
capabilitiesWorkbench.style.setProperty("--certificate-top", `${titleBottom + 24}px`);
}
const contentRight = featureContent.getBoundingClientRect().right + 32;
const contentBounds = featureContent.getBoundingClientRect();
const contentRight = contentBounds.right + 32;
const contentTop = contentBounds.top;
const contentBottom = stageBounds.bottom - parseFloat(window.getComputedStyle(stage).paddingBottom);
[featureObject, featureObjectSecondary].forEach((element) => {
if (!element) return;
element.style.removeProperty("width");
element.style.removeProperty("height");
element.style.removeProperty("--object-fit-x");
element.style.removeProperty("--object-fit-y");
if (!desktop || !hasDescription || !element.getAttribute("src")) return;
const style = window.getComputedStyle(element);
const width = parseFloat(style.width);
const height = parseFloat(style.height);
const available = Math.max(0, stageBounds.right - contentRight);
const availableHeight = Math.max(0, contentBottom - contentTop);
if (!width || !height) return;
// Fit the actual image ratio, not the potentially letterboxed CSS rectangle.
const naturalWidth = element.naturalWidth;
const naturalHeight = element.naturalHeight;
if (!naturalWidth || !naturalHeight) return;
const ratio = Math.min(width / naturalWidth, height / naturalHeight, available / naturalWidth);
const ratio = Math.min(width / naturalWidth, height / naturalHeight,
available / naturalWidth, availableHeight / naturalHeight);
element.style.width = `${naturalWidth * ratio}px`;
element.style.height = `${naturalHeight * ratio}px`;
const bounds = element.getBoundingClientRect();
const motionX = parseFloat(window.getComputedStyle(element).translate) || 0;
const settledLeft = bounds.left - motionX;
const fittedStyle = window.getComputedStyle(element);
const [motionX = 0, motionY = 0] = fittedStyle.translate.split(" ").map(parseFloat);
const transform = new DOMMatrixReadOnly(fittedStyle.transform);
const [originX, originY] = fittedStyle.transformOrigin.split(" ").map(parseFloat);
// Measure the final position, excluding entry translation and scale.
const settledLeft = bounds.left - motionX + (transform.a - 1) * originX;
const settledTop = bounds.top - motionY + (transform.d - 1) * originY;
const settledBottom = settledTop + naturalHeight * ratio;
element.style.setProperty("--object-fit-x", `${Math.max(0, contentRight - settledLeft)}px`);
element.style.setProperty("--object-fit-y", `${Math.max(contentTop - settledTop,
Math.min(0, contentBottom - settledBottom))}px`);
});
}