This commit is contained in:
msbfox
2026-06-14 10:39:56 +09:00
parent 4ee992e855
commit 0345b8cd08
514 changed files with 492 additions and 953 deletions
BIN
View File
Binary file not shown.
-3
View File
@@ -1,3 +0,0 @@
<?php if (!defined('_GNUBOARD_')) exit; ?>
YTozOntzOjQ6InRpbWUiO2k6MTc3MzE5NDI1ODtzOjM6InR0bCI7aToxMDgwMDtzOjQ6ImRhdGEiO047fQ==
-3
View File
@@ -1,3 +0,0 @@
<?php if (!defined('_GNUBOARD_')) exit; ?>
YTozOntzOjQ6InRpbWUiO2k6MTc4MDMyODU3NTtzOjM6InR0bCI7aToxMDgwMDtzOjQ6ImRhdGEiO047fQ==
-3
View File
@@ -1,3 +0,0 @@
<?php if (!defined('_GNUBOARD_')) exit; ?>
YTozOntzOjQ6InRpbWUiO2k6MTc3MzEyNDIzODtzOjM6InR0bCI7aToxMDgwMDtzOjQ6ImRhdGEiO2E6MTQ6e3M6NToiY29faWQiO3M6NzoiY29tcGFueSI7czo3OiJjb19odG1sIjtzOjE6IjEiO3M6MTA6ImNvX3N1YmplY3QiO3M6MTI6Iu2ajOyCrOyGjOqwnCI7czoxMDoiY29fY29udGVudCI7czo3OToiPHAgYWxpZ249Y2VudGVyPjxiPu2ajOyCrOyGjOqwnOyXkCDrjIDtlZwg64K07Jqp7J2EIOyeheugpe2VmOyLreyLnOyYpC48L2I+PC9wPiI7czoxMjoiY29fc2VvX3RpdGxlIjtzOjA6IiI7czoxNzoiY29fbW9iaWxlX2NvbnRlbnQiO3M6MDoiIjtzOjc6ImNvX3NraW4iO3M6NToiYmFzaWMiO3M6MTQ6ImNvX21vYmlsZV9za2luIjtzOjU6ImJhc2ljIjtzOjE3OiJjb190YWdfZmlsdGVyX3VzZSI7czoxOiIwIjtzOjY6ImNvX2hpdCI7czoxOiIwIjtzOjE1OiJjb19pbmNsdWRlX2hlYWQiO3M6MDoiIjtzOjE1OiJjb19pbmNsdWRlX3RhaWwiO3M6MDoiIjtzOjg6ImNvX2xldmVsIjtzOjE6IjEiO3M6MTI6ImNvX2xldmVsX29wdCI7czoxOiIxIjt9fQ==
-3
View File
@@ -1,3 +0,0 @@
<?php if (!defined('_GNUBOARD_')) exit; ?>
YTozOntzOjQ6InRpbWUiO2k6MTc4MDMyODU3NTtzOjM6InR0bCI7aToxMDgwMDtzOjQ6ImRhdGEiO2E6MTQ6e3M6NToiY29faWQiO3M6NzoiY29tcGFueSI7czo3OiJjb19odG1sIjtzOjE6IjEiO3M6MTA6ImNvX3N1YmplY3QiO3M6MTI6Iu2ajOyCrOyGjOqwnCI7czoxMDoiY29fY29udGVudCI7czo3OToiPHAgYWxpZ249Y2VudGVyPjxiPu2ajOyCrOyGjOqwnOyXkCDrjIDtlZwg64K07Jqp7J2EIOyeheugpe2VmOyLreyLnOyYpC48L2I+PC9wPiI7czoxMjoiY29fc2VvX3RpdGxlIjtzOjA6IiI7czoxNzoiY29fbW9iaWxlX2NvbnRlbnQiO3M6MDoiIjtzOjc6ImNvX3NraW4iO3M6NToiYmFzaWMiO3M6MTQ6ImNvX21vYmlsZV9za2luIjtzOjU6ImJhc2ljIjtzOjE3OiJjb190YWdfZmlsdGVyX3VzZSI7czoxOiIwIjtzOjY6ImNvX2hpdCI7czoxOiIwIjtzOjE1OiJjb19pbmNsdWRlX2hlYWQiO3M6MDoiIjtzOjE1OiJjb19pbmNsdWRlX3RhaWwiO3M6MDoiIjtzOjg6ImNvX2xldmVsIjtzOjE6IjEiO3M6MTI6ImNvX2xldmVsX29wdCI7czoxOiIxIjt9fQ==
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-3
View File
@@ -1,3 +0,0 @@
<?php if (!defined('_GNUBOARD_')) exit; ?>
YTozOntzOjQ6InRpbWUiO2k6MTc3MzE5NDI1ODtzOjM6InR0bCI7aToxMDgwMDtzOjQ6ImRhdGEiO2E6MTQ6e3M6NToiY29faWQiO3M6OToicHJvdmlzaW9uIjtzOjc6ImNvX2h0bWwiO3M6MToiMSI7czoxMDoiY29fc3ViamVjdCI7czoyMjoi7ISc67mE7IqkIOydtOyaqeyVveq0gCI7czoxMDoiY29fY29udGVudCI7czo4OToiPHAgYWxpZ249Y2VudGVyPjxiPuyEnOu5hOyKpCDsnbTsmqnslb3qtIDsl5Ag64yA7ZWcIOuCtOyaqeydhCDsnoXroKXtlZjsi63si5zsmKQuPC9iPjwvcD4iO3M6MTI6ImNvX3Nlb190aXRsZSI7czowOiIiO3M6MTc6ImNvX21vYmlsZV9jb250ZW50IjtzOjA6IiI7czo3OiJjb19za2luIjtzOjU6ImJhc2ljIjtzOjE0OiJjb19tb2JpbGVfc2tpbiI7czo1OiJiYXNpYyI7czoxNzoiY29fdGFnX2ZpbHRlcl91c2UiO3M6MToiMCI7czo2OiJjb19oaXQiO3M6MToiMCI7czoxNToiY29faW5jbHVkZV9oZWFkIjtzOjA6IiI7czoxNToiY29faW5jbHVkZV90YWlsIjtzOjA6IiI7czo4OiJjb19sZXZlbCI7czoxOiIxIjtzOjEyOiJjb19sZXZlbF9vcHQiO3M6MToiMSI7fX0=
-3
View File
@@ -1,3 +0,0 @@
<?php if (!defined('_GNUBOARD_')) exit; ?>
YTozOntzOjQ6InRpbWUiO2k6MTc4MDMyODU5MTtzOjM6InR0bCI7aToxMDgwMDtzOjQ6ImRhdGEiO2E6MTQ6e3M6NToiY29faWQiO3M6OToicHJvdmlzaW9uIjtzOjc6ImNvX2h0bWwiO3M6MToiMSI7czoxMDoiY29fc3ViamVjdCI7czoyMjoi7ISc67mE7IqkIOydtOyaqeyVveq0gCI7czoxMDoiY29fY29udGVudCI7czo4OToiPHAgYWxpZ249Y2VudGVyPjxiPuyEnOu5hOyKpCDsnbTsmqnslb3qtIDsl5Ag64yA7ZWcIOuCtOyaqeydhCDsnoXroKXtlZjsi63si5zsmKQuPC9iPjwvcD4iO3M6MTI6ImNvX3Nlb190aXRsZSI7czowOiIiO3M6MTc6ImNvX21vYmlsZV9jb250ZW50IjtzOjA6IiI7czo3OiJjb19za2luIjtzOjU6ImJhc2ljIjtzOjE0OiJjb19tb2JpbGVfc2tpbiI7czo1OiJiYXNpYyI7czoxNzoiY29fdGFnX2ZpbHRlcl91c2UiO3M6MToiMCI7czo2OiJjb19oaXQiO3M6MToiMCI7czoxNToiY29faW5jbHVkZV9oZWFkIjtzOjA6IiI7czoxNToiY29faW5jbHVkZV90YWlsIjtzOjA6IiI7czo4OiJjb19sZXZlbCI7czoxOiIxIjtzOjEyOiJjb19sZXZlbF9vcHQiO3M6MToiMSI7fX0=
@@ -1,3 +0,0 @@
<?php if (!defined('_GNUBOARD_')) exit; ?>
YTozOntzOjQ6InRpbWUiO2k6MTc4MDcyMTM5NTtzOjM6InR0bCI7aTozNjAwO3M6NDoiZGF0YSI7YToyOntzOjQ6Imxpc3QiO2E6MTp7aTowO2E6NjY6e3M6NToid3JfaWQiO3M6MToiMSI7czo2OiJ3cl9udW0iO3M6MjoiLTEiO3M6ODoid3JfcmVwbHkiO3M6MDoiIjtzOjk6IndyX3BhcmVudCI7czoxOiIxIjtzOjEzOiJ3cl9pc19jb21tZW50IjtzOjE6IjAiO3M6MTA6IndyX2NvbW1lbnQiO3M6MToiMCI7czoxNjoid3JfY29tbWVudF9yZXBseSI7czowOiIiO3M6NzoiY2FfbmFtZSI7czowOiIiO3M6OToid3Jfb3B0aW9uIjtzOjU6Imh0bWwyIjtzOjEwOiJ3cl9zdWJqZWN0IjtzOjM4OiLsoJztkogg7ISk7LmYIOq0gOugqCDrrLjsnZgg7J6F64uI64ukLiI7czoxMDoid3JfY29udGVudCI7czoyNDc6IjxwPuqyrOyggSDrrLjsnZgg65Oc66a964uI64ukLjwvcD4NCjxwPjxpbWcgc3JjPSJodHRwOi8vbG9jYWxob3N0L2RhdGEvZWRpdG9yLzI2MDYvMjAyNjA2MDYxMzQ5NDFfNzVjYTQ0NmQyOTU0ODNmM2Q1MjE0OGU3OTZiNmQ0Y2ZfOGx0ei5qcGciIGFsdD0iMjAyNjA2MDIyMDIyMTFfODE2NjFjNzJiMTgyYzRkZTIwNjNjNzM5NDRjYjhjNzVfbXlzbC5qcGciIHN0eWxlPSJ3aWR0aDogODAwcHg7IGhlaWdodDogMzUzcHg7IiAvPjwvcD4iO3M6MTI6IndyX3Nlb190aXRsZSI7czozNzoi7KCc7ZKILeyEpOy5mC3qtIDroKgt66y47J2YLeyeheuLiOuLpCI7czo4OiJ3cl9saW5rMSI7czowOiIiO3M6ODoid3JfbGluazIiO3M6MDoiIjtzOjEyOiJ3cl9saW5rMV9oaXQiO3M6MToiMCI7czoxMjoid3JfbGluazJfaGl0IjtzOjE6IjAiO3M6Njoid3JfaGl0IjtzOjE6IjciO3M6Nzoid3JfZ29vZCI7czoxOiIwIjtzOjk6IndyX25vZ29vZCI7czoxOiIwIjtzOjU6Im1iX2lkIjtzOjU6ImFkbWluIjtzOjc6IndyX25hbWUiO3M6MTU6Iuy1nOqzoOq0gOumrOyekCI7czo4OiJ3cl9lbWFpbCI7czowOiIiO3M6MTE6IndyX2hvbWVwYWdlIjtzOjA6IiI7czoxMToid3JfZGF0ZXRpbWUiO3M6MTk6IjIwMjYtMDUtMjUgMTA6MTg6MzQiO3M6Nzoid3JfZmlsZSI7czoxOiIxIjtzOjc6IndyX2xhc3QiO3M6MTk6IjIwMjYtMDUtMjUgMTA6MTg6MzQiO3M6NToid3JfaXAiO3M6MzoiOjoxIjtzOjE2OiJ3cl9mYWNlYm9va191c2VyIjtzOjA6IiI7czoxNToid3JfdHdpdHRlcl91c2VyIjtzOjA6IiI7czo0OiJ3cl8xIjtzOjk6IuyVhO2MjO2KuCI7czo0OiJ3cl8yIjtzOjA6IiI7czo0OiJ3cl8zIjtzOjA6IiI7czo0OiJ3cl80IjtzOjA6IiI7czo0OiJ3cl81IjtzOjA6IiI7czo0OiJ3cl82IjtzOjA6IiI7czo0OiJ3cl83IjtzOjA6IiI7czo0OiJ3cl84IjtzOjA6IiI7czo0OiJ3cl85IjtzOjA6IiI7czo1OiJ3cl8xMCI7czowOiIiO3M6MTQ6IndyX2lzX3Jlc2VydmVkIjtzOjE6IjAiO3M6MTc6IndyX3Jlc2VydmVkX3N0YXJ0IjtzOjE5OiIyMDI2LTA1LTI1IDAxOjE4OjM0IjtzOjE1OiJ3cl9yZXNlcnZlZF9lbmQiO3M6MTk6IjIwMjYtMDUtMjUgMDE6MTg6MzQiO3M6OToiaXNfbm90aWNlIjtiOjA7czo3OiJzdWJqZWN0IjtzOjM4OiLsoJztkogg7ISk7LmYIOq0gOugqCDrrLjsnZgg7J6F64uI64ukLiI7czoxMToiY29tbWVudF9jbnQiO3M6MDoiIjtzOjg6ImRhdGV0aW1lIjtzOjEwOiIyMDI2LTA1LTI1IjtzOjk6ImRhdGV0aW1lMiI7czo1OiIwNS0yNSI7czo0OiJsYXN0IjtzOjEwOiIyMDI2LTA1LTI1IjtzOjU6Imxhc3QyIjtzOjU6IjA1LTI1IjtzOjQ6Im5hbWUiO3M6NDY6IjxzcGFuIGNsYXNzPSJzdl9tZW1iZXIiPuy1nOqzoOq0gOumrOyekDwvc3Bhbj4iO3M6NToicmVwbHkiO2k6MDtzOjEwOiJpY29uX3JlcGx5IjtzOjA6IiI7czo5OiJpY29uX2xpbmsiO3M6MDoiIjtzOjEyOiJjYV9uYW1lX2hyZWYiO3M6NTM6Imh0dHA6Ly9sb2NhbGhvc3QvYmJzL2JvYXJkLnBocD9ib190YWJsZT1hYWFhJmFtcDtzY2E9IjtzOjQ6ImhyZWYiO3M6NTY6Imh0dHA6Ly9sb2NhbGhvc3QvYmJzL2JvYXJkLnBocD9ib190YWJsZT1hYWFhJmFtcDt3cl9pZD0xIjtzOjEyOiJjb21tZW50X2hyZWYiO3M6NTY6Imh0dHA6Ly9sb2NhbGhvc3QvYmJzL2JvYXJkLnBocD9ib190YWJsZT1hYWFhJmFtcDt3cl9pZD0xIjtzOjg6Imljb25fbmV3IjtzOjA6IiI7czo4OiJpY29uX2hvdCI7czowOiIiO3M6MTE6Imljb25fc2VjcmV0IjtzOjA6IiI7czo0OiJsaW5rIjthOjI6e2k6MTtOO2k6MjtOO31zOjk6ImxpbmtfaHJlZiI7YToyOntpOjE7czo2NDoiaHR0cDovL2xvY2FsaG9zdC9iYnMvbGluay5waHA/Ym9fdGFibGU9YWFhYSZhbXA7d3JfaWQ9MSZhbXA7bm89MSI7aToyO3M6NjQ6Imh0dHA6Ly9sb2NhbGhvc3QvYmJzL2xpbmsucGhwP2JvX3RhYmxlPWFhYWEmYW1wO3dyX2lkPTEmYW1wO25vPTIiO31zOjg6ImxpbmtfaGl0IjthOjI6e2k6MTtpOjA7aToyO2k6MDt9czo0OiJmaWxlIjthOjE6e3M6NToiY291bnQiO3M6MToiMSI7fXM6OToiaWNvbl9maWxlIjtzOjUwOiI8aSBjbGFzcz0iZmEgZmEtZG93bmxvYWQiIGFyaWEtaGlkZGVuPSJ0cnVlIj48L2k+ICI7czoxNjoiZmlyc3RfZmlsZV90aHVtYiI7YToyOntzOjc6ImJmX2ZpbGUiO3M6ODY6ImJlNDBjZDBjZDAxMjA5YjRhMjE0MjNmODVmZTI0NjExX1FPN2N2dEdTX2JmYmI1OTM0MjQxNzY0ZDQ2ZjE2ZjhjMGE2M2VlYWU5ZWFhMTY1ZDIuanBnIjtzOjEwOiJiZl9jb250ZW50IjtzOjA6IiI7fXM6ODoiYm9fdGFibGUiO3M6NDoiYWFhYSI7fX1zOjEwOiJib19zdWJqZWN0IjtzOjk6IuydtOuBjOumvCI7fX0=
@@ -1,3 +0,0 @@
<?php if (!defined('_GNUBOARD_')) exit; ?>
YTozOntzOjQ6InRpbWUiO2k6MTc3MzEwMTI1NjtzOjM6InR0bCI7aTozNjAwO3M6NDoiZGF0YSI7YToyOntzOjQ6Imxpc3QiO2E6MDp7fXM6MTA6ImJvX3N1YmplY3QiO3M6OToi6rCk65+s66asIjt9fQ==
-3
View File
File diff suppressed because one or more lines are too long
-3
View File
@@ -1,3 +0,0 @@
<?php if (!defined('_GNUBOARD_')) exit; ?>
YTozOntzOjQ6InRpbWUiO2k6MTc3NDkxNzI0NDtzOjM6InR0bCI7aTozNjAwO3M6NDoiZGF0YSI7YToyOntpOjA7YToxOTp7czo1OiJtZV9pZCI7czoyOiIxNSI7czoxMjoibWVfcGFyZW50X2lkIjtzOjE6IjAiO3M6ODoibWVfZGVwdGgiO3M6MToiMSI7czo3OiJtZV9jb2RlIjtzOjA6IiI7czo3OiJtZV9uYW1lIjtzOjEyOiLqsqzsoIHrrLjsnZgiO3M6NzoibWVfbGluayI7czo2MDoiaHR0cHM6Ly9kbnNzYXNoLmRza2ltLmtvem93LmNvbS9iYnMvYm9hcmQucGhwP2JvX3RhYmxlPW9yZGVyIjtzOjk6Im1lX3RhcmdldCI7czo0OiJzZWxmIjtzOjg6Im1lX29yZGVyIjtzOjE6IjAiO3M6NjoibWVfdXNlIjtzOjE6IjEiO3M6MTM6Im1lX21vYmlsZV91c2UiO3M6MToiMSI7czo4OiJtZV9sZXZlbCI7czoxOiIxIjtzOjEyOiJtZV9sZXZlbF9vcHQiO3M6MToiMSI7czoxMzoibWVfY3JlYXRlZF9ieSI7czo1OiJhZG1pbiI7czoxMzoibWVfdXBkYXRlZF9ieSI7czo1OiJhZG1pbiI7czoxMzoibWVfY3JlYXRlZF9hdCI7czoxOToiMjAyNi0wMS0yOSAxMzo1MzoyMCI7czoxMzoibWVfdXBkYXRlZF9hdCI7czoxOToiMjAyNi0wMy0xMyAxMDoxMToyNCI7czoxMzoibWVfZGVsZXRlZF9hdCI7TjtzOjExOiJvcmlfbWVfbGluayI7czo0NToiaHR0cDovL2xvY2FsaG9zdC9iYnMvYm9hcmQucGhwP2JvX3RhYmxlPW9yZGVyIjtzOjM6InN1YiI7YTowOnt9fWk6MTthOjE5OntzOjU6Im1lX2lkIjtzOjI6IjE5IjtzOjEyOiJtZV9wYXJlbnRfaWQiO3M6MToiMCI7czo4OiJtZV9kZXB0aCI7czoxOiIxIjtzOjc6Im1lX2NvZGUiO3M6MDoiIjtzOjc6Im1lX25hbWUiO3M6MTI6IuygnO2SiOyGjOqwnCI7czo3OiJtZV9saW5rIjtzOjYyOiJodHRwczovL2Ruc3Nhc2guZHNraW0ua296b3cuY29tL2Jicy9ib2FyZC5waHA/Ym9fdGFibGU9cHJvZHVjdCI7czo5OiJtZV90YXJnZXQiO3M6NDoic2VsZiI7czo4OiJtZV9vcmRlciI7czoxOiIxIjtzOjY6Im1lX3VzZSI7czoxOiIxIjtzOjEzOiJtZV9tb2JpbGVfdXNlIjtzOjE6IjEiO3M6ODoibWVfbGV2ZWwiO3M6MToiMSI7czoxMjoibWVfbGV2ZWxfb3B0IjtzOjE6IjEiO3M6MTM6Im1lX2NyZWF0ZWRfYnkiO3M6NToiYWRtaW4iO3M6MTM6Im1lX3VwZGF0ZWRfYnkiO3M6NToiYWRtaW4iO3M6MTM6Im1lX2NyZWF0ZWRfYXQiO3M6MTk6IjIwMjYtMDEtMjkgMTY6NDI6NTQiO3M6MTM6Im1lX3VwZGF0ZWRfYXQiO3M6MTk6IjIwMjYtMDMtMTMgMTA6Mzk6MTYiO3M6MTM6Im1lX2RlbGV0ZWRfYXQiO047czoxMToib3JpX21lX2xpbmsiO3M6NDc6Imh0dHA6Ly9sb2NhbGhvc3QvYmJzL2JvYXJkLnBocD9ib190YWJsZT1wcm9kdWN0IjtzOjM6InN1YiI7YTowOnt9fX19
@@ -8,7 +8,7 @@
.product-section .product-item { display: flex; align-items: center; gap: 20px; margin-bottom: 40px; position: relative; z-index: 10; } .product-section .product-item { display: flex; align-items: center; gap: 20px; margin-bottom: 40px; position: relative; z-index: 10; }
/* 2. 제품 이미지 기본 스타일 (200x200) */ /* 2. 제품 이미지 기본 스타일 (200x200) */
.product-img { width: 200px; height: 200px; overflow: visible; position: relative; } .product-img { width: 100px; height: 100px; overflow: visible; position: relative; }
.product-img img { width: 100%; height: 100%; object-fit: cover; border-radius: 12px; transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); box-shadow: 0 4px 15px rgba(0,0,0,0.3); } .product-img img { width: 100%; height: 100%; object-fit: cover; border-radius: 12px; transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); box-shadow: 0 4px 15px rgba(0,0,0,0.3); }
/* 💡 [모드 1] 메인 영역 내부 중앙 팝업 */ /* 💡 [모드 1] 메인 영역 내부 중앙 팝업 */
@@ -1,326 +0,0 @@
/* ============================================================
[최종 통합본] 메인 맵 전용 스타일 - 화살표 및 방향 보정 포함
============================================================ */
/* 1. 기본 레이아웃 */
.product-section {
display: flex;
justify-content: center;
align-items: flex-start;
gap: 40px;
padding: 40px 20px;
position: relative !important;
}
.product-section .product-side { display: flex; flex-direction: column; }
.product-section .product-item {
display: flex;
align-items: center;
gap: 20px;
margin-bottom: 40px;
position: relative;
z-index: 10;
}
/* 2. 제품 이미지 기본 스타일 */
.product-img { width: 200px; height: 200px; overflow: visible; position: relative; }
.product-img img {
width: 100%; height: 100%; object-fit: cover; border-radius: 12px;
transition: all 0.4s ease; box-shadow: 0 4px 15px rgba(0,0,0,0.3);
}
/* 💡 [모드 1] 메인 영역 내부 중앙 팝업 */
#active-popup-img {
position: absolute !important;
top: 50% !important; left: 50% !important;
transform: translate(-50%, -50%) scale(0.1) !important;
width: calc(100% - 40px) !important;
height: calc(100% - 40px) !important;
max-width: 1560px;
z-index: 9999 !important;
box-shadow: 0 40px 100px rgba(0,0,0,0.8);
border: 3px solid rgba(255,255,255,0.5);
object-fit: contain !important;
opacity: 0;
transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.3s ease;
pointer-events: none !important;
}
#active-popup-img.active { transform: translate(-50%, -50%) scale(1) !important; opacity: 1; }
/* 💡 점선 연결선 설정 (After: 선, Before: 화살표) */
.show-connector::after {
content: ''; position: absolute; top: 50%;
border-top: 3px dashed #ffd200; z-index: 500; pointer-events: none;
width: 0 !important;
transform: rotate(var(--line-angle, 0deg)) !important;
transition: width 0.5s ease;
display: block !important;
}
/* 💡 [추가] 점선 끝 화살표 머리 */
.show-connector::before {
content: ''; position: absolute; top: 50%;
width: 0; height: 0;
border-left: 12px solid #ffd200; /* 화살표 색상 */
border-top: 7px solid transparent;
border-bottom: 7px solid transparent;
z-index: 501; pointer-events: none;
opacity: 0;
/* 선의 끝 지점으로 화살표 이동 */
transform: rotate(var(--line-angle, 0deg)) translateX(var(--line-width, 0px)) translateY(-50%);
transition: opacity 0.3s ease 0.4s;
display: block !important;
}
.show-connector.active::after { width: var(--line-width, 0px) !important; animation: dash-move 1.5s linear infinite; }
.show-connector.active::before { opacity: 1; }
/* 💡 방향별 회전축 보정 */
/* 왼쪽 메뉴 -> 메인으로 (오른쪽 방향) */
.product-side.left .show-connector::after,
.product-side.left .show-connector::before {
left: 100% !important;
margin-left: 10px;
transform-origin: left center !important;
}
/* 오른쪽 메뉴 -> 메인으로 (왼쪽 방향) */
.product-side.right .show-connector::after,
.product-side.right .show-connector::before {
right: 100% !important; /* 👈 오른쪽 끝 기준 왼쪽으로 뻗음 */
left: auto !important;
margin-right: 10px;
transform-origin: right center !important; /* 👈 회전축을 우측 끝으로 고정 */
}
@keyframes dash-move { to { stroke-dashoffset: -20; } }
/* 3. 메인 이미지 및 블루 스폿 설정 */
.product-main { position: relative; width: 1600px; height: 1200px; flex-shrink: 0; overflow: hidden; }
.product-main img { width: 100%; height: 100%; object-fit: cover; }
.product-hotspot {
position: absolute !important; width: 45px !important; height: 45px !important;
background: rgba(0, 123, 255, 0.8) !important; border: 3px solid #fff !important;
border-radius: 50% !important; transform: translate(-50%, -50%) !important;
z-index: 500; display: none; animation: hotspot-pulse-blue 1.5s infinite;
}
@keyframes hotspot-pulse-blue {
0%, 100% { transform: translate(-50%, -50%) scale(1); opacity: 0.8; }
50% { transform: translate(-50%, -50%) scale(1.4); opacity: 1; }
}
/*잘되은거 아래*/
/*!* ============================================================
[최종 통합본] 메인 맵 전용 스타일 (products2.css)
============================================================ *!
!* 1. 기본 레이아웃 *!
.product-section {
display: flex;
justify-content: center;
align-items: flex-start;
gap: 40px;
padding: 40px 20px;
position: relative !important;
}
.product-section .product-side { display: flex; flex-direction: column; }
.product-section .product-item {
display: flex;
align-items: center;
gap: 20px;
margin-bottom: 40px;
position: relative;
z-index: 10;
}
!* 2. 제품 이미지 기본 스타일 *!
.product-img {
width: 200px;
height: 200px;
overflow: visible;
position: relative;
}
.product-img img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 12px;
transition: all 0.4s ease;
box-shadow: 0 4px 15px rgba(0,0,0,0.3);
}
!* 💡 [모드 1] 메인 영역 내부 중앙 팝업 *!
#active-popup-img {
position: absolute !important;
top: 50% !important;
left: 50% !important;
!* 시작 상태: 작고 투명하게 *!
transform: translate(-50%, -50%) scale(0.1) !important;
width: calc(100% - 40px) !important; !* 사방 20px 마진 *!
height: calc(100% - 40px) !important;
max-width: 1560px;
z-index: 9999 !important;
box-shadow: 0 40px 100px rgba(0,0,0,0.8);
border: 3px solid rgba(255,255,255,0.5);
object-fit: contain !important;
opacity: 0;
transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.3s ease;
!* 💡 [핵심] 마우스 이벤트를 무시하여 아이템 mouseleave 방해 금지 *!
pointer-events: none !important;
}
!* 활성화 상태: 정중앙 100% 크기 *!
#active-popup-img.active {
transform: translate(-50%, -50%) scale(1) !important;
opacity: 1;
}
!* 💡 [모드 2] 점선 연결선 *!
.show-connector::after {
content: '';
position: absolute;
top: 50%;
border-top: 3px dashed #ffd200;
z-index: 500;
pointer-events: none;
width: 0 !important; !* 시작은 길이 0 *!
transform: rotate(var(--line-angle, 0deg)) !important;
transform-origin: left center !important;
transition: width 0.5s ease;
display: block !important;
}
!* 활성화 상태: 계산된 길이만큼 뻗어나감 *!
.show-connector.active::after {
width: var(--line-width, 0px) !important;
animation: dash-move 1.5s linear infinite;
}
!* 방향별 연결선 시작 위치 *!
.product-side.left .show-connector::after { left: 100% !important; margin-left: 10px; }
.product-side.right .show-connector::after {
left: 0 !important;
margin-right: 10px;
transform-origin: right center !important;
}
@keyframes dash-move { to { stroke-dashoffset: -20; } }
!* 3. 메인 이미지 및 블루 스폿 설정 *!
.product-main {
position: relative;
width: 1600px;
height: 1200px;
flex-shrink: 0;
overflow: hidden;
}
.product-main img { width: 100%; height: 100%; object-fit: cover; }
.product-hotspot {
position: absolute !important;
width: 45px !important;
height: 45px !important;
background: rgba(0, 123, 255, 0.8) !important;
border: 3px solid #fff !important;
border-radius: 50% !important;
transform: translate(-50%, -50%) !important;
z-index: 500;
display: none;
animation: hotspot-pulse-blue 1.5s infinite;
}
@keyframes hotspot-pulse-blue {
0%, 100% { transform: translate(-50%, -50%) scale(1); opacity: 0.8; }
50% { transform: translate(-50%, -50%) scale(1.4); opacity: 1; }
}
.product-hotspot::after {
content: ''; position: absolute; top: 50%; left: 50%; width: 100%; height: 100%;
background-color: rgba(0, 123, 255, 0.3); border-radius: 50%;
transform: translate(-50%, -50%); animation: hotspot-ring 1.5s infinite;
}
@keyframes hotspot-ring {
0% { width: 45px; height: 45px; opacity: 0.5; }
100% { width: 120px; height: 120px; opacity: 0; }
}*/
/*
!* ============================================================
[최종 통합본] 메인 맵 전용 스타일 (products2.css)
============================================================ *!
!* 1. 컨테이너 및 레이아웃 *!
.product-section { display: flex; justify-content: center; align-items: flex-start; gap: 40px; padding: 40px 20px; }
.product-section .product-side { flex-direction: column; }
.product-section .product-item { display: flex; align-items: center; gap: 20px; margin-bottom: 40px; position: relative; z-index: 10; }
!* 2. 제품 이미지 기본 스타일 *!
.product-img { width: 200px; height: 200px; overflow: visible; position: relative; }
.product-img img { width: 100%; height: 100%; object-fit: cover; border-radius: 12px; transition: all 0.4s ease; box-shadow: 0 4px 15px rgba(0,0,0,0.3); }
!* 💡 [모드 1] 메인 영역(.product-main) 내부 중앙 팝업 *!
#active-popup-img {
position: absolute !important;
top: 50% !important; left: 50% !important;
transform: translate(-50%, -50%) !important;
width: calc(100% - 40px) !important; !* 💡 부모 내부 20px 마진 *!
height: calc(100% - 40px) !important;
max-width: 1560px;
z-index: 9999 !important;
box-shadow: 0 40px 100px rgba(0,0,0,0.8);
border: 3px solid rgba(255,255,255,0.5);
object-fit: contain !important;
pointer-events: none;
}
!* 💡 [모드 2] 점선 연결선 *!
.show-connector::after {
content: ''; position: absolute; top: 50%;
border-top: 3px dashed #ffd200; z-index: 500; pointer-events: none;
width: var(--line-width, 0px) !important;
transform: rotate(var(--line-angle, 0deg)) !important;
transform-origin: left center !important;
display: block !important;
}
.product-side.left .show-connector::after { left: 100% !important; margin-left: 10px; }
.product-side.right .show-connector::after { left: 0 !important; margin-right: 10px; transform-origin: right center !important; }
@keyframes dash-move { to { stroke-dashoffset: -20; } }
!* 3. 메인 이미지 및 블루 스폿 설정 *!
.product-main { position: relative; width: 1600px; height: 1200px; flex-shrink: 0; overflow: hidden; }
.product-main img { width: 100%; height: 100%; object-fit: cover; }
.product-hotspot {
position: absolute !important; width: 45px !important; height: 45px !important;
background: rgba(0, 123, 255, 0.8) !important; border: 3px solid #fff !important;
border-radius: 50% !important; transform: translate(-50%, -50%) !important;
z-index: 500; display: none; animation: hotspot-pulse-blue 1.5s infinite;
}
@keyframes hotspot-pulse-blue { 0%, 100% { transform: translate(-50%, -50%) scale(1); opacity: 0.8; } 50% { transform: translate(-50%, -50%) scale(1.4); opacity: 1; } }
.product-hotspot::after {
content: ''; position: absolute; top: 50%; left: 50%; width: 100%; height: 100%;
background-color: rgba(0, 123, 255, 0.3); border-radius: 50%;
transform: translate(-50%, -50%); animation: hotspot-ring 1.5s infinite;
}
@keyframes hotspot-ring { 0% { width: 45px; height: 45px; opacity: 0.5; } 100% { width: 120px; height: 120px; opacity: 0; } }
!* 💡 [모드 1] 메인 영역(.product-main) 내부 중앙 팝업 *!
#active-popup-img {
position: absolute !important;
top: 50% !important;
left: 50% !important;
transform: translate(-50%, -50%) scale(0.1) !important; !* 💡 초기 크기를 작게 시작 *!
width: calc(100% - 40px) !important; !* 메인 영역 너비의 90% *!
height: auto !important;
max-height: 90% !important;
z-index: 9999 !important;
box-shadow: 0 40px 100px rgba(0,0,0,0.8);
border: 3px solid rgba(255,255,255,0.5);
object-fit: contain !important;
pointer-events: none;
opacity: 0; !* 💡 초기 투명도 *!
transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.3s ease; !* 💡 애니메이션 추가 *!
}
!* 💡 [추가] 팝업이 활성화될 때 최종 크기로 *!
.product-section .product-main #active-popup-img.active {
transform: translate(-50%, -50%) scale(1) !important; !* 💡 최종 크기 *!
opacity: 1; !* 💡 최종 투명도 *!
}*/
@@ -1,124 +0,0 @@
@import url('https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.css');
/* --- [유지] 기본 및 공통 설정 --- */
.products-main-page { background-color: #0b0f1a; font-family: 'Pretendard', sans-serif; margin: 0; padding: 0; }
.main-container { width: 100%; overflow: hidden; }
.container { max-width: 1600px; margin: 0 auto; padding: 0 15px; box-sizing: border-box; }
.section-title { text-align: center; margin-bottom: 50px; }
.section-title h3 { font-size: 32px; font-weight: 700; color: #333; margin-bottom: 15px; position: relative; display: inline-block; }
.section-title h3::after { content: ''; display: block; width: 40px; height: 2px; background: #0054a6; margin: 15px auto 0; }
.section-title p { font-size: 16px; color: #666; }
/* --- [유지] 메인 비주얼 슬라이더 관련 (기존 소스) --- */
.main-visual { width: 100%; height: auto; position: relative; padding-top: 190px; box-sizing: border-box; }
.visual-slider, .swiper-wrapper, .swiper-slide { height: 100%; }
.visual-link { display: block; width: 100%; height: 100%; position: relative; }
.visual-img { width: 100%; height: 100%; background-size: cover; background-position: center; position: absolute; top: 0; left: 0; z-index: 1; }
.visual-layer-wrap { position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; z-index: 2; text-align: center; color: #fff; text-shadow: 2px 2px 8px rgba(0,0,0,0.5); }
.visual-title { font-size: 48px; font-weight: 700; color: #ffd200; margin: 0; }
.visual-divider { display: block; width: 50px; height: 2px; background-color: rgba(255, 255, 255, 0.5); margin: 30px auto; }
.visual-subtitle { font-size: 24px; font-weight: 500; line-height: 1.6; margin: 0; }
.main-visual .swiper-button-next, .main-visual .swiper-button-prev { width: 70px; height: 70px; background-color: rgba(0, 0, 0, 0.1); border-radius: 50%; color: #fff; position: absolute; top: calc(50% + 45px); transform: translateY(-50%); z-index: 10; display: flex; justify-content: center; align-items: center; cursor: pointer; }
.main-visual .swiper-button-prev { left: 20px; }
.main-visual .swiper-button-next { right: 20px; }
/* --- [유지] 히어로 섹션 관련 (기존 소스) --- */
.hero_container { padding: 120px 0 80px 0; background: #fff; }
.hero_container .text_area { position: relative; width: 100%; max-width: 1600px; padding: 0 20px; text-align: left; color: #111; line-height: 1.6; z-index: 2; }
/* ... (기존의 btn_box, main_title 등 생략하지만 파일에는 유지하셔야 합니다) ... */
/* --- [중요/수정] 팝업창 및 맵 전용 설정 --- */
/* 1. 최상위 섹션 기준점 (팝업 중앙 배치를 위해 필수) */
.product-map-section {
position: relative !important;
}
/* 2. 맵 및 핫스팟 보정 */
.map-wrapper {
position: relative; display: inline-block;
width: 1600px; overflow: hidden; border-radius: 24px;
margin: 0 auto; background: #161b26;
}
.map-container {
position: relative; width: 100%; height: 980px !important;
background-size: 100% 100% !important; cursor: crosshair;
}
.hotspot {
position: absolute; width: 22px !important; height: 22px !important;
background: rgba(59, 130, 246, 0.6); border: 2px solid #fff; border-radius: 50%;
transform: translate(-50%, -50%) !important; /* 💡 Y축 오차 해결 */
z-index: 10; cursor: pointer;
}
.hotspot::after { content: ''; position: absolute; top: 50%; left: 50%; width: 100%; height: 100%; background-color: rgba(0, 123, 255, 0.4); border-radius: 50%; transform: translate(-50%, -50%); animation: hotspot-pulse 2s infinite; }
@keyframes hotspot-pulse { 0% { width: 20px; height: 20px; opacity: 0.8; } 100% { width: 60px; height: 60px; opacity: 0; } }
/* 3. [최종] 팝업 통합 스타일 (중복 완전 제거) */
#hover-preview {
position: absolute !important; /* 💡 섹션 내 중앙 배치 */
left: 50% !important;
top: 50% !important;
transform: translate(-50%, -50%) !important;
display: none;
z-index: 1000;
background: rgba(15, 23, 42, 0.98) !important;
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 24px;
padding: 30px !important;
box-shadow: 0 50px 100px -20px rgba(0, 0, 0, 0.9);
/* 💡 사방 20px 마진 강제 */
max-width: calc(100% - 40px) !important;
max-height: calc(100% - 40px) !important;
width: 500px; /* 호버 시 기본 너비 */
transition: width 0.3s ease;
pointer-events: none;
}
/* 클릭 고정 시 */
#hover-preview.fixed-mode {
display: flex !important;
flex-direction: column !important; /* 💡 세로 배치를 강제하여 옆으로 벌어짐 해결 */
width: calc(100% - 40px) !important;
height: calc(100% - 40px) !important;
pointer-events: auto !important;
}
/* 4. 제품 그리드 및 아이템 레이아웃 */
.products-grid {
display: grid !important;
grid-template-columns: repeat(2, 1fr) !important;
gap: 20px;
flex: 1;
overflow-y: auto;
width: 100%;
}
.products-grid.single-item, .products-grid.full-html-mode {
grid-template-columns: 1fr !important;
}
/* 모든 아이템은 세로 정렬 */
.product-item.item-vertical {
display: flex !important;
flex-direction: column !important;
align-items: flex-start !important;
background: rgba(255, 255, 255, 0.05) !important;
padding: 20px;
border-radius: 16px;
color: #fff;
text-align: left !important;
}
.product-img-box { width: 100% !important; height: auto !important; max-height: 400px; background: #000; border-radius: 12px; overflow: hidden; display: flex; align-items: center; justify-content: center; margin-bottom: 15px; }
.product-img-box img { width: 100%; height: 100%; object-fit: contain; }
/* 5. 에디터 뷰 최적화 */
.editor-content-view { width: 100% !important; color: #eee !important; line-height: 1.7; margin-bottom: 20px; }
.editor-content-view img { max-width: 100% !important; width: auto !important; height: auto !important; display: block; margin: 15px auto; border-radius: 10px; }
/* 스크롤바 디자인 */
#preview-grid::-webkit-scrollbar { width: 6px; }
#preview-grid::-webkit-scrollbar-thumb { background: rgba(59, 130, 246, 0.5); border-radius: 10px; }
.content-divider { border: 0; border-top: 1px solid rgba(255,255,255,0.1); margin: 30px 0; }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 KiB

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 KiB

+158 -74
View File
@@ -3,7 +3,7 @@ if (!defined('_GNUBOARD_')) exit;
$skin_url = G5_THEME_URL . '/rb.layout/dnssash_main'; $skin_url = G5_THEME_URL . '/rb.layout/dnssash_main';
$today = G5_TIME_YMD; $today = G5_TIME_YMD;
function process_product_positions(&$db_products, $main_visual_data) { /*function process_product_positions(&$db_products, $main_visual_data) {
if (empty($db_products)) return; if (empty($db_products)) return;
foreach ($db_products as &$product) { foreach ($db_products as &$product) {
@@ -21,8 +21,54 @@ function process_product_positions(&$db_products, $main_visual_data) {
} }
} }
unset($product); unset($product);
} }*/
function process_product_positions(&$db_products, $main_visual_data) {
if (empty($db_products)) return;
// 1. $db_products를 title을 키로 하는 맵으로 변환하여 빠른 조회를 가능하게 합니다.
$db_products_map = [];
foreach ($db_products as $product) {
$db_products_map[$product['title']] = $product;
}
$ordered_products = [];
// 2. $main_visual_data의 순서에 따라 제품을 정렬하고 속성을 업데이트합니다.
foreach ($main_visual_data as $main_item) {
$title = $main_item['title'];
if (isset($db_products_map[$title])) {
$product = $db_products_map[$title];
// 기존 로직: pos 및 img_url 업데이트
if (isset($main_item['pos'])) {
$product['pos'] = $main_item['pos'];
}
if (isset($main_item['img_url'])) {
$product['img_url'] = $main_item['img_url'];
}
// main_visual_data에 xPct, yPct가 있다면 db_products의 값보다 우선합니다.
// if (isset($main_item['xPct'])) {
// $product['xPct'] = $main_item['xPct'];
// }
// if (isset($main_item['yPct'])) {
// $product['yPct'] = $main_item['yPct'];
// }
$ordered_products[] = $product;
unset($db_products_map[$title]); // 처리된 항목은 맵에서 제거합니다.
}
}
// 3. $main_visual_data에 포함되지 않은 나머지 제품들을 뒤에 추가합니다.
foreach ($db_products_map as $remaining_product) {
$ordered_products[] = $remaining_product;
}
// 4. 원본 $db_products 배열을 새로 정렬된 배열로 교체합니다.
$db_products = $ordered_products;
unset($product);
}
// 1. 게시판 데이터 로드 시도 // 1. 게시판 데이터 로드 시도
$bo_table = 'bconstruction'; // 게시판 ID $bo_table = 'bconstruction'; // 게시판 ID
$sql = " select * from {$g5['write_prefix']}$bo_table $sql = " select * from {$g5['write_prefix']}$bo_table
@@ -32,20 +78,22 @@ $sql = " select * from {$g5['write_prefix']}$bo_table
$result = sql_query($sql); $result = sql_query($sql);
$db_products = array(); $db_products = array();
$main_visual_product_data = [
["title" => "입면분할창","pos"=>"left", "page" => "20p", "desc" => "조망권과 환기를 극대화한 시스템", "img_url" => $skin_url . '/images/1.webp', 'xPct'=> 63.0, 'yPct'=> 24.0],
["title" => "프로젝트창","pos"=>"left", "page" => "48p", "desc" => "기밀성이 우수한 상부 개폐식 창호", "img_url" => $skin_url . '/images/2' . '.webp', "xPct" => 71.0, "yPct" => 30.3],
["title" => "소형단창,이중창","pos"=>"left", "page" => "36p", "desc" => "침실 소형 전용 슬라이딩 창호", "img_url" => $skin_url . '/images/3' . '.webp', "xPct" => 80.7, "yPct" => 43.6],
["title" => "학교창","pos"=>"left", "page" => "40p", "desc" => "안전 기능이 강화된 환기창", "img_url" => $skin_url . '/images/5' . '.webp', "xPct" => 78.7, "yPct" => 41.4],
["title" => "판넬창", "pos"=>"left","page" => "44p", "desc" => "벽체 마감용 내구성 우수 창호", "img_url" => $skin_url . '/images/4' . '.webp', "xPct" => 5.7, "yPct" => 91.4],
["title" => "대형단창","pos"=>"right", "page" => "32p", "desc" => "발코니 전용 단열 슬라이딩 단창", "img_url" => $skin_url . '/images/6' . '.webp', "xPct" => 25.6, "yPct" => 36.0], $main_visual_product_data = [
["title" => "대형이중","pos"=>"right", "page" => "28p", "desc" => "방음 및 풍압 견딤력이 우수한 이중창", "img_url" => $skin_url . '/images/7' . '.webp', "xPct" => 36.9, "yPct" => 29.8], ["title" => "입면분할", "pos"=>"left", "page" => "20p", "desc" => "조망권과 환기를 극대화한 시스템", "pruimg_url" => $skin_url . '/images/1.webp',"img_url" => $skin_url . '/images/1.webp', 'xPct'=> 46.4, 'yPct'=> 12.5],
["title" => "스마트 발코니 전용","pos"=>"right", "page" => "12p", "desc" => "외창 전용 고기밀 샷시", "img_url" => $skin_url . '/images/8' . '.webp', "xPct" => 23.0, "yPct" => 37.8], ["title" => "프로젝트","pos"=>"left", "page" => "48p", "desc" => "기밀성이 우수한 상부 개폐식 창호", "pruimg_url" => $skin_url . '/images/2.webp',"img_url" => $skin_url . '/images/2' . '.webp', "xPct" => 25.4, "yPct" => 24.8],
["title" => "스마트 이중","pos"=>"right", "page" => "24p", "desc" => "보안이 강화된 자동잠금 이중창", "img_url" => $skin_url . '/images/9' . '.webp', "xPct" => 46.7, "yPct" => 22.8], ["title" => "소형단","pos"=>"left", "page" => "36p", "desc" => "침실 소형 전용 슬라이딩 창호", "pruimg_url" => $skin_url . '/images/3.webp',"img_url" => $skin_url . '/images/3' . '.webp', "xPct" => 7.6, "yPct" => 33.0],
["title" => "발코니 확장 이중","pos"=>"right", "page" => "16p", "desc" => "거실 확장용 고단열 이중", "img_url" => $skin_url . '/images/10' . '.webp', "xPct" => 50.4, "yPct" => 20.8], ["title" => "학교","pos"=>"left", "page" => "40p", "desc" => "안전 기능이 강화된 환기", "pruimg_url" => $skin_url . '/images/5.webp',"img_url" => $skin_url . '/images/5' . '.webp' ],
["title" => "판넬창", "pos"=>"left", "page" => "44p", "desc" => "벽체 마감용 내구성 우수 창호", "pruimg_url" => $skin_url . '/images/4.webp',"img_url" => $skin_url . '/images/4' . '.webp'],
["title" => "대형단창","pos"=>"right", "page" => "32p", "desc" => "발코니 전용 단열 슬라이딩 단창", "pruimg_url" => $skin_url . '/images/6.webp',"img_url" => $skin_url . '/images/6' . '.webp', "xPct" => 62.1, "yPct" => 16.1],
["title" => "대형이중창","pos"=>"right", "page" => "28p", "desc" => "방음 및 풍압 견딤력이 우수한 이중창","pruimg_url" => $skin_url . '/images/7.webp', "img_url" => $skin_url . '/images/7' . '.webp', "xPct" => 62.1, "yPct" => 16.1],
["title" => "스마트 발코니 전용창","pos"=>"right", "page" => "12p", "desc" => "외창 전용 고기밀 샷시","pruimg_url" => $skin_url . '/images/8.webp', "img_url" => $skin_url . '/images/8' . '.webp', "xPct" => 62.1, "yPct" => 16.1],
["title" => "스마트 이중창","pos"=>"right", "right" => "24p", "desc" => "보안이 강화된 자동잠금 이중창","pruimg_url" => $skin_url . '/images/9.webp', "img_url" => $skin_url . '/images/9' . '.webp', "xPct" => 93.5, "yPct" => 41.6],
["title" => "발코니 확장 이중창","pos"=>"right", "page" => "16p", "desc" => "거실 확장용 고단열 이중창","pruimg_url" => $skin_url . '/images/10.webp', "img_url" => $skin_url . '/images/10' . '.webp', "xPct" => 93.5, "yPct" => 41.6],
]; ];
while($row = sql_fetch_array($result)) { while($row = sql_fetch_array($result)) {
@@ -78,21 +126,20 @@ while($row = sql_fetch_array($result)) {
); );
} }
//$db_products =null;
if (empty($db_products)) { if (empty($db_products)) {
$db_products = [ $db_products = [
["title" => "입면분할창", "pos"=>"left", "page" => "20p", "desc" => "조망권과 환기를 극대화한 시스템", "pruimg_url" => $skin_url . '/images/1.webp',"img_url" => $skin_url . '/images/1.webp', 'xPct'=> 63.0, 'yPct'=> 24.0,], ["title" => "입면분할창", "pos"=>"left", "page" => "20p", "desc" => "조망권과 환기를 극대화한 시스템", "pruimg_url" => $skin_url . '/images/1.webp',"img_url" => $skin_url . '/images/1.webp', 'xPct'=> 46.4, 'yPct'=> 12.5],
["title" => "프로젝트창","pos"=>"left", "page" => "48p", "desc" => "기밀성이 우수한 상부 개폐식 창호", "pruimg_url" => $skin_url . '/images/2.webp',"img_url" => $skin_url . '/images/2' . '.webp', "xPct" => 71.0, "yPct" => 30.3], ["title" => "프로젝트창","pos"=>"left", "page" => "48p", "desc" => "기밀성이 우수한 상부 개폐식 창호", "pruimg_url" => $skin_url . '/images/2.webp',"img_url" => $skin_url . '/images/2' . '.webp', "xPct" => 25.4, "yPct" => 24.8],
["title" => "소형단창,이중창","pos"=>"left", "page" => "36p", "desc" => "침실 소형 전용 슬라이딩 창호", "pruimg_url" => $skin_url . '/images/3.webp',"img_url" => $skin_url . '/images/3' . '.webp', "xPct" => 80.7, "yPct" => 43.6], ["title" => "소형단창","pos"=>"left", "page" => "36p", "desc" => "침실 소형 전용 슬라이딩 창호", "pruimg_url" => $skin_url . '/images/3.webp',"img_url" => $skin_url . '/images/3' . '.webp', "xPct" => 7.6, "yPct" => 33.0],
["title" => "학교창","pos"=>"left", "page" => "40p", "desc" => "안전 기능이 강화된 환기창", "pruimg_url" => $skin_url . '/images/5.webp',"img_url" => $skin_url . '/images/5' . '.webp', "xPct" => 78.7, "yPct" => 41.4], ["title" => "학교창","pos"=>"left", "page" => "40p", "desc" => "안전 기능이 강화된 환기창", "pruimg_url" => $skin_url . '/images/5.webp',"img_url" => $skin_url . '/images/5' . '.webp' ],
["title" => "판넬창", "pos"=>"left", "page" => "44p", "desc" => "벽체 마감용 내구성 우수 창호", "pruimg_url" => $skin_url . '/images/4.webp',"img_url" => $skin_url . '/images/4' . '.webp', "xPct" => 5.7, "yPct" => 91.4], ["title" => "판넬창", "pos"=>"left", "page" => "44p", "desc" => "벽체 마감용 내구성 우수 창호", "pruimg_url" => $skin_url . '/images/4.webp',"img_url" => $skin_url . '/images/4' . '.webp'],
["title" => "대형단창","pos"=>"right", "page" => "32p", "desc" => "발코니 전용 단열 슬라이딩 단창", "pruimg_url" => $skin_url . '/images/6.webp',"img_url" => $skin_url . '/images/6' . '.webp', "xPct" => 25.6, "yPct" => 36.0], ["title" => "대형단창","pos"=>"right", "page" => "32p", "desc" => "발코니 전용 단열 슬라이딩 단창", "pruimg_url" => $skin_url . '/images/6.webp',"img_url" => $skin_url . '/images/6' . '.webp', "xPct" => 62.1, "yPct" => 16.1],
["title" => "대형이중창","pos"=>"right", "page" => "28p", "desc" => "방음 및 풍압 견딤력이 우수한 이중창","pruimg_url" => $skin_url . '/images/7.webp', "img_url" => $skin_url . '/images/7' . '.webp', "xPct" => 36.9, "yPct" => 29.8], ["title" => "대형이중창","pos"=>"right", "page" => "28p", "desc" => "방음 및 풍압 견딤력이 우수한 이중창","pruimg_url" => $skin_url . '/images/7.webp', "img_url" => $skin_url . '/images/7' . '.webp', "xPct" => 62.1, "yPct" => 16.1],
["title" => "스마트 발코니 전용창","pos"=>"right", "page" => "12p", "desc" => "외창 전용 고기밀 샷시","pruimg_url" => $skin_url . '/images/8.webp', "img_url" => $skin_url . '/images/8' . '.webp', "xPct" => 23.0, "yPct" => 37.8], ["title" => "스마트 발코니 전용창","pos"=>"right", "page" => "12p", "desc" => "외창 전용 고기밀 샷시","pruimg_url" => $skin_url . '/images/8.webp', "img_url" => $skin_url . '/images/8' . '.webp', "xPct" => 62.1, "yPct" => 16.1],
["title" => "스마트 이중창","pos"=>"right", "right" => "24p", "desc" => "보안이 강화된 자동잠금 이중창","pruimg_url" => $skin_url . '/images/9.webp', "img_url" => $skin_url . '/images/9' . '.webp', "xPct" => 46.7, "yPct" => 22.8], ["title" => "스마트 이중창","pos"=>"right", "right" => "24p", "desc" => "보안이 강화된 자동잠금 이중창","pruimg_url" => $skin_url . '/images/9.webp', "img_url" => $skin_url . '/images/9' . '.webp', "xPct" => 93.5, "yPct" => 41.6],
["title" => "발코니 확장 이중창","pos"=>"right", "page" => "16p", "desc" => "거실 확장용 고단열 이중창","pruimg_url" => $skin_url . '/images/10.webp', "img_url" => $skin_url . '/images/10' . '.webp', "xPct" => 50.4, "yPct" => 20.8], ["title" => "발코니 확장 이중창","pos"=>"right", "page" => "16p", "desc" => "거실 확장용 고단열 이중창","pruimg_url" => $skin_url . '/images/10.webp', "img_url" => $skin_url . '/images/10' . '.webp', "xPct" => 93.5, "yPct" => 41.6],
]; ];
} else { } else {
@@ -119,6 +166,9 @@ function render_product_side($db_products, $side) {
data-x1="'.$item['xPct'].'" data-x1="'.$item['xPct'].'"
data-y1="'.$item['yPct'].'" data-y1="'.$item['yPct'].'"
data-img="'.$item['pruimg_url'].'" data-img="'.$item['pruimg_url'].'"
data-title="'.htmlspecialchars($item['title']).'"
data-desc="'.htmlspecialchars($item['desc']).'"
data-content="'.htmlspecialchars(isset($item['content']) ? $item['content'] : "").'"
'.$link_attr.'> '.$link_attr.'>
<div class="product-img"> <div class="product-img">
<img src="'.$item['img_url'].'" alt="'.htmlspecialchars($item['title']).'"> <img src="'.$item['img_url'].'" alt="'.htmlspecialchars($item['title']).'">
@@ -160,8 +210,11 @@ add_stylesheet('<link rel="stylesheet" href="'.G5_THEME_URL.'/rb.layout/'.$rb_co
//add_stylesheet('<link rel="stylesheet" href="'.G5_THEME_URL.'/rb.layout/'.$rb_core['layout'].'/css/style.css?ver='.G5_CSS_VER.'">', 0); //add_stylesheet('<link rel="stylesheet" href="'.G5_THEME_URL.'/rb.layout/'.$rb_core['layout'].'/css/style.css?ver='.G5_CSS_VER.'">', 0);
add_stylesheet('<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css" />', 0); add_stylesheet('<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css" />', 0);
add_javascript('<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>', 0); add_javascript('<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>', 0);
//add_javascript('<script src="'.G5_THEME_URL.'/rb.layout/'.$rb_core['layout'].'/js/products.js?ver='.G5_CSS_VER.'"></script>', 0); add_javascript('<script src="'.G5_THEME_URL.'/rb.layout/'.$rb_core['layout'].'/js/products.js?ver='.G5_CSS_VER.'"></script>', 0);
add_javascript('<script src="'.G5_THEME_URL.'/rb.layout/'.$rb_core['layout'].'/js/products2.js?ver='.G5_CSS_VER.'"></script>', 0); add_javascript('<script src="'.G5_THEME_URL.'/rb.layout/'.$rb_core['layout'].'/js/products2.js?ver='.G5_CSS_VER.'"></script>', 0);
// JS 추가 (GSAP 라이브러리를 먼저 로드하고, 그 뒤에 custom.js를 로드)
add_javascript('<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>', 0);
include_once(G5_ADMIN_PATH . '/order_manage/components/_expert_visit_popups.php'); include_once(G5_ADMIN_PATH . '/order_manage/components/_expert_visit_popups.php');
?> ?>
<div class="main-container"> <div class="main-container">
@@ -211,11 +264,18 @@ include_once(G5_ADMIN_PATH . '/order_manage/components/_expert_visit_popups.php'
<?php echo render_product_side( $db_products, 'left');?> <?php echo render_product_side( $db_products, 'left');?>
</div> </div>
<div class="product-main"> <div class="product-main" >
<img src='<?php echo $skin_url?>/images/main.webp' alt="메인 사진"> <img id="main-map" src='<?php echo $skin_url?>/images/main.png' alt="메인 사진">
<div class="product-hotspot"></div> <div class="product-hotspot"></div>
</div> </div>
<div id="popup-box" style="position:absolute; width:0; height:0; display:none; overflow:hidden; z-index:100; box-shadow:0 10px 30px rgba(0,0,0,0.3);">
<div class="content" >
팝업 내용
</div>
</div>
<div class="product-side right"> <div class="product-side right">
<?php echo render_product_side( $db_products, 'right');?> <?php echo render_product_side( $db_products, 'right');?>
</div> </div>
@@ -224,7 +284,7 @@ include_once(G5_ADMIN_PATH . '/order_manage/components/_expert_visit_popups.php'
<div class="main-container"> <div class="main-container">
<div class="main-visual"> <div class="main-visual">
<section class="process-section"> <!-- <section class="process-section">-->
<div class="container"> <div class="container">
<main class="layout-main-content1"> <main class="layout-main-content1">
@@ -258,60 +318,67 @@ include_once(G5_ADMIN_PATH . '/order_manage/components/_expert_visit_popups.php'
</main> </main>
</div> </div>
</section> <!-- </section>-->
<section class="min-h-screen flex flex-col items-center justify-center p-6">
<div class="mb-10 text-center">
<h1 class="text-3xl md:text-4xl font-bold text-white mb-3">
고객의 공간을 완성하는 프리미엄 창호 시스템 <span class="highlight_blue">이끌림샤시</span>로 시작하세요
</h1>
<p class="text-gray-300 text-sm md:text-base max-w-2xl mx-auto leading-relaxed"> </div>
고객의 생활 공간을 고려한 설계와 정밀한 기술력으로<br> </div>
최고의 품질과 안정적인 A/S를 제공합니다. <section class="min-h-screen flex flex-col items-center justify-center p-6">
</p> <div class="mb-10 text-center">
</div> <h1 class="text-3xl md:text-4xl font-bold text-white mb-3">
고객의 공간을 완성하는 프리미엄 창호 시스템 <span class="highlight_blue">이끌림샤시</span>로 시작하세요
</h1>
<section class="hero_container"> <p class="text-gray-300 text-sm md::text-base max-w-2xl mx-auto leading-relaxed">
<div class="content_inner"> 고객의 생활 공간을 고려한 설계와 정밀한 기술력으로<br>
최고의 품질과 안정적인 A/S를 제공합니다.
</p>
</div>
<!-- <img src="--><?php //echo G5_THEME_URL ?><!--/rb.layout/dnssash/img/main1.png" alt="메인 배경 이미지" class="main_bg_img">-->
<div class="text_area">
<p class="label_blue">SMART WINDOW ORDERING</p>
<h1 class="main_title">
창호의 새로운 기준,<br>
<span class="highlight_blue">이끌림샤시</span>로 시작하세요
</h1>
<p class="sub_description">
복잡한 창호 교체, 이제 온라인 견적으로 투명하게.<br>
고품질 자재와 전문가의 손길을 클릭 한 번으로 만나보세요.
</p>
<!-- 전문가 상담 및 예약 버튼 (상단 오른쪽) -->
<div class="main_action_row"> </section>
<a href="<?php echo G5_BBS_URL ?>/board.php?bo_table=order" class="btn_box white" > <div class="main-container">
<div class="btn_icon">🏢</div> <!-- 1. 메인 비주얼 섹션 (클래스명 변경됨) -->
<div class="btn_txt"> <section class="map-main-visual">
<strong>창호 온라인 주문</strong> <section class="hero_container">
<p>실시간 직접 견적 및 주문 확정</p> <div class="content_inner">
</div>
</a> <!-- <img src="--><?php //echo G5_THEME_URL ?><!--/rb.layout/dnssash/img/main1.png" alt="메인 배경 이미지" class="main_bg_img">-->
<div class="btn_box white" onclick="openExpertVisitPopup(<?php /*echo $wr_id; */?>)"> <div class="text_area">
<div class="btn_icon">👨‍🔧</div> <p class="label_blue">SMART WINDOW ORDERING</p>
<div class="btn_txt"> <h1 class="main_title">
<strong>창호 전문가 부르기</strong> 창호의 새로운 기준,<br>
<p>방문 실측 및 맞춤 상담 신청</p> <span class="highlight_blue">이끌림샤시</span>로 시작하세요
</div> </h1>
<p class="sub_description">
복잡한 창호 교체, 이제 온라인 견적으로 투명하게.<br>
고품질 자재와 전문가의 손길을 클릭 한 번으로 만나보세요.
</p>
<!-- 전문가 상담 및 예약 버튼 (상단 오른쪽) -->
<div class="main_action_row">
<a href="<?php echo G5_BBS_URL ?>/board.php?bo_table=order" class="btn_box white" >
<div class="btn_icon">🏢</div>
<div class="btn_txt">
<strong>창호 온라인 주문</strong>
<p>실시간 직접 견적 및 주문 확정</p>
</div>
</a>
<div class="btn_box white" onclick="openExpertVisitPopup(<?php echo $wr_id; ?>)">
<div class="btn_icon">👨‍🔧</div>
<div class="btn_txt">
<strong>창호 전문가 부르기</strong>
<p>방문 실측 및 맞춤 상담 신청</p>
</div> </div>
</div> </div>
</div> </div>
<div class="floating_as_badge">
<p>전국 대리점망</p>
<strong>A/S 10년 보장</strong>
</div>
</div> </div>
</section> </div>
<div class="floating_as_badge">
<p>전국 대리점망</p>
<strong>A/S 10년 보장</strong>
</div>
</section> </section>
</div> </section>
</div> </div>
@@ -321,6 +388,23 @@ include_once(G5_ADMIN_PATH . '/order_manage/components/_expert_visit_popups.php'
if (typeof initProducts === 'function') { if (typeof initProducts === 'function') {
initProducts(productsData); initProducts(productsData);
} }
// Get the house map container
const houseMapContainer = document.getElementById('house-map-container');
// Get all product items
const productItems = document.querySelectorAll('.product-item');
// Add click event listener to each product item
productItems.forEach(item => {
item.addEventListener('click', function() {
// Get the image URL from the data-img attribute
const imageUrl = this.getAttribute('data-img');
if (imageUrl) {
// Update the background image of the house map container
houseMapContainer.style.backgroundImage = `url('${imageUrl}')`;
}
});
});
}); });
</script> </script>
@@ -377,4 +461,4 @@ include_once(G5_ADMIN_PATH . '/order_manage/components/_expert_visit_popups.php'
} }
}); });
</script> </script>
@@ -79,7 +79,7 @@ function initProducts(productsData) {
preview.classList.add('fixed-mode'); preview.classList.add('fixed-mode');
preview.style.display = 'flex'; preview.style.display = 'flex';
}; };
hotspotLayer.appendChild(hs); // hotspotLayer.appendChild(hs);
}); });
@@ -1,56 +0,0 @@
function initProducts(productsData) {
const map = document.getElementById('main-map');
const hotspotLayer = document.getElementById('hotspot-layer');
const preview = document.getElementById('hover-preview');
const previewGrid = document.getElementById('preview-grid');
// const coordDisplay = document.getElementById('coord-display');
// 그룹화 로직
const groups = {};
productsData.forEach(p => {
const key = `${parseFloat(p.xPct).toFixed(1)}_${parseFloat(p.yPct).toFixed(1)}`;
if (!groups[key]) groups[key] = [];
groups[key].push(p);
});
Object.keys(groups).forEach(key => {
const group = groups[key];
const hs = document.createElement('div');
hs.className = 'hotspot';
hs.style.left = group[0].xPct + '%';
hs.style.top = group[0].yPct + '%';
hs.style.transform = 'translate(-50%, -50%)';
hs.onmouseenter = () => {
preview.style.display = 'block';
previewGrid.innerHTML = group.map(p => `
<div class="product-item">
<div class="product-img-box"><img src="${p.img_url}" onerror="this.src='https://via.placeholder.com/200x120?text=No+Image'"></div>
<div class="text-[10px] text-blue-400 font-bold mb-1">${p.page}</div>
<h4 class="text-white text-[13px] font-bold truncate">${p.title}</h4>
<p class="text-slate-500 text-[10px] line-clamp-2">${p.desc}</p>
</div>
`).join('');
};
hs.onmouseleave = () => preview.style.display = 'none';
hs.onmousemove = (e) => {
let x = e.clientX + 30;
let y = e.clientY + 30;
if (x + 540 > window.innerWidth) x = e.clientX - 560;
if (y + preview.offsetHeight > window.innerHeight) y = window.innerHeight - preview.offsetHeight - 20;
preview.style.left = x + 'px';
preview.style.top = y + 'px';
};
hotspotLayer.appendChild(hs);
});
// 좌표 도구
// map.onmousemove = (e) => {
// const r = map.getBoundingClientRect();
// coordDisplay.textContent = `X: ${((e.clientX-r.left)/r.width*100).toFixed(1)}%, Y: ${((e.clientY-r.top)/r.height*100).toFixed(1)}%`;
// };
// map.onclick = (e) => {
// const r = map.getBoundingClientRect();
// prompt("좌표:", `xPct: ${((e.clientX-r.left)/r.width*100).toFixed(1)}, yPct: ${((e.clientY-r.top)/r.height*100).toFixed(1)}`);
// };
}
@@ -1,103 +0,0 @@
function initProducts(productsData) {
const map = document.getElementById('main-map');
const hotspotLayer = document.getElementById('hotspot-layer');
const preview = document.getElementById('hover-preview');
const previewGrid = document.getElementById('preview-grid');
// const coordDisplay = document.getElementById('coord-display');
let isFixed = false; // 팝업 고정 상태 변수
const groups = {};
productsData.forEach(p => {
const key = `${parseFloat(p.x_pct || p.xPct).toFixed(1)}_${parseFloat(p.y_pct || p.yPct).toFixed(1)}`;
if (!groups[key]) groups[key] = [];
groups[key].push(p);
});
Object.keys(groups).forEach(key => {
const group = groups[key];
const hs = document.createElement('div');
hs.className = 'hotspot';
hs.style.left = (group[0].x_pct || group[0].xPct) + '%';
hs.style.top = (group[0].y_pct || group[0].yPct) + '%';
hs.style.transform = 'translate(-50%, -50%)';
// 팝업 내용 생성 함수
const updateContent = () => {
const gridClass = group.length === 1 ? 'single-item' : 'multi-items';
previewGrid.className = `products-grid ${gridClass}`;
previewGrid.innerHTML = group.map(p => `
<div class="product-item">
<div class="product-img-box"><img src="${p.img_url}" onerror="this.src='https://via.placeholder.com/200x120?text=No+Image'"></div>
<div class="info-content">
<div class="text-[11px] text-blue-400 font-bold mb-1">${p.page}</div>
<h4 class="text-white text-[16px] font-bold mb-2">${p.title}</h4>
<p class="text-slate-400 text-[13px] leading-relaxed">${p.desc}</p>
</div>
</div>
`).join('');
};
// 마우스 진입 (호버 시작)
hs.onmouseenter = (e) => {
if (isFixed) return;
updateContent();
preview.style.display = 'block';
};
// 마우스 이동 (팝업이 마우스를 따라다님)
hs.onmousemove = (e) => {
if (isFixed) return;
let x = e.clientX + 20;
let y = e.clientY - (preview.offsetHeight / 2);
if (x + 560 > window.innerWidth) x = e.clientX - 580;
if (y < 20) y = 20;
preview.style.left = x + 'px';
preview.style.top = y + 'px';
};
// 마우스 이탈 (호버 종료)
hs.onmouseleave = () => {
if (isFixed) return;
preview.style.display = 'none';
};
// 클릭 시 고정
hs.onclick = (e) => {
e.stopPropagation();
isFixed = true;
updateContent();
preview.style.display = 'block';
// 고정 시에는 화면 중앙 근처 적절한 위치에 배치
let x = e.clientX + 30;
let y = e.clientY - (preview.offsetHeight / 2);
if (x + 720 > window.innerWidth) x = e.clientX - 730;
if (y < 20) y = 20;
preview.style.left = x + 'px';
preview.style.top = y + 'px';
preview.classList.add('fixed-mode');
};
hotspotLayer.appendChild(hs);
});
// 외부 클릭 시 고정 해제 및 닫기
document.addEventListener('click', (e) => {
if (!preview.contains(e.target)) {
isFixed = false;
preview.style.display = 'none';
preview.classList.remove('fixed-mode');
}
});
// // 좌표 도구
// map.onmousemove = (e) => {
// const r = map.getBoundingClientRect();
// coordDisplay.textContent = `X: ${((e.clientX-r.left)/r.width*100).toFixed(1)}%, Y: ${((e.clientY-r.top)/r.height*100).toFixed(1)}%`;
// };
// map.onclick = (e) => {
// const r = map.getBoundingClientRect();
// prompt("좌표:", `xPct: ${((e.clientX-r.left)/r.width*100).toFixed(1)}, yPct: ${((e.clientY-r.top)/r.height*100).toFixed(1)}`);
// };
}
@@ -1,16 +1,24 @@
/** /**
* [최종 통합본] 제품 사이드 메뉴 및 메인 맵 연동 모듈 (products2.js) * [최종 통합본] 제품 사이드 메뉴 및 메인 맵 연동 모듈 (products2.js)
* 1: 중앙팝업, 2: 점선연결, 3: 제자리확대, 4: 1초 지연 후 중앙팝업 * 1: 중앙팝업, 2: 점선연결, 3: 제자리확대, 4: 1초 지연 후 중앙팝업, 5: 팝업 박스 표시, 6: 점선연결 + 팝업 박스 표시
* 5: 점선연결 + 1초후 중앙팝업
*/ */
(function() { (function() {
// 💡 설정: 원하는 모드 번호를 입력하세요. // 💡 설정: 원하는 모드 번호를 입력하세요.
const POPUP_MODE = 5; const POPUP_MODE = 6; // 6번 모드로 설정합니다.
// 💡 설정: 팝업 박스에 표시할 내용의 타입을 선택하세요 ('text' 또는 'image').
const POPUP_CONTENT_DISPLAY_TYPE = 'image'; // 'text' 또는 'image'로 변경하여 테스트
let popupTimer = null; // 4번 모드용 타이머 let popupTimer = null; // 4번 모드용 타이머
let hoverTimeout = null; // 호버 인텐트 지연 타이머
const HOVER_DELAY = 50; // 마우스가 완전히 벗어났다고 판단할 지연 시간 (밀리초)
const MAX_MOVE_DISTANCE = 10; // 팝업이 핫스팟에서 이동할 수 있는 최대 거리 (px)
// 모든 효과와 타이머를 제거하는 청소기 함수 (popupBox 숨기는 로직은 제외)
function clearAllPopupsContent() { // 함수 이름 변경: 내용만 초기화
console.log('clearAllPopupsContent called!');
// console.trace(); // 이제 제거해도 됩니다.
// 모든 효과와 타이머를 제거하는 청소기 함수
function clearAllPopups() {
// 실행 중인 타이머가 있으면 취소 // 실행 중인 타이머가 있으면 취소
if (popupTimer) { if (popupTimer) {
clearTimeout(popupTimer); clearTimeout(popupTimer);
@@ -35,12 +43,24 @@
img.style.transform = ''; img.style.transform = '';
} }
}); });
// popupContent 내용 및 스타일 초기화 (popupBox 자체는 건드리지 않음)
const popupBox = document.getElementById('popup-box');
if (popupBox) {
const popupContent = popupBox.querySelector('.content');
if (popupContent) {
popupContent.innerHTML = ''; // 내용 비우기
popupContent.style.cssText = ''; // 모든 인라인 스타일 초기화
}
}
} }
function initProductEvents() { function initProductEvents() {
const productItems = document.querySelectorAll('.product-section .product-item'); const productItems = document.querySelectorAll('.product-section .product-item');
const mainArea = document.querySelector('.product-main'); const mainArea = document.querySelector('.product-main');
const section = document.querySelector('.product-section'); const section = document.querySelector('.product-section');
const popupBox = document.getElementById('popup-box');
const popupContent = popupBox ? popupBox.querySelector('.content') : null;
if (!productItems.length || !mainArea) return; if (!productItems.length || !mainArea) return;
@@ -48,24 +68,262 @@
const imgElement = item.querySelector('.product-img img'); const imgElement = item.querySelector('.product-img img');
const imgSrc = item.dataset.img || (imgElement ? imgElement.src : ''); const imgSrc = item.dataset.img || (imgElement ? imgElement.src : '');
const proLink = item.dataset.prolink; const proLink = item.dataset.prolink;
const x1 = parseFloat(item.dataset.x1);
const y1 = parseFloat(item.dataset.y1);
// Get product details from data attributes
const title = item.dataset.title;
const desc = item.dataset.desc;
const content = item.dataset.content;
item.addEventListener('mouseenter', function() { item.addEventListener('mouseenter', function() {
clearAllPopups(); // 새로 시작하기 전 초기화 console.log('Mouse entered product-item:', title);
const x1 = this.dataset.x1; // 섹션 숨김 지연 타이머가 있다면 취소 (호버 깜빡임 방지)
const y1 = this.dataset.y1; if (hoverTimeout) {
console.log('Clearing hoverTimeout on mouseenter.');
clearTimeout(hoverTimeout);
hoverTimeout = null;
}
if (x1 && y1) { // 새로운 팝업을 띄우기 전에 이전 팝업의 내용만 초기화
// 1. 핫스팟(블루 스폿)은 어떤 모드든 즉시 표시 clearAllPopupsContent();
const hotspot = document.querySelector('.product-hotspot');
if (hotspot) { // 팝업 박스를 명시적으로 보이는 상태로 설정 (애니메이션 시작 전)
hotspot.style.left = x1 + '%'; if (popupBox) {
hotspot.style.top = y1 + '%'; gsap.killTweensOf(popupBox); // 혹시 모를 숨김 애니메이션 중단
hotspot.style.display = 'block'; popupBox.style.display = 'block';
// 애니메이션 초기 상태 설정
popupBox.style.width = '0px';
popupBox.style.height = '0px';
popupBox.style.opacity = '0';
popupBox.style.transform = 'none'; // transform 제거
// mainRect는 popupBox를 포함하는 product-main의 크기 정보
// 핫스팟 위치에 따라 팝업의 초기 시작 지점 설정
const isLeft = this.closest('.product-side').classList.contains('left');
const rect = item.getBoundingClientRect();//this.getBoundingClientRect();
const mainRect = mainArea.getBoundingClientRect();
const startX = isLeft ? rect.right : rect.left;
const startY = rect.bottom + (rect.height / 2); //rect.bottom + (rect.height / 2);
if (isLeft) {
// 핫스팟이 왼쪽에 있으면 팝업은 mainArea의 좌측 상단에서 시작
popupBox.style.left = startX+'px';
popupBox.style.top = startY+'px';
} else {
// 핫스팟이 오른쪽에 있으면 팝업은 mainArea의 우측 상단에서 시작
popupBox.style.left = startX +'px';
popupBox.style.top = startY +'px';
} }
}
if (!isNaN(x1) && !isNaN(y1)) {
// 1. 핫스팟(블루 스폿)은 어떤 모드든 즉시 표시
if(x1 && y1) {
const hotspot = document.querySelector('.product-hotspot');
if (hotspot) {
hotspot.style.left = x1 + '%';
hotspot.style.top = y1 + '%';
hotspot.style.display = 'block';
}
}
// 2. 모드별 효과 실행 // 2. 모드별 효과 실행
if (POPUP_MODE === 4) { if (POPUP_MODE === 5 || POPUP_MODE === 6) {
if (popupBox && popupContent) {
// Set popup dimensions
const popupWidth = 600; // 요청하신 대로 600
const popupHeight = 500; // 요청하신 대로 500
const POPUP_HORIZONTAL_OFFSET = 20; // 핫스팟과 팝업 사이의 가로 간격 (px)
// popupContent의 기본 스타일 설정 (매우 중요!)
popupContent.style.width = '100%';
popupContent.style.height = '100%';
popupContent.style.display = 'flex';
popupContent.style.alignItems = 'center';
popupContent.style.justifyContent = 'center';
popupContent.style.color = '#fff'; // 기본 텍스트 색상
popupContent.style.overflow = 'hidden'; // 이 줄을 추가합니다.
popupContent.style.padding = '0'; // 이 줄을 추가합니다.
// Populate popup content based on POPUP_CONTENT_DISPLAY_TYPE
if (POPUP_CONTENT_DISPLAY_TYPE === 'image' && imgElement) {
popupContent.innerHTML = ''; // Clear previous content
popupContent.style.background = 'none'; // 이미지 표시 시 배경 제거
const clone = imgElement.cloneNode(true);
clone.src = imgSrc;
// clone.id = 'active-popup-img'; // 이 줄을 제거합니다!
clone.style.width = '100%'; // Make image fit popup box
clone.style.height = '100%';
clone.style.objectFit = 'contain'; // 'cover'에서 'contain'으로 변경
// clone.style.cursor = proLink ? 'pointer' : 'default'; // 이 줄을 주석 처리하거나 제거합니다.
clone.style.pointerEvents = 'none'; // 이미지 위에서 마우스 이벤트 방지
if (proLink) {
clone.onclick = () => { location.href = proLink; };
}
// console.log('--- Debugging appendChild ---');
// console.log('proLink:', proLink);
// console.log('Clone element:', clone);
// console.log('popupContent BEFORE append:', popupContent.innerHTML);
// console.log('popupContent.style.cssText BEFORE append:', popupContent.style.cssText); // 스타일 확인
popupContent.appendChild(clone);
// console.log('popupContent AFTER append:', popupContent.innerHTML);
// console.log('popupContent.style.cssText AFTER append:', popupContent.style.cssText); // 스타일 확인
// console.log('popupContent children AFTER append:', popupContent.children.length);
// console.log('popupContent ', popupContent);
// console.log('popupBox ', popupBox);
// console.log('--- End Debugging ---');
} else { // Default to text content or if POPUP_CONTENT_DISPLAY_TYPE is 'text'
popupContent.innerHTML = `
<h3>${title}</h3>
<p>${desc}</p>
<p>${content}</p>
${proLink ? `<a href="${proLink}" style="color: white; text-decoration: underline;">자세히 보기</a>` : ''}
`;
// 텍스트 표시 시 배경 적용
popupContent.style.background = 'linear-gradient(135deg, #667eea, #764ba2)';
}
const isLeft = this.closest('.product-side').classList.contains('left');
const rect = item.getBoundingClientRect();
const mainRect = mainArea.getBoundingClientRect();
const startX = isLeft ? rect.right : rect.left;
const startY = rect.bottom + (rect.height / 2);
// const hotspotX_px = startX +popupWidth;
// const hotspotY_px = !isLeft ? startY+popupHeight : startY - popupHeight;
const corners = {
topLeft: {
x: rect.left ,
y: rect.top
},
topRight: {
x: rect.right ,
y: rect.top
},
bottomLeft: {
x: rect.left ,
y: rect.bottom
},
bottomRight: {
x: rect.right ,
y: rect.bottom
}
};
// const hotspotX_px = isLeft ? rect.right : rect.left - popupWidth -POPUP_HORIZONTAL_OFFSET ;
// const hotspotY_px = isLeft ? rect.top - rect.height :rect.y;
const hotspotX_px = isLeft ? 462.0833435058594 : 922.0833740234375 ;
const hotspotY_px = isLeft ? 174.66668701171875 :247.33334350585938;
console.log(isLeft);
console.log(mainRect);
console.log(rect);
console.log(hotspotX_px);
console.log(hotspotY_px);
// Determine if hotspot is on the left or right half of the mainArea
const isHotspotLeftHalf = x1 < 50;
// Calculate ideal raw targetLeft based on side
let idealTargetLeft_px;
if (isHotspotLeftHalf) {
// Hotspot on left, popup appears to its right
idealTargetLeft_px = hotspotX_px + POPUP_HORIZONTAL_OFFSET;
} else {
// Hotspot on right, popup appears to its left
idealTargetLeft_px = hotspotX_px - popupWidth - POPUP_HORIZONTAL_OFFSET;
}
// Calculate ideal raw targetTop (vertically centered with hotspot)
let idealTargetTop_px = hotspotY_px - (popupHeight / 2);
// Clamp ideal targetLeft and targetTop to stay within mainArea bounds
let clampedIdealTargetLeft_px = Math.max(0, Math.min(idealTargetLeft_px, mainRect.width - popupWidth));
let clampedIdealTargetTop_px = Math.max(0, Math.min(idealTargetTop_px, mainRect.height - popupHeight));
// Calculate actual final position, limiting movement distance
let finalTargetLeft_px = clampedIdealTargetLeft_px;
let finalTargetTop_px = clampedIdealTargetTop_px;
// Calculate distance from hotspot center to clamped ideal popup center
const hotspotCenterToPopupCenterX = clampedIdealTargetLeft_px + (popupWidth / 2) - hotspotX_px;
const hotspotCenterToPopupCenterY = clampedIdealTargetTop_px + (popupHeight / 2) - hotspotY_px;
const distance = Math.sqrt(
hotspotCenterToPopupCenterX * hotspotCenterToPopupCenterX +
hotspotCenterToPopupCenterY * hotspotCenterToPopupCenterY
);
if (distance > MAX_MOVE_DISTANCE) {
// If distance exceeds max, scale down the movement vector
const ratio = MAX_MOVE_DISTANCE / distance;
finalTargetLeft_px = hotspotX_px + (hotspotCenterToPopupCenterX * ratio) - (popupWidth / 2);
finalTargetTop_px = hotspotY_px + (hotspotCenterToPopupCenterY * ratio) - (popupHeight / 2);
// Re-clamp to ensure it stays within mainArea bounds after adjustment
finalTargetLeft_px = Math.max(0, Math.min(finalTargetLeft_px, mainRect.width - popupWidth));
finalTargetTop_px = Math.max(0, Math.min(finalTargetTop_px, mainRect.height - popupHeight));
}
// 팝업 박스를 보여주는 애니메이션 (크기, 위치, 투명도 동시 애니메이션)
gsap.to(popupBox, {
width: popupWidth, // 최종 너비
height: popupHeight, // 최종 높이
left: hotspotX_px,//finalTargetLeft_px, // 최종 왼쪽 위치
top: hotspotY_px,//finalTargetTop_px, // 최종 위쪽 위치
opacity: 1, // 최종 투명도
transform: 'none', // transform 속성 제거
duration: 1.5, // 요청하신 대로 1.5초로 변경
ease: "power2.out",
pointerEvents: 'auto',
onComplete: () => { // 이 onComplete 콜백을 추가합니다.
popupBox.style.opacity = '1'; // 애니메이션 완료 후 opacity를 1로 강제
popupBox.style.display = 'block'; // 애니메이션 완료 후 display를 block으로 강제
popupBox.style.transform = 'none'; // transform 속성 초기화 (중요)
}
});
}
// Mode 6 specific: Add connector line
if (POPUP_MODE === 6 &&x1 &&y1) {
const rect = this.getBoundingClientRect();
const mainRect = mainArea.getBoundingClientRect();
const targetX = mainRect.left + (mainRect.width * (x1 / 100));
const targetY = mainRect.top + (mainRect.height * (y1 / 100));
const isLeft = this.closest('.product-side').classList.contains('left');
const startX = isLeft ? rect.right : rect.left;
const startY = rect.top + (rect.height / 2);
const dx = targetX - startX;
const dy = targetY - startY;
const dist = Math.sqrt(dx * dx + dy * dy);
let angle = Math.atan2(dy, dx) * 180 / Math.PI;
if (!isLeft) angle += 180;
this.style.setProperty('--line-width', dist + 'px');
this.style.setProperty('--line-angle', angle + 'deg');
this.classList.add('show-connector');
requestAnimationFrame(() => this.classList.add('active'));
// Note: imgElement.style.transform = 'scale(1.2)' is not applied here
// as the image is either in the popup or not the primary focus for mode 6.
// If you want the side image to scale, uncomment the following:
// if (imgElement) imgElement.style.transform = 'scale(1.2)';
if (imgElement && proLink) {
// This click handler is for the side image, not the popup image
// imgElement.style.cursor = 'pointer'; // 이 줄을 주석 처리하거나 제거합니다.
imgElement.onclick = () => { location.href = proLink; };
}
}
} else if (POPUP_MODE === 4) {
// [모드 4] 1초 지연 후 중앙 팝업 // [모드 4] 1초 지연 후 중앙 팝업
popupTimer = setTimeout(() => { popupTimer = setTimeout(() => {
if (imgElement) { if (imgElement) {
@@ -99,8 +357,8 @@
// [모드 2] 점선 연결선 정밀 계산 // [모드 2] 점선 연결선 정밀 계산
const rect = this.getBoundingClientRect(); const rect = this.getBoundingClientRect();
const mainRect = mainArea.getBoundingClientRect(); const mainRect = mainArea.getBoundingClientRect();
const targetX = mainRect.left + (mainRect.width * (parseFloat(x1) / 100)); const targetX = mainRect.left + (mainRect.width * (x1 / 100));
const targetY = mainRect.top + (mainRect.height * (parseFloat(y1) / 100)); const targetY = mainRect.top + (mainRect.height * (y1 / 100));
const isLeft = this.closest('.product-side').classList.contains('left'); const isLeft = this.closest('.product-side').classList.contains('left');
const startX = isLeft ? rect.right : rect.left; const startX = isLeft ? rect.right : rect.left;
const startY = rect.top + (rect.height / 2); const startY = rect.top + (rect.height / 2);
@@ -120,57 +378,15 @@
imgElement.style.cursor = 'pointer'; imgElement.style.cursor = 'pointer';
imgElement.onclick = () => { location.href = proLink; }; imgElement.onclick = () => { location.href = proLink; };
} }
} else if (POPUP_MODE === 3 && imgElement) { // 3번 모드 로직 추가
} else if (POPUP_MODE === 3 && imgElement) {
// [모드 3] 제자리 600 확대 // [모드 3] 제자리 600 확대
imgElement.src = imgSrc; // imgElement.src = imgSrc;
imgElement.classList.add('mode-self-zoom'); imgElement.classList.add('mode-self-zoom');
if (proLink) { if (proLink) {
imgElement.style.cursor = 'pointer'; imgElement.style.cursor = 'pointer';
imgElement.style.pointerEvents = 'auto'; imgElement.style.pointerEvents = 'auto';
imgElement.onclick = () => { location.href = proLink; }; imgElement.onclick = () => { location.href = proLink; };
} }
} else if(POPUP_MODE === 5 && imgElement) {
// [모드 2] 점선 연결선 정밀 계산
const rect = this.getBoundingClientRect();
const mainRect = mainArea.getBoundingClientRect();
const targetX = mainRect.left + (mainRect.width * (parseFloat(x1) / 100));
const targetY = mainRect.top + (mainRect.height * (parseFloat(y1) / 100));
const isLeft = this.closest('.product-side').classList.contains('left');
const startX = isLeft ? rect.right : rect.left;
const startY = rect.top + (rect.height / 2);
const dx = targetX - startX;
const dy = targetY - startY;
const dist = Math.sqrt(dx * dx + dy * dy);
let angle = Math.atan2(dy, dx) * 180 / Math.PI;
if (!isLeft) angle += 180;
this.style.setProperty('--line-width', dist + 'px');
this.style.setProperty('--line-angle', angle + 'deg');
this.classList.add('show-connector');
requestAnimationFrame(() => this.classList.add('active'));
if (imgElement) imgElement.style.transform = 'scale(1.2)';
if (imgElement && proLink) {
imgElement.style.cursor = 'pointer';
imgElement.onclick = () => { location.href = proLink; };
}
var popupTimer = setTimeout(() => {
if (imgElement) {
const clone = imgElement.cloneNode(true);
clone.id = 'active-popup-img';
clone.src = imgSrc;
if (proLink) {
clone.style.cursor = 'pointer';
clone.style.pointerEvents = 'auto';
clone.onclick = () => { location.href = proLink; };
}
mainArea.appendChild(clone);
requestAnimationFrame(() => clone.classList.add('active'));
}
}, 1000); // 1000ms = 1초 대기
} }
} }
}); });
@@ -186,12 +402,58 @@
} }
// 마우스가 아이템을 벗어나면 즉시 청소 (타이머 취소 포함) // 마우스가 아이템을 벗어나면 즉시 청소 (타이머 취소 포함)
item.addEventListener('mouseleave', clearAllPopups); item.addEventListener('mouseleave', function() {
console.log('Mouse left item!');
console.log('Setting hoverTimeout to hide popup in', HOVER_DELAY, 'ms');
// 팝업 숨김을 HOVER_DELAY만큼 지연시킵니다.
hoverTimeout = setTimeout(() => {
console.log('hoverTimeout triggered: Hiding popup.');
clearAllPopupsContent(); // 내용만 초기화
const popupBox = document.getElementById('popup-box');
if (popupBox) {
gsap.killTweensOf(popupBox); // 현재 진행 중인 애니메이션을 즉시 중단
gsap.to(popupBox, {
width: 0,
height: 0,
opacity: 0,
duration: 0.4,
onComplete: () => {
popupBox.style.display = 'none';
}
});
}
}, HOVER_DELAY); // HOVER_DELAY만큼 지연
});
}); });
// 섹션 전체를 벗어나도 청소 (안전장치) // 섹션 전체를 벗어나면 팝업 박스 숨기기
if (section) { if (section) {
section.addEventListener('mouseleave', clearAllPopups); section.addEventListener('mouseleave', function() {
console.log('Mouse left product-section!');
console.log('Setting hoverTimeout to hide popup in', HOVER_DELAY, 'ms');
// 팝업 숨김을 HOVER_DELAY만큼 지연시킵니다.
hoverTimeout = setTimeout(() => {
console.log('hoverTimeout triggered: Hiding popup.');
clearAllPopupsContent(); // 내용만 초기화
const popupBox = document.getElementById('popup-box');
if (popupBox) {
gsap.killTweensOf(popupBox); // 현재 진행 중인 애니메이션을 즉시 중단
gsap.to(popupBox, {
width: 0,
height: 0,
opacity: 0,
duration: 0.4,
onComplete: () => {
popupBox.style.display = 'none';
}
});
}
}, HOVER_DELAY); // HOVER_DELAY만큼 지연
});
} }
} }
@@ -201,4 +463,5 @@
} else { } else {
initProductEvents(); initProductEvents();
} }
})(); })();
@@ -1,64 +0,0 @@
function initProductEvents() {
// 💡 설정: 1(메인중앙), 2(점선), 3(제자리확대)
const POPUP_MODE = 1;
const productItems = document.querySelectorAll('.product-section .product-item');
const mainArea = document.querySelector('.product-main');
if (!productItems.length) return;
productItems.forEach(item => {
// 💡 [수정] 데이터 속성에서 이미지 경로 직접 읽기 (없으면 내부 img 사용)
const imgSrc = item.dataset.img || item.querySelector('.product-img img').src;
const imgElement = item.querySelector('.product-img img');
item.addEventListener('mouseenter', function() {
clearAllPopups();
const x1 = this.dataset.x1;
const y1 = this.dataset.y1;
if (x1 && y1) {
ProductModule.setHotspot(x1, y1);
if (POPUP_MODE === 1 && mainArea) {
// [모드 1] 메인 중앙 복제 팝업
const clone = document.createElement('img');
clone.src = imgSrc; // 💡 전달받은 경로로 로드
clone.id = 'active-popup-img';
mainArea.appendChild(clone);
requestAnimationFrame(() => clone.classList.add('active'));
} else if (POPUP_MODE === 2) {
// [모드 2] 점선 연결 (기존 로직 동일)
const rect = this.getBoundingClientRect();
const mainRect = mainArea.getBoundingClientRect();
const targetX = mainRect.left + (mainRect.width * (parseFloat(x1) / 100));
const targetY = mainRect.top + (mainRect.height * (parseFloat(y1) / 100));
const isLeft = this.closest('.product-side').classList.contains('left');
const startX = isLeft ? rect.right : rect.left;
const dx = targetX - startX;
const dy = targetY - (rect.top + rect.height/2);
let angle = Math.atan2(dy, dx) * 180 / Math.PI;
if (!isLeft) angle += 180;
this.style.setProperty('--line-width', Math.sqrt(dx*dx + dy*dy) + 'px');
this.style.setProperty('--line-angle', angle + 'deg');
this.classList.add('show-connector');
requestAnimationFrame(() => this.classList.add('active'));
} else if (POPUP_MODE === 3) {
// 💡 [모드 3] 제자리 400x400 확대
if (imgElement) {
imgElement.src = imgSrc; // 💡 이미지 경로 갱신
imgElement.classList.add('mode-self-zoom');
}
}
}
});
item.addEventListener('mouseleave', function() {
clearAllPopups();
if (imgElement) imgElement.classList.remove('mode-self-zoom');
});
});
}
@@ -1,99 +0,0 @@
function initProducts(productsData) {
const map = document.getElementById('main-map');
const hotspotLayer = document.getElementById('hotspot-layer');
const preview = document.getElementById('hover-preview');
const previewGrid = document.getElementById('preview-grid');
let isFixed = false;
const noImg = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAwIiBoZWlnaHQ9IjMwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjMjIyIi8+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZpbGw9IiM0NDQiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGR5PSIuM2VtIiBmb250LWZhbWl5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjI0Ij5ObyBJbWFnZTwvdGV4dD48L3N2Zz4=";
// 1. 그룹화 로직
const groups = {};
productsData.forEach(p => {
const key = `${parseFloat(p.xPct).toFixed(1)}_${parseFloat(p.yPct).toFixed(1)}`;
if (!groups[key]) groups[key] = [];
groups[key].push(p);
});
// 2. 핫스팟 생성 및 이벤트
Object.keys(groups).forEach(key => {
const group = groups[key];
const hs = document.createElement('div');
hs.className = 'hotspot';
hs.style.left = group[0].xPct + '%';
hs.style.top = group[0].yPct + '%';
hs.style.transform = 'translate(-50%, -50%)';
const updateContent = (showFullContent) => {
const hasContent = group.some(p => p.content && p.content.trim() !== "");
const isSingle = group.length === 1;
if (showFullContent && hasContent) {
// 클릭 시: 상세 HTML 모드
previewGrid.className = `products-grid full-html-mode ${isSingle ? 'single-item' : 'multi-items'}`;
previewGrid.innerHTML = group.map(p => {
if (p.content && p.content.trim() !== "") {
return `<div class="editor-content-view">${p.content}</div>`;
} else {
const linkAttr = (p.link) ? `onclick="location.href='${p.link}'" style="cursor:pointer;"` : "";
return `<div class="product-item" ${linkAttr}>
<div class="product-img-box"><img src="${p.img_url}" onerror="this.onerror=null; this.src='${noImg}';"></div>
<div class="info-content">
<div class="text-[12px] text-blue-400 font-bold mb-2">${p.page}</div>
<h4 class="text-white text-[18px] font-bold mb-3">${p.title}</h4>
<p class="text-slate-400 text-[14px] leading-relaxed">${p.desc}</p>
${p.link ? '<div class="text-[11px] text-blue-500 mt-2">자세히 보기 ></div>' : ''}
</div>
</div>`;
}
}).join('<hr class="content-divider">');
} else {
// 호버 시: 요약 모드
const gridClass = isSingle ? 'single-item' : 'multi-items';
previewGrid.className = `products-grid ${gridClass}`;
previewGrid.innerHTML = group.map(p => {
const linkAttr = (p.link) ? `onclick="location.href='${p.link}'" style="cursor:pointer;"` : "";
return `<div class="product-item" ${linkAttr}>
<div class="product-img-box"><img src="${p.img_url}" onerror="this.onerror=null; this.src='${noImg}';"></div>
<div class="info-content">
<div class="text-[12px] text-blue-400 font-bold mb-2">${p.page}</div>
<h4 class="text-white text-[18px] font-bold mb-3">${p.title}</h4>
<p class="text-slate-400 text-[14px] leading-relaxed">${p.desc}</p>
</div>
</div>`;
}).join('');
}
};
hs.onmouseenter = () => {
if (isFixed) return;
updateContent(false);
preview.style.display = 'block';
preview.classList.remove('fixed-mode');
};
hs.onmouseleave = () => {
if (isFixed) return;
preview.style.display = 'none';
};
hs.onclick = (e) => {
e.stopPropagation();
isFixed = true;
updateContent(true);
preview.classList.add('fixed-mode');
preview.style.display = 'flex';
};
hotspotLayer.appendChild(hs);
});
// 외부 클릭 시 닫기
document.addEventListener('click', (e) => {
if (!preview.contains(e.target)) {
isFixed = false;
preview.style.display = 'none';
preview.classList.remove('fixed-mode');
}
});
}
@@ -34,8 +34,8 @@ body { background-color: #0b0f1a; font-family: 'Pretendard', sans-serif; margin:
/* 1. 맵 컨테이너 크기를 rb.layout과 동일하게 1600x980으로 고정 */ /* 1. 맵 컨테이너 크기를 rb.layout과 동일하게 1600x980으로 고정 */
.map-container { .map-container {
position: relative; position: relative;
width: 1600px !important; width: 1000px !important;
height: 980px !important; height: 800px !important;
background-size: 100% 100% !important; /* 이미지 늘림 방식을 동일하게 설정 */ background-size: 100% 100% !important; /* 이미지 늘림 방식을 동일하게 설정 */
cursor: crosshair; cursor: crosshair;
margin: 0 auto; margin: 0 auto;
@@ -58,7 +58,7 @@ body { background-color: #0b0f1a; font-family: 'Pretendard', sans-serif; margin:
.map-wrapper { .map-wrapper {
position: relative; position: relative;
display: inline-block; display: inline-block;
width: 1600px; width: 1000px;
border-radius: 24px; border-radius: 24px;
overflow: hidden; overflow: hidden;
background: #161b26; background: #161b26;

Before

Width:  |  Height:  |  Size: 513 KiB

After

Width:  |  Height:  |  Size: 513 KiB

Before

Width:  |  Height:  |  Size: 537 KiB

After

Width:  |  Height:  |  Size: 537 KiB

Before

Width:  |  Height:  |  Size: 496 KiB

After

Width:  |  Height:  |  Size: 496 KiB

Before

Width:  |  Height:  |  Size: 547 KiB

After

Width:  |  Height:  |  Size: 547 KiB

Before

Width:  |  Height:  |  Size: 679 KiB

After

Width:  |  Height:  |  Size: 679 KiB

Before

Width:  |  Height:  |  Size: 512 KiB

After

Width:  |  Height:  |  Size: 512 KiB

Before

Width:  |  Height:  |  Size: 523 KiB

After

Width:  |  Height:  |  Size: 523 KiB

Before

Width:  |  Height:  |  Size: 561 KiB

After

Width:  |  Height:  |  Size: 561 KiB

Before

Width:  |  Height:  |  Size: 562 KiB

After

Width:  |  Height:  |  Size: 562 KiB

Before

Width:  |  Height:  |  Size: 615 KiB

After

Width:  |  Height:  |  Size: 615 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 MiB

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 510 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 645 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 691 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 949 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1012 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 319 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Some files were not shown because too many files have changed in this diff Show More