Compare commits
1 Commits
main
...
claude/vig
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b5008b2f5d |
@ -298,7 +298,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer-bottom">
|
<div class="footer-bottom">
|
||||||
<p>© 2025 밍글 스튜디오. All rights reserved.</p>
|
<p>© <script>document.write(new Date().getFullYear())</script> 밍글 스튜디오. All rights reserved.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@ -27,7 +27,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer-bottom">
|
<div class="footer-bottom">
|
||||||
<p>© 2025 밍글 스튜디오. All rights reserved.</p>
|
<p>© <script>document.write(new Date().getFullYear())</script> 밍글 스튜디오. All rights reserved.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
@ -226,7 +226,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer-bottom">
|
<div class="footer-bottom">
|
||||||
<p>© 2025 밍글 스튜디오. All rights reserved.</p>
|
<p>© <script>document.write(new Date().getFullYear())</script> 밍글 스튜디오. All rights reserved.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@ -326,10 +326,6 @@
|
|||||||
/* ========================================
|
/* ========================================
|
||||||
다크모드
|
다크모드
|
||||||
======================================== */
|
======================================== */
|
||||||
[data-theme="dark"] .page-header {
|
|
||||||
background: var(--dark-header-gradient);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] .info-grid {
|
[data-theme="dark"] .info-grid {
|
||||||
background: var(--dark-surface);
|
background: var(--dark-surface);
|
||||||
border: 1px solid var(--glass-border);
|
border: 1px solid var(--glass-border);
|
||||||
|
|||||||
@ -412,13 +412,6 @@ body {
|
|||||||
gap: var(--spacing-md);
|
gap: var(--spacing-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-bottom {
|
|
||||||
text-align: center;
|
|
||||||
padding-top: var(--spacing-xl);
|
|
||||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
||||||
color: rgba(255, 255, 255, 0.6);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 백업/기본 푸터 */
|
/* 백업/기본 푸터 */
|
||||||
.site-footer {
|
.site-footer {
|
||||||
background: var(--text-primary);
|
background: var(--text-primary);
|
||||||
@ -1501,7 +1494,7 @@ body {
|
|||||||
|
|
||||||
.streamer-card {
|
.streamer-card {
|
||||||
background: var(--bg-white);
|
background: var(--bg-white);
|
||||||
border-radius: var(--radius-xl);
|
border-radius: var(--border-radius-xl);
|
||||||
box-shadow: var(--shadow-md);
|
box-shadow: var(--shadow-md);
|
||||||
padding: var(--spacing-2xl);
|
padding: var(--spacing-2xl);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -1577,7 +1570,7 @@ body {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
border-radius: var(--radius-lg);
|
border-radius: var(--border-radius-lg);
|
||||||
font-size: var(--font-sm);
|
font-size: var(--font-sm);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
@ -1686,7 +1679,7 @@ body {
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
border-radius: var(--radius-lg);
|
border-radius: var(--border-radius-lg);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: var(--bg-light);
|
background: var(--bg-light);
|
||||||
box-shadow: var(--shadow-sm);
|
box-shadow: var(--shadow-sm);
|
||||||
|
|||||||
@ -507,10 +507,6 @@
|
|||||||
/* ========================================
|
/* ========================================
|
||||||
다크모드
|
다크모드
|
||||||
======================================== */
|
======================================== */
|
||||||
[data-theme="dark"] .page-header {
|
|
||||||
background: var(--dark-header-gradient);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] .contact-card {
|
[data-theme="dark"] .contact-card {
|
||||||
background: rgba(255, 255, 255, 0.04);
|
background: rgba(255, 255, 255, 0.04);
|
||||||
backdrop-filter: blur(20px);
|
backdrop-filter: blur(20px);
|
||||||
|
|||||||
@ -929,10 +929,6 @@
|
|||||||
/* ========================================
|
/* ========================================
|
||||||
다크모드
|
다크모드
|
||||||
======================================== */
|
======================================== */
|
||||||
[data-theme="dark"] .page-header {
|
|
||||||
background: var(--dark-header-gradient);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] .gallery-grid {
|
[data-theme="dark"] .gallery-grid {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -817,10 +817,6 @@
|
|||||||
/* ========================================
|
/* ========================================
|
||||||
다크모드
|
다크모드
|
||||||
======================================== */
|
======================================== */
|
||||||
[data-theme="dark"] .page-header {
|
|
||||||
background: var(--dark-header-gradient);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] .tab-btn {
|
[data-theme="dark"] .tab-btn {
|
||||||
color: var(--dark-text-secondary);
|
color: var(--dark-text-secondary);
|
||||||
border-color: var(--glass-border);
|
border-color: var(--glass-border);
|
||||||
|
|||||||
@ -419,10 +419,6 @@
|
|||||||
/* ========================================
|
/* ========================================
|
||||||
다크모드
|
다크모드
|
||||||
======================================== */
|
======================================== */
|
||||||
[data-theme="dark"] .page-header {
|
|
||||||
background: var(--dark-header-gradient);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] .faq-search {
|
[data-theme="dark"] .faq-search {
|
||||||
background: var(--dark-surface);
|
background: var(--dark-surface);
|
||||||
border: 1px solid var(--glass-border);
|
border: 1px solid var(--glass-border);
|
||||||
|
|||||||
@ -2127,10 +2127,6 @@
|
|||||||
/* ========================================
|
/* ========================================
|
||||||
다크모드
|
다크모드
|
||||||
======================================== */
|
======================================== */
|
||||||
[data-theme="dark"] .page-header {
|
|
||||||
background: var(--dark-header-gradient);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] .service-package {
|
[data-theme="dark"] .service-package {
|
||||||
background-color: var(--dark-surface);
|
background-color: var(--dark-surface);
|
||||||
border: 1px solid var(--glass-border);
|
border: 1px solid var(--glass-border);
|
||||||
|
|||||||
@ -215,7 +215,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer-bottom">
|
<div class="footer-bottom">
|
||||||
<p>© 2025 밍글 스튜디오. All rights reserved.</p>
|
<p>© <script>document.write(new Date().getFullYear())</script> 밍글 스튜디오. All rights reserved.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
BIN
images/sign/구슬요.webp
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
images/sign/얌하.webp
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 221 KiB After Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 248 KiB After Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 476 KiB After Width: | Height: | Size: 247 KiB |
|
Before Width: | Height: | Size: 384 KiB After Width: | Height: | Size: 202 KiB |
|
Before Width: | Height: | Size: 382 KiB After Width: | Height: | Size: 198 KiB |
|
Before Width: | Height: | Size: 459 KiB After Width: | Height: | Size: 240 KiB |
|
Before Width: | Height: | Size: 177 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 302 KiB After Width: | Height: | Size: 150 KiB |
|
Before Width: | Height: | Size: 214 KiB After Width: | Height: | Size: 889 KiB |
|
Before Width: | Height: | Size: 278 KiB After Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 204 KiB After Width: | Height: | Size: 862 KiB |
|
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 503 KiB After Width: | Height: | Size: 238 KiB |
82
index.html
@ -686,39 +686,45 @@
|
|||||||
|
|
||||||
<div class="signs-marquee">
|
<div class="signs-marquee">
|
||||||
<div class="signs-marquee-track">
|
<div class="signs-marquee-track">
|
||||||
<!-- 원본 10개 -->
|
<!-- 원본 12개 -->
|
||||||
<div class="sign-item"><img src="/images/sign/김마늘.jpg" alt="김마늘 사인"><span class="sign-name">김마늘</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/김마늘.jpg" alt="김마늘 사인"><span class="sign-name">김마늘</span></div>
|
||||||
<div class="sign-item"><img src="/images/sign/만타.jpg" alt="만타 사인"><span class="sign-name">만타</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/만타.jpg" alt="만타 사인"><span class="sign-name">만타</span></div>
|
||||||
<div class="sign-item"><img src="/images/sign/문모모.jpg" alt="문모모 사인"><span class="sign-name">문모모</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/문모모.jpg" alt="문모모 사인"><span class="sign-name">문모모</span></div>
|
||||||
<div class="sign-item"><img src="/images/sign/베니.jpg" alt="베니 사인"><span class="sign-name">베니</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/베니.jpg" alt="베니 사인"><span class="sign-name">베니</span></div>
|
||||||
<div class="sign-item"><img src="/images/sign/시에.jpg" alt="시에 사인"><span class="sign-name">시에</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/시에.jpg" alt="시에 사인"><span class="sign-name">시에</span></div>
|
||||||
<div class="sign-item"><img src="/images/sign/요나카.jpg" alt="요나카 사인"><span class="sign-name">요나카</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/얌하.webp" alt="얌하 사인"><span class="sign-name">얌하</span></div>
|
||||||
<div class="sign-item"><img src="/images/sign/이무지.jpg" alt="이무지 사인"><span class="sign-name">이무지</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/요나카.jpg" alt="요나카 사인"><span class="sign-name">요나카</span></div>
|
||||||
<div class="sign-item"><img src="/images/sign/지한이또.jpg" alt="지한이또 사인"><span class="sign-name">지한이또</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/이무지.jpg" alt="이무지 사인"><span class="sign-name">이무지</span></div>
|
||||||
<div class="sign-item"><img src="/images/sign/최또.jpg" alt="최또 사인"><span class="sign-name">최또</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/구슬요.webp" alt="구슬요 사인"><span class="sign-name">구슬요</span></div>
|
||||||
<div class="sign-item"><img src="/images/sign/치요.jpg" alt="치요 사인"><span class="sign-name">치요</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/지한이또.jpg" alt="지한이또 사인"><span class="sign-name">지한이또</span></div>
|
||||||
|
<div class="sign-item"><img loading="lazy" src="/images/sign/최또.jpg" alt="최또 사인"><span class="sign-name">최또</span></div>
|
||||||
|
<div class="sign-item"><img loading="lazy" src="/images/sign/치요.jpg" alt="치요 사인"><span class="sign-name">치요</span></div>
|
||||||
<!-- 복제 1 -->
|
<!-- 복제 1 -->
|
||||||
<div class="sign-item"><img src="/images/sign/김마늘.jpg" alt="김마늘 사인"><span class="sign-name">김마늘</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/김마늘.jpg" alt="김마늘 사인"><span class="sign-name">김마늘</span></div>
|
||||||
<div class="sign-item"><img src="/images/sign/만타.jpg" alt="만타 사인"><span class="sign-name">만타</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/만타.jpg" alt="만타 사인"><span class="sign-name">만타</span></div>
|
||||||
<div class="sign-item"><img src="/images/sign/문모모.jpg" alt="문모모 사인"><span class="sign-name">문모모</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/문모모.jpg" alt="문모모 사인"><span class="sign-name">문모모</span></div>
|
||||||
<div class="sign-item"><img src="/images/sign/베니.jpg" alt="베니 사인"><span class="sign-name">베니</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/베니.jpg" alt="베니 사인"><span class="sign-name">베니</span></div>
|
||||||
<div class="sign-item"><img src="/images/sign/시에.jpg" alt="시에 사인"><span class="sign-name">시에</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/시에.jpg" alt="시에 사인"><span class="sign-name">시에</span></div>
|
||||||
<div class="sign-item"><img src="/images/sign/요나카.jpg" alt="요나카 사인"><span class="sign-name">요나카</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/얌하.webp" alt="얌하 사인"><span class="sign-name">얌하</span></div>
|
||||||
<div class="sign-item"><img src="/images/sign/이무지.jpg" alt="이무지 사인"><span class="sign-name">이무지</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/요나카.jpg" alt="요나카 사인"><span class="sign-name">요나카</span></div>
|
||||||
<div class="sign-item"><img src="/images/sign/지한이또.jpg" alt="지한이또 사인"><span class="sign-name">지한이또</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/이무지.jpg" alt="이무지 사인"><span class="sign-name">이무지</span></div>
|
||||||
<div class="sign-item"><img src="/images/sign/최또.jpg" alt="최또 사인"><span class="sign-name">최또</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/구슬요.webp" alt="구슬요 사인"><span class="sign-name">구슬요</span></div>
|
||||||
<div class="sign-item"><img src="/images/sign/치요.jpg" alt="치요 사인"><span class="sign-name">치요</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/지한이또.jpg" alt="지한이또 사인"><span class="sign-name">지한이또</span></div>
|
||||||
|
<div class="sign-item"><img loading="lazy" src="/images/sign/최또.jpg" alt="최또 사인"><span class="sign-name">최또</span></div>
|
||||||
|
<div class="sign-item"><img loading="lazy" src="/images/sign/치요.jpg" alt="치요 사인"><span class="sign-name">치요</span></div>
|
||||||
<!-- 복제 2 -->
|
<!-- 복제 2 -->
|
||||||
<div class="sign-item"><img src="/images/sign/김마늘.jpg" alt="김마늘 사인"><span class="sign-name">김마늘</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/김마늘.jpg" alt="김마늘 사인"><span class="sign-name">김마늘</span></div>
|
||||||
<div class="sign-item"><img src="/images/sign/만타.jpg" alt="만타 사인"><span class="sign-name">만타</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/만타.jpg" alt="만타 사인"><span class="sign-name">만타</span></div>
|
||||||
<div class="sign-item"><img src="/images/sign/문모모.jpg" alt="문모모 사인"><span class="sign-name">문모모</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/문모모.jpg" alt="문모모 사인"><span class="sign-name">문모모</span></div>
|
||||||
<div class="sign-item"><img src="/images/sign/베니.jpg" alt="베니 사인"><span class="sign-name">베니</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/베니.jpg" alt="베니 사인"><span class="sign-name">베니</span></div>
|
||||||
<div class="sign-item"><img src="/images/sign/시에.jpg" alt="시에 사인"><span class="sign-name">시에</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/시에.jpg" alt="시에 사인"><span class="sign-name">시에</span></div>
|
||||||
<div class="sign-item"><img src="/images/sign/요나카.jpg" alt="요나카 사인"><span class="sign-name">요나카</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/얌하.webp" alt="얌하 사인"><span class="sign-name">얌하</span></div>
|
||||||
<div class="sign-item"><img src="/images/sign/이무지.jpg" alt="이무지 사인"><span class="sign-name">이무지</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/요나카.jpg" alt="요나카 사인"><span class="sign-name">요나카</span></div>
|
||||||
<div class="sign-item"><img src="/images/sign/지한이또.jpg" alt="지한이또 사인"><span class="sign-name">지한이또</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/이무지.jpg" alt="이무지 사인"><span class="sign-name">이무지</span></div>
|
||||||
<div class="sign-item"><img src="/images/sign/최또.jpg" alt="최또 사인"><span class="sign-name">최또</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/구슬요.webp" alt="구슬요 사인"><span class="sign-name">구슬요</span></div>
|
||||||
<div class="sign-item"><img src="/images/sign/치요.jpg" alt="치요 사인"><span class="sign-name">치요</span></div>
|
<div class="sign-item"><img loading="lazy" src="/images/sign/지한이또.jpg" alt="지한이또 사인"><span class="sign-name">지한이또</span></div>
|
||||||
|
<div class="sign-item"><img loading="lazy" src="/images/sign/최또.jpg" alt="최또 사인"><span class="sign-name">최또</span></div>
|
||||||
|
<div class="sign-item"><img loading="lazy" src="/images/sign/치요.jpg" alt="치요 사인"><span class="sign-name">치요</span></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -735,13 +741,13 @@
|
|||||||
<!-- 클라이언트 로고 마키 -->
|
<!-- 클라이언트 로고 마키 -->
|
||||||
<div class="partners-marquee">
|
<div class="partners-marquee">
|
||||||
<div class="partners-marquee-track">
|
<div class="partners-marquee-track">
|
||||||
<div class="partner-logo-item"><img src="/images/partners/메가메타.jpg" alt="메가메타 - 밍글 스튜디오 클라이언트" class="partner-logo"></div>
|
<div class="partner-logo-item"><img loading="lazy" src="/images/partners/메가메타.jpg" alt="메가메타 - 밍글 스튜디오 클라이언트" class="partner-logo"></div>
|
||||||
<div class="partner-logo-item"><img src="/images/partners/(주)세광종합기술단.png" alt="(주)세광종합기술단 - 밍글 스튜디오 클라이언트" class="partner-logo"></div>
|
<div class="partner-logo-item"><img loading="lazy" src="/images/partners/(주)세광종합기술단.png" alt="(주)세광종합기술단 - 밍글 스튜디오 클라이언트" class="partner-logo"></div>
|
||||||
<!-- 무한 루프용 복제 -->
|
<!-- 무한 루프용 복제 -->
|
||||||
<div class="partner-logo-item"><img src="/images/partners/메가메타.jpg" alt="메가메타" class="partner-logo"></div>
|
<div class="partner-logo-item"><img loading="lazy" src="/images/partners/메가메타.jpg" alt="메가메타" class="partner-logo"></div>
|
||||||
<div class="partner-logo-item"><img src="/images/partners/(주)세광종합기술단.png" alt="(주)세광종합기술단" class="partner-logo"></div>
|
<div class="partner-logo-item"><img loading="lazy" src="/images/partners/(주)세광종합기술단.png" alt="(주)세광종합기술단" class="partner-logo"></div>
|
||||||
<div class="partner-logo-item"><img src="/images/partners/메가메타.jpg" alt="메가메타" class="partner-logo"></div>
|
<div class="partner-logo-item"><img loading="lazy" src="/images/partners/메가메타.jpg" alt="메가메타" class="partner-logo"></div>
|
||||||
<div class="partner-logo-item"><img src="/images/partners/(주)세광종합기술단.png" alt="(주)세광종합기술단" class="partner-logo"></div>
|
<div class="partner-logo-item"><img loading="lazy" src="/images/partners/(주)세광종합기술단.png" alt="(주)세광종합기술단" class="partner-logo"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -839,7 +845,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer-bottom">
|
<div class="footer-bottom">
|
||||||
<p>© 2025 밍글 스튜디오. All rights reserved.</p>
|
<p>© <script>document.write(new Date().getFullYear())</script> 밍글 스튜디오. All rights reserved.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@ -107,6 +107,8 @@
|
|||||||
* 이벤트 리스너 설정
|
* 이벤트 리스너 설정
|
||||||
*/
|
*/
|
||||||
function setupEventListeners() {
|
function setupEventListeners() {
|
||||||
|
if (!elements.searchInput || !elements.imageModal || !elements.filterTags) return;
|
||||||
|
|
||||||
// 검색
|
// 검색
|
||||||
elements.searchInput.addEventListener('input', debounce((e) => {
|
elements.searchInput.addEventListener('input', debounce((e) => {
|
||||||
searchQuery = e.target.value.toLowerCase();
|
searchQuery = e.target.value.toLowerCase();
|
||||||
|
|||||||
68
js/common.js
@ -35,12 +35,14 @@ async function loadComponents() {
|
|||||||
if (headerPlaceholder) {
|
if (headerPlaceholder) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('components/header.html');
|
const response = await fetch('components/header.html');
|
||||||
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||||
const html = await response.text();
|
const html = await response.text();
|
||||||
headerPlaceholder.innerHTML = html;
|
headerPlaceholder.innerHTML = html;
|
||||||
initializeNavigation(); // 헤더 로드 후 네비게이션 초기화
|
initializeNavigation(); // 헤더 로드 후 네비게이션 초기화
|
||||||
initThemeToggle(); // 테마 토글 초기화
|
initThemeToggle(); // 테마 토글 초기화
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading header:', error);
|
console.error('Error loading header:', error);
|
||||||
|
headerPlaceholder.innerHTML = '<nav class="navbar"><div class="nav-container"><a href="/" class="nav-logo"><span>밍글 스튜디오</span></a></div></nav>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,11 +51,18 @@ async function loadComponents() {
|
|||||||
if (footerPlaceholder) {
|
if (footerPlaceholder) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('components/footer.html');
|
const response = await fetch('components/footer.html');
|
||||||
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||||
const html = await response.text();
|
const html = await response.text();
|
||||||
footerPlaceholder.innerHTML = html;
|
footerPlaceholder.innerHTML = html;
|
||||||
|
|
||||||
|
// 동적 푸터의 저작권 연도 자동 업데이트
|
||||||
|
const footerYear = footerPlaceholder.querySelector('.footer-bottom p');
|
||||||
|
if (footerYear) {
|
||||||
|
footerYear.innerHTML = `© ${new Date().getFullYear()} 밍글 스튜디오. All rights reserved.`;
|
||||||
|
}
|
||||||
|
|
||||||
// 동적 푸터 로드 성공 시 백업 푸터 숨기기
|
// 동적 푸터 로드 성공 시 백업 푸터 숨기기
|
||||||
const backupFooter = document.querySelector('footer.site-footer');
|
const backupFooter = document.querySelector('footer.site-footer#backupFooter');
|
||||||
if (backupFooter) {
|
if (backupFooter) {
|
||||||
backupFooter.style.display = 'none';
|
backupFooter.style.display = 'none';
|
||||||
}
|
}
|
||||||
@ -67,11 +76,15 @@ async function loadComponents() {
|
|||||||
// ========================================
|
// ========================================
|
||||||
// 네비게이션 기능
|
// 네비게이션 기능
|
||||||
// ========================================
|
// ========================================
|
||||||
|
let _navInitialized = false;
|
||||||
|
let _scrollListenerAdded = false;
|
||||||
|
|
||||||
function initializeNavigation() {
|
function initializeNavigation() {
|
||||||
const hamburger = document.querySelector('.hamburger');
|
const hamburger = document.querySelector('.hamburger');
|
||||||
const navMenu = document.querySelector('.nav-menu');
|
const navMenu = document.querySelector('.nav-menu');
|
||||||
|
|
||||||
if (hamburger && navMenu) {
|
if (hamburger && navMenu && !hamburger._listenerAdded) {
|
||||||
|
hamburger._listenerAdded = true;
|
||||||
hamburger.addEventListener('click', function() {
|
hamburger.addEventListener('click', function() {
|
||||||
const isActive = hamburger.classList.toggle('active');
|
const isActive = hamburger.classList.toggle('active');
|
||||||
navMenu.classList.toggle('active');
|
navMenu.classList.toggle('active');
|
||||||
@ -90,20 +103,23 @@ function initializeNavigation() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 스크롤 시 네비게이션 바 스타일 변경 (RAF 최적화)
|
// 스크롤 시 네비게이션 바 스타일 변경 (RAF 최적화) - 한 번만 등록
|
||||||
let scrollTicking = false;
|
if (!_scrollListenerAdded) {
|
||||||
const navbar = document.querySelector('.navbar');
|
_scrollListenerAdded = true;
|
||||||
window.addEventListener('scroll', () => {
|
let scrollTicking = false;
|
||||||
if (!scrollTicking) {
|
window.addEventListener('scroll', () => {
|
||||||
requestAnimationFrame(() => {
|
if (!scrollTicking) {
|
||||||
if (navbar) {
|
requestAnimationFrame(() => {
|
||||||
navbar.classList.toggle('scrolled', window.pageYOffset > 20);
|
const navbar = document.querySelector('.navbar');
|
||||||
}
|
if (navbar) {
|
||||||
scrollTicking = false;
|
navbar.classList.toggle('scrolled', window.pageYOffset > 20);
|
||||||
});
|
}
|
||||||
scrollTicking = true;
|
scrollTicking = false;
|
||||||
}
|
});
|
||||||
});
|
scrollTicking = true;
|
||||||
|
}
|
||||||
|
}, { passive: true });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
@ -292,9 +308,6 @@ function initLazyLoading() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================
|
|
||||||
// Export 함수들 (다른 스크립트에서 사용 가능)
|
|
||||||
// ========================================
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// 로딩 상태 관리
|
// 로딩 상태 관리
|
||||||
// ========================================
|
// ========================================
|
||||||
@ -327,12 +340,17 @@ function hidePageLoading() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function showComponentLoading(element, text = '로딩 중...') {
|
function showComponentLoading(element, text = '로딩 중...') {
|
||||||
element.innerHTML = `
|
const wrapper = document.createElement('div');
|
||||||
<div class="component-loading">
|
wrapper.className = 'component-loading';
|
||||||
<div class="loading-spinner"></div>
|
const spinner = document.createElement('div');
|
||||||
<div class="loading-text">${text}</div>
|
spinner.className = 'loading-spinner';
|
||||||
</div>
|
const label = document.createElement('div');
|
||||||
`;
|
label.className = 'loading-text';
|
||||||
|
label.textContent = text;
|
||||||
|
wrapper.appendChild(spinner);
|
||||||
|
wrapper.appendChild(label);
|
||||||
|
element.innerHTML = '';
|
||||||
|
element.appendChild(wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideComponentLoading(element, content) {
|
function hideComponentLoading(element, content) {
|
||||||
|
|||||||
@ -79,16 +79,18 @@ async function handleFormSubmit(e) {
|
|||||||
|
|
||||||
// 서버 전송 (mailto 기반 폴백)
|
// 서버 전송 (mailto 기반 폴백)
|
||||||
async function submitContactForm(data) {
|
async function submitContactForm(data) {
|
||||||
// mailto 링크로 이메일 클라이언트 열기
|
// 입력값에서 줄바꿈/캐리지리턴 제거 (이메일 헤더 인젝션 방지)
|
||||||
const subject = encodeURIComponent(`[밍글 스튜디오 문의] ${data.name || '웹사이트 문의'}`);
|
const sanitize = (str) => (str || '').replace(/[\r\n]/g, ' ').trim();
|
||||||
|
|
||||||
|
const subject = encodeURIComponent(`[밍글 스튜디오 문의] ${sanitize(data.name) || '웹사이트 문의'}`);
|
||||||
const body = encodeURIComponent(
|
const body = encodeURIComponent(
|
||||||
`이름: ${data.name || ''}\n` +
|
`이름: ${sanitize(data.name)}\n` +
|
||||||
`이메일: ${data.email || ''}\n` +
|
`이메일: ${sanitize(data.email)}\n` +
|
||||||
`전화번호: ${data.phone || ''}\n` +
|
`전화번호: ${sanitize(data.phone)}\n` +
|
||||||
`문의 유형: ${data.service || ''}\n` +
|
`문의 유형: ${sanitize(data.service)}\n` +
|
||||||
`\n문의 내용:\n${data.message || ''}`
|
`\n문의 내용:\n${(data.message || '').trim()}`
|
||||||
);
|
);
|
||||||
window.location.href = `mailto:mingle_studio@naver.com?subject=${subject}&body=${body}`;
|
window.location.href = `mailto:help@minglestudio.co.kr?subject=${subject}&body=${body}`;
|
||||||
return { success: true, message: '이메일 클라이언트가 열렸습니다.' };
|
return { success: true, message: '이메일 클라이언트가 열렸습니다.' };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,6 +209,11 @@ function isValidPhone(phone) {
|
|||||||
function formatPhoneNumber(e) {
|
function formatPhoneNumber(e) {
|
||||||
let value = e.target.value.replace(/[^0-9]/g, '');
|
let value = e.target.value.replace(/[^0-9]/g, '');
|
||||||
|
|
||||||
|
// 최대 11자리 제한 (한국 휴대폰 번호)
|
||||||
|
if (value.length > 11) {
|
||||||
|
value = value.slice(0, 11);
|
||||||
|
}
|
||||||
|
|
||||||
if (value.length >= 11) {
|
if (value.length >= 11) {
|
||||||
// 01X-XXXX-XXXX 형태
|
// 01X-XXXX-XXXX 형태
|
||||||
value = value.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3');
|
value = value.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3');
|
||||||
|
|||||||
218
js/gallery.js
@ -13,8 +13,24 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
function initGallery() {
|
function initGallery() {
|
||||||
const galleryItems = document.querySelectorAll('.gallery-item');
|
const galleryItems = document.querySelectorAll('.gallery-item');
|
||||||
|
|
||||||
|
// 레이지 로딩용 옵저버를 하나만 생성 (이미지마다 생성하지 않음)
|
||||||
|
let imageObserver = null;
|
||||||
|
if ('IntersectionObserver' in window) {
|
||||||
|
imageObserver = new IntersectionObserver((entries) => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
const img = entry.target;
|
||||||
|
img.src = img.dataset.src || img.src;
|
||||||
|
img.classList.remove('lazy');
|
||||||
|
imageObserver.unobserve(img);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
galleryItems.forEach((item, index) => {
|
galleryItems.forEach((item, index) => {
|
||||||
const img = item.querySelector('.gallery-img');
|
const img = item.querySelector('.gallery-img');
|
||||||
|
if (!img) return;
|
||||||
|
|
||||||
// 이미지 클릭 시 라이트박스 열기
|
// 이미지 클릭 시 라이트박스 열기
|
||||||
img.addEventListener('click', () => openLightbox(index));
|
img.addEventListener('click', () => openLightbox(index));
|
||||||
@ -26,30 +42,20 @@ function initGallery() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 레이지 로딩 구현
|
// 레이지 로딩 구현
|
||||||
if ('IntersectionObserver' in window) {
|
if (imageObserver && img.dataset.src) {
|
||||||
const imageObserver = new IntersectionObserver((entries) => {
|
imageObserver.observe(img);
|
||||||
entries.forEach(entry => {
|
|
||||||
if (entry.isIntersecting) {
|
|
||||||
const img = entry.target;
|
|
||||||
img.src = img.dataset.src || img.src;
|
|
||||||
img.classList.remove('lazy');
|
|
||||||
imageObserver.unobserve(img);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (img.dataset.src) {
|
|
||||||
imageObserver.observe(img);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 라이트박스 기능
|
// 라이트박스 기능
|
||||||
let currentImageIndex = 0;
|
let currentImageIndex = 0;
|
||||||
const galleryImages = document.querySelectorAll('.gallery-img');
|
let galleryImages = [];
|
||||||
|
|
||||||
function initLightbox() {
|
function initLightbox() {
|
||||||
|
// DOM 준비 후 갤러리 이미지 수집
|
||||||
|
galleryImages = document.querySelectorAll('.gallery-img');
|
||||||
|
|
||||||
// 라이트박스 HTML 생성
|
// 라이트박스 HTML 생성
|
||||||
const lightboxHTML = `
|
const lightboxHTML = `
|
||||||
<div id="lightbox" class="lightbox" role="dialog" aria-label="이미지 뷰어">
|
<div id="lightbox" class="lightbox" role="dialog" aria-label="이미지 뷰어">
|
||||||
@ -65,8 +71,10 @@ function initLightbox() {
|
|||||||
|
|
||||||
document.body.insertAdjacentHTML('beforeend', lightboxHTML);
|
document.body.insertAdjacentHTML('beforeend', lightboxHTML);
|
||||||
|
|
||||||
// ESC 키로 라이트박스 닫기
|
// ESC 키로 라이트박스 닫기 (라이트박스가 열려 있을 때만)
|
||||||
document.addEventListener('keydown', function(e) {
|
document.addEventListener('keydown', function(e) {
|
||||||
|
const lightbox = document.getElementById('lightbox');
|
||||||
|
if (!lightbox || !lightbox.classList.contains('active')) return;
|
||||||
if (e.key === 'Escape') closeLightbox();
|
if (e.key === 'Escape') closeLightbox();
|
||||||
if (e.key === 'ArrowLeft') previousImage();
|
if (e.key === 'ArrowLeft') previousImage();
|
||||||
if (e.key === 'ArrowRight') nextImage();
|
if (e.key === 'ArrowRight') nextImage();
|
||||||
@ -158,143 +166,45 @@ function initGalleryAnimations() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 갤러리 필터 기능 (향후 확장용)
|
|
||||||
function initGalleryFilters() {
|
|
||||||
const filterButtons = document.querySelectorAll('.filter-btn');
|
|
||||||
const galleryItems = document.querySelectorAll('.gallery-item');
|
|
||||||
|
|
||||||
filterButtons.forEach(btn => {
|
|
||||||
btn.addEventListener('click', function() {
|
|
||||||
// 활성 버튼 업데이트
|
|
||||||
filterButtons.forEach(b => b.classList.remove('active'));
|
|
||||||
this.classList.add('active');
|
|
||||||
|
|
||||||
const filter = this.dataset.filter;
|
|
||||||
|
|
||||||
galleryItems.forEach(item => {
|
|
||||||
if (filter === 'all' || item.dataset.category === filter) {
|
|
||||||
item.style.display = 'block';
|
|
||||||
setTimeout(() => {
|
|
||||||
item.style.opacity = '1';
|
|
||||||
item.style.transform = 'scale(1)';
|
|
||||||
}, 10);
|
|
||||||
} else {
|
|
||||||
item.style.opacity = '0';
|
|
||||||
item.style.transform = 'scale(0.8)';
|
|
||||||
setTimeout(() => {
|
|
||||||
item.style.display = 'none';
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 이미지 프리로딩
|
|
||||||
function preloadImages() {
|
|
||||||
galleryImages.forEach(img => {
|
|
||||||
const imagePreload = new Image();
|
|
||||||
imagePreload.src = img.src;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 갤러리 검색 기능 (향후 확장용)
|
|
||||||
function initGallerySearch() {
|
|
||||||
const searchInput = document.getElementById('gallery-search');
|
|
||||||
const galleryItems = document.querySelectorAll('.gallery-item');
|
|
||||||
|
|
||||||
if (searchInput) {
|
|
||||||
searchInput.addEventListener('input', function() {
|
|
||||||
const searchTerm = this.value.toLowerCase();
|
|
||||||
|
|
||||||
galleryItems.forEach(item => {
|
|
||||||
const caption = item.querySelector('.gallery-caption');
|
|
||||||
const alt = item.querySelector('.gallery-img').alt;
|
|
||||||
const text = (caption ? caption.textContent : '') + ' ' + alt;
|
|
||||||
|
|
||||||
if (text.toLowerCase().includes(searchTerm)) {
|
|
||||||
item.style.display = 'block';
|
|
||||||
} else {
|
|
||||||
item.style.display = 'none';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 이미지 로딩 상태 표시
|
|
||||||
function showGalleryLoading() {
|
|
||||||
const loading = document.querySelector('.gallery-loading');
|
|
||||||
if (loading) {
|
|
||||||
loading.style.display = 'block';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function hideGalleryLoading() {
|
|
||||||
const loading = document.querySelector('.gallery-loading');
|
|
||||||
if (loading) {
|
|
||||||
loading.style.display = 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 갤러리 그리드 리사이즈 최적화
|
|
||||||
let resizeTimeout;
|
|
||||||
window.addEventListener('resize', function() {
|
|
||||||
clearTimeout(resizeTimeout);
|
|
||||||
resizeTimeout = setTimeout(function() {
|
|
||||||
// 갤러리 그리드 재조정 로직
|
|
||||||
const galleryGrid = document.querySelector('.gallery-grid');
|
|
||||||
if (galleryGrid) {
|
|
||||||
galleryGrid.style.opacity = '0.8';
|
|
||||||
setTimeout(() => {
|
|
||||||
galleryGrid.style.opacity = '1';
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
}, 250);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 터치 이벤트 지원 (모바일) - 라이트박스에만 스코프 제한
|
// 터치 이벤트 지원 (모바일) - 라이트박스에만 스코프 제한
|
||||||
let touchStartX = 0;
|
// 리스너는 한 번만 등록하고, 라이트박스 활성 상태에서만 동작
|
||||||
let touchEndX = 0;
|
let lightboxTouchStartX = 0;
|
||||||
|
let lightboxTouchListenersAdded = false;
|
||||||
|
|
||||||
function handleLightboxTouchStart(e) {
|
function setupLightboxTouchListeners() {
|
||||||
touchStartX = e.changedTouches[0].screenX;
|
if (lightboxTouchListenersAdded) return;
|
||||||
}
|
const lightbox = document.getElementById('lightbox');
|
||||||
|
if (!lightbox) return;
|
||||||
|
lightboxTouchListenersAdded = true;
|
||||||
|
|
||||||
function handleLightboxTouchEnd(e) {
|
lightbox.addEventListener('touchstart', function(e) {
|
||||||
touchEndX = e.changedTouches[0].screenX;
|
if (!lightbox.classList.contains('active')) return;
|
||||||
const swipeThreshold = 50;
|
lightboxTouchStartX = e.changedTouches[0].screenX;
|
||||||
const diff = touchStartX - touchEndX;
|
});
|
||||||
|
|
||||||
if (Math.abs(diff) > swipeThreshold) {
|
lightbox.addEventListener('touchend', function(e) {
|
||||||
if (diff > 0) {
|
if (!lightbox.classList.contains('active')) return;
|
||||||
nextImage();
|
const touchEndX = e.changedTouches[0].screenX;
|
||||||
} else {
|
const swipeThreshold = 50;
|
||||||
previousImage();
|
const diff = lightboxTouchStartX - touchEndX;
|
||||||
|
|
||||||
|
if (Math.abs(diff) > swipeThreshold) {
|
||||||
|
if (diff > 0) {
|
||||||
|
nextImage();
|
||||||
|
} else {
|
||||||
|
previousImage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// openLightbox/closeLightbox에서 터치 리스너 관리를 위해 원본 함수 래핑
|
// openLightbox 래핑: 터치 리스너 초기화 보장
|
||||||
const _originalOpenLightbox = openLightbox;
|
const _originalOpenLightbox = openLightbox;
|
||||||
openLightbox = function(index) {
|
openLightbox = function(index) {
|
||||||
_originalOpenLightbox(index);
|
_originalOpenLightbox(index);
|
||||||
const lightbox = document.getElementById('lightbox');
|
setupLightboxTouchListeners();
|
||||||
if (lightbox) {
|
|
||||||
lightbox.addEventListener('touchstart', handleLightboxTouchStart);
|
|
||||||
lightbox.addEventListener('touchend', handleLightboxTouchEnd);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const _originalCloseLightbox = closeLightbox;
|
|
||||||
closeLightbox = function() {
|
|
||||||
const lightbox = document.getElementById('lightbox');
|
|
||||||
if (lightbox) {
|
|
||||||
lightbox.removeEventListener('touchstart', handleLightboxTouchStart);
|
|
||||||
lightbox.removeEventListener('touchend', handleLightboxTouchEnd);
|
|
||||||
}
|
|
||||||
_originalCloseLightbox();
|
|
||||||
};
|
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// 간단한 360도 파노라마 뷰어 - 좌우 스크롤 방식
|
// 간단한 360도 파노라마 뷰어 - 좌우 스크롤 방식
|
||||||
@ -378,8 +288,7 @@ class Easy360Viewer {
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
display: block;
|
display: block;
|
||||||
image-rendering: -webkit-optimize-contrast;
|
image-rendering: auto;
|
||||||
image-rendering: crisp-edges;
|
|
||||||
filter: contrast(1.05) saturate(1.1);
|
filter: contrast(1.05) saturate(1.1);
|
||||||
`;
|
`;
|
||||||
this.image.draggable = false;
|
this.image.draggable = false;
|
||||||
@ -408,18 +317,23 @@ class Easy360Viewer {
|
|||||||
// 컨테이너 크기 가져오기
|
// 컨테이너 크기 가져오기
|
||||||
const containerHeight = this.container.clientHeight;
|
const containerHeight = this.container.clientHeight;
|
||||||
const containerWidth = this.container.clientWidth;
|
const containerWidth = this.container.clientWidth;
|
||||||
const imageAspectRatio = this.image.naturalWidth / this.image.naturalHeight;
|
const naturalW = this.image.naturalWidth;
|
||||||
|
const naturalH = this.image.naturalHeight;
|
||||||
|
const imageAspectRatio = naturalW / naturalH;
|
||||||
|
|
||||||
// 모바일에서 줌 시 경계 문제 해결을 위한 더 정확한 크기 계산
|
// 컨테이너 높이에 맞추되, 원본 해상도를 초과하지 않도록 제한
|
||||||
let imageHeight = containerHeight * this.zoom;
|
let imageHeight = Math.min(containerHeight * this.zoom, naturalH);
|
||||||
let imageWidth = imageHeight * imageAspectRatio;
|
let imageWidth = imageHeight * imageAspectRatio;
|
||||||
|
|
||||||
// 360도 이미지는 매우 가로가 길므로 최소 크기 보장
|
// 최소 너비 보장: 컨테이너 1.5배 (3배까지 늘리면 흐릿해짐)
|
||||||
const minWidth = Math.max(containerWidth * 3, imageWidth);
|
// 단, 원본 너비를 초과하지 않도록 제한
|
||||||
imageWidth = minWidth;
|
const minWidth = Math.min(containerWidth * 1.5, naturalW);
|
||||||
imageHeight = imageWidth / imageAspectRatio;
|
if (imageWidth < minWidth) {
|
||||||
|
imageWidth = minWidth;
|
||||||
|
imageHeight = imageWidth / imageAspectRatio;
|
||||||
|
}
|
||||||
|
|
||||||
// 줌 레벨에 따른 크기 조정 (모바일 확대 시 경계 문제 해결)
|
// 줌 레벨에 따른 크기 조정
|
||||||
if (this.zoom > 1) {
|
if (this.zoom > 1) {
|
||||||
imageHeight = Math.max(containerHeight, imageHeight);
|
imageHeight = Math.max(containerHeight, imageHeight);
|
||||||
imageWidth = imageHeight * imageAspectRatio;
|
imageWidth = imageHeight * imageAspectRatio;
|
||||||
|
|||||||
@ -81,7 +81,7 @@ function initParallaxImages() {
|
|||||||
});
|
});
|
||||||
ticking = true;
|
ticking = true;
|
||||||
}
|
}
|
||||||
});
|
}, { passive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
@ -107,7 +107,7 @@ function initHeroScrollFade() {
|
|||||||
});
|
});
|
||||||
ticking = true;
|
ticking = true;
|
||||||
}
|
}
|
||||||
});
|
}, { passive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
@ -315,8 +315,9 @@ function initPortfolioTabs() {
|
|||||||
activePanel.classList.add('active');
|
activePanel.classList.add('active');
|
||||||
// 탭 전환 시 lazy loading 트리거
|
// 탭 전환 시 lazy loading 트리거
|
||||||
activePanel.querySelectorAll('iframe[data-src]').forEach(iframe => {
|
activePanel.querySelectorAll('iframe[data-src]').forEach(iframe => {
|
||||||
if (!iframe.src) {
|
if (iframe.dataset.src) {
|
||||||
iframe.src = iframe.dataset.src;
|
iframe.src = iframe.dataset.src;
|
||||||
|
iframe.removeAttribute('data-src');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -80,7 +80,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
|
|
||||||
// ESC 키로 닫기
|
// ESC 키로 닫기
|
||||||
document.addEventListener('keydown', function(e) {
|
document.addEventListener('keydown', function(e) {
|
||||||
if (e.key === 'Escape') {
|
const popup = document.getElementById('mainPopup');
|
||||||
|
if (e.key === 'Escape' && popup && popup.classList.contains('active')) {
|
||||||
closePopup();
|
closePopup();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -323,6 +323,8 @@ function initYouTubePlayers() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// pauseOtherVideos에서 사용할 수 있도록 전역에 저장
|
||||||
|
window.youtubePlayers = players;
|
||||||
return players;
|
return players;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,7 +337,8 @@ function extractVideoId(url) {
|
|||||||
// 플레이어 준비 완료
|
// 플레이어 준비 완료
|
||||||
function onPlayerReady(event) {
|
function onPlayerReady(event) {
|
||||||
// 플레이어 컨테이너에 로딩 완료 클래스 추가
|
// 플레이어 컨테이너에 로딩 완료 클래스 추가
|
||||||
const playerElement = event.target.getIframe();
|
const playerElement = event.target.getIframe?.();
|
||||||
|
if (!playerElement) return;
|
||||||
const wrapper = playerElement.closest('.video-wrapper');
|
const wrapper = playerElement.closest('.video-wrapper');
|
||||||
if (wrapper) {
|
if (wrapper) {
|
||||||
wrapper.classList.add('loaded');
|
wrapper.classList.add('loaded');
|
||||||
@ -348,7 +351,8 @@ function onPlayerReady(event) {
|
|||||||
|
|
||||||
// 플레이어 상태 변경
|
// 플레이어 상태 변경
|
||||||
function onPlayerStateChange(event) {
|
function onPlayerStateChange(event) {
|
||||||
const playerElement = event.target.getIframe();
|
const playerElement = event.target.getIframe?.();
|
||||||
|
if (!playerElement) return;
|
||||||
const videoCard = playerElement.closest('.video-card');
|
const videoCard = playerElement.closest('.video-card');
|
||||||
|
|
||||||
if (event.data === YT.PlayerState.PLAYING) {
|
if (event.data === YT.PlayerState.PLAYING) {
|
||||||
@ -374,13 +378,19 @@ function onPlayerStateChange(event) {
|
|||||||
// 플레이어 오류 처리
|
// 플레이어 오류 처리
|
||||||
function onPlayerError(event) {
|
function onPlayerError(event) {
|
||||||
console.error('YouTube player error:', event.data);
|
console.error('YouTube player error:', event.data);
|
||||||
const playerElement = event.target.getIframe();
|
const playerElement = event.target.getIframe?.();
|
||||||
|
if (!playerElement) return;
|
||||||
const wrapper = playerElement.closest('.video-wrapper');
|
const wrapper = playerElement.closest('.video-wrapper');
|
||||||
|
|
||||||
if (wrapper) {
|
if (wrapper) {
|
||||||
const errorDiv = document.createElement('div');
|
const errorDiv = document.createElement('div');
|
||||||
errorDiv.className = 'video-error';
|
errorDiv.className = 'video-error';
|
||||||
errorDiv.innerHTML = '비디오를 로드할 수 없습니다<br><small>네트워크 연결을 확인해주세요</small>';
|
const mainText = document.createElement('div');
|
||||||
|
mainText.textContent = '비디오를 로드할 수 없습니다';
|
||||||
|
const subText = document.createElement('small');
|
||||||
|
subText.textContent = '네트워크 연결을 확인해주세요';
|
||||||
|
errorDiv.appendChild(mainText);
|
||||||
|
errorDiv.appendChild(subText);
|
||||||
errorDiv.style.cssText = `
|
errorDiv.style.cssText = `
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
|
|||||||
@ -74,6 +74,8 @@
|
|||||||
* 이벤트 리스너 설정
|
* 이벤트 리스너 설정
|
||||||
*/
|
*/
|
||||||
function setupEventListeners() {
|
function setupEventListeners() {
|
||||||
|
if (!elements.searchInput || !elements.imageModal) return;
|
||||||
|
|
||||||
// 검색
|
// 검색
|
||||||
elements.searchInput.addEventListener('input', debounce((e) => {
|
elements.searchInput.addEventListener('input', debounce((e) => {
|
||||||
searchQuery = e.target.value.toLowerCase();
|
searchQuery = e.target.value.toLowerCase();
|
||||||
|
|||||||
15
js/qna.js
@ -143,7 +143,9 @@ function handleSearch(query) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const searchRegex = new RegExp(query, 'gi');
|
// 검색어 이스케이프 및 g 플래그 없이 생성 (.test()의 lastIndex 문제 방지)
|
||||||
|
const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
|
const searchRegex = new RegExp(escapedQuery, 'i');
|
||||||
|
|
||||||
faqItems.forEach(item => {
|
faqItems.forEach(item => {
|
||||||
const question = item.querySelector('.faq-question h3');
|
const question = item.querySelector('.faq-question h3');
|
||||||
@ -162,8 +164,8 @@ function handleSearch(query) {
|
|||||||
// 검색어 하이라이트
|
// 검색어 하이라이트
|
||||||
highlightSearchTerm(item, query);
|
highlightSearchTerm(item, query);
|
||||||
|
|
||||||
// 답변에 매칭되는 경우 자동으로 열기
|
// 답변에 매칭되는 경우 자동으로 열기 (이미 열려 있으면 무시)
|
||||||
if (answerMatch && !questionMatch) {
|
if (answerMatch && !questionMatch && !item.classList.contains('active')) {
|
||||||
toggleFAQ(item);
|
toggleFAQ(item);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -303,7 +305,6 @@ function hideSuggestions() {
|
|||||||
|
|
||||||
// 검색 결과 없음 표시
|
// 검색 결과 없음 표시
|
||||||
function showNoResults(query) {
|
function showNoResults(query) {
|
||||||
const safeQuery = query.replace(/[<>&"]/g, c => ({'<':'<','>':'>','&':'&','"':'"'})[c]);
|
|
||||||
let noResults = document.querySelector('.no-results');
|
let noResults = document.querySelector('.no-results');
|
||||||
if (!noResults) {
|
if (!noResults) {
|
||||||
noResults = document.createElement('div');
|
noResults = document.createElement('div');
|
||||||
@ -311,13 +312,13 @@ function showNoResults(query) {
|
|||||||
noResults.innerHTML = `
|
noResults.innerHTML = `
|
||||||
<div class="no-results-icon">🔍</div>
|
<div class="no-results-icon">🔍</div>
|
||||||
<h3>검색 결과가 없습니다</h3>
|
<h3>검색 결과가 없습니다</h3>
|
||||||
<p><strong>"${safeQuery}"</strong>와 관련된 질문을 찾을 수 없습니다.</p>
|
<p><strong></strong>와 관련된 질문을 찾을 수 없습니다.</p>
|
||||||
<p>다른 키워드로 검색해보시거나 <a href="contact.html">직접 문의</a>해 주세요.</p>
|
<p>다른 키워드로 검색해보시거나 <a href="contact.html">직접 문의</a>해 주세요.</p>
|
||||||
`;
|
`;
|
||||||
document.querySelector('.faq-list').appendChild(noResults);
|
document.querySelector('.faq-list').appendChild(noResults);
|
||||||
} else {
|
|
||||||
noResults.querySelector('p strong').textContent = `"${query}"`;
|
|
||||||
}
|
}
|
||||||
|
// textContent로 안전하게 삽입 (XSS 방지)
|
||||||
|
noResults.querySelector('p strong').textContent = `"${query}"`;
|
||||||
noResults.classList.add('active');
|
noResults.classList.add('active');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -659,7 +659,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer-bottom">
|
<div class="footer-bottom">
|
||||||
<p>© 2025 밍글 스튜디오. All rights reserved.</p>
|
<p>© <script>document.write(new Date().getFullYear())</script> 밍글 스튜디오. All rights reserved.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
2
qna.html
@ -446,7 +446,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer-bottom">
|
<div class="footer-bottom">
|
||||||
<p>© 2025 밍글 스튜디오. All rights reserved.</p>
|
<p>© <script>document.write(new Date().getFullYear())</script> 밍글 스튜디오. All rights reserved.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@ -760,7 +760,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer-bottom">
|
<div class="footer-bottom">
|
||||||
<p>© 2025 밍글 스튜디오. All rights reserved.</p>
|
<p>© <script>document.write(new Date().getFullYear())</script> 밍글 스튜디오. All rights reserved.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||