뷰어 수정
@69609a072a39bf30080461c3dc2d301f91a94bda
--- html/onSejong/index.html
+++ html/onSejong/index.html
... | ... | @@ -438,10 +438,7 @@ |
| 438 | 438 |
<li>[뷰어]수업설계 |
| 439 | 439 |
<ul> |
| 440 | 440 |
<li>수업설계,manager/class/classViewer.html</li> |
| 441 |
- <li>수업설계-영상</li> |
|
| 442 |
- <li>수업설계-이미지,manager/class/classViewer_img.html</li> |
|
| 443 |
- <li>수업설계-판서,manager/class/classViewer_painter.html</li> |
|
| 444 |
- <li>수업설계-문서,manager/class/classViewer_painter_doc.html</li> |
|
| 441 |
+ <li>수업설계(이미지/영상/문서),manager/class/classViewer_cont.html</li> |
|
| 445 | 442 |
<li>수업설계-링크,manager/class/classViewer_URL.html</li> |
| 446 | 443 |
<li>[팝업]파일 업로드,manager/class/pop_addFile.html</li> |
| 447 | 444 |
<li>[팝업]링크추가,manager/class/pop_addLink.html</li> |
--- html/onSejong/manager/class/classViewer.html
+++ html/onSejong/manager/class/classViewer.html
... | ... | @@ -1,17 +1,17 @@ |
| 1 |
-<!DOCTYPE html> |
|
| 1 |
+ <!DOCTYPE html> |
|
| 2 | 2 |
<html lang="ko"> |
| 3 | 3 |
<head> |
| 4 | 4 |
<title>온세종학교</title> |
| 5 | 5 |
|
| 6 |
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> |
|
| 7 |
- <meta name="Author" content="" /> |
|
| 8 |
- <meta name="Keywords" content="" /> |
|
| 9 |
- <meta name="Description" content="" /> |
|
| 10 |
- <meta name="format-detection" content="telephone=no" /> |
|
| 11 |
- <meta http-equiv="X-UA-Compatible" content="IE=edge" /> |
|
| 12 |
- <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0" /> |
|
| 6 |
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> |
|
| 7 |
+ <meta name="Author" content=""> |
|
| 8 |
+ <meta name="Keywords" content=""> |
|
| 9 |
+ <meta name="Description" content=""> |
|
| 10 |
+ <meta name="format-detection" content="telephone=no"> |
|
| 11 |
+ <meta http-equiv="X-UA-Compatible" content="IE=edge"> |
|
| 12 |
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0"> |
|
| 13 | 13 |
|
| 14 |
- <link href="/resources/front/site/SITE_00000/css/style.css" rel="stylesheet" /> |
|
| 14 |
+ <link href="/resources/front/site/SITE_00000/css/style.css" rel="stylesheet"> |
|
| 15 | 15 |
|
| 16 | 16 |
<!-- 공통 plugin --> |
| 17 | 17 |
<script src="/resources/front/site/SITE_00000/js/common/jquery/jquery-3.6.0.min.js"></script> |
... | ... | @@ -19,6 +19,7 @@ |
| 19 | 19 |
<!-- //공통 plugin --> |
| 20 | 20 |
|
| 21 | 21 |
<!-- 페이지 plugin--> |
| 22 |
+ <script type="module" src="/resources/front/site/SITE_00000/js/common/module/colouredPenciles.js"></script> |
|
| 22 | 23 |
<!-- //페이지 plugin--> |
| 23 | 24 |
|
| 24 | 25 |
<!-- 공통 퍼블 layout: 개발시 삭제--> |
... | ... | @@ -92,7 +93,7 @@ |
| 92 | 93 |
</div> |
| 93 | 94 |
<div class="nav-cont"> |
| 94 | 95 |
<div class="img-area"> |
| 95 |
- <img src="/resources/front/site/SITE_00000/images/temp/img1.jpg" alt="" /> |
|
| 96 |
+ <img src="/resources/front/site/SITE_00000/images/temp/img1.jpg" alt=""> |
|
| 96 | 97 |
</div> |
| 97 | 98 |
<div class="txt-area"> |
| 98 | 99 |
<div class="label-area"><span class="label-cont-video">영상</span></div> |
... | ... | @@ -133,44 +134,28 @@ |
| 133 | 134 |
<li> |
| 134 | 135 |
<button type="button" class="lnk-full">추가</button> |
| 135 | 136 |
<figure> |
| 136 |
- <img |
|
| 137 |
- src="/resources/front/site/SITE_00000/images/common/ico-img/ico-img-folder.svg" |
|
| 138 |
- alt="" |
|
| 139 |
- aria-hidden="true" |
|
| 140 |
- /> |
|
| 137 |
+ <img src="/resources/front/site/SITE_00000/images/common/ico-img/ico-img-folder.svg" alt="" aria-hidden="true"> |
|
| 141 | 138 |
</figure> |
| 142 | 139 |
<span>파일 업로드</span> |
| 143 | 140 |
</li> |
| 144 | 141 |
<li> |
| 145 | 142 |
<button type="button" class="lnk-full">추가</button> |
| 146 | 143 |
<figure> |
| 147 |
- <img |
|
| 148 |
- src="/resources/front/site/SITE_00000/images/common/ico-img/ico-img-link.svg" |
|
| 149 |
- alt="" |
|
| 150 |
- aria-hidden="true" |
|
| 151 |
- /> |
|
| 144 |
+ <img src="/resources/front/site/SITE_00000/images/common/ico-img/ico-img-link.svg" alt="" aria-hidden="true"> |
|
| 152 | 145 |
</figure> |
| 153 | 146 |
<span>링크 추가</span> |
| 154 | 147 |
</li> |
| 155 | 148 |
<li> |
| 156 | 149 |
<button type="button" class="lnk-full">추가</button> |
| 157 | 150 |
<figure> |
| 158 |
- <img |
|
| 159 |
- src="/resources/front/site/SITE_00000/images/common/ico-img/ico-img-filebox.svg" |
|
| 160 |
- alt="" |
|
| 161 |
- aria-hidden="true" |
|
| 162 |
- /> |
|
| 151 |
+ <img src="/resources/front/site/SITE_00000/images/common/ico-img/ico-img-filebox.svg" alt="" aria-hidden="true"> |
|
| 163 | 152 |
</figure> |
| 164 | 153 |
<span>나의 보관함에서 선택</span> |
| 165 | 154 |
</li> |
| 166 | 155 |
<li> |
| 167 | 156 |
<button type="button" class="lnk-full">추가</button> |
| 168 | 157 |
<figure> |
| 169 |
- <img |
|
| 170 |
- src="/resources/front/site/SITE_00000/images/common/ico-img/ico-img-search.svg" |
|
| 171 |
- alt="" |
|
| 172 |
- aria-hidden="true" |
|
| 173 |
- /> |
|
| 158 |
+ <img src="/resources/front/site/SITE_00000/images/common/ico-img/ico-img-search.svg" alt="" aria-hidden="true"> |
|
| 174 | 159 |
</figure> |
| 175 | 160 |
<span>콘텐츠에서 검색</span> |
| 176 | 161 |
</li> |
... | ... | @@ -185,20 +170,12 @@ |
| 185 | 170 |
<ul class="viewer-tool"> |
| 186 | 171 |
<li> |
| 187 | 172 |
<button type="button" class="btn-painter-toggle lnk-full">판서</button> |
| 188 |
- <img |
|
| 189 |
- src="/resources/front/site/SITE_00000/images/common/ico-img/ico-img-pencil.svg" |
|
| 190 |
- alt="" |
|
| 191 |
- aria-hidden="true" |
|
| 192 |
- /> |
|
| 173 |
+ <img src="/resources/front/site/SITE_00000/images/common/ico-img/ico-img-pencil.svg" alt="" aria-hidden="true"> |
|
| 193 | 174 |
<strong>판서</strong> |
| 194 | 175 |
</li> |
| 195 | 176 |
<li> |
| 196 | 177 |
<button type="button" class="lnk-full">채팅 열기&닫기</button> |
| 197 |
- <img |
|
| 198 |
- src="/resources/front/site/SITE_00000/images/common/ico-img/ico-img-chat.svg" |
|
| 199 |
- alt="" |
|
| 200 |
- aria-hidden="true" |
|
| 201 |
- /> |
|
| 178 |
+ <img src="/resources/front/site/SITE_00000/images/common/ico-img/ico-img-chat.svg" alt="" aria-hidden="true"> |
|
| 202 | 179 |
<strong>채팅</strong> |
| 203 | 180 |
<span class="badge min">새 채팅</span> |
| 204 | 181 |
</li> |
... | ... | @@ -232,8 +209,6 @@ |
| 232 | 209 |
}); |
| 233 | 210 |
}, |
| 234 | 211 |
}); |
| 235 |
- |
|
| 236 |
- |
|
| 237 | 212 |
</script> |
| 238 | 213 |
</body> |
| 239 |
-</html> |
|
| 214 |
+</html>(파일 끝에 줄바꿈 문자 없음) |
--- html/onSejong/manager/class/classViewer_URL.html
+++ html/onSejong/manager/class/classViewer_URL.html
... | ... | @@ -1,136 +1,137 @@ |
| 1 | 1 |
<!DOCTYPE html> |
| 2 | 2 |
<html lang="ko"> |
| 3 |
- <head> |
|
| 4 |
- <title>온세종학교</title> |
|
| 5 | 3 |
|
| 6 |
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> |
|
| 7 |
- <meta name="Author" content="" /> |
|
| 8 |
- <meta name="Keywords" content="" /> |
|
| 9 |
- <meta name="Description" content="" /> |
|
| 10 |
- <meta name="format-detection" content="telephone=no" /> |
|
| 11 |
- <meta http-equiv="X-UA-Compatible" content="IE=edge" /> |
|
| 12 |
- <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0" /> |
|
| 4 |
+<head> |
|
| 5 |
+ <title>온세종학교</title> |
|
| 6 |
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> |
|
| 7 |
+ <meta name="Author" content="" /> |
|
| 8 |
+ <meta name="Keywords" content="" /> |
|
| 9 |
+ <meta name="Description" content="" /> |
|
| 10 |
+ <meta name="format-detection" content="telephone=no" /> |
|
| 11 |
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" /> |
|
| 12 |
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0" /> |
|
| 13 |
+ <link href="/resources/front/site/SITE_00000/css/style.css" rel="stylesheet" /> |
|
| 14 |
+ <!-- 공통 plugin --> |
|
| 15 |
+ <script src="/resources/front/site/SITE_00000/js/common/jquery/jquery-3.6.0.min.js"></script> |
|
| 16 |
+ <script src="/resources/front/site/SITE_00000/js/common/jquery/jquery-ui.min.js"></script> |
|
| 17 |
+ <!-- //공통 plugin --> |
|
| 18 |
+ <!-- 페이지 plugin--> |
|
| 19 |
+ <script type="module" src="/resources/front/site/SITE_00000/js/common/module/colouredPenciles.js"></script> |
|
| 20 |
+ <!-- //페이지 plugin--> |
|
| 21 |
+ <!-- 공통 퍼블 layout: 개발시 삭제--> |
|
| 22 |
+ <!-- <script src="/resources/front/site/SITE_00000/js/_layout.js"></script> --> |
|
| 23 |
+ <!--//퍼블 layout--> |
|
| 24 |
+ <!-- 공통 메뉴 js--> |
|
| 25 |
+ <!-- //공통 메뉴 js --> |
|
| 26 |
+ <!--공통 퍼블 js--> |
|
| 27 |
+ <script src="/resources/front/site/SITE_00000/js/common.js"></script> |
|
| 28 |
+ <script src="/resources/front/site/SITE_00000/js/common-custom.js"></script> |
|
| 29 |
+ <!--//공통 퍼블 js--> |
|
| 30 |
+</head> |
|
| 13 | 31 |
|
| 14 |
- <link href="/resources/front/site/SITE_00000/css/style.css" rel="stylesheet" /> |
|
| 15 |
- |
|
| 16 |
- <!-- 공통 plugin --> |
|
| 17 |
- <script src="/resources/front/site/SITE_00000/js/common/jquery/jquery-3.6.0.min.js"></script> |
|
| 18 |
- <script src="/resources/front/site/SITE_00000/js/common/jquery/jquery-ui.min.js"></script> |
|
| 19 |
- <!-- //공통 plugin --> |
|
| 20 |
- |
|
| 21 |
- <!-- 페이지 plugin--> |
|
| 22 |
- <!-- //페이지 plugin--> |
|
| 23 |
- |
|
| 24 |
- <!-- 공통 퍼블 layout: 개발시 삭제--> |
|
| 25 |
- <!-- <script src="/resources/front/site/SITE_00000/js/_layout.js"></script> --> |
|
| 26 |
- <!--//퍼블 layout--> |
|
| 27 |
- |
|
| 28 |
- <!-- 공통 메뉴 js--> |
|
| 29 |
- <!-- //공통 메뉴 js --> |
|
| 30 |
- |
|
| 31 |
- <!--공통 퍼블 js--> |
|
| 32 |
- <script src="/resources/front/site/SITE_00000/js/common.js"></script> |
|
| 33 |
- <script src="/resources/front/site/SITE_00000/js/common-custom.js"></script> |
|
| 34 |
- <!--//공통 퍼블 js--> |
|
| 35 |
- </head> |
|
| 36 |
- |
|
| 37 |
- <body> |
|
| 38 |
- <section class="class-viewer"> |
|
| 39 |
- <header class="viewer-header"> |
|
| 40 |
- <div class="l-area"> |
|
| 41 |
- <button type="button" class="btn-class-viewer-menu btn circle ico-menu sm">메뉴</button> |
|
| 42 |
- <div class="viewer-tit"> |
|
| 43 |
- <h1 class=""> |
|
| 44 |
- 활동1) 무늬를 꾸며보는 방법 알아보기활동 |
|
| 45 |
- </h1> |
|
| 46 |
- </div> |
|
| 32 |
+<body> |
|
| 33 |
+ <section class="class-viewer"> |
|
| 34 |
+ <header class="viewer-header"> |
|
| 35 |
+ <div class="l-area"> |
|
| 36 |
+ <button type="button" class="btn-class-viewer-menu btn circle ico-menu sm">메뉴</button> |
|
| 37 |
+ <div class="viewer-tit"> |
|
| 38 |
+ <h1 class=""> 활동1) 무늬를 꾸며보는 방법 알아보기활동1) 무늬를 꾸며보는 방법 알아보기활동1) 무늬를 꾸며보는 방법 알아보기활동1) 무늬를 꾸며보는 방법 |
|
| 39 |
+ 알아보기활동1) 무늬를 꾸며보는 방법 알아보기 </h1> |
|
| 47 | 40 |
</div> |
| 48 |
- <div class="r-area pcta-flex"> |
|
| 49 |
- <div class="btn-group"> |
|
| 50 |
- <button type="button" class="btn sm primary"><i class="ico-save"></i> 저장</button> |
|
| 51 |
- <button type="button" class="btn sm"><i class="ico-logout"></i> 나가기</button> |
|
| 52 |
- </div> |
|
| 41 |
+ </div> |
|
| 42 |
+ <div class="r-area pcta-flex"> |
|
| 43 |
+ <div class="btn-group"> |
|
| 44 |
+ <button type="button" class="btn sm primary"><i class="ico-save"></i> 저장</button> |
|
| 45 |
+ <button type="button" class="btn sm"><i class="ico-logout"></i> 나가기</button> |
|
| 53 | 46 |
</div> |
| 54 |
- <div class="r-area mo-flex"> |
|
| 55 |
- <div class="etc-fnc-area"> |
|
| 56 |
- <button type="button" class="btn-etc-fnc btn-ico ico-more-vert">더보기</button> |
|
| 57 |
- <ul class="etc-fnc"> |
|
| 58 |
- <li><button type="button">북마크</button></li> |
|
| 59 |
- <li><button type="button">학생뷰어보기</button></li> |
|
| 60 |
- <li><button type="button">저장</button></li> |
|
| 61 |
- <li><button type="button">나가기</button></li> |
|
| 62 |
- </ul> |
|
| 63 |
- </div> |
|
| 47 |
+ </div> |
|
| 48 |
+ <div class="r-area mo-flex"> |
|
| 49 |
+ <div class="etc-fnc-area"> |
|
| 50 |
+ <button type="button" class="btn-etc-fnc btn-ico ico-more-vert">더보기</button> |
|
| 51 |
+ <ul class="etc-fnc"> |
|
| 52 |
+ <li><button type="button">북마크</button></li> |
|
| 53 |
+ <li><button type="button">학생뷰어보기</button></li> |
|
| 54 |
+ <li><button type="button">저장</button></li> |
|
| 55 |
+ <li><button type="button">나가기</button></li> |
|
| 56 |
+ </ul> |
|
| 64 | 57 |
</div> |
| 65 |
- </header> |
|
| 66 |
- <div class="viewer-body"> |
|
| 67 |
- <div class="viewer-menu-area"> |
|
| 68 |
- <div class="viewer-menu-header"> |
|
| 69 |
- <div class="menu-add-area"> |
|
| 70 |
- <button class="btn-menu-add"><span>추가</span></button> |
|
| 71 |
- <div class="menu-add-option"> |
|
| 72 |
- <button type="button">파일 업로드</button> |
|
| 73 |
- <button type="button">링크추가</button> |
|
| 74 |
- <button type="button">나의 보관함에서 선택</button> |
|
| 75 |
- <button type="button">콘텐츠에서 검색</button> |
|
| 76 |
- </div> |
|
| 58 |
+ </div> |
|
| 59 |
+ </header> |
|
| 60 |
+ <div class="viewer-body"> |
|
| 61 |
+ <div class="viewer-menu-area"> |
|
| 62 |
+ <div class="viewer-menu-header"> |
|
| 63 |
+ <div class="menu-add-area"> |
|
| 64 |
+ <button class="btn-menu-add"><span>추가</span></button> |
|
| 65 |
+ <div class="menu-add-option"> |
|
| 66 |
+ <button type="button">파일 업로드</button> |
|
| 67 |
+ <button type="button">링크추가</button> |
|
| 68 |
+ <button type="button">나의 보관함에서 선택</button> |
|
| 69 |
+ <button type="button">콘텐츠에서 검색</button> |
|
| 77 | 70 |
</div> |
| 78 | 71 |
</div> |
| 79 |
- <div class="viewer-menu-body"> |
|
| 80 |
- <nav class="viewer-nav"> |
|
| 81 |
- <ul> |
|
| 82 |
- <li class="is-active"> |
|
| 83 |
- <a href="#none" class="lnk-full">상세보기</a> |
|
| 84 |
- <div class="nav-info"> |
|
| 85 |
- <span class="num">1</span> |
|
| 86 |
- <div class="fnc order-front"> |
|
| 87 |
- <button type="button" class="ico-delete order-front">삭제</button> |
|
| 88 |
- <i class="ico-move">이동</i> |
|
| 89 |
- </div> |
|
| 90 |
- </div> |
|
| 91 |
- <div class="nav-cont"> |
|
| 92 |
- <div class="img-area"> |
|
| 93 |
- <img src="/resources/front/site/SITE_00000/images/temp/img1.jpg" alt="" /> |
|
| 94 |
- </div> |
|
| 95 |
- <div class="txt-area"> |
|
| 96 |
- <div class="label-area"><span class="label-cont-url">링크</span></div> |
|
| 97 |
- <div class="txt"> |
|
| 98 |
- 파일명파일명 |
|
| 99 |
- 파일명파일명파일명파일명파일명파일명파일명파일명파일명파일명.mp4 |
|
| 100 |
- </div> |
|
| 101 |
- </div> |
|
| 102 |
- </div> |
|
| 103 |
- </li> |
|
| 104 |
- <li> |
|
| 105 |
- <a href="#none" class="lnk-full">상세보기</a> |
|
| 106 |
- <div class="nav-info"> |
|
| 107 |
- <span class="num">2</span> |
|
| 108 |
- <div class="fnc order-front"> |
|
| 109 |
- <button type="button" class="ico-delete order-front">삭제</button> |
|
| 110 |
- <i class="ico-move">이동</i> |
|
| 111 |
- </div> |
|
| 112 |
- </div> |
|
| 113 |
- <div class="nav-cont"> |
|
| 114 |
- <div class="txt-area"> |
|
| 115 |
- <div class="label-area"><span class="label-cont-video">영상</span></div> |
|
| 116 |
- <div class="txt">파일명.mp4</div> |
|
| 117 |
- </div> |
|
| 118 |
- </div> |
|
| 119 |
- </li> |
|
| 120 |
- </ul> |
|
| 121 |
- </nav> |
|
| 122 |
- </div> |
|
| 123 |
- <button type="button" class="btn-viewer-menu-toggle"> |
|
| 124 |
- <span class="txt-hide">메뉴 열기&닫기</span> |
|
| 125 |
- </button> |
|
| 126 | 72 |
</div> |
| 127 |
- <div class="viewer-cont"> |
|
| 128 |
- <div class="wrap-xsm viewer-cont-url-area"> |
|
| 73 |
+ <div class="viewer-menu-body"> |
|
| 74 |
+ <nav class="viewer-nav"> |
|
| 75 |
+ <ul> |
|
| 76 |
+ <li class="is-active"> |
|
| 77 |
+ <a href="#none" class="lnk-full">상세보기</a> |
|
| 78 |
+ <div class="nav-info"> |
|
| 79 |
+ <span class="num">1</span> |
|
| 80 |
+ <div class="fnc order-front"> |
|
| 81 |
+ <button type="button" class="ico-delete order-front">삭제</button> |
|
| 82 |
+ <i class="ico-move">이동</i> |
|
| 83 |
+ </div> |
|
| 84 |
+ </div> |
|
| 85 |
+ <div class="nav-cont"> |
|
| 86 |
+ <div class="img-area"> |
|
| 87 |
+ <img src="/resources/front/site/SITE_00000/images/temp/img1.jpg" alt="" /> |
|
| 88 |
+ </div> |
|
| 89 |
+ <div class="txt-area"> |
|
| 90 |
+ <div class="label-area"><span class="label-cont-video">영상</span></div> |
|
| 91 |
+ <div class="txt"> 파일명파일명 파일명파일명파일명파일명파일명파일명파일명파일명파일명파일명.mp4 </div> |
|
| 92 |
+ </div> |
|
| 93 |
+ </div> |
|
| 94 |
+ </li> |
|
| 95 |
+ <li> |
|
| 96 |
+ <a href="#none" class="lnk-full">상세보기</a> |
|
| 97 |
+ <div class="nav-info"> |
|
| 98 |
+ <span class="num">2</span> |
|
| 99 |
+ <div class="fnc order-front"> |
|
| 100 |
+ <button type="button" class="ico-delete order-front">삭제</button> |
|
| 101 |
+ <i class="ico-move">이동</i> |
|
| 102 |
+ </div> |
|
| 103 |
+ </div> |
|
| 104 |
+ <div class="nav-cont"> |
|
| 105 |
+ <div class="txt-area"> |
|
| 106 |
+ <div class="label-area"><span class="label-cont-video">영상</span></div> |
|
| 107 |
+ <div class="txt">파일명.mp4</div> |
|
| 108 |
+ </div> |
|
| 109 |
+ </div> |
|
| 110 |
+ </li> |
|
| 111 |
+ </ul> |
|
| 112 |
+ </nav> |
|
| 113 |
+ </div> |
|
| 114 |
+ <button type="button" class="btn-viewer-menu-toggle"> |
|
| 115 |
+ <span class="txt-hide">메뉴 열기&닫기</span> |
|
| 116 |
+ </button> |
|
| 117 |
+ </div> |
|
| 118 |
+ <div class="viewer-cont"> |
|
| 119 |
+ <!-- content: 이미지, 비디오, iframe --> |
|
| 120 |
+ <!-- <div class="viewer-cont-box"> |
|
| 121 |
+ <img src="/resources/front/site/SITE_00000/images/temp/img1.jpg" alt=""> |
|
| 122 |
+ <video controls="controls"> |
|
| 123 |
+ <source src="https://www.w3schools.com/tags/mov_bbb.mp4" type="video/mp4"> |
|
| 124 |
+ </video> |
|
| 125 |
+ <iframe src="/resources/front/site/SITE_00000/images/temp/pdf.pdf"></iframe> |
|
| 126 |
+ </div> --> |
|
| 127 |
+ <!-- url --> |
|
| 128 |
+ <div class="wrap-xsm viewer-cont-url-area"> |
|
| 129 | 129 |
<ul class="btn-box-list"> |
| 130 | 130 |
<li> |
| 131 | 131 |
<a href="#none" type="button" class="lnk-full" target="_blank" title="새창 열기">새창열기</a> |
| 132 | 132 |
<figure> |
| 133 |
- <img src="/resources/front/site/SITE_00000/images/custom/ico-cont/ico-cont-link.svg" alt="" aria-hidden="true"> |
|
| 133 |
+ <img src="/resources/front/site/SITE_00000/images/custom/ico-cont/ico-cont-link.svg" alt="" |
|
| 134 |
+ aria-hidden="true"> |
|
| 134 | 135 |
</figure> |
| 135 | 136 |
<span>www.naver.comwww.naver</span> |
| 136 | 137 |
<div class="fnc"> |
... | ... | @@ -139,64 +140,55 @@ |
| 139 | 140 |
</li> |
| 140 | 141 |
</ul> |
| 141 | 142 |
</div> |
| 142 |
- <div class="viewer-fnc-cont"> |
|
| 143 |
- <!-- 색연필 --> |
|
| 144 |
- <div class="viewer-painter paint-target"></div> |
|
| 145 |
- </div> |
|
| 143 |
+ <div class="viewer-fnc-cont"> |
|
| 144 |
+ <!-- 색연필 --> |
|
| 145 |
+ <div class="viewer-painter paint-target"></div> |
|
| 146 | 146 |
</div> |
| 147 | 147 |
</div> |
| 148 |
- <ul class="viewer-tool"> |
|
| 149 |
- <li> |
|
| 150 |
- <button type="button" class="btn-painter-toggle lnk-full">판서</button> |
|
| 151 |
- <img |
|
| 152 |
- src="/resources/front/site/SITE_00000/images/common/ico-img/ico-img-pencil.svg" |
|
| 153 |
- alt="" |
|
| 154 |
- aria-hidden="true" |
|
| 155 |
- /> |
|
| 156 |
- <strong>판서</strong> |
|
| 157 |
- </li> |
|
| 158 |
- <li> |
|
| 159 |
- <button type="button" class="lnk-full">채팅 열기&닫기</button> |
|
| 160 |
- <img |
|
| 161 |
- src="/resources/front/site/SITE_00000/images/common/ico-img/ico-img-chat.svg" |
|
| 162 |
- alt="" |
|
| 163 |
- aria-hidden="true" |
|
| 164 |
- /> |
|
| 165 |
- <strong>채팅</strong> |
|
| 166 |
- <span class="badge min">새 채팅</span> |
|
| 167 |
- </li> |
|
| 168 |
- </ul> |
|
| 169 |
- </section> |
|
| 148 |
+ </div> |
|
| 149 |
+ <ul class="viewer-tool"> |
|
| 150 |
+ <li> |
|
| 151 |
+ <button type="button" class="btn-painter-toggle lnk-full">판서</button> |
|
| 152 |
+ <img src="/resources/front/site/SITE_00000/images/common/ico-img/ico-img-pencil.svg" alt="" |
|
| 153 |
+ aria-hidden="true"> |
|
| 154 |
+ <strong>판서</strong> |
|
| 155 |
+ </li> |
|
| 156 |
+ <li> |
|
| 157 |
+ <button type="button" class="lnk-full">채팅 열기&닫기</button> |
|
| 158 |
+ <img src="/resources/front/site/SITE_00000/images/common/ico-img/ico-img-chat.svg" alt="" |
|
| 159 |
+ aria-hidden="true"> |
|
| 160 |
+ <strong>채팅</strong> |
|
| 161 |
+ <span class="badge min">새 채팅</span> |
|
| 162 |
+ </li> |
|
| 163 |
+ </ul> |
|
| 164 |
+ </section> |
|
| 165 |
+ <!--toast--> |
|
| 166 |
+ <div class="toast"></div> |
|
| 167 |
+ <script> |
|
| 168 |
+ // 과정추가 |
|
| 169 |
+ $(document).on("click", ".btn-menu-add", function (e) {
|
|
| 170 |
+ e.stopPropagation(); |
|
| 171 |
+ $(".menu-add-area").toggleClass("is-active");
|
|
| 172 |
+ }); |
|
| 170 | 173 |
|
| 171 |
- <!--toast--> |
|
| 172 |
- <div class="toast"></div> |
|
| 174 |
+ $(document).on("click", function () {
|
|
| 175 |
+ $(".menu-add-area").removeClass("is-active");
|
|
| 176 |
+ }); |
|
| 173 | 177 |
|
| 174 |
- <script> |
|
| 175 |
- // 과정추가 |
|
| 176 |
- $(document).on("click", ".btn-menu-add", function (e) {
|
|
| 177 |
- e.stopPropagation(); |
|
| 178 |
- $(".menu-add-area").toggleClass("is-active");
|
|
| 179 |
- }); |
|
| 178 |
+ //과정 순서 변경 |
|
| 179 |
+ $(".class-viewer-nav>ul").sortable({
|
|
| 180 |
+ handle: $(".class-viewer-nav>ul>li").find(".ico-move"),
|
|
| 181 |
+ containment: "parent", |
|
| 182 |
+ update: function (event, ui) {
|
|
| 183 |
+ var $list = $(this).children("li");
|
|
| 184 |
+ $list.each(function () {
|
|
| 185 |
+ var $li = $(this); |
|
| 186 |
+ var newVal = $(this).index() + 1; |
|
| 187 |
+ $(this).find(".num").html(newVal);
|
|
| 188 |
+ }); |
|
| 189 |
+ }, |
|
| 190 |
+ }); |
|
| 191 |
+ </script> |
|
| 192 |
+</body> |
|
| 180 | 193 |
|
| 181 |
- $(document).on("click", function () {
|
|
| 182 |
- $(".menu-add-area").removeClass("is-active");
|
|
| 183 |
- }); |
|
| 184 |
- |
|
| 185 |
- //과정 순서 변경 |
|
| 186 |
- $(".class-viewer-nav>ul").sortable({
|
|
| 187 |
- handle: $(".class-viewer-nav>ul>li").find(".ico-move"),
|
|
| 188 |
- containment: "parent", |
|
| 189 |
- update: function (event, ui) {
|
|
| 190 |
- var $list = $(this).children("li");
|
|
| 191 |
- $list.each(function () {
|
|
| 192 |
- var $li = $(this); |
|
| 193 |
- var newVal = $(this).index() + 1; |
|
| 194 |
- $(this).find(".num").html(newVal);
|
|
| 195 |
- }); |
|
| 196 |
- }, |
|
| 197 |
- }); |
|
| 198 |
- |
|
| 199 |
- |
|
| 200 |
- </script> |
|
| 201 |
- </body> |
|
| 202 |
-</html> |
|
| 194 |
+</html>(파일 끝에 줄바꿈 문자 없음) |
--- html/onSejong/manager/class/classViewer_img.html
+++ html/onSejong/manager/class/classViewer_cont.html
... | ... | @@ -1,4 +1,4 @@ |
| 1 |
-<!DOCTYPE html> |
|
| 1 |
+ <!DOCTYPE html> |
|
| 2 | 2 |
<html lang="ko"> |
| 3 | 3 |
<head> |
| 4 | 4 |
<title>온세종학교</title> |
... | ... | @@ -19,6 +19,7 @@ |
| 19 | 19 |
<!-- //공통 plugin --> |
| 20 | 20 |
|
| 21 | 21 |
<!-- 페이지 plugin--> |
| 22 |
+ <script type="module" src="/resources/front/site/SITE_00000/js/common/module/colouredPenciles.js"></script> |
|
| 22 | 23 |
<!-- //페이지 plugin--> |
| 23 | 24 |
|
| 24 | 25 |
<!-- 공통 퍼블 layout: 개발시 삭제--> |
... | ... | @@ -41,7 +42,9 @@ |
| 41 | 42 |
<button type="button" class="btn-class-viewer-menu btn circle ico-menu sm">메뉴</button> |
| 42 | 43 |
<div class="viewer-tit"> |
| 43 | 44 |
<h1 class=""> |
| 44 |
- 활동1) 무늬를 꾸며보는 방법 알아보기활동 |
|
| 45 |
+ 활동1) 무늬를 꾸며보는 방법 알아보기활동1) 무늬를 꾸며보는 방법 알아보기활동1) 무늬를 |
|
| 46 |
+ 꾸며보는 방법 알아보기활동1) 무늬를 꾸며보는 방법 알아보기활동1) 무늬를 꾸며보는 방법 |
|
| 47 |
+ 알아보기 |
|
| 45 | 48 |
</h1> |
| 46 | 49 |
</div> |
| 47 | 50 |
</div> |
... | ... | @@ -93,7 +96,7 @@ |
| 93 | 96 |
<img src="/resources/front/site/SITE_00000/images/temp/img1.jpg" alt="" /> |
| 94 | 97 |
</div> |
| 95 | 98 |
<div class="txt-area"> |
| 96 |
- <div class="label-area"><span class="label-cont-img">이미지</span></div> |
|
| 99 |
+ <div class="label-area"><span class="label-cont-video">영상</span></div> |
|
| 97 | 100 |
<div class="txt"> |
| 98 | 101 |
파일명파일명 |
| 99 | 102 |
파일명파일명파일명파일명파일명파일명파일명파일명파일명파일명.mp4 |
... | ... | @@ -125,9 +128,30 @@ |
| 125 | 128 |
</button> |
| 126 | 129 |
</div> |
| 127 | 130 |
<div class="viewer-cont"> |
| 128 |
- <figure> |
|
| 131 |
+ <!-- content: 이미지, 비디오, iframe --> |
|
| 132 |
+ <div class="viewer-cont-box"> |
|
| 129 | 133 |
<img src="/resources/front/site/SITE_00000/images/temp/img1.jpg" alt=""> |
| 130 |
- </figure> |
|
| 134 |
+ <!-- <video controls="controls"> |
|
| 135 |
+ <source src="https://www.w3schools.com/tags/mov_bbb.mp4" type="video/mp4"> |
|
| 136 |
+ </video> |
|
| 137 |
+ <iframe src="/resources/front/site/SITE_00000/images/temp/pdf.pdf"></iframe> --> |
|
| 138 |
+ </div> |
|
| 139 |
+ <!-- url --> |
|
| 140 |
+ <!-- <div class="wrap-xsm viewer-cont-url-area"> |
|
| 141 |
+ <ul class="btn-box-list"> |
|
| 142 |
+ <li> |
|
| 143 |
+ <a href="#none" type="button" class="lnk-full" target="_blank" title="새창 열기">새창열기</a> |
|
| 144 |
+ <figure> |
|
| 145 |
+ <img src="/resources/front/site/SITE_00000/images/custom/ico-cont/ico-cont-link.svg" alt="" |
|
| 146 |
+ aria-hidden="true"> |
|
| 147 |
+ </figure> |
|
| 148 |
+ <span>www.naver.comwww.naver</span> |
|
| 149 |
+ <div class="fnc"> |
|
| 150 |
+ <i class="ico-openinnew">새 창 열기</i> |
|
| 151 |
+ </div> |
|
| 152 |
+ </li> |
|
| 153 |
+ </ul> |
|
| 154 |
+ </div> --> |
|
| 131 | 155 |
<div class="viewer-fnc-cont"> |
| 132 | 156 |
<!-- 색연필 --> |
| 133 | 157 |
<div class="viewer-painter paint-target"></div> |
... | ... | @@ -137,20 +161,12 @@ |
| 137 | 161 |
<ul class="viewer-tool"> |
| 138 | 162 |
<li> |
| 139 | 163 |
<button type="button" class="btn-painter-toggle lnk-full">판서</button> |
| 140 |
- <img |
|
| 141 |
- src="/resources/front/site/SITE_00000/images/common/ico-img/ico-img-pencil.svg" |
|
| 142 |
- alt="" |
|
| 143 |
- aria-hidden="true" |
|
| 144 |
- /> |
|
| 164 |
+ <img src="/resources/front/site/SITE_00000/images/common/ico-img/ico-img-pencil.svg" alt="" aria-hidden="true"> |
|
| 145 | 165 |
<strong>판서</strong> |
| 146 | 166 |
</li> |
| 147 | 167 |
<li> |
| 148 | 168 |
<button type="button" class="lnk-full">채팅 열기&닫기</button> |
| 149 |
- <img |
|
| 150 |
- src="/resources/front/site/SITE_00000/images/common/ico-img/ico-img-chat.svg" |
|
| 151 |
- alt="" |
|
| 152 |
- aria-hidden="true" |
|
| 153 |
- /> |
|
| 169 |
+ <img src="/resources/front/site/SITE_00000/images/common/ico-img/ico-img-chat.svg" alt="" aria-hidden="true"> |
|
| 154 | 170 |
<strong>채팅</strong> |
| 155 | 171 |
<span class="badge min">새 채팅</span> |
| 156 | 172 |
</li> |
... | ... | @@ -184,8 +200,6 @@ |
| 184 | 200 |
}); |
| 185 | 201 |
}, |
| 186 | 202 |
}); |
| 187 |
- |
|
| 188 |
- |
|
| 189 | 203 |
</script> |
| 190 | 204 |
</body> |
| 191 |
-</html> |
|
| 205 |
+</html>(파일 끝에 줄바꿈 문자 없음) |
--- html/onSejong/manager/class/classViewer_painter.html
... | ... | @@ -1,231 +0,0 @@ |
| 1 | -<!DOCTYPE html> | |
| 2 | -<html lang="ko"> | |
| 3 | - | |
| 4 | -<head> | |
| 5 | - <title>온세종학교</title> | |
| 6 | - | |
| 7 | - <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> | |
| 8 | - <meta name="Author" content=""> | |
| 9 | - <meta name="Keywords" content=""> | |
| 10 | - <meta name="Description" content=""> | |
| 11 | - <meta name="format-detection" content="telephone=no"> | |
| 12 | - <meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
| 13 | - <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0"> | |
| 14 | - | |
| 15 | - <link href="/resources/front/site/SITE_00000/css/style.css" rel="stylesheet"> | |
| 16 | - | |
| 17 | - <!-- 공통 plugin --> | |
| 18 | - <script src="/resources/front/site/SITE_00000/js/common/jquery/jquery-3.6.0.min.js"></script> | |
| 19 | - <script src="/resources/front/site/SITE_00000/js/common/jquery/jquery-ui.min.js"></script> | |
| 20 | - <!-- //공통 plugin --> | |
| 21 | - | |
| 22 | - <!-- 페이지 plugin--> | |
| 23 | - <!-- //페이지 plugin--> | |
| 24 | - | |
| 25 | - <!-- 공통 퍼블 layout: 개발시 삭제--> | |
| 26 | - <!-- <script src="/resources/front/site/SITE_00000/js/_layout.js"></script> --> | |
| 27 | - <!--//퍼블 layout--> | |
| 28 | - | |
| 29 | - <!-- 공통 메뉴 js--> | |
| 30 | - <!-- //공통 메뉴 js --> | |
| 31 | - | |
| 32 | - <!--공통 퍼블 js--> | |
| 33 | - <script src="/resources/front/site/SITE_00000/js/common.js"></script> | |
| 34 | - <script src="/resources/front/site/SITE_00000/js/common-custom.js"></script> | |
| 35 | - <!--//공통 퍼블 js--> | |
| 36 | -</head> | |
| 37 | - | |
| 38 | -<body> | |
| 39 | - <section class="class-viewer"> | |
| 40 | - <header class="viewer-header"> | |
| 41 | - <div class="l-area"> | |
| 42 | - <!-- <button type="button" class="btn-viewer-menu btn circle ico-menu sm">메뉴</button> --> | |
| 43 | - <div class="viewer-tit"> | |
| 44 | - <h1 class="">활동1) 무늬를 꾸며보는 방법 알아보기활동1) 무늬를 꾸며보는 방법 알아보기활동1) 무늬를 꾸며보는 방법 알아보기활동1) 무늬를 꾸며보는 방법 알아보기활동1) 무늬를 꾸며보는 방법 알아보기</h1> | |
| 45 | - </div> | |
| 46 | - </div> | |
| 47 | - <div class="r-area"> | |
| 48 | - <div class="btn-group"> | |
| 49 | - <button type="button" class="btn sm primary"><i class="ico-save"></i> 저장</button> | |
| 50 | - <button type="button" class="btn sm"><i class="ico-logout"></i> 나가기</button> | |
| 51 | - </div> | |
| 52 | - </div> | |
| 53 | - </header> | |
| 54 | - <div class="viewer-body"> | |
| 55 | - <div class="viewer-menu-area"> | |
| 56 | - <div class="viewer-menu-header"> | |
| 57 | - <div class="menu-add-area"> | |
| 58 | - <button class="btn-menu-add"><span>과정추가</span></button> | |
| 59 | - <div class="menu-add-option" > | |
| 60 | - <button type="button">파일 업로드</button> | |
| 61 | - <button type="button">링크추가</button> | |
| 62 | - <button type="button">나의 보관함에서 선택</button> | |
| 63 | - <button type="button">콘텐츠에서 검색</button> | |
| 64 | - </div> | |
| 65 | - </div> | |
| 66 | - </div> | |
| 67 | - <div class="viewer-menu-body"> | |
| 68 | - <nav class="viewer-nav"> | |
| 69 | - <ul> | |
| 70 | - <li class="is-active"> | |
| 71 | - <a href="javascript:changeIfr(1)" class="lnk-full">상세보기</a> | |
| 72 | - <div class="nav-info"> | |
| 73 | - <span class="num">1</span> | |
| 74 | - <div class="fnc order-front"> | |
| 75 | - <button type="button" class="ico-delete order-front">삭제</button> | |
| 76 | - <i class="ico-move">이동</i> | |
| 77 | - </div> | |
| 78 | - </div> | |
| 79 | - <div class="nav-cont"> | |
| 80 | - <div class="img-area"> | |
| 81 | - <img src="/resources/front/site/SITE_00000/images/temp/img1.jpg" alt=""> | |
| 82 | - </div> | |
| 83 | - <div class="txt-area"> | |
| 84 | - <div class="label-area"><span class="label-cont-img">이미지</span></div> | |
| 85 | - <div class="txt">이미지</div> | |
| 86 | - </div> | |
| 87 | - </div> | |
| 88 | - </li> | |
| 89 | - <li> | |
| 90 | - <a href="javascript:changeIfr(2)" class="lnk-full">상세보기</a> | |
| 91 | - <div class="nav-info"> | |
| 92 | - <span class="num">2</span> | |
| 93 | - <div class="fnc order-front"> | |
| 94 | - <button type="button" class="ico-delete order-front">삭제</button> | |
| 95 | - <i class="ico-move">이동</i> | |
| 96 | - </div> | |
| 97 | - </div> | |
| 98 | - <div class="nav-cont"> | |
| 99 | - <div class="txt-area"> | |
| 100 | - <div class="label-area"><span class="label-cont-video">영상</span></div> | |
| 101 | - <div class="txt">영상</div> | |
| 102 | - </div> | |
| 103 | - </div> | |
| 104 | - </li> | |
| 105 | - <li> | |
| 106 | - <a href="javascript:changeIfr(3)" class="lnk-full">상세보기</a> | |
| 107 | - <div class="nav-info"> | |
| 108 | - <span class="num">3</span> | |
| 109 | - <div class="fnc order-front"> | |
| 110 | - <button type="button" class="ico-delete order-front">삭제</button> | |
| 111 | - <i class="ico-move">유튜브</i> | |
| 112 | - </div> | |
| 113 | - </div> | |
| 114 | - <div class="nav-cont"> | |
| 115 | - <div class="txt-area"> | |
| 116 | - <div class="label-area"><span class="label-cont-video">영상</span></div> | |
| 117 | - <div class="txt">유튜브</div> | |
| 118 | - </div> | |
| 119 | - </div> | |
| 120 | - </li> | |
| 121 | - <li> | |
| 122 | - </ul> | |
| 123 | - </nav> | |
| 124 | - </div> | |
| 125 | - <button type="button" class="btn-viewer-menu-toggle"><span class="txt-hide">메뉴 열기&닫기</span></button> | |
| 126 | - </div> | |
| 127 | - <div class="viewer-cont"> | |
| 128 | - <figure> | |
| 129 | - <img src="/resources/front/site/SITE_00000/images/temp/img1.jpg" alt=""> | |
| 130 | - </figure> | |
| 131 | - <!-- <div class="iframe-area"> | |
| 132 | - <iframe src="/resources/front/site/SITE_00000/images/temp/img1.jpg" class="view_iframe"></iframe> | |
| 133 | - </div> --> | |
| 134 | - <div class="viewer-cont-fnc-cont"> | |
| 135 | - <!-- 색연필 --> | |
| 136 | - <div class="viewer-painter paint-target"></div> | |
| 137 | - </div> | |
| 138 | - </div> | |
| 139 | - <ul class="viewer-tool"> | |
| 140 | - <li> | |
| 141 | - <button type="button" class="btn-painter-toggle lnk-full">판서</button> | |
| 142 | - <img src="/resources/front/site/SITE_00000/images/common/ico-img/ico-img-pencil.svg" alt="" aria-hidden="true"> | |
| 143 | - <strong>판서</strong> | |
| 144 | - </li> | |
| 145 | - <li> | |
| 146 | - <button type="button" class="lnk-full">채팅 열기&닫기</button> | |
| 147 | - <img src="/resources/front/site/SITE_00000/images/common/ico-img/ico-img-chat.svg" alt="" aria-hidden="true"> | |
| 148 | - <strong>채팅</strong> | |
| 149 | - <span class="badge min">새 채팅</span> | |
| 150 | - </li> | |
| 151 | - </ul> | |
| 152 | - </div> | |
| 153 | - </section> | |
| 154 | - | |
| 155 | - <!--toast--> | |
| 156 | - <div class="toast"></div> | |
| 157 | - | |
| 158 | - <!--popup--> | |
| 159 | - <script> | |
| 160 | - // 과정추가 | |
| 161 | - $(document).on('click', '.btn-menu-add', function (e) { | |
| 162 | - e.stopPropagation(); | |
| 163 | - $('.menu-add-area').toggleClass('is-active'); | |
| 164 | - }); | |
| 165 | - | |
| 166 | - $(document).on('click', function () { | |
| 167 | - $('.menu-add-area').removeClass('is-active'); | |
| 168 | - }); | |
| 169 | - | |
| 170 | - //과정 순서 변경 | |
| 171 | - $(".viewer-nav>ul").sortable({ | |
| 172 | - handle: $(".viewer-nav>ul>li").find(".ico-move"), | |
| 173 | - axis: "y", | |
| 174 | - //containment: "parent", | |
| 175 | - update: function (event, ui) { | |
| 176 | - var $list = $(this).children("li"); | |
| 177 | - $list.each(function () { | |
| 178 | - var $li = $(this); | |
| 179 | - var newVal = $(this).index() + 1; | |
| 180 | - $(this).find(".num").html(newVal); | |
| 181 | - }); | |
| 182 | - }, | |
| 183 | - }); | |
| 184 | - </script> | |
| 185 | - | |
| 186 | - | |
| 187 | - <!-- 모듈로 스크립트를 불러오기 --> | |
| 188 | - <script type="module"> | |
| 189 | - import { initColouredPenciles } from '/resources/front/site/SITE_00000/js/custom/module/colouredPencilesSvg_pub.js'; | |
| 190 | - </script> | |
| 191 | - | |
| 192 | - <script> | |
| 193 | - // colouredPencilesSvg.js 데이터 전달 스크립트 | |
| 194 | - const cpInstanceData = (data) => { | |
| 195 | - if (window.cpInstance) { | |
| 196 | - window.cpInstance.setDataParam(data); | |
| 197 | - } else { | |
| 198 | - // console.log('cpInstance 아직 정의되지 않음, 500ms 후 다시 시도.'); | |
| 199 | - setTimeout(cpInstanceData, 500, data); // cpInstance 정의 전까지 500ms 간격으로 재시도 | |
| 200 | - } | |
| 201 | - } | |
| 202 | - const cpInstanceSave = () => { | |
| 203 | - if (window.cpInstance) { | |
| 204 | - window.cpInstance.saveDrawing(); | |
| 205 | - } else { | |
| 206 | - // console.log('cpInstance 아직 정의되지 않음, 500ms 후 다시 시도.'); | |
| 207 | - setTimeout(cpInstanceSave, 500, data); // cpInstance 정의 전까지 500ms 간격으로 재시도 | |
| 208 | - } | |
| 209 | - } | |
| 210 | - | |
| 211 | - // 페이지 로드 시 사용자 키 및 콘텐츠 키 전달 | |
| 212 | - window.addEventListener('DOMContentLoaded', () => { | |
| 213 | - const page = 0; | |
| 214 | - cpInstanceData({ userKey: 'testuser', contentsKey: `ifrCnt_${page}` }); // 사용자 키 설정 | |
| 215 | - }); | |
| 216 | - // 콘텐츠 변경 시, 콘텐츠 키 발행 및 전달 | |
| 217 | - const changeIfr = (page) => { | |
| 218 | - cpInstanceSave(); // 페이지 이동 전 저장 | |
| 219 | - const ifr = document.querySelector('.iframe-area iframe'); | |
| 220 | - ifr.src = `ifrCnt/ifrCnt_${page}.html`; | |
| 221 | - cpInstanceData({ contentsKey: `ifrCnt_${page}` }); // 콘텐츠 키 설정 | |
| 222 | - } | |
| 223 | - | |
| 224 | - $('.viewer-nav li').on('click', function() { | |
| 225 | - $('.viewer-nav li').removeClass('is-active'); | |
| 226 | - $(this).addClass('is-active'); | |
| 227 | - }); | |
| 228 | - </script> | |
| 229 | -</body> | |
| 230 | - | |
| 231 | -</html>(파일 끝에 줄바꿈 문자 없음) |
--- html/onSejong/manager/class/classViewer_painter_doc.html
... | ... | @@ -1,231 +0,0 @@ |
| 1 | -<!DOCTYPE html> | |
| 2 | -<html lang="ko"> | |
| 3 | - | |
| 4 | -<head> | |
| 5 | - <title>온세종학교</title> | |
| 6 | - | |
| 7 | - <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> | |
| 8 | - <meta name="Author" content=""> | |
| 9 | - <meta name="Keywords" content=""> | |
| 10 | - <meta name="Description" content=""> | |
| 11 | - <meta name="format-detection" content="telephone=no"> | |
| 12 | - <meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
| 13 | - <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0"> | |
| 14 | - | |
| 15 | - <link href="/resources/front/site/SITE_00000/css/style.css" rel="stylesheet"> | |
| 16 | - | |
| 17 | - <!-- 공통 plugin --> | |
| 18 | - <script src="/resources/front/site/SITE_00000/js/common/jquery/jquery-3.6.0.min.js"></script> | |
| 19 | - <script src="/resources/front/site/SITE_00000/js/common/jquery/jquery-ui.min.js"></script> | |
| 20 | - <!-- //공통 plugin --> | |
| 21 | - | |
| 22 | - <!-- 페이지 plugin--> | |
| 23 | - <!-- //페이지 plugin--> | |
| 24 | - | |
| 25 | - <!-- 공통 퍼블 layout: 개발시 삭제--> | |
| 26 | - <!-- <script src="/resources/front/site/SITE_00000/js/_layout.js"></script> --> | |
| 27 | - <!--//퍼블 layout--> | |
| 28 | - | |
| 29 | - <!-- 공통 메뉴 js--> | |
| 30 | - <!-- //공통 메뉴 js --> | |
| 31 | - | |
| 32 | - <!--공통 퍼블 js--> | |
| 33 | - <script src="/resources/front/site/SITE_00000/js/common.js"></script> | |
| 34 | - <script src="/resources/front/site/SITE_00000/js/common-custom.js"></script> | |
| 35 | - <!--//공통 퍼블 js--> | |
| 36 | -</head> | |
| 37 | - | |
| 38 | -<body> | |
| 39 | - <section class="class-viewer"> | |
| 40 | - <header class="viewer-header"> | |
| 41 | - <div class="l-area"> | |
| 42 | - <!-- <button type="button" class="btn-viewer-menu btn circle ico-menu sm">메뉴</button> --> | |
| 43 | - <div class="viewer-tit"> | |
| 44 | - <h1 class="">활동1) 무늬를 꾸며보는 방법 알아보기</h1> | |
| 45 | - </div> | |
| 46 | - </div> | |
| 47 | - <div class="r-area"> | |
| 48 | - <div class="btn-group"> | |
| 49 | - <button type="button" class="btn sm primary"><i class="ico-save"></i> 저장</button> | |
| 50 | - <button type="button" class="btn sm"><i class="ico-logout"></i> 나가기</button> | |
| 51 | - </div> | |
| 52 | - </div> | |
| 53 | - </header> | |
| 54 | - <div class="viewer-body"> | |
| 55 | - <div class="viewer-menu-area"> | |
| 56 | - <div class="viewer-menu-header"> | |
| 57 | - <div class="menu-add-area"> | |
| 58 | - <button class="btn-menu-add"><span>과정추가</span></button> | |
| 59 | - <div class="menu-add-option" > | |
| 60 | - <button type="button">파일 업로드</button> | |
| 61 | - <button type="button">링크추가</button> | |
| 62 | - <button type="button">나의 보관함에서 선택</button> | |
| 63 | - <button type="button">콘텐츠에서 검색</button> | |
| 64 | - </div> | |
| 65 | - </div> | |
| 66 | - </div> | |
| 67 | - <div class="viewer-menu-body"> | |
| 68 | - <nav class="viewer-nav"> | |
| 69 | - <ul> | |
| 70 | - <li class="is-active"> | |
| 71 | - <a href="javascript:changeIfr(1)" class="lnk-full">상세보기</a> | |
| 72 | - <div class="nav-info"> | |
| 73 | - <span class="num">1</span> | |
| 74 | - <div class="fnc order-front"> | |
| 75 | - <button type="button" class="ico-delete order-front">삭제</button> | |
| 76 | - <i class="ico-move">이동</i> | |
| 77 | - </div> | |
| 78 | - </div> | |
| 79 | - <div class="nav-cont"> | |
| 80 | - <div class="img-area"> | |
| 81 | - <img src="/resources/front/site/SITE_00000/images/temp/img1.jpg" alt=""> | |
| 82 | - </div> | |
| 83 | - <div class="txt-area"> | |
| 84 | - <div class="label-area"><span class="label-cont-doc">문서</span></div> | |
| 85 | - <div class="txt">문서</div> | |
| 86 | - </div> | |
| 87 | - </div> | |
| 88 | - </li> | |
| 89 | - <li> | |
| 90 | - <a href="javascript:changeIfr(2)" class="lnk-full">상세보기</a> | |
| 91 | - <div class="nav-info"> | |
| 92 | - <span class="num">2</span> | |
| 93 | - <div class="fnc order-front"> | |
| 94 | - <button type="button" class="ico-delete order-front">삭제</button> | |
| 95 | - <i class="ico-move">이동</i> | |
| 96 | - </div> | |
| 97 | - </div> | |
| 98 | - <div class="nav-cont"> | |
| 99 | - <div class="txt-area"> | |
| 100 | - <div class="label-area"><span class="label-cont-video">영상</span></div> | |
| 101 | - <div class="txt">영상</div> | |
| 102 | - </div> | |
| 103 | - </div> | |
| 104 | - </li> | |
| 105 | - <li> | |
| 106 | - <a href="javascript:changeIfr(3)" class="lnk-full">상세보기</a> | |
| 107 | - <div class="nav-info"> | |
| 108 | - <span class="num">3</span> | |
| 109 | - <div class="fnc order-front"> | |
| 110 | - <button type="button" class="ico-delete order-front">삭제</button> | |
| 111 | - <i class="ico-move">유튜브</i> | |
| 112 | - </div> | |
| 113 | - </div> | |
| 114 | - <div class="nav-cont"> | |
| 115 | - <div class="txt-area"> | |
| 116 | - <div class="label-area"><span class="label-cont-video">영상</span></div> | |
| 117 | - <div class="txt">유튜브</div> | |
| 118 | - </div> | |
| 119 | - </div> | |
| 120 | - </li> | |
| 121 | - <li> | |
| 122 | - </ul> | |
| 123 | - </nav> | |
| 124 | - </div> | |
| 125 | - <button type="button" class="btn-viewer-menu-toggle"><span class="txt-hide">메뉴 열기&닫기</span></button> | |
| 126 | - </div> | |
| 127 | - <div class="viewer-cont"> | |
| 128 | - <div class="iframe-area"> | |
| 129 | - <input type="hidden" id="d8f68ffff73a47ff9b9185caf2452b0c_1" value="https://devdcuai.cbe.go.kr/contents/classLesson/202504/20250402125701229/20250402125701229_0001.jpg"> | |
| 130 | - <input type="hidden" id="d8f68ffff73a47ff9b9185caf2452b0c_2" value="https://devdcuai.cbe.go.kr/contents/classLesson/202504/20250402125701229/20250402125701229_0002.jpg"> | |
| 131 | - <div class="iframe-doc-area"> | |
| 132 | - <iframe src="/resources/front/site/SITE_00000/images/temp/img-pdf.jpg" data-link="" class="view_iframe"></iframe> | |
| 133 | - <div class="doc-fnc-area"> | |
| 134 | - <button class="btn circle ico-pv">이전</button> | |
| 135 | - <button class="btn circle ico-fw">다음</button> | |
| 136 | - </div> | |
| 137 | - </div> | |
| 138 | - </div> | |
| 139 | - <ul class="viewer-tool"> | |
| 140 | - <li> | |
| 141 | - <button type="button" class="btn-painter-toggle lnk-full">판서</button> | |
| 142 | - <img src="/resources/front/site/SITE_00000/images/common/ico-img/ico-img-pencil.svg" alt="" aria-hidden="true"> | |
| 143 | - <strong>판서</strong> | |
| 144 | - </li> | |
| 145 | - <li> | |
| 146 | - <button type="button" class="lnk-full">채팅 열기&닫기</button> | |
| 147 | - <img src="/resources/front/site/SITE_00000/images/common/ico-img/ico-img-chat.svg" alt="" aria-hidden="true"> | |
| 148 | - <strong>채팅</strong> | |
| 149 | - <span class="badge min">새 채팅</span> | |
| 150 | - </li> | |
| 151 | - </ul> | |
| 152 | - </div> | |
| 153 | - </section> | |
| 154 | - | |
| 155 | - <!--toast--> | |
| 156 | - <div class="toast"></div> | |
| 157 | - | |
| 158 | - <!--popup--> | |
| 159 | - <script> | |
| 160 | - // 과정추가 | |
| 161 | - $(document).on('click', '.btn-menu-add', function (e) { | |
| 162 | - e.stopPropagation(); | |
| 163 | - $('.menu-add-area').toggleClass('is-active'); | |
| 164 | - }); | |
| 165 | - | |
| 166 | - $(document).on('click', function () { | |
| 167 | - $('.menu-add-area').removeClass('is-active'); | |
| 168 | - }); | |
| 169 | - | |
| 170 | - //과정 순서 변경 | |
| 171 | - $(".viewer-nav>ul").sortable({ | |
| 172 | - handle: $(".viewer-nav>ul>li").find(".ico-move"), | |
| 173 | - axis: "y", | |
| 174 | - //containment: "parent", | |
| 175 | - update: function (event, ui) { | |
| 176 | - var $list = $(this).children("li"); | |
| 177 | - $list.each(function () { | |
| 178 | - var $li = $(this); | |
| 179 | - var newVal = $(this).index() + 1; | |
| 180 | - $(this).find(".num").html(newVal); | |
| 181 | - }); | |
| 182 | - }, | |
| 183 | - }); | |
| 184 | - </script> | |
| 185 | - | |
| 186 | - | |
| 187 | - <!-- 모듈로 스크립트를 불러오기 --> | |
| 188 | - <script type="module"> | |
| 189 | - import { initColouredPenciles } from '/resources/front/site/SITE_00000/js/custom/module/colouredPencilesSvg_pub.js'; | |
| 190 | - </script> | |
| 191 | - | |
| 192 | - <script> | |
| 193 | - // colouredPencilesSvg.js 데이터 전달 스크립트 | |
| 194 | - const cpInstanceData = (data) => { | |
| 195 | - if (window.cpInstance) { | |
| 196 | - window.cpInstance.setDataParam(data); | |
| 197 | - } else { | |
| 198 | - // console.log('cpInstance 아직 정의되지 않음, 500ms 후 다시 시도.'); | |
| 199 | - setTimeout(cpInstanceData, 500, data); // cpInstance 정의 전까지 500ms 간격으로 재시도 | |
| 200 | - } | |
| 201 | - } | |
| 202 | - const cpInstanceSave = () => { | |
| 203 | - if (window.cpInstance) { | |
| 204 | - window.cpInstance.saveDrawing(); | |
| 205 | - } else { | |
| 206 | - // console.log('cpInstance 아직 정의되지 않음, 500ms 후 다시 시도.'); | |
| 207 | - setTimeout(cpInstanceSave, 500, data); // cpInstance 정의 전까지 500ms 간격으로 재시도 | |
| 208 | - } | |
| 209 | - } | |
| 210 | - | |
| 211 | - // 페이지 로드 시 사용자 키 및 콘텐츠 키 전달 | |
| 212 | - window.addEventListener('DOMContentLoaded', () => { | |
| 213 | - const page = 0; | |
| 214 | - cpInstanceData({ userKey: 'testuser', contentsKey: `ifrCnt_${page}` }); // 사용자 키 설정 | |
| 215 | - }); | |
| 216 | - // 콘텐츠 변경 시, 콘텐츠 키 발행 및 전달 | |
| 217 | - const changeIfr = (page) => { | |
| 218 | - cpInstanceSave(); // 페이지 이동 전 저장 | |
| 219 | - const ifr = document.querySelector('.iframe-area iframe'); | |
| 220 | - ifr.src = `ifrCnt/ifrCnt_${page}.html`; | |
| 221 | - cpInstanceData({ contentsKey: `ifrCnt_${page}` }); // 콘텐츠 키 설정 | |
| 222 | - } | |
| 223 | - | |
| 224 | - $('.viewer-nav li').on('click', function() { | |
| 225 | - $('.viewer-nav li').removeClass('is-active'); | |
| 226 | - $(this).addClass('is-active'); | |
| 227 | - }); | |
| 228 | - </script> | |
| 229 | -</body> | |
| 230 | - | |
| 231 | -</html>(파일 끝에 줄바꿈 문자 없음) |
--- resources/front/site/SITE_00000/css/common/viewer/_colouredPenciles.scss
+++ resources/front/site/SITE_00000/css/common/viewer/_colouredPenciles.scss
... | ... | @@ -32,6 +32,7 @@ |
| 32 | 32 |
bottom: 0; |
| 33 | 33 |
right: 0; |
| 34 | 34 |
width: 100% !important; |
| 35 |
+ height: 100% !important; |
|
| 35 | 36 |
min-height: 100%; |
| 36 | 37 |
// height: 100% !important; |
| 37 | 38 |
cursor: url($url-img + 'common/common/img_painter.svg'), auto; |
--- resources/front/site/SITE_00000/css/common/viewer/_viewer.scss
+++ resources/front/site/SITE_00000/css/common/viewer/_viewer.scss
... | ... | @@ -246,9 +246,9 @@ |
| 246 | 246 |
opacity: 0.3; |
| 247 | 247 |
} |
| 248 | 248 |
.btn-viewer-block-close {
|
| 249 |
- position: absolute; |
|
| 250 |
- right: 4rem; |
|
| 251 |
- top: 1.6rem; |
|
| 249 |
+ position: absolute; |
|
| 250 |
+ right: 4rem; |
|
| 251 |
+ top: 1.6rem; |
|
| 252 | 252 |
} |
| 253 | 253 |
} |
| 254 | 254 |
.viewer-cont {
|
... | ... | @@ -263,6 +263,42 @@ |
| 263 | 263 |
overflow-x: hidden; |
| 264 | 264 |
overflow-y: auto; |
| 265 | 265 |
} |
| 266 |
+.viewer-cont-box {
|
|
| 267 |
+ display: flex; |
|
| 268 |
+ justify-content: center; |
|
| 269 |
+ align-items: center; |
|
| 270 |
+ background-color: $color-font-default-primary; |
|
| 271 |
+ width: 100%; |
|
| 272 |
+ height: 100%; |
|
| 273 |
+ img, |
|
| 274 |
+ video {
|
|
| 275 |
+ object-fit: contain; |
|
| 276 |
+ max-width: 100%; |
|
| 277 |
+ max-height: 100%; |
|
| 278 |
+ } |
|
| 279 |
+ video {
|
|
| 280 |
+ width: 100%; |
|
| 281 |
+ max-height: 100%; |
|
| 282 |
+ } |
|
| 283 |
+ iframe {
|
|
| 284 |
+ display: block; |
|
| 285 |
+ width: 100%; |
|
| 286 |
+ height: 100%; |
|
| 287 |
+ } |
|
| 288 |
+} |
|
| 289 |
+.viewer-painter {
|
|
| 290 |
+ position: absolute; |
|
| 291 |
+ top: 0; |
|
| 292 |
+ left: 0; |
|
| 293 |
+ bottom: 0; |
|
| 294 |
+ right: 0; |
|
| 295 |
+ z-index: -1; |
|
| 296 |
+ opacity: 0; |
|
| 297 |
+ &.is-active {
|
|
| 298 |
+ z-index: 1; |
|
| 299 |
+ opacity: 1; |
|
| 300 |
+ } |
|
| 301 |
+} |
|
| 266 | 302 |
.viewer-wrap {
|
| 267 | 303 |
min-height: calc(100% - 4rem); |
| 268 | 304 |
max-width: 144rem; |
--- resources/front/site/SITE_00000/css/style.css
+++ resources/front/site/SITE_00000/css/style.css
... | ... | @@ -20821,6 +20821,7 @@ |
| 20821 | 20821 |
bottom: 0; |
| 20822 | 20822 |
right: 0; |
| 20823 | 20823 |
width: 100% !important; |
| 20824 |
+ height: 100% !important; |
|
| 20824 | 20825 |
min-height: 100%; |
| 20825 | 20826 |
cursor: url("../images/common/common/img_painter.svg"), auto;
|
| 20826 | 20827 |
z-index: 10; |
... | ... | @@ -24360,6 +24361,51 @@ |
| 24360 | 24361 |
overflow-y: auto; |
| 24361 | 24362 |
} |
| 24362 | 24363 |
|
| 24364 |
+.viewer-cont-box {
|
|
| 24365 |
+ display: -webkit-box; |
|
| 24366 |
+ display: -ms-flexbox; |
|
| 24367 |
+ display: flex; |
|
| 24368 |
+ -webkit-box-pack: center; |
|
| 24369 |
+ -ms-flex-pack: center; |
|
| 24370 |
+ justify-content: center; |
|
| 24371 |
+ -webkit-box-align: center; |
|
| 24372 |
+ -ms-flex-align: center; |
|
| 24373 |
+ align-items: center; |
|
| 24374 |
+ background-color: var(--color-font-default-primary); |
|
| 24375 |
+ width: 100%; |
|
| 24376 |
+ height: 100%; |
|
| 24377 |
+} |
|
| 24378 |
+.viewer-cont-box img, |
|
| 24379 |
+.viewer-cont-box video {
|
|
| 24380 |
+ -o-object-fit: contain; |
|
| 24381 |
+ object-fit: contain; |
|
| 24382 |
+ max-width: 100%; |
|
| 24383 |
+ max-height: 100%; |
|
| 24384 |
+} |
|
| 24385 |
+.viewer-cont-box video {
|
|
| 24386 |
+ width: 100%; |
|
| 24387 |
+ max-height: 100%; |
|
| 24388 |
+} |
|
| 24389 |
+.viewer-cont-box iframe {
|
|
| 24390 |
+ display: block; |
|
| 24391 |
+ width: 100%; |
|
| 24392 |
+ height: 100%; |
|
| 24393 |
+} |
|
| 24394 |
+ |
|
| 24395 |
+.viewer-painter {
|
|
| 24396 |
+ position: absolute; |
|
| 24397 |
+ top: 0; |
|
| 24398 |
+ left: 0; |
|
| 24399 |
+ bottom: 0; |
|
| 24400 |
+ right: 0; |
|
| 24401 |
+ z-index: -1; |
|
| 24402 |
+ opacity: 0; |
|
| 24403 |
+} |
|
| 24404 |
+.viewer-painter.is-active {
|
|
| 24405 |
+ z-index: 1; |
|
| 24406 |
+ opacity: 1; |
|
| 24407 |
+} |
|
| 24408 |
+ |
|
| 24363 | 24409 |
.viewer-wrap {
|
| 24364 | 24410 |
min-height: calc(100% - 4rem); |
| 24365 | 24411 |
max-width: 144rem; |
--- resources/front/site/SITE_00000/css/style.css.map
+++ resources/front/site/SITE_00000/css/style.css.map
| This diff is too big to display. |
--- resources/front/site/SITE_00000/css/style.min.css
+++ resources/front/site/SITE_00000/css/style.min.css
| This diff is too big to display. |
--- resources/front/site/SITE_00000/css/style.min.css.map
+++ resources/front/site/SITE_00000/css/style.min.css.map
| This diff is too big to display. |
--- resources/front/site/SITE_00000/js/common.js
+++ resources/front/site/SITE_00000/js/common.js
... | ... | @@ -476,9 +476,9 @@ |
| 476 | 476 |
$('.btn-util-area').btnUtilFunc();
|
| 477 | 477 |
$('.site-menu-area').siteMenuFunc();
|
| 478 | 478 |
|
| 479 |
- $('input[type=date]').on('focus', function() {
|
|
| 480 |
- this.showPicker(); |
|
| 481 |
- }); |
|
| 479 |
+ // $('input[type=date]').on('focus', function() {
|
|
| 480 |
+ // this.showPicker(); |
|
| 481 |
+ // }); |
|
| 482 | 482 |
}); |
| 483 | 483 |
|
| 484 | 484 |
$(document).on('keydown', '.site-header .gnb > ul > li > a, .site-header .gnb > ul > li > button', function (event) {
|
... | ... | @@ -499,6 +499,7 @@ |
| 499 | 499 |
$(this).showPicker(); |
| 500 | 500 |
}); |
| 501 | 501 |
|
| 502 |
+//비밀번호 숨기기 |
|
| 502 | 503 |
$(document).on('click', '.btn-pw-toggle', function () {
|
| 503 | 504 |
const $btn = $(this); |
| 504 | 505 |
const $pwArea = $btn.closest('.input-pw-area');
|
--- resources/front/site/SITE_00000/js/common/module/colouredPenciles.ts
... | ... | @@ -1,357 +0,0 @@ |
| 1 | -/* | |
| 2 | -* Copyright (c) FOX EDU CO., LTD ALL RIGHT RESERVED. | |
| 3 | -* by flashkid | |
| 4 | -* 25.OCT.2023(WED) | |
| 5 | -*/ | |
| 6 | - | |
| 7 | -class colouredPenciles { | |
| 8 | - private btnInit: HTMLElement; | |
| 9 | - private ctrlCon: HTMLElement; | |
| 10 | - private canvasCon: HTMLElement; | |
| 11 | - | |
| 12 | - private canvas: HTMLCanvasElement; | |
| 13 | - private context: CanvasRenderingContext2D; | |
| 14 | - private eraseObj: HTMLInputElement; | |
| 15 | - private previewObj: HTMLElement; | |
| 16 | - | |
| 17 | - private paint: boolean; | |
| 18 | - | |
| 19 | - private points: object[] = []; | |
| 20 | - private dopoints: object[] = []; | |
| 21 | - | |
| 22 | - private coloursArr: any; | |
| 23 | - private coloursSelect: number; | |
| 24 | - private sizeRange: object; | |
| 25 | - private size: number; | |
| 26 | - private erase: boolean = false; | |
| 27 | - | |
| 28 | - constructor(btnInit, canvasCon, coloursArr, coloursSelect = 1, brushMin = 0.5, brushMax = 10) { | |
| 29 | - this.btnInit = btnInit; | |
| 30 | - this.canvasCon = canvasCon; | |
| 31 | - this.coloursArr = coloursArr.map(c => c.toLowerCase()); | |
| 32 | - this.coloursSelect = coloursSelect - 1; | |
| 33 | - brushMax = brushMax > 60 ? 60 : brushMax; | |
| 34 | - this.sizeRange = { | |
| 35 | - min: brushMin, | |
| 36 | - max: brushMax | |
| 37 | - } | |
| 38 | - this.size = Math.round((brushMin + brushMax) / 2); | |
| 39 | - | |
| 40 | - this.btnInit.onclick = () => { | |
| 41 | - if(this.btnInit.classList.contains('is-active')) { | |
| 42 | - this.clearCanvas(); | |
| 43 | - } else { | |
| 44 | - const canvas = document.createElement('canvas') as HTMLCanvasElement; | |
| 45 | - canvas.width = this.canvasCon.offsetWidth; | |
| 46 | - canvas.height = this.canvasCon.clientHeight; | |
| 47 | - const context = canvas.getContext("2d"); | |
| 48 | - context.lineCap = 'round'; | |
| 49 | - context.lineJoin = 'round'; | |
| 50 | - | |
| 51 | - this.canvas = canvas; | |
| 52 | - this.canvas.id = 'painterCanvas'; | |
| 53 | - this.context = context; | |
| 54 | - this.canvasCon.append(this.canvas); | |
| 55 | - | |
| 56 | - this.uiUpdate(); | |
| 57 | - | |
| 58 | - this.redraw(); | |
| 59 | - this.createDrawEvents(); | |
| 60 | - } | |
| 61 | - } | |
| 62 | - } | |
| 63 | - | |
| 64 | - private uiUpdate = () => { | |
| 65 | - this.btnInit.classList.add('is-active'); | |
| 66 | - this.canvasCon.classList.add('is-active'); | |
| 67 | - | |
| 68 | - this.ctrlCon = document.createElement('div'); | |
| 69 | - this.ctrlCon.classList.add('painterbar'); | |
| 70 | - this.canvasCon.append(this.ctrlCon); | |
| 71 | - | |
| 72 | - const minBtn = document.createElement('button'); | |
| 73 | - minBtn.type = "button"; | |
| 74 | - minBtn.classList.add('btn-painterbar-size'); | |
| 75 | - minBtn.textContent = '색연필 최소화'; | |
| 76 | - this.ctrlCon.append(minBtn); | |
| 77 | - minBtn.onclick = () => { | |
| 78 | - this.ctrlCon.classList.toggle('min'); | |
| 79 | - } | |
| 80 | - | |
| 81 | - const previewCon = document.createElement('div'); | |
| 82 | - previewCon.classList.add('pencil-ctrl-options', 'pencil-preview'); | |
| 83 | - previewCon.style.width = `${this.sizeRange['max'] * 0.1 + 0.2}rem`; | |
| 84 | - previewCon.style.height = `${this.sizeRange['max'] * 0.1 + 0.2}rem`; | |
| 85 | - this.ctrlCon.append(previewCon); | |
| 86 | - this.previewObj = document.createElement('div'); | |
| 87 | - previewCon.append(this.previewObj); | |
| 88 | - | |
| 89 | - const sizeCon = document.createElement('div'); | |
| 90 | - sizeCon.classList.add('pencil-ctrl-options', 'painterbar-stroke'); | |
| 91 | - this.ctrlCon.append(sizeCon); | |
| 92 | - | |
| 93 | - const range = document.createElement('input'); | |
| 94 | - range.type = "range"; | |
| 95 | - range.min = this.sizeRange['min']; | |
| 96 | - range.max = this.sizeRange['max']; | |
| 97 | - range.step = "0.1"; | |
| 98 | - range.setAttribute("orient", "vertical"); | |
| 99 | - range.value = this.size.toString(); | |
| 100 | - sizeCon.append(range); | |
| 101 | - range.oninput = () => { | |
| 102 | - this.changeSize(range.value); | |
| 103 | - } | |
| 104 | - | |
| 105 | - const fncCon = document.createElement('div'); | |
| 106 | - fncCon.classList.add('pencil-ctrl-options', 'painterbar-fnc'); | |
| 107 | - this.ctrlCon.append(fncCon); | |
| 108 | - | |
| 109 | - const undo = document.createElement('button'); | |
| 110 | - undo.type = "button"; | |
| 111 | - undo.classList.add('undo'); | |
| 112 | - undo.textContent = "undo"; | |
| 113 | - fncCon.append(undo); | |
| 114 | - undo.addEventListener('click', this.undo); | |
| 115 | - | |
| 116 | - const redo = document.createElement('button'); | |
| 117 | - redo.type = "button"; | |
| 118 | - redo.classList.add('redo'); | |
| 119 | - redo.textContent = "redo"; | |
| 120 | - fncCon.append(redo); | |
| 121 | - redo.addEventListener('click', this.redo); | |
| 122 | - | |
| 123 | - this.eraseObj = document.createElement('input'); | |
| 124 | - this.eraseObj.type = "checkbox"; | |
| 125 | - this.eraseObj.id = "colouredPencils_eraser"; | |
| 126 | - this.eraseObj.classList.add('clear'); | |
| 127 | - this.eraseObj.checked = this.erase; | |
| 128 | - fncCon.append(this.eraseObj); | |
| 129 | - const eraserLB = document.createElement('label'); | |
| 130 | - eraserLB.setAttribute("for", this.eraseObj.id); | |
| 131 | - eraserLB.textContent = "지우개"; | |
| 132 | - fncCon.append(eraserLB); | |
| 133 | - this.eraseObj.onchange = () => { | |
| 134 | - this.switchEraser(); | |
| 135 | - } | |
| 136 | - | |
| 137 | - const reset = document.createElement('button'); | |
| 138 | - reset.type = 'button'; | |
| 139 | - reset.classList.add('reset'); | |
| 140 | - reset.textContent = 'reset'; | |
| 141 | - fncCon.append(reset); | |
| 142 | - reset.onclick = () => this.reset(); | |
| 143 | - | |
| 144 | - const colorDiv = document.createElement('div'); | |
| 145 | - colorDiv.classList.add('pencil-ctrl-options', 'painterbar-color'); | |
| 146 | - colorDiv.setAttribute('data-opt', this.coloursArr[this.coloursSelect]); | |
| 147 | - this.ctrlCon.append(colorDiv); | |
| 148 | - this.coloursArr.forEach ((el, i) => { | |
| 149 | - const radio = document.createElement('input'); | |
| 150 | - radio.type = "radio"; | |
| 151 | - radio.name = "colouredPencils_colour"; | |
| 152 | - radio.id = "colouredPencils_colour_" + i; | |
| 153 | - radio.value = i.toString(); | |
| 154 | - if (i == this.coloursSelect) radio.checked = true; | |
| 155 | - colorDiv.append(radio); | |
| 156 | - const label = document.createElement('label'); | |
| 157 | - label.setAttribute("for", radio.id); | |
| 158 | - label.textContent = el; | |
| 159 | - label.style.backgroundColor = el; | |
| 160 | - if(el == '#ffffff') label.classList.add('white'); | |
| 161 | - colorDiv.append(label); | |
| 162 | - radio.onchange = (e:Event) => { | |
| 163 | - this.changeColour(i); | |
| 164 | - } | |
| 165 | - }); | |
| 166 | - | |
| 167 | - const close = document.createElement('button'); | |
| 168 | - close.type = "button"; | |
| 169 | - close.classList.add('btn-viewer-painter-close'); | |
| 170 | - close.textContent = "색연필 닫기"; | |
| 171 | - this.ctrlCon.append(close); | |
| 172 | - close.onclick = () => { | |
| 173 | - this.clearCanvas(); | |
| 174 | - } | |
| 175 | - | |
| 176 | - this.updatePreview(); | |
| 177 | - } | |
| 178 | - | |
| 179 | - private undo = () => { | |
| 180 | - if (this.points.length == 0) return; | |
| 181 | - const arrKeys = this.points.map(el => el["mode"]); | |
| 182 | - const lastIndex = arrKeys.lastIndexOf(false); | |
| 183 | - const tempPoints = this.points.slice(lastIndex, this.points.length - 1); | |
| 184 | - this.dopoints = [...this.dopoints, ...tempPoints]; | |
| 185 | - this.points = this.points.slice(0, lastIndex); | |
| 186 | - this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| 187 | - this.redraw(); | |
| 188 | - } | |
| 189 | - | |
| 190 | - private redo = () => { | |
| 191 | - if (this.dopoints.length == 0) return; | |
| 192 | - const arrKeys = this.dopoints.map(el => el["mode"]); | |
| 193 | - const lastIndex = arrKeys.lastIndexOf(false); | |
| 194 | - const tempPoints = this.dopoints.slice(lastIndex, this.dopoints.length - 1); | |
| 195 | - this.points = [...this.points, ...tempPoints]; | |
| 196 | - this.dopoints = this.dopoints.slice(0, lastIndex); | |
| 197 | - this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| 198 | - this.redraw(); | |
| 199 | - } | |
| 200 | - | |
| 201 | - private reset = () => { | |
| 202 | - this.points = []; | |
| 203 | - this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| 204 | - this.redraw(); | |
| 205 | - if(this.erase) { | |
| 206 | - this.switchEraser(); | |
| 207 | - } | |
| 208 | - } | |
| 209 | - | |
| 210 | - private changeSize = (size) => { | |
| 211 | - this.size = +size; | |
| 212 | - this.updatePreview(); | |
| 213 | - } | |
| 214 | - | |
| 215 | - private changeColour = (key) => { | |
| 216 | - this.coloursSelect = key; | |
| 217 | - if(this.erase) this.switchEraser(); | |
| 218 | - this.updatePreview(); | |
| 219 | - } | |
| 220 | - | |
| 221 | - private switchEraser = () => { | |
| 222 | - this.erase = !this.erase; | |
| 223 | - this.eraseObj.checked = this.erase; | |
| 224 | - this.updatePreview(); | |
| 225 | - } | |
| 226 | - | |
| 227 | - private updatePreview = () => { | |
| 228 | - this.previewObj.style.cssText = ` | |
| 229 | - width: ${this.size * 0.1}rem; | |
| 230 | - height: ${this.size * 0.1}rem; | |
| 231 | - ` | |
| 232 | - if (this.coloursArr[this.coloursSelect] == '#ffffff') { | |
| 233 | - this.previewObj.classList.add('white'); | |
| 234 | - } else { | |
| 235 | - this.previewObj.classList.remove('white'); | |
| 236 | - } | |
| 237 | - | |
| 238 | - if (this.erase) { | |
| 239 | - this.previewObj.classList.add('erase'); | |
| 240 | - } else { | |
| 241 | - this.previewObj.classList.remove('erase'); | |
| 242 | - this.previewObj.style.backgroundColor = `${this.coloursArr[this.coloursSelect]}`; | |
| 243 | - } | |
| 244 | - } | |
| 245 | - | |
| 246 | - private clearCanvas = () => { | |
| 247 | - this.ctrlCon.remove(); | |
| 248 | - this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| 249 | - this.points = []; | |
| 250 | - this.dopoints = []; | |
| 251 | - this.canvasCon.removeChild(this.canvas); | |
| 252 | - this.erase = false; | |
| 253 | - | |
| 254 | - this.btnInit.classList.remove('is-active'); | |
| 255 | - this.canvasCon.classList.remove('is-active'); | |
| 256 | - } | |
| 257 | - | |
| 258 | - private createDrawEvents = () => { | |
| 259 | - const canvas = this.canvas; | |
| 260 | - | |
| 261 | - canvas.addEventListener("mousedown", this.pressEventHandler); | |
| 262 | - canvas.addEventListener("mousemove", this.dragEventHandler); | |
| 263 | - canvas.addEventListener("mouseup", this.releaseEventHandler); | |
| 264 | - canvas.addEventListener("mouseout", this.cancelEventHandler); | |
| 265 | - | |
| 266 | - canvas.addEventListener("touchstart", this.pressEventHandler); | |
| 267 | - canvas.addEventListener("touchmove", this.dragEventHandler); | |
| 268 | - canvas.addEventListener("touchend", this.releaseEventHandler); | |
| 269 | - canvas.addEventListener("touchcancel", this.cancelEventHandler); | |
| 270 | - } | |
| 271 | - | |
| 272 | - private redraw = () => { | |
| 273 | - const points = this.points; | |
| 274 | - if (points.length == 0) return; | |
| 275 | - const context = this.context; | |
| 276 | - context.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| 277 | - for (let i = 0; i < points.length; ++i) { | |
| 278 | - if (points[i]["erase"]) { | |
| 279 | - context.globalCompositeOperation = "destination-out"; | |
| 280 | - } else { | |
| 281 | - context.globalCompositeOperation = "source-over"; | |
| 282 | - } | |
| 283 | - context.beginPath(); | |
| 284 | - if (points[i]["mode"] && i) { | |
| 285 | - context.moveTo(points[i - 1]["x"], points[i - 1]["y"]); | |
| 286 | - } else { | |
| 287 | - context.moveTo(points[i]["x"] - 1, points[i]["y"]); | |
| 288 | - } | |
| 289 | - | |
| 290 | - context.lineTo(points[i]["x"], points[i]["y"]); | |
| 291 | - context.strokeStyle = points[i]["color"]; | |
| 292 | - context.lineWidth = points[i]["size"]; | |
| 293 | - context.stroke(); | |
| 294 | - } | |
| 295 | - context.closePath(); | |
| 296 | - } | |
| 297 | - | |
| 298 | - private addClick = (x: number, y: number, color: string, size: number, dragging: boolean, erase: boolean) => { | |
| 299 | - this.points.push({ x: x, y: y, color: color, size: size, mode: dragging, erase: erase }) | |
| 300 | - } | |
| 301 | - | |
| 302 | - private releaseEventHandler = () => { | |
| 303 | - if (this.paint) { | |
| 304 | - this.paint = false; | |
| 305 | - this.redraw(); | |
| 306 | - } | |
| 307 | - } | |
| 308 | - | |
| 309 | - private cancelEventHandler = () => { | |
| 310 | - this.releaseEventHandler(); | |
| 311 | - } | |
| 312 | - | |
| 313 | - private pressEventHandler = (e: MouseEvent | TouchEvent) => { | |
| 314 | - this.dopoints = []; | |
| 315 | - let mouseX = (e as TouchEvent).changedTouches ? | |
| 316 | - (e as TouchEvent).changedTouches[0].pageX : | |
| 317 | - (e as MouseEvent).pageX; | |
| 318 | - let mouseY = (e as TouchEvent).changedTouches ? | |
| 319 | - (e as TouchEvent).changedTouches[0].pageY : | |
| 320 | - (e as MouseEvent).pageY; | |
| 321 | - mouseX -= (this.canvasCon.getBoundingClientRect().left + window.pageXOffset); | |
| 322 | - mouseY -= (this.canvasCon.getBoundingClientRect().top + window.pageYOffset); | |
| 323 | - | |
| 324 | - this.paint = true; | |
| 325 | - this.addClick(mouseX, mouseY, this.coloursArr[this.coloursSelect], this.size, false, this.erase); | |
| 326 | - this.redraw(); | |
| 327 | - } | |
| 328 | - | |
| 329 | - private dragEventHandler = (e: MouseEvent | TouchEvent) => { | |
| 330 | - let mouseX = (e as TouchEvent).changedTouches ? | |
| 331 | - (e as TouchEvent).changedTouches[0].pageX : | |
| 332 | - (e as MouseEvent).pageX; | |
| 333 | - let mouseY = (e as TouchEvent).changedTouches ? | |
| 334 | - (e as TouchEvent).changedTouches[0].pageY : | |
| 335 | - (e as MouseEvent).pageY; | |
| 336 | - mouseX -= (this.canvasCon.getBoundingClientRect().left + window.pageXOffset); | |
| 337 | - mouseY -= (this.canvasCon.getBoundingClientRect().top + window.pageYOffset); | |
| 338 | - | |
| 339 | - if (this.paint) { | |
| 340 | - this.addClick(mouseX, mouseY, this.coloursArr[this.coloursSelect], this.size, true, this.erase); | |
| 341 | - this.redraw(); | |
| 342 | - } | |
| 343 | - | |
| 344 | - e.preventDefault(); | |
| 345 | - } | |
| 346 | -} | |
| 347 | - | |
| 348 | -// 환경설정 | |
| 349 | -const colorArray = ["#222222","#fd4418","#ffce00","#00d56a","#2f77ff","#ffffff"]; | |
| 350 | -const defaultColor = 5; | |
| 351 | -const brushMin = 0.5, brushMax = 40; | |
| 352 | -// 대상 개체 | |
| 353 | -const btnInit = document.querySelector('.btn-painter-toggle') as HTMLElement; | |
| 354 | -const targetObj = document.querySelector('.paint-target') as HTMLElement; | |
| 355 | -// 구동 defaultColor, brushMin, brushMax 은 넘기지 않아도 기본 값에 의해 작동됨 | |
| 356 | -new colouredPenciles(btnInit, targetObj, colorArray, defaultColor, brushMin, brushMax); | |
| 357 | -// new colouredPenciles(btnInit, targetObj, colorArray); |
--- resources/front/site/SITE_00000/js/common/module/colouredPenciles_new.js
... | ... | @@ -1,356 +0,0 @@ |
| 1 | -/* | |
| 2 | -* Copyright (c) FOX EDU CO., LTD ALL RIGHT RESERVED. | |
| 3 | -* by flashkid | |
| 4 | -* 25.OCT.2023(WED) | |
| 5 | -*/ | |
| 6 | -class colouredPenciles { | |
| 7 | - constructor(btnInit, canvasCon, coloursArr, coloursSelect = 1, brushMin = 0.5, brushMax = 10) { | |
| 8 | - this.points = []; | |
| 9 | - this.dopoints = []; | |
| 10 | - this.erase = false; | |
| 11 | - this.uiUpdate = () => { | |
| 12 | - this.btnInit.classList.add('is-active'); | |
| 13 | - this.canvasCon.classList.add('is-active'); | |
| 14 | - this.ctrlCon = document.createElement('div'); | |
| 15 | - this.ctrlCon.classList.add('painterbar'); | |
| 16 | - this.canvasCon.append(this.ctrlCon); | |
| 17 | - const minBtn = document.createElement('button'); | |
| 18 | - minBtn.type = "button"; | |
| 19 | - minBtn.classList.add('btn-painterbar-size'); | |
| 20 | - minBtn.textContent = '색연필 최소화'; | |
| 21 | - this.ctrlCon.append(minBtn); | |
| 22 | - minBtn.onclick = () => { | |
| 23 | - this.ctrlCon.classList.toggle('min'); | |
| 24 | - }; | |
| 25 | - const previewCon = document.createElement('div'); | |
| 26 | - previewCon.classList.add('pencil-ctrl-options', 'pencil-preview'); | |
| 27 | - previewCon.style.width = `${this.sizeRange['max'] * 0.1 + 0.2}rem`; | |
| 28 | - previewCon.style.height = `${this.sizeRange['max'] * 0.1 + 0.2}rem`; | |
| 29 | - this.ctrlCon.append(previewCon); | |
| 30 | - this.previewObj = document.createElement('div'); | |
| 31 | - previewCon.append(this.previewObj); | |
| 32 | - const sizeCon = document.createElement('div'); | |
| 33 | - sizeCon.classList.add('pencil-ctrl-options', 'painterbar-stroke'); | |
| 34 | - this.ctrlCon.append(sizeCon); | |
| 35 | - const range = document.createElement('input'); | |
| 36 | - range.type = "range"; | |
| 37 | - range.min = this.sizeRange['min']; | |
| 38 | - range.max = this.sizeRange['max']; | |
| 39 | - range.step = "0.1"; | |
| 40 | - range.setAttribute("orient", "vertical"); | |
| 41 | - range.value = this.size.toString(); | |
| 42 | - sizeCon.append(range); | |
| 43 | - range.oninput = () => { | |
| 44 | - this.changeSize(range.value); | |
| 45 | - }; | |
| 46 | - const fncCon = document.createElement('div'); | |
| 47 | - fncCon.classList.add('pencil-ctrl-options', 'painterbar-fnc'); | |
| 48 | - this.ctrlCon.append(fncCon); | |
| 49 | - const undo = document.createElement('button'); | |
| 50 | - undo.type = "button"; | |
| 51 | - undo.classList.add('undo'); | |
| 52 | - undo.textContent = "undo"; | |
| 53 | - fncCon.append(undo); | |
| 54 | - undo.addEventListener('click', this.undo); | |
| 55 | - const redo = document.createElement('button'); | |
| 56 | - redo.type = "button"; | |
| 57 | - redo.classList.add('redo'); | |
| 58 | - redo.textContent = "redo"; | |
| 59 | - fncCon.append(redo); | |
| 60 | - redo.addEventListener('click', this.redo); | |
| 61 | - this.eraseObj = document.createElement('input'); | |
| 62 | - this.eraseObj.type = "checkbox"; | |
| 63 | - this.eraseObj.id = "colouredPencils_eraser"; | |
| 64 | - this.eraseObj.classList.add('clear'); | |
| 65 | - this.eraseObj.checked = this.erase; | |
| 66 | - fncCon.append(this.eraseObj); | |
| 67 | - const eraserLB = document.createElement('label'); | |
| 68 | - eraserLB.setAttribute("for", this.eraseObj.id); | |
| 69 | - eraserLB.textContent = "지우개"; | |
| 70 | - fncCon.append(eraserLB); | |
| 71 | - this.eraseObj.onchange = () => { | |
| 72 | - this.switchEraser(); | |
| 73 | - }; | |
| 74 | - const reset = document.createElement('button'); | |
| 75 | - reset.type = 'button'; | |
| 76 | - reset.classList.add('reset'); | |
| 77 | - reset.textContent = 'reset'; | |
| 78 | - fncCon.append(reset); | |
| 79 | - reset.onclick = () => this.reset(); | |
| 80 | - const colorDiv = document.createElement('div'); | |
| 81 | - colorDiv.classList.add('pencil-ctrl-options', 'painterbar-color'); | |
| 82 | - colorDiv.setAttribute('data-opt', this.coloursArr[this.coloursSelect]); | |
| 83 | - this.ctrlCon.append(colorDiv); | |
| 84 | - this.coloursArr.forEach((el, i) => { | |
| 85 | - const radio = document.createElement('input'); | |
| 86 | - radio.type = "radio"; | |
| 87 | - radio.name = "colouredPencils_colour"; | |
| 88 | - radio.id = "colouredPencils_colour_" + i; | |
| 89 | - radio.value = i.toString(); | |
| 90 | - if (i == this.coloursSelect) | |
| 91 | - radio.checked = true; | |
| 92 | - colorDiv.append(radio); | |
| 93 | - const label = document.createElement('label'); | |
| 94 | - label.setAttribute("for", radio.id); | |
| 95 | - label.textContent = el; | |
| 96 | - label.style.backgroundColor = el; | |
| 97 | - if (el == '#ffffff') | |
| 98 | - label.classList.add('white'); | |
| 99 | - colorDiv.append(label); | |
| 100 | - radio.onchange = (e) => { | |
| 101 | - this.changeColour(i); | |
| 102 | - }; | |
| 103 | - }); | |
| 104 | - const close = document.createElement('button'); | |
| 105 | - close.type = "button"; | |
| 106 | - close.classList.add('btn-viewer-painter-close'); | |
| 107 | - close.textContent = "색연필 닫기"; | |
| 108 | - this.ctrlCon.append(close); | |
| 109 | - close.onclick = () => { | |
| 110 | - this.clearCanvas(); | |
| 111 | - }; | |
| 112 | - this.updatePreview(); | |
| 113 | - }; | |
| 114 | - this.undo = () => { | |
| 115 | - if (this.points.length == 0) | |
| 116 | - return; | |
| 117 | - const arrKeys = this.points.map(el => el["mode"]); | |
| 118 | - const lastIndex = arrKeys.lastIndexOf(false); | |
| 119 | - const tempPoints = this.points.slice(lastIndex, this.points.length - 1); | |
| 120 | - this.dopoints = [...this.dopoints, ...tempPoints]; | |
| 121 | - this.points = this.points.slice(0, lastIndex); | |
| 122 | - this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| 123 | - this.redraw(); | |
| 124 | - }; | |
| 125 | - this.redo = () => { | |
| 126 | - if (this.dopoints.length == 0) | |
| 127 | - return; | |
| 128 | - const arrKeys = this.dopoints.map(el => el["mode"]); | |
| 129 | - const lastIndex = arrKeys.lastIndexOf(false); | |
| 130 | - const tempPoints = this.dopoints.slice(lastIndex, this.dopoints.length - 1); | |
| 131 | - this.points = [...this.points, ...tempPoints]; | |
| 132 | - this.dopoints = this.dopoints.slice(0, lastIndex); | |
| 133 | - this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| 134 | - this.redraw(); | |
| 135 | - }; | |
| 136 | - this.reset = () => { | |
| 137 | - this.points = []; | |
| 138 | - this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| 139 | - this.redraw(); | |
| 140 | - if (this.erase) { | |
| 141 | - this.switchEraser(); | |
| 142 | - } | |
| 143 | - }; | |
| 144 | - this.changeSize = (size) => { | |
| 145 | - this.size = +size; | |
| 146 | - this.updatePreview(); | |
| 147 | - }; | |
| 148 | - this.changeColour = (key) => { | |
| 149 | - this.coloursSelect = key; | |
| 150 | - if (this.erase) | |
| 151 | - this.switchEraser(); | |
| 152 | - this.updatePreview(); | |
| 153 | - }; | |
| 154 | - this.switchEraser = () => { | |
| 155 | - this.erase = !this.erase; | |
| 156 | - this.eraseObj.checked = this.erase; | |
| 157 | - this.updatePreview(); | |
| 158 | - const area = document.querySelector('.paint-target'); | |
| 159 | - if (this.erase) { | |
| 160 | - area.classList.add('is-eraser'); | |
| 161 | - } else { | |
| 162 | - area.classList.remove('is-eraser'); | |
| 163 | - } | |
| 164 | - }; | |
| 165 | - this.updatePreview = () => { | |
| 166 | - this.previewObj.style.cssText = ` | |
| 167 | - width: ${this.size * 0.1}rem; | |
| 168 | - height: ${this.size * 0.1}rem; | |
| 169 | - `; | |
| 170 | - if (this.coloursArr[this.coloursSelect] == '#ffffff') { | |
| 171 | - this.previewObj.classList.add('white'); | |
| 172 | - } | |
| 173 | - else { | |
| 174 | - this.previewObj.classList.remove('white'); | |
| 175 | - } | |
| 176 | - if (this.erase) { | |
| 177 | - this.previewObj.classList.add('erase'); | |
| 178 | - } | |
| 179 | - else { | |
| 180 | - this.previewObj.classList.remove('erase'); | |
| 181 | - this.previewObj.style.backgroundColor = `${this.coloursArr[this.coloursSelect]}`; | |
| 182 | - } | |
| 183 | - }; | |
| 184 | - this.clearCanvas = () => { | |
| 185 | - this.ctrlCon.remove(); | |
| 186 | - this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| 187 | - this.points = []; | |
| 188 | - this.dopoints = []; | |
| 189 | - this.canvasCon.removeChild(this.canvas); | |
| 190 | - this.erase = false; | |
| 191 | - this.btnInit.classList.remove('is-active'); | |
| 192 | - this.canvasCon.classList.remove('is-active'); | |
| 193 | - }; | |
| 194 | - this.createDrawEvents = () => { | |
| 195 | - const canvas = this.canvas; | |
| 196 | - canvas.addEventListener("mousedown", this.pressEventHandler); | |
| 197 | - canvas.addEventListener("mousemove", this.dragEventHandler); | |
| 198 | - canvas.addEventListener("mouseup", this.releaseEventHandler); | |
| 199 | - canvas.addEventListener("mouseout", this.cancelEventHandler); | |
| 200 | - canvas.addEventListener("touchstart", this.pressEventHandler); | |
| 201 | - canvas.addEventListener("touchmove", this.dragEventHandler); | |
| 202 | - canvas.addEventListener("touchend", this.releaseEventHandler); | |
| 203 | - canvas.addEventListener("touchcancel", this.cancelEventHandler); | |
| 204 | - }; | |
| 205 | - this.redraw = () => { | |
| 206 | - const points = this.points; | |
| 207 | - if (points.length == 0) | |
| 208 | - return; | |
| 209 | - const context = this.context; | |
| 210 | - context.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| 211 | - for (let i = 0; i < points.length; ++i) { | |
| 212 | - if (points[i]["erase"]) { | |
| 213 | - context.globalCompositeOperation = "destination-out"; | |
| 214 | - } | |
| 215 | - else { | |
| 216 | - context.globalCompositeOperation = "source-over"; | |
| 217 | - } | |
| 218 | - context.beginPath(); | |
| 219 | - if (points[i]["mode"] && i) { | |
| 220 | - context.moveTo(points[i - 1]["x"], points[i - 1]["y"]); | |
| 221 | - } | |
| 222 | - else { | |
| 223 | - context.moveTo(points[i]["x"] - 1, points[i]["y"]); | |
| 224 | - } | |
| 225 | - context.lineTo(points[i]["x"], points[i]["y"]); | |
| 226 | - context.strokeStyle = points[i]["color"]; | |
| 227 | - context.lineWidth = points[i]["size"]; | |
| 228 | - context.stroke(); | |
| 229 | - } | |
| 230 | - context.closePath(); | |
| 231 | - }; | |
| 232 | - this.addClick = (x, y, color, size, dragging, erase) => { | |
| 233 | - this.points.push({ x: x, y: y, color: color, size: size, mode: dragging, erase: erase }); | |
| 234 | - }; | |
| 235 | - this.releaseEventHandler = () => { | |
| 236 | - if (this.paint) { | |
| 237 | - this.paint = false; | |
| 238 | - this.redraw(); | |
| 239 | - } | |
| 240 | - }; | |
| 241 | - this.cancelEventHandler = () => { | |
| 242 | - this.releaseEventHandler(); | |
| 243 | - }; | |
| 244 | - this.pressEventHandler = (e) => { | |
| 245 | - this.dopoints = []; | |
| 246 | - let mouseX = e.changedTouches ? | |
| 247 | - e.changedTouches[0].pageX : | |
| 248 | - e.pageX; | |
| 249 | - let mouseY = e.changedTouches ? | |
| 250 | - e.changedTouches[0].pageY : | |
| 251 | - e.pageY; | |
| 252 | - mouseX -= (this.canvasCon.getBoundingClientRect().left + window.pageXOffset); | |
| 253 | - mouseY -= (this.canvasCon.getBoundingClientRect().top + window.pageYOffset); | |
| 254 | - this.paint = true; | |
| 255 | - this.addClick(mouseX, mouseY, this.coloursArr[this.coloursSelect], this.size, false, this.erase); | |
| 256 | - this.redraw(); | |
| 257 | - }; | |
| 258 | - this.dragEventHandler = (e) => { | |
| 259 | - let mouseX = e.changedTouches ? | |
| 260 | - e.changedTouches[0].pageX : | |
| 261 | - e.pageX; | |
| 262 | - let mouseY = e.changedTouches ? | |
| 263 | - e.changedTouches[0].pageY : | |
| 264 | - e.pageY; | |
| 265 | - mouseX -= (this.canvasCon.getBoundingClientRect().left + window.pageXOffset); | |
| 266 | - mouseY -= (this.canvasCon.getBoundingClientRect().top + window.pageYOffset); | |
| 267 | - if (this.paint) { | |
| 268 | - this.addClick(mouseX, mouseY, this.coloursArr[this.coloursSelect], this.size, true, this.erase); | |
| 269 | - this.redraw(); | |
| 270 | - } | |
| 271 | - e.preventDefault(); | |
| 272 | - }; | |
| 273 | - this.btnInit = btnInit; | |
| 274 | - this.canvasCon = canvasCon; | |
| 275 | - this.coloursArr = coloursArr.map(c => c.toLowerCase()); | |
| 276 | - this.coloursSelect = coloursSelect - 1; | |
| 277 | - brushMax = brushMax > 60 ? 60 : brushMax; | |
| 278 | - this.sizeRange = { | |
| 279 | - min: brushMin, | |
| 280 | - max: brushMax | |
| 281 | - }; | |
| 282 | - this.size = Math.round((brushMin + brushMax) / 2); | |
| 283 | - // this.btnInit.onclick = () => { | |
| 284 | - // if (this.btnInit.classList.contains('is-active')) { | |
| 285 | - // this.clearCanvas(); | |
| 286 | - // } | |
| 287 | - // else { | |
| 288 | - // const canvas = document.createElement('canvas'); | |
| 289 | - // canvas.width = this.canvasCon.offsetWidth; | |
| 290 | - // canvas.height = this.canvasCon.clientHeight; | |
| 291 | - // const context = canvas.getContext("2d"); | |
| 292 | - // context.lineCap = 'round'; | |
| 293 | - // context.lineJoin = 'round'; | |
| 294 | - // this.canvas = canvas; | |
| 295 | - // this.canvas.id = 'painterCanvas'; | |
| 296 | - // this.context = context; | |
| 297 | - // this.canvasCon.append(this.canvas); | |
| 298 | - // this.uiUpdate(); | |
| 299 | - // this.redraw(); | |
| 300 | - // this.createDrawEvents(); | |
| 301 | - // } | |
| 302 | - // }; | |
| 303 | - this.btnInit.onclick = () => { | |
| 304 | - if (this.btnInit.classList.contains('is-active')) { | |
| 305 | - this.clearCanvas(); | |
| 306 | - } else { | |
| 307 | - const canvas = document.createElement('canvas'); | |
| 308 | - let width = this.canvasCon.clientWidth; | |
| 309 | - let height = this.canvasCon.clientHeight; | |
| 310 | - const isQuizViewer = this.canvasCon.closest('.quiz-viewer'); | |
| 311 | - | |
| 312 | - if (isQuizViewer) { | |
| 313 | - const wrap = isQuizViewer.querySelector('.quiz-viewer-wrap'); | |
| 314 | - if (wrap) { | |
| 315 | - height = wrap.clientHeight; | |
| 316 | - } | |
| 317 | - } | |
| 318 | - | |
| 319 | - // viewer-body 최소 높이 반영 | |
| 320 | - const viewerBody = document.querySelector('.viewer-body'); | |
| 321 | - if (viewerBody) { | |
| 322 | - const viewerBodyHeight = viewerBody.clientHeight; | |
| 323 | - height = Math.max(height, viewerBodyHeight); // 더 큰 값 사용 | |
| 324 | - } | |
| 325 | - | |
| 326 | - canvas.width = width; | |
| 327 | - canvas.height = height; | |
| 328 | - canvas.style.width = width + 'px'; | |
| 329 | - canvas.style.height = height + 'px'; | |
| 330 | - | |
| 331 | - const context = canvas.getContext("2d"); | |
| 332 | - context.lineCap = 'round'; | |
| 333 | - context.lineJoin = 'round'; | |
| 334 | - | |
| 335 | - this.canvas = canvas; | |
| 336 | - this.canvas.id = 'painterCanvas'; | |
| 337 | - this.context = context; | |
| 338 | - this.canvasCon.append(this.canvas); | |
| 339 | - this.uiUpdate(); | |
| 340 | - this.redraw(); | |
| 341 | - this.createDrawEvents(); | |
| 342 | - } | |
| 343 | - }; | |
| 344 | - | |
| 345 | - } | |
| 346 | -} | |
| 347 | -// 환경설정 | |
| 348 | -const colorArray = ["#222222", "#fd4418", "#ffce00", "#00d56a", "#2f77ff", "#ffffff"]; | |
| 349 | -const defaultColor = 5; | |
| 350 | -const brushMin = 0.5, brushMax = 40; | |
| 351 | -// 대상 개체 | |
| 352 | -const btnInit = document.querySelector('.btn-painter-toggle'); | |
| 353 | -const targetObj = document.querySelector('.paint-target'); | |
| 354 | -// 구동 defaultColor, brushMin, brushMax 은 넘기지 않아도 기본 값에 의해 작동됨 | |
| 355 | -new colouredPenciles(btnInit, targetObj, colorArray, defaultColor, brushMin, brushMax); | |
| 356 | -// new colouredPenciles(btnInit, targetObj, colorArray); |
--- resources/front/site/SITE_00000/js/common/module/colouredPenciles_old.js
... | ... | @@ -1,314 +0,0 @@ |
| 1 | -/* | |
| 2 | -* Copyright (c) FOX EDU CO., LTD ALL RIGHT RESERVED. | |
| 3 | -* by flashkid | |
| 4 | -* 25.OCT.2023(WED) | |
| 5 | -*/ | |
| 6 | -class colouredPenciles { | |
| 7 | - constructor(btnInit, canvasCon, coloursArr, coloursSelect = 1, brushMin = 0.5, brushMax = 10) { | |
| 8 | - this.points = []; | |
| 9 | - this.dopoints = []; | |
| 10 | - this.erase = false; | |
| 11 | - this.uiUpdate = () => { | |
| 12 | - this.btnInit.classList.add('is-active'); | |
| 13 | - this.canvasCon.classList.add('is-active'); | |
| 14 | - this.ctrlCon = document.createElement('div'); | |
| 15 | - this.ctrlCon.classList.add('painterbar'); | |
| 16 | - this.canvasCon.append(this.ctrlCon); | |
| 17 | - const minBtn = document.createElement('button'); | |
| 18 | - minBtn.type = "button"; | |
| 19 | - minBtn.classList.add('btn-painterbar-size'); | |
| 20 | - minBtn.textContent = '색연필 최소화'; | |
| 21 | - this.ctrlCon.append(minBtn); | |
| 22 | - minBtn.onclick = () => { | |
| 23 | - this.ctrlCon.classList.toggle('min'); | |
| 24 | - }; | |
| 25 | - const previewCon = document.createElement('div'); | |
| 26 | - previewCon.classList.add('pencil-ctrl-options', 'pencil-preview'); | |
| 27 | - previewCon.style.width = `${this.sizeRange['max'] * 0.1 + 0.2}rem`; | |
| 28 | - previewCon.style.height = `${this.sizeRange['max'] * 0.1 + 0.2}rem`; | |
| 29 | - this.ctrlCon.append(previewCon); | |
| 30 | - this.previewObj = document.createElement('div'); | |
| 31 | - previewCon.append(this.previewObj); | |
| 32 | - const sizeCon = document.createElement('div'); | |
| 33 | - sizeCon.classList.add('pencil-ctrl-options', 'painterbar-stroke'); | |
| 34 | - this.ctrlCon.append(sizeCon); | |
| 35 | - const range = document.createElement('input'); | |
| 36 | - range.type = "range"; | |
| 37 | - range.min = this.sizeRange['min']; | |
| 38 | - range.max = this.sizeRange['max']; | |
| 39 | - range.step = "0.1"; | |
| 40 | - range.setAttribute("orient", "vertical"); | |
| 41 | - range.value = this.size.toString(); | |
| 42 | - sizeCon.append(range); | |
| 43 | - range.oninput = () => { | |
| 44 | - this.changeSize(range.value); | |
| 45 | - }; | |
| 46 | - const fncCon = document.createElement('div'); | |
| 47 | - fncCon.classList.add('pencil-ctrl-options', 'painterbar-fnc'); | |
| 48 | - this.ctrlCon.append(fncCon); | |
| 49 | - const undo = document.createElement('button'); | |
| 50 | - undo.type = "button"; | |
| 51 | - undo.classList.add('undo'); | |
| 52 | - undo.textContent = "undo"; | |
| 53 | - fncCon.append(undo); | |
| 54 | - undo.addEventListener('click', this.undo); | |
| 55 | - const redo = document.createElement('button'); | |
| 56 | - redo.type = "button"; | |
| 57 | - redo.classList.add('redo'); | |
| 58 | - redo.textContent = "redo"; | |
| 59 | - fncCon.append(redo); | |
| 60 | - redo.addEventListener('click', this.redo); | |
| 61 | - this.eraseObj = document.createElement('input'); | |
| 62 | - this.eraseObj.type = "checkbox"; | |
| 63 | - this.eraseObj.id = "colouredPencils_eraser"; | |
| 64 | - this.eraseObj.classList.add('clear'); | |
| 65 | - this.eraseObj.checked = this.erase; | |
| 66 | - fncCon.append(this.eraseObj); | |
| 67 | - const eraserLB = document.createElement('label'); | |
| 68 | - eraserLB.setAttribute("for", this.eraseObj.id); | |
| 69 | - eraserLB.textContent = "지우개"; | |
| 70 | - fncCon.append(eraserLB); | |
| 71 | - this.eraseObj.onchange = () => { | |
| 72 | - this.switchEraser(); | |
| 73 | - }; | |
| 74 | - const reset = document.createElement('button'); | |
| 75 | - reset.type = 'button'; | |
| 76 | - reset.classList.add('reset'); | |
| 77 | - reset.textContent = 'reset'; | |
| 78 | - fncCon.append(reset); | |
| 79 | - reset.onclick = () => this.reset(); | |
| 80 | - const colorDiv = document.createElement('div'); | |
| 81 | - colorDiv.classList.add('pencil-ctrl-options', 'painterbar-color'); | |
| 82 | - colorDiv.setAttribute('data-opt', this.coloursArr[this.coloursSelect]); | |
| 83 | - this.ctrlCon.append(colorDiv); | |
| 84 | - this.coloursArr.forEach((el, i) => { | |
| 85 | - const radio = document.createElement('input'); | |
| 86 | - radio.type = "radio"; | |
| 87 | - radio.name = "colouredPencils_colour"; | |
| 88 | - radio.id = "colouredPencils_colour_" + i; | |
| 89 | - radio.value = i.toString(); | |
| 90 | - if (i == this.coloursSelect) | |
| 91 | - radio.checked = true; | |
| 92 | - colorDiv.append(radio); | |
| 93 | - const label = document.createElement('label'); | |
| 94 | - label.setAttribute("for", radio.id); | |
| 95 | - label.textContent = el; | |
| 96 | - label.style.backgroundColor = el; | |
| 97 | - if (el == '#ffffff') | |
| 98 | - label.classList.add('white'); | |
| 99 | - colorDiv.append(label); | |
| 100 | - radio.onchange = (e) => { | |
| 101 | - this.changeColour(i); | |
| 102 | - }; | |
| 103 | - }); | |
| 104 | - const close = document.createElement('button'); | |
| 105 | - close.type = "button"; | |
| 106 | - close.classList.add('btn-viewer-painter-close'); | |
| 107 | - close.textContent = "색연필 닫기"; | |
| 108 | - this.ctrlCon.append(close); | |
| 109 | - close.onclick = () => { | |
| 110 | - this.clearCanvas(); | |
| 111 | - }; | |
| 112 | - this.updatePreview(); | |
| 113 | - }; | |
| 114 | - this.undo = () => { | |
| 115 | - if (this.points.length == 0) | |
| 116 | - return; | |
| 117 | - const arrKeys = this.points.map(el => el["mode"]); | |
| 118 | - const lastIndex = arrKeys.lastIndexOf(false); | |
| 119 | - const tempPoints = this.points.slice(lastIndex, this.points.length - 1); | |
| 120 | - this.dopoints = [...this.dopoints, ...tempPoints]; | |
| 121 | - this.points = this.points.slice(0, lastIndex); | |
| 122 | - this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| 123 | - this.redraw(); | |
| 124 | - }; | |
| 125 | - this.redo = () => { | |
| 126 | - if (this.dopoints.length == 0) | |
| 127 | - return; | |
| 128 | - const arrKeys = this.dopoints.map(el => el["mode"]); | |
| 129 | - const lastIndex = arrKeys.lastIndexOf(false); | |
| 130 | - const tempPoints = this.dopoints.slice(lastIndex, this.dopoints.length - 1); | |
| 131 | - this.points = [...this.points, ...tempPoints]; | |
| 132 | - this.dopoints = this.dopoints.slice(0, lastIndex); | |
| 133 | - this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| 134 | - this.redraw(); | |
| 135 | - }; | |
| 136 | - this.reset = () => { | |
| 137 | - this.points = []; | |
| 138 | - this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| 139 | - this.redraw(); | |
| 140 | - if (this.erase) { | |
| 141 | - this.switchEraser(); | |
| 142 | - } | |
| 143 | - }; | |
| 144 | - this.changeSize = (size) => { | |
| 145 | - this.size = +size; | |
| 146 | - this.updatePreview(); | |
| 147 | - }; | |
| 148 | - this.changeColour = (key) => { | |
| 149 | - this.coloursSelect = key; | |
| 150 | - if (this.erase) | |
| 151 | - this.switchEraser(); | |
| 152 | - this.updatePreview(); | |
| 153 | - }; | |
| 154 | - this.switchEraser = () => { | |
| 155 | - this.erase = !this.erase; | |
| 156 | - this.eraseObj.checked = this.erase; | |
| 157 | - this.updatePreview(); | |
| 158 | - const area = document.querySelector('.paint-target'); | |
| 159 | - if (this.erase) { | |
| 160 | - area.classList.add('is-eraser'); | |
| 161 | - } else { | |
| 162 | - area.classList.remove('is-eraser'); | |
| 163 | - } | |
| 164 | - }; | |
| 165 | - this.updatePreview = () => { | |
| 166 | - this.previewObj.style.cssText = ` | |
| 167 | - width: ${this.size * 0.1}rem; | |
| 168 | - height: ${this.size * 0.1}rem; | |
| 169 | - `; | |
| 170 | - if (this.coloursArr[this.coloursSelect] == '#ffffff') { | |
| 171 | - this.previewObj.classList.add('white'); | |
| 172 | - } | |
| 173 | - else { | |
| 174 | - this.previewObj.classList.remove('white'); | |
| 175 | - } | |
| 176 | - if (this.erase) { | |
| 177 | - this.previewObj.classList.add('erase'); | |
| 178 | - } | |
| 179 | - else { | |
| 180 | - this.previewObj.classList.remove('erase'); | |
| 181 | - this.previewObj.style.backgroundColor = `${this.coloursArr[this.coloursSelect]}`; | |
| 182 | - } | |
| 183 | - }; | |
| 184 | - this.clearCanvas = () => { | |
| 185 | - this.ctrlCon.remove(); | |
| 186 | - this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| 187 | - this.points = []; | |
| 188 | - this.dopoints = []; | |
| 189 | - this.canvasCon.removeChild(this.canvas); | |
| 190 | - this.erase = false; | |
| 191 | - this.btnInit.classList.remove('is-active'); | |
| 192 | - this.canvasCon.classList.remove('is-active'); | |
| 193 | - }; | |
| 194 | - this.createDrawEvents = () => { | |
| 195 | - const canvas = this.canvas; | |
| 196 | - canvas.addEventListener("mousedown", this.pressEventHandler); | |
| 197 | - canvas.addEventListener("mousemove", this.dragEventHandler); | |
| 198 | - canvas.addEventListener("mouseup", this.releaseEventHandler); | |
| 199 | - canvas.addEventListener("mouseout", this.cancelEventHandler); | |
| 200 | - canvas.addEventListener("touchstart", this.pressEventHandler); | |
| 201 | - canvas.addEventListener("touchmove", this.dragEventHandler); | |
| 202 | - canvas.addEventListener("touchend", this.releaseEventHandler); | |
| 203 | - canvas.addEventListener("touchcancel", this.cancelEventHandler); | |
| 204 | - }; | |
| 205 | - this.redraw = () => { | |
| 206 | - const points = this.points; | |
| 207 | - if (points.length == 0) | |
| 208 | - return; | |
| 209 | - const context = this.context; | |
| 210 | - context.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| 211 | - for (let i = 0; i < points.length; ++i) { | |
| 212 | - if (points[i]["erase"]) { | |
| 213 | - context.globalCompositeOperation = "destination-out"; | |
| 214 | - } | |
| 215 | - else { | |
| 216 | - context.globalCompositeOperation = "source-over"; | |
| 217 | - } | |
| 218 | - context.beginPath(); | |
| 219 | - if (points[i]["mode"] && i) { | |
| 220 | - context.moveTo(points[i - 1]["x"], points[i - 1]["y"]); | |
| 221 | - } | |
| 222 | - else { | |
| 223 | - context.moveTo(points[i]["x"] - 1, points[i]["y"]); | |
| 224 | - } | |
| 225 | - context.lineTo(points[i]["x"], points[i]["y"]); | |
| 226 | - context.strokeStyle = points[i]["color"]; | |
| 227 | - context.lineWidth = points[i]["size"]; | |
| 228 | - context.stroke(); | |
| 229 | - } | |
| 230 | - context.closePath(); | |
| 231 | - }; | |
| 232 | - this.addClick = (x, y, color, size, dragging, erase) => { | |
| 233 | - this.points.push({ x: x, y: y, color: color, size: size, mode: dragging, erase: erase }); | |
| 234 | - }; | |
| 235 | - this.releaseEventHandler = () => { | |
| 236 | - if (this.paint) { | |
| 237 | - this.paint = false; | |
| 238 | - this.redraw(); | |
| 239 | - } | |
| 240 | - }; | |
| 241 | - this.cancelEventHandler = () => { | |
| 242 | - this.releaseEventHandler(); | |
| 243 | - }; | |
| 244 | - this.pressEventHandler = (e) => { | |
| 245 | - this.dopoints = []; | |
| 246 | - let mouseX = e.changedTouches ? | |
| 247 | - e.changedTouches[0].pageX : | |
| 248 | - e.pageX; | |
| 249 | - let mouseY = e.changedTouches ? | |
| 250 | - e.changedTouches[0].pageY : | |
| 251 | - e.pageY; | |
| 252 | - mouseX -= (this.canvasCon.getBoundingClientRect().left + window.pageXOffset); | |
| 253 | - mouseY -= (this.canvasCon.getBoundingClientRect().top + window.pageYOffset); | |
| 254 | - this.paint = true; | |
| 255 | - this.addClick(mouseX, mouseY, this.coloursArr[this.coloursSelect], this.size, false, this.erase); | |
| 256 | - this.redraw(); | |
| 257 | - }; | |
| 258 | - this.dragEventHandler = (e) => { | |
| 259 | - let mouseX = e.changedTouches ? | |
| 260 | - e.changedTouches[0].pageX : | |
| 261 | - e.pageX; | |
| 262 | - let mouseY = e.changedTouches ? | |
| 263 | - e.changedTouches[0].pageY : | |
| 264 | - e.pageY; | |
| 265 | - mouseX -= (this.canvasCon.getBoundingClientRect().left + window.pageXOffset); | |
| 266 | - mouseY -= (this.canvasCon.getBoundingClientRect().top + window.pageYOffset); | |
| 267 | - if (this.paint) { | |
| 268 | - this.addClick(mouseX, mouseY, this.coloursArr[this.coloursSelect], this.size, true, this.erase); | |
| 269 | - this.redraw(); | |
| 270 | - } | |
| 271 | - e.preventDefault(); | |
| 272 | - }; | |
| 273 | - this.btnInit = btnInit; | |
| 274 | - this.canvasCon = canvasCon; | |
| 275 | - this.coloursArr = coloursArr.map(c => c.toLowerCase()); | |
| 276 | - this.coloursSelect = coloursSelect - 1; | |
| 277 | - brushMax = brushMax > 60 ? 60 : brushMax; | |
| 278 | - this.sizeRange = { | |
| 279 | - min: brushMin, | |
| 280 | - max: brushMax | |
| 281 | - }; | |
| 282 | - this.size = Math.round((brushMin + brushMax) / 2); | |
| 283 | - this.btnInit.onclick = () => { | |
| 284 | - if (this.btnInit.classList.contains('is-active')) { | |
| 285 | - this.clearCanvas(); | |
| 286 | - } | |
| 287 | - else { | |
| 288 | - const canvas = document.createElement('canvas'); | |
| 289 | - canvas.width = this.canvasCon.offsetWidth; | |
| 290 | - canvas.height = this.canvasCon.clientHeight; | |
| 291 | - const context = canvas.getContext("2d"); | |
| 292 | - context.lineCap = 'round'; | |
| 293 | - context.lineJoin = 'round'; | |
| 294 | - this.canvas = canvas; | |
| 295 | - this.canvas.id = 'painterCanvas'; | |
| 296 | - this.context = context; | |
| 297 | - this.canvasCon.append(this.canvas); | |
| 298 | - this.uiUpdate(); | |
| 299 | - this.redraw(); | |
| 300 | - this.createDrawEvents(); | |
| 301 | - } | |
| 302 | - }; | |
| 303 | - } | |
| 304 | -} | |
| 305 | -// 환경설정 | |
| 306 | -const colorArray = ["#222222", "#fd4418", "#ffce00", "#00d56a", "#2f77ff", "#ffffff"]; | |
| 307 | -const defaultColor = 5; | |
| 308 | -const brushMin = 0.5, brushMax = 40; | |
| 309 | -// 대상 개체 | |
| 310 | -const btnInit = document.querySelector('.btn-painter-toggle'); | |
| 311 | -const targetObj = document.querySelector('.paint-target'); | |
| 312 | -// 구동 defaultColor, brushMin, brushMax 은 넘기지 않아도 기본 값에 의해 작동됨 | |
| 313 | -new colouredPenciles(btnInit, targetObj, colorArray, defaultColor, brushMin, brushMax); | |
| 314 | -// new colouredPenciles(btnInit, targetObj, colorArray); |
--- resources/front/site/SITE_00000/js/common/module/datePicker.js
... | ... | @@ -1,204 +0,0 @@ |
| 1 | -/**************************** | |
| 2 | - * datePicker.js | |
| 3 | - * create | |
| 4 | - * 2023.08.08 | |
| 5 | - * son tae-jin | |
| 6 | -****************************/ | |
| 7 | - | |
| 8 | -const dayArr = [ | |
| 9 | - "일", "월", "화", "수", "목", "금", "토" | |
| 10 | -]; | |
| 11 | -const month = [ | |
| 12 | - "January", | |
| 13 | - "February", | |
| 14 | - "March", | |
| 15 | - "April", | |
| 16 | - "May", | |
| 17 | - "June", | |
| 18 | - "July", | |
| 19 | - "August", | |
| 20 | - "September", | |
| 21 | - "October", | |
| 22 | - "November", | |
| 23 | - "December" | |
| 24 | -]; | |
| 25 | - | |
| 26 | -const isValidDate = d => { | |
| 27 | - return d instanceof Date && !isNaN(d); | |
| 28 | -} | |
| 29 | - | |
| 30 | -const detectOutsideClick = (caller, container, handler) => { | |
| 31 | - const clickOb = () => { | |
| 32 | - if(!container.contains(event.target) && caller != event.target) { | |
| 33 | - container.remove(); | |
| 34 | - window.removeEventListener('click', clickOb, false); | |
| 35 | - handler(); | |
| 36 | - } | |
| 37 | - } | |
| 38 | - window.addEventListener('click',clickOb, false); | |
| 39 | -} | |
| 40 | - | |
| 41 | -const datePicker = (event) => { | |
| 42 | - if(event.target.dataset.picker) return; | |
| 43 | - const element = event.target; | |
| 44 | - element.dataset.picker = true; | |
| 45 | - const container = document.createElement('div'); | |
| 46 | - container.classList.add('datepicker'); | |
| 47 | - element.parentElement.append(container); | |
| 48 | - container.style.top = element.offsetTop + element.offsetHeight + 'px'; | |
| 49 | - container.style.left = element.offsetLeft + 'px'; | |
| 50 | - detectOutsideClick(element, container, () => {element.removeAttribute('data-picker')}); | |
| 51 | - | |
| 52 | - const dateObj = new Object(); | |
| 53 | - const datePropertys = ['type', 'value', 'min', 'max', 'step']; | |
| 54 | - datePropertys.forEach(el => { | |
| 55 | - if(element[el]) dateObj[el] = element[el]; | |
| 56 | - }); | |
| 57 | - | |
| 58 | - initDataPicker(container, dateObj); | |
| 59 | -} | |
| 60 | - | |
| 61 | -const initDataPicker = (container,property) => { | |
| 62 | - console.log(container, property); | |
| 63 | - | |
| 64 | - switch(property.type) { | |
| 65 | - default: | |
| 66 | - initCalendar(container, property); | |
| 67 | - break; | |
| 68 | - case "month": | |
| 69 | - initMonth(container, property); | |
| 70 | - break; | |
| 71 | - } | |
| 72 | -} | |
| 73 | - | |
| 74 | -const generateCalendar = (container, property, date) => { | |
| 75 | - const ul = container.querySelector('.calendar-con') ? container.querySelector('.calendar-con') : document.createElement('ul'); | |
| 76 | - if(ul.classList.contains('calendar-con')) { | |
| 77 | - ul.innerHTML = ''; | |
| 78 | - } else { | |
| 79 | - ul.classList.add('calendar-con'); | |
| 80 | - container.append(ul); | |
| 81 | - } | |
| 82 | - | |
| 83 | - const today = new Date(); | |
| 84 | - const ny = today.getFullYear(), nm = today.getMonth(), nd = today.getDate(); | |
| 85 | - const cYear = date.getFullYear(); | |
| 86 | - const cMonth = date.getMonth(); | |
| 87 | - const lastDate = new Date(cYear, cMonth + 1, 0).getDate(); | |
| 88 | - const startDay = new Date(cYear, cMonth, 1).getDay(); | |
| 89 | - const beforeLastDate = new Date(cYear, cMonth, 0).getDate(); | |
| 90 | - | |
| 91 | - const selectedDate = property.value ? new Date(property.value) : null; | |
| 92 | - const selectedDateStr = selectedDate ? `${selectedDate.getFullYear()}-${selectedDate.getMonth().toString().padStart(2, '0')}-${selectedDate.getDate().toString().padStart(2, '0')}` : ''; | |
| 93 | - | |
| 94 | - const genDateLi = (date) => { | |
| 95 | - const li = document.createElement('li'); | |
| 96 | - const dateNm = document.createElement('button'); | |
| 97 | - dateNm.type = "button"; | |
| 98 | - dateNm.textContent = new Date(date).getDate(); | |
| 99 | - li.appendChild(dateNm); | |
| 100 | - dateNm.dataset.date = `${date.getFullYear()}-${date.getMonth().toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`; | |
| 101 | - return li; | |
| 102 | - } | |
| 103 | - // 이전달 날짜 | |
| 104 | - for (let k = 0; k < startDay; k++) { | |
| 105 | - const date = new Date(cYear, cMonth-1, beforeLastDate - startDay + 1 + k); | |
| 106 | - const li = genDateLi(date); | |
| 107 | - ul.appendChild(li); | |
| 108 | - li.classList.add('before'); | |
| 109 | - } | |
| 110 | - // 이번달 날짜 표시 | |
| 111 | - for (let i = 0; i < lastDate; i++) { | |
| 112 | - const date = new Date(cYear, cMonth, i+1); | |
| 113 | - const li = genDateLi(date); | |
| 114 | - ul.appendChild(li); | |
| 115 | - if (selectedDate && selectedDateStr == li.querySelector('button').dataset.date) { | |
| 116 | - li.classList.add('selected'); | |
| 117 | - } else if(!selectedDate) { | |
| 118 | - if (ny == cYear && nm == cMonth && nd == i + 1) { | |
| 119 | - li.classList.add('today'); | |
| 120 | - } else if (nd == i + 1) { | |
| 121 | - li.classList.add('sameday'); | |
| 122 | - } | |
| 123 | - } | |
| 124 | - } | |
| 125 | - // 다음달 날짜 표시 | |
| 126 | - const leftAfterDates = 42 - ul.querySelectorAll('li').length; | |
| 127 | - for (let j = 0; j < leftAfterDates; j++){ | |
| 128 | - const date = new Date(cYear, cMonth + 1, j+1); | |
| 129 | - const li = genDateLi(date); | |
| 130 | - ul.appendChild(li); | |
| 131 | - li.classList.add('after'); | |
| 132 | - } | |
| 133 | - | |
| 134 | - return ul; | |
| 135 | -} | |
| 136 | - | |
| 137 | -const initCalendar = (container, property) => { | |
| 138 | - const valueDate = isValidDate(new Date(property.value)) ? new Date(property.value) : new Date(); | |
| 139 | - const ymNavCon = document.createElement('div'); | |
| 140 | - ymNavCon.classList.add('ym-nav-con'); | |
| 141 | - container.append(ymNavCon); | |
| 142 | - | |
| 143 | - const ymSelector = document.createElement('input'); | |
| 144 | - ymSelector.type = 'month'; | |
| 145 | - ymSelector.value = `${valueDate.getFullYear()}-${(valueDate.getMonth()+1).toString().padStart(2, '0')}`; | |
| 146 | - ymSelector.classList.add('custom-datepicker'); | |
| 147 | - ymSelector.onchange = event => { | |
| 148 | - console.log('onchange', event.target); | |
| 149 | - generateCalendar(container, property, new Date(ymSelector.value)); | |
| 150 | - } | |
| 151 | - ymSelector.onclick = event => datePicker(event); | |
| 152 | - ymNavCon.append(ymSelector); | |
| 153 | - | |
| 154 | - const ymPrev = document.createElement('button'); | |
| 155 | - ymPrev.type = "button"; | |
| 156 | - ymPrev.textContent = "이전"; | |
| 157 | - ymPrev.classList.add('btn','prev', 'ico-pv', 'icon-only'); | |
| 158 | - ymNavCon.prepend(ymPrev); | |
| 159 | - ymPrev.onclick = () => ymNav(); | |
| 160 | - | |
| 161 | - const ymNext = document.createElement('button'); | |
| 162 | - ymNext.type = "button"; | |
| 163 | - ymNext.textContent = "다음"; | |
| 164 | - ymNext.classList.add('btn','next', 'ico-fw', 'icon-only'); | |
| 165 | - ymNavCon.append(ymNext); | |
| 166 | - ymNext.onclick = () => ymNav(); | |
| 167 | - | |
| 168 | - const ul = generateCalendar(container, property, new Date(ymSelector.value)); | |
| 169 | -} | |
| 170 | - | |
| 171 | -const ymNav = () => { | |
| 172 | - const ymInput = event.target.parentElement.querySelector('input'); | |
| 173 | - const now = new Date(ymInput.value); | |
| 174 | - if(event.target.classList.contains('prev')) { | |
| 175 | - now.setMonth(now.getMonth() - 1); | |
| 176 | - } else { | |
| 177 | - now.setMonth(now.getMonth() + 1); | |
| 178 | - } | |
| 179 | - ymInput.value = `${now.getFullYear()}-${(now.getMonth()+1).toString().padStart(2, '0')}`; | |
| 180 | - ymInput.dispatchEvent(new Event('change')); | |
| 181 | -} | |
| 182 | - | |
| 183 | -const initMonth = (container, property) => { | |
| 184 | - console.log(container, property); | |
| 185 | -} | |
| 186 | - | |
| 187 | -document.querySelectorAll('input[type=date], input[type=time], input[type=month], input[type=week], input[type=datetime-local]').forEach(el => { | |
| 188 | - el.onchange = () => { | |
| 189 | - if(el.value == '') { | |
| 190 | - el.removeAttribute('value'); | |
| 191 | - } else { | |
| 192 | - el.setAttribute('value',el.value); | |
| 193 | - } | |
| 194 | - } | |
| 195 | -}); | |
| 196 | -document.querySelectorAll('input.custom-datepicker').forEach(el => { | |
| 197 | - el.onclick = (event) => { | |
| 198 | - datePicker(event); | |
| 199 | - } | |
| 200 | -}); | |
| 201 | - | |
| 202 | -// document.querySelectorAll('.custom-datepicker:not(input)').forEach(el => { | |
| 203 | -// initDataPicker(el); | |
| 204 | -// })(파일 끝에 줄바꿈 문자 없음) |
--- resources/front/site/SITE_00000/js/custom/module/colouredPenciles.js
... | ... | @@ -1,314 +0,0 @@ |
| 1 | -/* | |
| 2 | -* Copyright (c) FOX EDU CO., LTD ALL RIGHT RESERVED. | |
| 3 | -* by flashkid | |
| 4 | -* 25.OCT.2023(WED) | |
| 5 | -*/ | |
| 6 | -class colouredPenciles { | |
| 7 | - constructor(btnInit, canvasCon, coloursArr, coloursSelect = 1, brushMin = 0.5, brushMax = 10) { | |
| 8 | - this.points = []; | |
| 9 | - this.dopoints = []; | |
| 10 | - this.erase = false; | |
| 11 | - this.uiUpdate = () => { | |
| 12 | - this.btnInit.classList.add('is-active'); | |
| 13 | - this.canvasCon.classList.add('is-active'); | |
| 14 | - this.ctrlCon = document.createElement('div'); | |
| 15 | - this.ctrlCon.classList.add('painterbar'); | |
| 16 | - this.canvasCon.append(this.ctrlCon); | |
| 17 | - const minBtn = document.createElement('button'); | |
| 18 | - minBtn.type = "button"; | |
| 19 | - minBtn.classList.add('btn-painterbar-size'); | |
| 20 | - minBtn.textContent = '색연필 최소화'; | |
| 21 | - this.ctrlCon.append(minBtn); | |
| 22 | - minBtn.onclick = () => { | |
| 23 | - this.ctrlCon.classList.toggle('min'); | |
| 24 | - }; | |
| 25 | - const previewCon = document.createElement('div'); | |
| 26 | - previewCon.classList.add('pencil-ctrl-options', 'pencil-preview'); | |
| 27 | - previewCon.style.width = `${this.sizeRange['max'] * 0.1 + 0.2}rem`; | |
| 28 | - previewCon.style.height = `${this.sizeRange['max'] * 0.1 + 0.2}rem`; | |
| 29 | - this.ctrlCon.append(previewCon); | |
| 30 | - this.previewObj = document.createElement('div'); | |
| 31 | - previewCon.append(this.previewObj); | |
| 32 | - const sizeCon = document.createElement('div'); | |
| 33 | - sizeCon.classList.add('pencil-ctrl-options', 'painterbar-stroke'); | |
| 34 | - this.ctrlCon.append(sizeCon); | |
| 35 | - const range = document.createElement('input'); | |
| 36 | - range.type = "range"; | |
| 37 | - range.min = this.sizeRange['min']; | |
| 38 | - range.max = this.sizeRange['max']; | |
| 39 | - range.step = "0.1"; | |
| 40 | - range.setAttribute("orient", "vertical"); | |
| 41 | - range.value = this.size.toString(); | |
| 42 | - sizeCon.append(range); | |
| 43 | - range.oninput = () => { | |
| 44 | - this.changeSize(range.value); | |
| 45 | - }; | |
| 46 | - const fncCon = document.createElement('div'); | |
| 47 | - fncCon.classList.add('pencil-ctrl-options', 'painterbar-fnc'); | |
| 48 | - this.ctrlCon.append(fncCon); | |
| 49 | - const undo = document.createElement('button'); | |
| 50 | - undo.type = "button"; | |
| 51 | - undo.classList.add('undo'); | |
| 52 | - undo.textContent = "undo"; | |
| 53 | - fncCon.append(undo); | |
| 54 | - undo.addEventListener('click', this.undo); | |
| 55 | - const redo = document.createElement('button'); | |
| 56 | - redo.type = "button"; | |
| 57 | - redo.classList.add('redo'); | |
| 58 | - redo.textContent = "redo"; | |
| 59 | - fncCon.append(redo); | |
| 60 | - redo.addEventListener('click', this.redo); | |
| 61 | - this.eraseObj = document.createElement('input'); | |
| 62 | - this.eraseObj.type = "checkbox"; | |
| 63 | - this.eraseObj.id = "colouredPencils_eraser"; | |
| 64 | - this.eraseObj.classList.add('clear'); | |
| 65 | - this.eraseObj.checked = this.erase; | |
| 66 | - fncCon.append(this.eraseObj); | |
| 67 | - const eraserLB = document.createElement('label'); | |
| 68 | - eraserLB.setAttribute("for", this.eraseObj.id); | |
| 69 | - eraserLB.textContent = "지우개"; | |
| 70 | - fncCon.append(eraserLB); | |
| 71 | - this.eraseObj.onchange = () => { | |
| 72 | - this.switchEraser(); | |
| 73 | - }; | |
| 74 | - const reset = document.createElement('button'); | |
| 75 | - reset.type = 'button'; | |
| 76 | - reset.classList.add('reset'); | |
| 77 | - reset.textContent = 'reset'; | |
| 78 | - fncCon.append(reset); | |
| 79 | - reset.onclick = () => this.reset(); | |
| 80 | - const colorDiv = document.createElement('div'); | |
| 81 | - colorDiv.classList.add('pencil-ctrl-options', 'painterbar-color'); | |
| 82 | - colorDiv.setAttribute('data-opt', this.coloursArr[this.coloursSelect]); | |
| 83 | - this.ctrlCon.append(colorDiv); | |
| 84 | - this.coloursArr.forEach((el, i) => { | |
| 85 | - const radio = document.createElement('input'); | |
| 86 | - radio.type = "radio"; | |
| 87 | - radio.name = "colouredPencils_colour"; | |
| 88 | - radio.id = "colouredPencils_colour_" + i; | |
| 89 | - radio.value = i.toString(); | |
| 90 | - if (i == this.coloursSelect) | |
| 91 | - radio.checked = true; | |
| 92 | - colorDiv.append(radio); | |
| 93 | - const label = document.createElement('label'); | |
| 94 | - label.setAttribute("for", radio.id); | |
| 95 | - label.textContent = el; | |
| 96 | - label.style.backgroundColor = el; | |
| 97 | - if (el == '#ffffff') | |
| 98 | - label.classList.add('white'); | |
| 99 | - colorDiv.append(label); | |
| 100 | - radio.onchange = (e) => { | |
| 101 | - this.changeColour(i); | |
| 102 | - }; | |
| 103 | - }); | |
| 104 | - const close = document.createElement('button'); | |
| 105 | - close.type = "button"; | |
| 106 | - close.classList.add('btn-viewer-painter-close'); | |
| 107 | - close.textContent = "색연필 닫기"; | |
| 108 | - this.ctrlCon.append(close); | |
| 109 | - close.onclick = () => { | |
| 110 | - this.clearCanvas(); | |
| 111 | - }; | |
| 112 | - this.updatePreview(); | |
| 113 | - }; | |
| 114 | - this.undo = () => { | |
| 115 | - if (this.points.length == 0) | |
| 116 | - return; | |
| 117 | - const arrKeys = this.points.map(el => el["mode"]); | |
| 118 | - const lastIndex = arrKeys.lastIndexOf(false); | |
| 119 | - const tempPoints = this.points.slice(lastIndex, this.points.length - 1); | |
| 120 | - this.dopoints = [...this.dopoints, ...tempPoints]; | |
| 121 | - this.points = this.points.slice(0, lastIndex); | |
| 122 | - this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| 123 | - this.redraw(); | |
| 124 | - }; | |
| 125 | - this.redo = () => { | |
| 126 | - if (this.dopoints.length == 0) | |
| 127 | - return; | |
| 128 | - const arrKeys = this.dopoints.map(el => el["mode"]); | |
| 129 | - const lastIndex = arrKeys.lastIndexOf(false); | |
| 130 | - const tempPoints = this.dopoints.slice(lastIndex, this.dopoints.length - 1); | |
| 131 | - this.points = [...this.points, ...tempPoints]; | |
| 132 | - this.dopoints = this.dopoints.slice(0, lastIndex); | |
| 133 | - this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| 134 | - this.redraw(); | |
| 135 | - }; | |
| 136 | - this.reset = () => { | |
| 137 | - this.points = []; | |
| 138 | - this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| 139 | - this.redraw(); | |
| 140 | - if (this.erase) { | |
| 141 | - this.switchEraser(); | |
| 142 | - } | |
| 143 | - }; | |
| 144 | - this.changeSize = (size) => { | |
| 145 | - this.size = +size; | |
| 146 | - this.updatePreview(); | |
| 147 | - }; | |
| 148 | - this.changeColour = (key) => { | |
| 149 | - this.coloursSelect = key; | |
| 150 | - if (this.erase) | |
| 151 | - this.switchEraser(); | |
| 152 | - this.updatePreview(); | |
| 153 | - }; | |
| 154 | - this.switchEraser = () => { | |
| 155 | - this.erase = !this.erase; | |
| 156 | - this.eraseObj.checked = this.erase; | |
| 157 | - this.updatePreview(); | |
| 158 | - const area = document.querySelector('.paint-target'); | |
| 159 | - if (this.erase) { | |
| 160 | - area.classList.add('is-eraser'); | |
| 161 | - } else { | |
| 162 | - area.classList.remove('is-eraser'); | |
| 163 | - } | |
| 164 | - }; | |
| 165 | - this.updatePreview = () => { | |
| 166 | - this.previewObj.style.cssText = ` | |
| 167 | - width: ${this.size * 0.1}rem; | |
| 168 | - height: ${this.size * 0.1}rem; | |
| 169 | - `; | |
| 170 | - if (this.coloursArr[this.coloursSelect] == '#ffffff') { | |
| 171 | - this.previewObj.classList.add('white'); | |
| 172 | - } | |
| 173 | - else { | |
| 174 | - this.previewObj.classList.remove('white'); | |
| 175 | - } | |
| 176 | - if (this.erase) { | |
| 177 | - this.previewObj.classList.add('erase'); | |
| 178 | - } | |
| 179 | - else { | |
| 180 | - this.previewObj.classList.remove('erase'); | |
| 181 | - this.previewObj.style.backgroundColor = `${this.coloursArr[this.coloursSelect]}`; | |
| 182 | - } | |
| 183 | - }; | |
| 184 | - this.clearCanvas = () => { | |
| 185 | - this.ctrlCon.remove(); | |
| 186 | - this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| 187 | - this.points = []; | |
| 188 | - this.dopoints = []; | |
| 189 | - this.canvasCon.removeChild(this.canvas); | |
| 190 | - this.erase = false; | |
| 191 | - this.btnInit.classList.remove('is-active'); | |
| 192 | - this.canvasCon.classList.remove('is-active'); | |
| 193 | - }; | |
| 194 | - this.createDrawEvents = () => { | |
| 195 | - const canvas = this.canvas; | |
| 196 | - canvas.addEventListener("mousedown", this.pressEventHandler); | |
| 197 | - canvas.addEventListener("mousemove", this.dragEventHandler); | |
| 198 | - canvas.addEventListener("mouseup", this.releaseEventHandler); | |
| 199 | - canvas.addEventListener("mouseout", this.cancelEventHandler); | |
| 200 | - canvas.addEventListener("touchstart", this.pressEventHandler); | |
| 201 | - canvas.addEventListener("touchmove", this.dragEventHandler); | |
| 202 | - canvas.addEventListener("touchend", this.releaseEventHandler); | |
| 203 | - canvas.addEventListener("touchcancel", this.cancelEventHandler); | |
| 204 | - }; | |
| 205 | - this.redraw = () => { | |
| 206 | - const points = this.points; | |
| 207 | - if (points.length == 0) | |
| 208 | - return; | |
| 209 | - const context = this.context; | |
| 210 | - context.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| 211 | - for (let i = 0; i < points.length; ++i) { | |
| 212 | - if (points[i]["erase"]) { | |
| 213 | - context.globalCompositeOperation = "destination-out"; | |
| 214 | - } | |
| 215 | - else { | |
| 216 | - context.globalCompositeOperation = "source-over"; | |
| 217 | - } | |
| 218 | - context.beginPath(); | |
| 219 | - if (points[i]["mode"] && i) { | |
| 220 | - context.moveTo(points[i - 1]["x"], points[i - 1]["y"]); | |
| 221 | - } | |
| 222 | - else { | |
| 223 | - context.moveTo(points[i]["x"] - 1, points[i]["y"]); | |
| 224 | - } | |
| 225 | - context.lineTo(points[i]["x"], points[i]["y"]); | |
| 226 | - context.strokeStyle = points[i]["color"]; | |
| 227 | - context.lineWidth = points[i]["size"]; | |
| 228 | - context.stroke(); | |
| 229 | - } | |
| 230 | - context.closePath(); | |
| 231 | - }; | |
| 232 | - this.addClick = (x, y, color, size, dragging, erase) => { | |
| 233 | - this.points.push({ x: x, y: y, color: color, size: size, mode: dragging, erase: erase }); | |
| 234 | - }; | |
| 235 | - this.releaseEventHandler = () => { | |
| 236 | - if (this.paint) { | |
| 237 | - this.paint = false; | |
| 238 | - this.redraw(); | |
| 239 | - } | |
| 240 | - }; | |
| 241 | - this.cancelEventHandler = () => { | |
| 242 | - this.releaseEventHandler(); | |
| 243 | - }; | |
| 244 | - this.pressEventHandler = (e) => { | |
| 245 | - this.dopoints = []; | |
| 246 | - let mouseX = e.changedTouches ? | |
| 247 | - e.changedTouches[0].pageX : | |
| 248 | - e.pageX; | |
| 249 | - let mouseY = e.changedTouches ? | |
| 250 | - e.changedTouches[0].pageY : | |
| 251 | - e.pageY; | |
| 252 | - mouseX -= (this.canvasCon.getBoundingClientRect().left + window.pageXOffset); | |
| 253 | - mouseY -= (this.canvasCon.getBoundingClientRect().top + window.pageYOffset); | |
| 254 | - this.paint = true; | |
| 255 | - this.addClick(mouseX, mouseY, this.coloursArr[this.coloursSelect], this.size, false, this.erase); | |
| 256 | - this.redraw(); | |
| 257 | - }; | |
| 258 | - this.dragEventHandler = (e) => { | |
| 259 | - let mouseX = e.changedTouches ? | |
| 260 | - e.changedTouches[0].pageX : | |
| 261 | - e.pageX; | |
| 262 | - let mouseY = e.changedTouches ? | |
| 263 | - e.changedTouches[0].pageY : | |
| 264 | - e.pageY; | |
| 265 | - mouseX -= (this.canvasCon.getBoundingClientRect().left + window.pageXOffset); | |
| 266 | - mouseY -= (this.canvasCon.getBoundingClientRect().top + window.pageYOffset); | |
| 267 | - if (this.paint) { | |
| 268 | - this.addClick(mouseX, mouseY, this.coloursArr[this.coloursSelect], this.size, true, this.erase); | |
| 269 | - this.redraw(); | |
| 270 | - } | |
| 271 | - e.preventDefault(); | |
| 272 | - }; | |
| 273 | - this.btnInit = btnInit; | |
| 274 | - this.canvasCon = canvasCon; | |
| 275 | - this.coloursArr = coloursArr.map(c => c.toLowerCase()); | |
| 276 | - this.coloursSelect = coloursSelect - 1; | |
| 277 | - brushMax = brushMax > 60 ? 60 : brushMax; | |
| 278 | - this.sizeRange = { | |
| 279 | - min: brushMin, | |
| 280 | - max: brushMax | |
| 281 | - }; | |
| 282 | - this.size = Math.round((brushMin + brushMax) / 2); | |
| 283 | - this.btnInit.onclick = () => { | |
| 284 | - if (this.btnInit.classList.contains('is-active')) { | |
| 285 | - this.clearCanvas(); | |
| 286 | - } | |
| 287 | - else { | |
| 288 | - const canvas = document.createElement('canvas'); | |
| 289 | - canvas.width = this.canvasCon.offsetWidth; | |
| 290 | - canvas.height = this.canvasCon.clientHeight; | |
| 291 | - const context = canvas.getContext("2d"); | |
| 292 | - context.lineCap = 'round'; | |
| 293 | - context.lineJoin = 'round'; | |
| 294 | - this.canvas = canvas; | |
| 295 | - this.canvas.id = 'painterCanvas'; | |
| 296 | - this.context = context; | |
| 297 | - this.canvasCon.append(this.canvas); | |
| 298 | - this.uiUpdate(); | |
| 299 | - this.redraw(); | |
| 300 | - this.createDrawEvents(); | |
| 301 | - } | |
| 302 | - }; | |
| 303 | - } | |
| 304 | -} | |
| 305 | -// 환경설정 | |
| 306 | -const colorArray = ["#222222", "#fd4418", "#ffce00", "#00d56a", "#2f77ff", "#ffffff"]; | |
| 307 | -const defaultColor = 5; | |
| 308 | -const brushMin = 0.5, brushMax = 40; | |
| 309 | -// 대상 개체 | |
| 310 | -const btnInit = document.querySelector('.btn-painter-toggle'); | |
| 311 | -const targetObj = document.querySelector('.paint-target'); | |
| 312 | -// 구동 defaultColor, brushMin, brushMax 은 넘기지 않아도 기본 값에 의해 작동됨 | |
| 313 | -new colouredPenciles(btnInit, targetObj, colorArray, defaultColor, brushMin, brushMax); | |
| 314 | -// new colouredPenciles(btnInit, targetObj, colorArray); |
--- resources/front/site/SITE_00000/js/custom/module/colouredPenciles.ts
... | ... | @@ -1,357 +0,0 @@ |
| 1 | -/* | |
| 2 | -* Copyright (c) FOX EDU CO., LTD ALL RIGHT RESERVED. | |
| 3 | -* by flashkid | |
| 4 | -* 25.OCT.2023(WED) | |
| 5 | -*/ | |
| 6 | - | |
| 7 | -class colouredPenciles { | |
| 8 | - private btnInit: HTMLElement; | |
| 9 | - private ctrlCon: HTMLElement; | |
| 10 | - private canvasCon: HTMLElement; | |
| 11 | - | |
| 12 | - private canvas: HTMLCanvasElement; | |
| 13 | - private context: CanvasRenderingContext2D; | |
| 14 | - private eraseObj: HTMLInputElement; | |
| 15 | - private previewObj: HTMLElement; | |
| 16 | - | |
| 17 | - private paint: boolean; | |
| 18 | - | |
| 19 | - private points: object[] = []; | |
| 20 | - private dopoints: object[] = []; | |
| 21 | - | |
| 22 | - private coloursArr: any; | |
| 23 | - private coloursSelect: number; | |
| 24 | - private sizeRange: object; | |
| 25 | - private size: number; | |
| 26 | - private erase: boolean = false; | |
| 27 | - | |
| 28 | - constructor(btnInit, canvasCon, coloursArr, coloursSelect = 1, brushMin = 0.5, brushMax = 10) { | |
| 29 | - this.btnInit = btnInit; | |
| 30 | - this.canvasCon = canvasCon; | |
| 31 | - this.coloursArr = coloursArr.map(c => c.toLowerCase()); | |
| 32 | - this.coloursSelect = coloursSelect - 1; | |
| 33 | - brushMax = brushMax > 60 ? 60 : brushMax; | |
| 34 | - this.sizeRange = { | |
| 35 | - min: brushMin, | |
| 36 | - max: brushMax | |
| 37 | - } | |
| 38 | - this.size = Math.round((brushMin + brushMax) / 2); | |
| 39 | - | |
| 40 | - this.btnInit.onclick = () => { | |
| 41 | - if(this.btnInit.classList.contains('is-active')) { | |
| 42 | - this.clearCanvas(); | |
| 43 | - } else { | |
| 44 | - const canvas = document.createElement('canvas') as HTMLCanvasElement; | |
| 45 | - canvas.width = this.canvasCon.offsetWidth; | |
| 46 | - canvas.height = this.canvasCon.clientHeight; | |
| 47 | - const context = canvas.getContext("2d"); | |
| 48 | - context.lineCap = 'round'; | |
| 49 | - context.lineJoin = 'round'; | |
| 50 | - | |
| 51 | - this.canvas = canvas; | |
| 52 | - this.canvas.id = 'painterCanvas'; | |
| 53 | - this.context = context; | |
| 54 | - this.canvasCon.append(this.canvas); | |
| 55 | - | |
| 56 | - this.uiUpdate(); | |
| 57 | - | |
| 58 | - this.redraw(); | |
| 59 | - this.createDrawEvents(); | |
| 60 | - } | |
| 61 | - } | |
| 62 | - } | |
| 63 | - | |
| 64 | - private uiUpdate = () => { | |
| 65 | - this.btnInit.classList.add('is-active'); | |
| 66 | - this.canvasCon.classList.add('is-active'); | |
| 67 | - | |
| 68 | - this.ctrlCon = document.createElement('div'); | |
| 69 | - this.ctrlCon.classList.add('painterbar'); | |
| 70 | - this.canvasCon.append(this.ctrlCon); | |
| 71 | - | |
| 72 | - const minBtn = document.createElement('button'); | |
| 73 | - minBtn.type = "button"; | |
| 74 | - minBtn.classList.add('btn-painterbar-size'); | |
| 75 | - minBtn.textContent = '색연필 최소화'; | |
| 76 | - this.ctrlCon.append(minBtn); | |
| 77 | - minBtn.onclick = () => { | |
| 78 | - this.ctrlCon.classList.toggle('min'); | |
| 79 | - } | |
| 80 | - | |
| 81 | - const previewCon = document.createElement('div'); | |
| 82 | - previewCon.classList.add('pencil-ctrl-options', 'pencil-preview'); | |
| 83 | - previewCon.style.width = `${this.sizeRange['max'] * 0.1 + 0.2}rem`; | |
| 84 | - previewCon.style.height = `${this.sizeRange['max'] * 0.1 + 0.2}rem`; | |
| 85 | - this.ctrlCon.append(previewCon); | |
| 86 | - this.previewObj = document.createElement('div'); | |
| 87 | - previewCon.append(this.previewObj); | |
| 88 | - | |
| 89 | - const sizeCon = document.createElement('div'); | |
| 90 | - sizeCon.classList.add('pencil-ctrl-options', 'painterbar-stroke'); | |
| 91 | - this.ctrlCon.append(sizeCon); | |
| 92 | - | |
| 93 | - const range = document.createElement('input'); | |
| 94 | - range.type = "range"; | |
| 95 | - range.min = this.sizeRange['min']; | |
| 96 | - range.max = this.sizeRange['max']; | |
| 97 | - range.step = "0.1"; | |
| 98 | - range.setAttribute("orient", "vertical"); | |
| 99 | - range.value = this.size.toString(); | |
| 100 | - sizeCon.append(range); | |
| 101 | - range.oninput = () => { | |
| 102 | - this.changeSize(range.value); | |
| 103 | - } | |
| 104 | - | |
| 105 | - const fncCon = document.createElement('div'); | |
| 106 | - fncCon.classList.add('pencil-ctrl-options', 'painterbar-fnc'); | |
| 107 | - this.ctrlCon.append(fncCon); | |
| 108 | - | |
| 109 | - const undo = document.createElement('button'); | |
| 110 | - undo.type = "button"; | |
| 111 | - undo.classList.add('undo'); | |
| 112 | - undo.textContent = "undo"; | |
| 113 | - fncCon.append(undo); | |
| 114 | - undo.addEventListener('click', this.undo); | |
| 115 | - | |
| 116 | - const redo = document.createElement('button'); | |
| 117 | - redo.type = "button"; | |
| 118 | - redo.classList.add('redo'); | |
| 119 | - redo.textContent = "redo"; | |
| 120 | - fncCon.append(redo); | |
| 121 | - redo.addEventListener('click', this.redo); | |
| 122 | - | |
| 123 | - this.eraseObj = document.createElement('input'); | |
| 124 | - this.eraseObj.type = "checkbox"; | |
| 125 | - this.eraseObj.id = "colouredPencils_eraser"; | |
| 126 | - this.eraseObj.classList.add('clear'); | |
| 127 | - this.eraseObj.checked = this.erase; | |
| 128 | - fncCon.append(this.eraseObj); | |
| 129 | - const eraserLB = document.createElement('label'); | |
| 130 | - eraserLB.setAttribute("for", this.eraseObj.id); | |
| 131 | - eraserLB.textContent = "지우개"; | |
| 132 | - fncCon.append(eraserLB); | |
| 133 | - this.eraseObj.onchange = () => { | |
| 134 | - this.switchEraser(); | |
| 135 | - } | |
| 136 | - | |
| 137 | - const reset = document.createElement('button'); | |
| 138 | - reset.type = 'button'; | |
| 139 | - reset.classList.add('reset'); | |
| 140 | - reset.textContent = 'reset'; | |
| 141 | - fncCon.append(reset); | |
| 142 | - reset.onclick = () => this.reset(); | |
| 143 | - | |
| 144 | - const colorDiv = document.createElement('div'); | |
| 145 | - colorDiv.classList.add('pencil-ctrl-options', 'painterbar-color'); | |
| 146 | - colorDiv.setAttribute('data-opt', this.coloursArr[this.coloursSelect]); | |
| 147 | - this.ctrlCon.append(colorDiv); | |
| 148 | - this.coloursArr.forEach ((el, i) => { | |
| 149 | - const radio = document.createElement('input'); | |
| 150 | - radio.type = "radio"; | |
| 151 | - radio.name = "colouredPencils_colour"; | |
| 152 | - radio.id = "colouredPencils_colour_" + i; | |
| 153 | - radio.value = i.toString(); | |
| 154 | - if (i == this.coloursSelect) radio.checked = true; | |
| 155 | - colorDiv.append(radio); | |
| 156 | - const label = document.createElement('label'); | |
| 157 | - label.setAttribute("for", radio.id); | |
| 158 | - label.textContent = el; | |
| 159 | - label.style.backgroundColor = el; | |
| 160 | - if(el == '#ffffff') label.classList.add('white'); | |
| 161 | - colorDiv.append(label); | |
| 162 | - radio.onchange = (e:Event) => { | |
| 163 | - this.changeColour(i); | |
| 164 | - } | |
| 165 | - }); | |
| 166 | - | |
| 167 | - const close = document.createElement('button'); | |
| 168 | - close.type = "button"; | |
| 169 | - close.classList.add('btn-viewer-painter-close'); | |
| 170 | - close.textContent = "색연필 닫기"; | |
| 171 | - this.ctrlCon.append(close); | |
| 172 | - close.onclick = () => { | |
| 173 | - this.clearCanvas(); | |
| 174 | - } | |
| 175 | - | |
| 176 | - this.updatePreview(); | |
| 177 | - } | |
| 178 | - | |
| 179 | - private undo = () => { | |
| 180 | - if (this.points.length == 0) return; | |
| 181 | - const arrKeys = this.points.map(el => el["mode"]); | |
| 182 | - const lastIndex = arrKeys.lastIndexOf(false); | |
| 183 | - const tempPoints = this.points.slice(lastIndex, this.points.length - 1); | |
| 184 | - this.dopoints = [...this.dopoints, ...tempPoints]; | |
| 185 | - this.points = this.points.slice(0, lastIndex); | |
| 186 | - this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| 187 | - this.redraw(); | |
| 188 | - } | |
| 189 | - | |
| 190 | - private redo = () => { | |
| 191 | - if (this.dopoints.length == 0) return; | |
| 192 | - const arrKeys = this.dopoints.map(el => el["mode"]); | |
| 193 | - const lastIndex = arrKeys.lastIndexOf(false); | |
| 194 | - const tempPoints = this.dopoints.slice(lastIndex, this.dopoints.length - 1); | |
| 195 | - this.points = [...this.points, ...tempPoints]; | |
| 196 | - this.dopoints = this.dopoints.slice(0, lastIndex); | |
| 197 | - this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| 198 | - this.redraw(); | |
| 199 | - } | |
| 200 | - | |
| 201 | - private reset = () => { | |
| 202 | - this.points = []; | |
| 203 | - this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| 204 | - this.redraw(); | |
| 205 | - if(this.erase) { | |
| 206 | - this.switchEraser(); | |
| 207 | - } | |
| 208 | - } | |
| 209 | - | |
| 210 | - private changeSize = (size) => { | |
| 211 | - this.size = +size; | |
| 212 | - this.updatePreview(); | |
| 213 | - } | |
| 214 | - | |
| 215 | - private changeColour = (key) => { | |
| 216 | - this.coloursSelect = key; | |
| 217 | - if(this.erase) this.switchEraser(); | |
| 218 | - this.updatePreview(); | |
| 219 | - } | |
| 220 | - | |
| 221 | - private switchEraser = () => { | |
| 222 | - this.erase = !this.erase; | |
| 223 | - this.eraseObj.checked = this.erase; | |
| 224 | - this.updatePreview(); | |
| 225 | - } | |
| 226 | - | |
| 227 | - private updatePreview = () => { | |
| 228 | - this.previewObj.style.cssText = ` | |
| 229 | - width: ${this.size * 0.1}rem; | |
| 230 | - height: ${this.size * 0.1}rem; | |
| 231 | - ` | |
| 232 | - if (this.coloursArr[this.coloursSelect] == '#ffffff') { | |
| 233 | - this.previewObj.classList.add('white'); | |
| 234 | - } else { | |
| 235 | - this.previewObj.classList.remove('white'); | |
| 236 | - } | |
| 237 | - | |
| 238 | - if (this.erase) { | |
| 239 | - this.previewObj.classList.add('erase'); | |
| 240 | - } else { | |
| 241 | - this.previewObj.classList.remove('erase'); | |
| 242 | - this.previewObj.style.backgroundColor = `${this.coloursArr[this.coloursSelect]}`; | |
| 243 | - } | |
| 244 | - } | |
| 245 | - | |
| 246 | - private clearCanvas = () => { | |
| 247 | - this.ctrlCon.remove(); | |
| 248 | - this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| 249 | - this.points = []; | |
| 250 | - this.dopoints = []; | |
| 251 | - this.canvasCon.removeChild(this.canvas); | |
| 252 | - this.erase = false; | |
| 253 | - | |
| 254 | - this.btnInit.classList.remove('is-active'); | |
| 255 | - this.canvasCon.classList.remove('is-active'); | |
| 256 | - } | |
| 257 | - | |
| 258 | - private createDrawEvents = () => { | |
| 259 | - const canvas = this.canvas; | |
| 260 | - | |
| 261 | - canvas.addEventListener("mousedown", this.pressEventHandler); | |
| 262 | - canvas.addEventListener("mousemove", this.dragEventHandler); | |
| 263 | - canvas.addEventListener("mouseup", this.releaseEventHandler); | |
| 264 | - canvas.addEventListener("mouseout", this.cancelEventHandler); | |
| 265 | - | |
| 266 | - canvas.addEventListener("touchstart", this.pressEventHandler); | |
| 267 | - canvas.addEventListener("touchmove", this.dragEventHandler); | |
| 268 | - canvas.addEventListener("touchend", this.releaseEventHandler); | |
| 269 | - canvas.addEventListener("touchcancel", this.cancelEventHandler); | |
| 270 | - } | |
| 271 | - | |
| 272 | - private redraw = () => { | |
| 273 | - const points = this.points; | |
| 274 | - if (points.length == 0) return; | |
| 275 | - const context = this.context; | |
| 276 | - context.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| 277 | - for (let i = 0; i < points.length; ++i) { | |
| 278 | - if (points[i]["erase"]) { | |
| 279 | - context.globalCompositeOperation = "destination-out"; | |
| 280 | - } else { | |
| 281 | - context.globalCompositeOperation = "source-over"; | |
| 282 | - } | |
| 283 | - context.beginPath(); | |
| 284 | - if (points[i]["mode"] && i) { | |
| 285 | - context.moveTo(points[i - 1]["x"], points[i - 1]["y"]); | |
| 286 | - } else { | |
| 287 | - context.moveTo(points[i]["x"] - 1, points[i]["y"]); | |
| 288 | - } | |
| 289 | - | |
| 290 | - context.lineTo(points[i]["x"], points[i]["y"]); | |
| 291 | - context.strokeStyle = points[i]["color"]; | |
| 292 | - context.lineWidth = points[i]["size"]; | |
| 293 | - context.stroke(); | |
| 294 | - } | |
| 295 | - context.closePath(); | |
| 296 | - } | |
| 297 | - | |
| 298 | - private addClick = (x: number, y: number, color: string, size: number, dragging: boolean, erase: boolean) => { | |
| 299 | - this.points.push({ x: x, y: y, color: color, size: size, mode: dragging, erase: erase }) | |
| 300 | - } | |
| 301 | - | |
| 302 | - private releaseEventHandler = () => { | |
| 303 | - if (this.paint) { | |
| 304 | - this.paint = false; | |
| 305 | - this.redraw(); | |
| 306 | - } | |
| 307 | - } | |
| 308 | - | |
| 309 | - private cancelEventHandler = () => { | |
| 310 | - this.releaseEventHandler(); | |
| 311 | - } | |
| 312 | - | |
| 313 | - private pressEventHandler = (e: MouseEvent | TouchEvent) => { | |
| 314 | - this.dopoints = []; | |
| 315 | - let mouseX = (e as TouchEvent).changedTouches ? | |
| 316 | - (e as TouchEvent).changedTouches[0].pageX : | |
| 317 | - (e as MouseEvent).pageX; | |
| 318 | - let mouseY = (e as TouchEvent).changedTouches ? | |
| 319 | - (e as TouchEvent).changedTouches[0].pageY : | |
| 320 | - (e as MouseEvent).pageY; | |
| 321 | - mouseX -= (this.canvasCon.getBoundingClientRect().left + window.pageXOffset); | |
| 322 | - mouseY -= (this.canvasCon.getBoundingClientRect().top + window.pageYOffset); | |
| 323 | - | |
| 324 | - this.paint = true; | |
| 325 | - this.addClick(mouseX, mouseY, this.coloursArr[this.coloursSelect], this.size, false, this.erase); | |
| 326 | - this.redraw(); | |
| 327 | - } | |
| 328 | - | |
| 329 | - private dragEventHandler = (e: MouseEvent | TouchEvent) => { | |
| 330 | - let mouseX = (e as TouchEvent).changedTouches ? | |
| 331 | - (e as TouchEvent).changedTouches[0].pageX : | |
| 332 | - (e as MouseEvent).pageX; | |
| 333 | - let mouseY = (e as TouchEvent).changedTouches ? | |
| 334 | - (e as TouchEvent).changedTouches[0].pageY : | |
| 335 | - (e as MouseEvent).pageY; | |
| 336 | - mouseX -= (this.canvasCon.getBoundingClientRect().left + window.pageXOffset); | |
| 337 | - mouseY -= (this.canvasCon.getBoundingClientRect().top + window.pageYOffset); | |
| 338 | - | |
| 339 | - if (this.paint) { | |
| 340 | - this.addClick(mouseX, mouseY, this.coloursArr[this.coloursSelect], this.size, true, this.erase); | |
| 341 | - this.redraw(); | |
| 342 | - } | |
| 343 | - | |
| 344 | - e.preventDefault(); | |
| 345 | - } | |
| 346 | -} | |
| 347 | - | |
| 348 | -// 환경설정 | |
| 349 | -const colorArray = ["#222222","#fd4418","#ffce00","#00d56a","#2f77ff","#ffffff"]; | |
| 350 | -const defaultColor = 5; | |
| 351 | -const brushMin = 0.5, brushMax = 40; | |
| 352 | -// 대상 개체 | |
| 353 | -const btnInit = document.querySelector('.btn-painter-toggle') as HTMLElement; | |
| 354 | -const targetObj = document.querySelector('.paint-target') as HTMLElement; | |
| 355 | -// 구동 defaultColor, brushMin, brushMax 은 넘기지 않아도 기본 값에 의해 작동됨 | |
| 356 | -new colouredPenciles(btnInit, targetObj, colorArray, defaultColor, brushMin, brushMax); | |
| 357 | -// new colouredPenciles(btnInit, targetObj, colorArray); |
--- resources/front/site/SITE_00000/js/custom/module/colouredPencilesSvg.js
... | ... | @@ -1,863 +0,0 @@ |
| 1 | -/* | |
| 2 | -* Copyright (c) FOX EDU CO., LTD ALL RIGHT RESERVED. | |
| 3 | -* by flashkid | |
| 4 | -* 25.SEP.2024(WED) | |
| 5 | -*/ | |
| 6 | -export class colouredPenciles { | |
| 7 | - constructor(btnInit, objArr, coloursArr, coloursSelect = 1, brushMin = 0.5, brushMax = 10, saveDataURL = '', loadDataURL = '', deleteDataURL = '', dataParam = {}) { | |
| 8 | - var _a; | |
| 9 | - this.svgNS = "http://www.w3.org/2000/svg"; | |
| 10 | - this.ctrlCon = null; | |
| 11 | - this.targetCon = null; | |
| 12 | - this.targetArr = []; | |
| 13 | - this.ifrObj = null; | |
| 14 | - this.ifrDoc = null; | |
| 15 | - this.ifrContents = null; | |
| 16 | - this.ifrContentsType = "etc"; | |
| 17 | - this.svg = null; | |
| 18 | - this.eraseObj = null; | |
| 19 | - this.previewObj = null; | |
| 20 | - this.loadedDrawingData = false; | |
| 21 | - this.drawingGroup = null; | |
| 22 | - this.maskingGroup = null; | |
| 23 | - this.drawPathElement = null; | |
| 24 | - this.eraserPathElement = null; | |
| 25 | - this.undoStack = []; | |
| 26 | - this.redoStack = []; | |
| 27 | - this.currentPath = []; | |
| 28 | - this.drawing = false; | |
| 29 | - this.isErasing = false; | |
| 30 | - this.detectTargetObj = () => { | |
| 31 | - let tobj, i = 0; | |
| 32 | - while (!tobj && i <= this.targetArr.length) { | |
| 33 | - tobj = document.querySelector(`.${this.targetArr[i]}`); | |
| 34 | - i++; | |
| 35 | - } | |
| 36 | - if (tobj) { | |
| 37 | - this.targetCon = tobj; | |
| 38 | - this.targetCon.classList.add('painter-target'); | |
| 39 | - } | |
| 40 | - }; | |
| 41 | - // Function to handle the scroll event | |
| 42 | - this.onScroll = () => { | |
| 43 | - var _a; | |
| 44 | - if (['img', 'iframe', 'etc', 'unknown'].includes(this.ifrContentsType) && ((_a = this.ifrObj) === null || _a === void 0 ? void 0 : _a.contentWindow) && this.targetCon) { | |
| 45 | - this.ifrObj.contentWindow.scrollTo(this.targetCon.scrollLeft, this.targetCon.scrollTop); | |
| 46 | - } | |
| 47 | - else if (this.ifrContents && this.targetCon) { | |
| 48 | - this.ifrContents.scrollTo(this.targetCon.scrollLeft, this.targetCon.scrollTop); | |
| 49 | - } | |
| 50 | - }; | |
| 51 | - // 아이프레임 온로드 | |
| 52 | - this.onIfrLoad = () => { | |
| 53 | - var _a, _b, _c; | |
| 54 | - if (this.btnInit.classList.contains('is-active')) { | |
| 55 | - this.closeDrawing(); | |
| 56 | - this.onResize(); | |
| 57 | - } | |
| 58 | - const ifrDoc = ((_b = (_a = this.ifrObj) === null || _a === void 0 ? void 0 : _a.contentWindow) === null || _b === void 0 ? void 0 : _b.document) || ((_c = this.ifrObj) === null || _c === void 0 ? void 0 : _c.contentDocument); | |
| 59 | - if (!ifrDoc) | |
| 60 | - return; | |
| 61 | - this.ifrDoc = ifrDoc; | |
| 62 | - // ifrContents 선언 | |
| 63 | - this.ifrContents = this.ifrDoc.querySelector('#contents2') || this.ifrDoc.querySelector('.viewer .viewer-body .viewer-cont .quiz-viewer .quiz-viewer-body') || null; | |
| 64 | - if (this.ifrContents.classList.contains('quiz-viewer-body')) { | |
| 65 | - this.ifrContentsType = 'quiz'; | |
| 66 | - this.setBaseSize({ | |
| 67 | - w: this.ifrContents.querySelector('.quiz-viewer-cont').clientWidth, | |
| 68 | - h: this.ifrContents.scrollHeight | |
| 69 | - }); | |
| 70 | - this.setBaseSize({ | |
| 71 | - x: -this.ifrContents.querySelector('.quiz-viewer-cont').clientWidth / 2, | |
| 72 | - y: 0 | |
| 73 | - }); | |
| 74 | - } | |
| 75 | - else if (this.ifrContents.id === 'contents2') { | |
| 76 | - const video = this.ifrContents.querySelector('video'); | |
| 77 | - const img = this.ifrContents.querySelector('img'); | |
| 78 | - const iframe = this.ifrContents.querySelector('iframe'); | |
| 79 | - const youtube = this.ifrContents.querySelector('input#tempCntntsUrl'); | |
| 80 | - const whiteboard = this.ifrContents.querySelector('.whiteboard'); | |
| 81 | - if (video) { | |
| 82 | - // 비디오 태그가 있을 경우 | |
| 83 | - this.ifrContentsType = 'video'; | |
| 84 | - this.setBaseSize({ x: -video.videoWidth / 2, y: -video.videoHeight / 2, w: video.videoWidth, h: video.videoHeight }); | |
| 85 | - } | |
| 86 | - else if (img) { | |
| 87 | - // 이미지 태그가 있을 경우 | |
| 88 | - this.ifrContentsType = 'img'; | |
| 89 | - this.setBaseSize({ x: -img.naturalWidth / 2, y: -img.naturalHeight / 2, w: img.naturalWidth, h: img.naturalHeight }); | |
| 90 | - } | |
| 91 | - else if (iframe) { | |
| 92 | - // 아이프레임이 있을 경우 | |
| 93 | - this.ifrContentsType = 'iframe'; | |
| 94 | - this.setBaseSize({ x: -iframe.clientWidth / 2, y: 0, w: iframe.clientWidth, h: iframe.scrollHeight }); | |
| 95 | - } | |
| 96 | - else if (youtube) { | |
| 97 | - // 유튜브 프레임이 들어온 경우 | |
| 98 | - this.ifrContentsType = 'youtube'; | |
| 99 | - this.setBaseSize({ x: -960, y: -540, w: 1920, h: 1080 }); | |
| 100 | - } | |
| 101 | - else if (whiteboard) { | |
| 102 | - // 화이트보드가 있을 경우 | |
| 103 | - this.ifrContentsType = 'whiteboard'; | |
| 104 | - this.setBaseSize({ x: -whiteboard.clientWidth / 2, y: -whiteboard.clientHeight / 2, w: whiteboard.clientWidth, h: whiteboard.clientHeight }); | |
| 105 | - } | |
| 106 | - else { | |
| 107 | - this.ifrContentsType = 'etc'; | |
| 108 | - this.setBaseSize({ w: this.ifrContents.clientWidth, h: this.ifrContents.scrollHeight }); | |
| 109 | - this.setBaseSize({ | |
| 110 | - x: -this.ifrContents.clientWidth / 2, | |
| 111 | - y: 0 | |
| 112 | - }); | |
| 113 | - } | |
| 114 | - } | |
| 115 | - else { | |
| 116 | - this.ifrContentsType = 'unknown'; | |
| 117 | - this.setBaseSize({ w: this.ifrContents.clientWidth, h: this.ifrContents.scrollHeight }); | |
| 118 | - this.setBaseSize({ | |
| 119 | - x: -this.ifrContents.clientWidth / 2, | |
| 120 | - y: 0 | |
| 121 | - }); | |
| 122 | - } | |
| 123 | - }; | |
| 124 | - this.onResize = () => { | |
| 125 | - if (!this.ifrDoc || !this.svg || !this.ifrContents) | |
| 126 | - return; | |
| 127 | - const hasYScroll = this.ifrContents.scrollHeight > this.ifrContents.clientHeight; | |
| 128 | - if (hasYScroll) { | |
| 129 | - switch (this.ifrContentsType) { | |
| 130 | - case 'img': | |
| 131 | - // this.svg.style.height = `${this.ifrContents.scrollHeight}px`; | |
| 132 | - // this.setViewBox(this.baseSize); | |
| 133 | - // break; | |
| 134 | - case 'quiz': | |
| 135 | - this.svg.style.height = `${this.ifrContents.scrollHeight}px`; | |
| 136 | - this.setViewBox(this.baseSize); | |
| 137 | - break; | |
| 138 | - case 'iframe': | |
| 139 | - case 'unknown': | |
| 140 | - case 'etc': | |
| 141 | - this.setViewBox({ x: -this.ifrContents.clientWidth / 2, w: this.ifrContents.clientWidth, h: this.ifrContents.scrollHeight }); | |
| 142 | - break; | |
| 143 | - case 'video': | |
| 144 | - case 'youtube': | |
| 145 | - case 'whiteboard': | |
| 146 | - default: | |
| 147 | - this.setViewBox(this.baseSize); | |
| 148 | - break; | |
| 149 | - } | |
| 150 | - } | |
| 151 | - else { | |
| 152 | - this.svg.style.height = ``; | |
| 153 | - switch (this.ifrContentsType) { | |
| 154 | - case 'quiz': | |
| 155 | - this.svg.style.height = `${this.ifrContents.scrollHeight}px`; | |
| 156 | - this.setViewBox({ h: this.ifrContents.scrollHeight }); | |
| 157 | - break; | |
| 158 | - case 'iframe': | |
| 159 | - case 'unknown': | |
| 160 | - case 'etc': | |
| 161 | - this.setViewBox({ x: -this.ifrContents.clientWidth / 2, w: this.ifrContents.clientWidth, h: this.ifrContents.scrollHeight }); | |
| 162 | - break; | |
| 163 | - case 'img': | |
| 164 | - case 'video': | |
| 165 | - case 'youtube': | |
| 166 | - case 'whiteboard': | |
| 167 | - default: | |
| 168 | - this.setViewBox(this.baseSize); | |
| 169 | - break; | |
| 170 | - } | |
| 171 | - } | |
| 172 | - }; | |
| 173 | - this.setBaseSize = (newBaseSize) => { | |
| 174 | - this.baseSize = Object.assign(Object.assign({}, this.baseSize), newBaseSize); // Merge with the existing baseSize | |
| 175 | - }; | |
| 176 | - this.setViewBox = (newSize) => { | |
| 177 | - const newBaseSize = Object.assign(Object.assign({}, this.baseSize), newSize); | |
| 178 | - if (!this.svg) | |
| 179 | - return; | |
| 180 | - this.svg.setAttribute("viewBox", `${newBaseSize.x} ${newBaseSize.y} ${newBaseSize.w} ${newBaseSize.h}`); | |
| 181 | - }; | |
| 182 | - this.createDrawingGroup = () => { | |
| 183 | - var _a; | |
| 184 | - const drawingGroup = document.createElementNS(this.svgNS, 'g'); | |
| 185 | - (_a = this.svg) === null || _a === void 0 ? void 0 : _a.append(drawingGroup); | |
| 186 | - this.drawingGroup = drawingGroup; | |
| 187 | - }; | |
| 188 | - this.createMaskingGroup = () => { | |
| 189 | - var _a, _b; | |
| 190 | - const maskingGroup = document.createElementNS(this.svgNS, 'mask'); | |
| 191 | - maskingGroup.id = 'eraser-mask'; | |
| 192 | - (_a = this.svg) === null || _a === void 0 ? void 0 : _a.append(maskingGroup); | |
| 193 | - this.maskingGroup = maskingGroup; | |
| 194 | - (_b = this.drawingGroup) === null || _b === void 0 ? void 0 : _b.setAttribute('mask', 'url(#eraser-mask)'); | |
| 195 | - // 흰색 배경 (지우개 영역은 투명) | |
| 196 | - const maskRect = document.createElementNS(this.svgNS, "rect"); | |
| 197 | - maskRect.setAttribute("fill", "white"); | |
| 198 | - maskRect.setAttribute("x", `${this.baseSize.x * 100}`); | |
| 199 | - maskRect.setAttribute("y", `${this.baseSize.y * 100}`); | |
| 200 | - maskRect.setAttribute("width", `${this.baseSize.w * 100}`); | |
| 201 | - maskRect.setAttribute("height", `${this.baseSize.h * 100}`); | |
| 202 | - this.maskingGroup.appendChild(maskRect); | |
| 203 | - }; | |
| 204 | - // 콘트롤 패널 드래그 앤 드롭 기능 추가 | |
| 205 | - // this.enableDrag = (element) => { | |
| 206 | - // let offsetX = 0; | |
| 207 | - // let offsetY = 0; | |
| 208 | - // let isDragging = false; | |
| 209 | - // const onMouseDown = (e) => { | |
| 210 | - // var _a; | |
| 211 | - // e.preventDefault(); | |
| 212 | - // isDragging = true; | |
| 213 | - // // 현재 요소의 fixed 위치 계산 | |
| 214 | - // const rect = element.getBoundingClientRect(); | |
| 215 | - // const targetConRect = (_a = this.targetCon) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect(); | |
| 216 | - // if (!targetConRect) | |
| 217 | - // return; | |
| 218 | - // // 마우스 포인터와 요소의 **오른쪽** 및 **위쪽** 경계와의 거리 계산 | |
| 219 | - // // offsetX = rect.right - e.clientX; | |
| 220 | - // // offsetY = e.clientY - rect.top; | |
| 221 | - // // // 요소의 스타일을 고정 위치에 맞추어 설정 | |
| 222 | - // // element.style.right = `${window.innerWidth - rect.right}px`; // right 기준으로 위치 지정 | |
| 223 | - // // element.style.top = `${rect.top}px`; | |
| 224 | - // // element.style.left = 'auto'; // left 속성 제거 | |
| 225 | - // // element.style.transform = 'none'; // 기존 translateY 제거 | |
| 226 | - // // element.style.transition = 'none'; | |
| 227 | - // }; | |
| 228 | - // const onMouseMove = (e) => { | |
| 229 | - // var _a; | |
| 230 | - // if (!isDragging) | |
| 231 | - // return; | |
| 232 | - // e.preventDefault(); | |
| 233 | - // // painter-target 영역의 크기 가져오기 | |
| 234 | - // const containerRect = (_a = this.targetCon) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect(); | |
| 235 | - // if (!containerRect) | |
| 236 | - // return; | |
| 237 | - // // 새로운 위치 계산 | |
| 238 | - // // let right = window.innerWidth - e.clientX - offsetX; | |
| 239 | - // // let y = e.clientY - offsetY; | |
| 240 | - // // // 요소의 x, y 위치가 painter-target 영역을 넘지 않도록 제한 | |
| 241 | - // // right = Math.max(0, Math.min(right, window.innerWidth - containerRect.left - element.offsetWidth)); | |
| 242 | - // // y = Math.max(containerRect.top, Math.min(y, containerRect.bottom - element.offsetHeight)); | |
| 243 | - // // // 요소의 위치 업데이트 (오른쪽 기준) | |
| 244 | - // // element.style.right = `${right}px`; | |
| 245 | - // // element.style.top = `${y}px`; | |
| 246 | - // }; | |
| 247 | - // const onMouseUp = () => { | |
| 248 | - // isDragging = false; | |
| 249 | - // element.style.transition = ''; | |
| 250 | - // }; | |
| 251 | - // // 이벤트 리스너 등록 | |
| 252 | - // element.addEventListener('mousedown', onMouseDown); | |
| 253 | - // document.addEventListener('mousemove', onMouseMove); | |
| 254 | - // document.addEventListener('mouseup', onMouseUp); | |
| 255 | - // }; | |
| 256 | - this.uiUpdate = () => { | |
| 257 | - if (!this.targetCon) | |
| 258 | - return; // targetCon이 null인지 확인 | |
| 259 | - this.btnInit.classList.add('is-active'); | |
| 260 | - this.targetCon.classList.add('is-active'); | |
| 261 | - this.ctrlCon = document.createElement('aside'); | |
| 262 | - this.ctrlCon.classList.add('painterbar'); | |
| 263 | - this.targetCon.append(this.ctrlCon); | |
| 264 | - // 드래그 기능 활성화 | |
| 265 | - //this.enableDrag(this.ctrlCon); | |
| 266 | - const minBtn = document.createElement('button'); | |
| 267 | - minBtn.type = "button"; | |
| 268 | - minBtn.classList.add('btn-painterbar-size'); | |
| 269 | - minBtn.textContent = '색연필 최소화'; | |
| 270 | - this.ctrlCon.append(minBtn); | |
| 271 | - minBtn.onclick = () => { | |
| 272 | - var _a; | |
| 273 | - (_a = this.ctrlCon) === null || _a === void 0 ? void 0 : _a.classList.toggle('min'); // null 체크 | |
| 274 | - }; | |
| 275 | - const previewCon = document.createElement('div'); | |
| 276 | - previewCon.classList.add('pencil-ctrl-options', 'pencil-preview'); | |
| 277 | - previewCon.style.width = `${this.sizeRange['max'] * 0.1 + 0.2}rem`; | |
| 278 | - previewCon.style.height = `${this.sizeRange['max'] * 0.1 + 0.2}rem`; | |
| 279 | - this.ctrlCon.append(previewCon); | |
| 280 | - this.previewObj = document.createElement('div'); | |
| 281 | - previewCon.append(this.previewObj); | |
| 282 | - const sizeCon = document.createElement('div'); | |
| 283 | - sizeCon.classList.add('pencil-ctrl-options', 'painterbar-stroke'); | |
| 284 | - this.ctrlCon.append(sizeCon); | |
| 285 | - const range = document.createElement('input'); | |
| 286 | - range.type = "range"; | |
| 287 | - range.min = this.sizeRange['min'].toString(); | |
| 288 | - range.max = this.sizeRange['max'].toString(); | |
| 289 | - range.step = "0.1"; | |
| 290 | - range.setAttribute("orient", "vertical"); | |
| 291 | - range.value = this.size.toString(); | |
| 292 | - sizeCon.append(range); | |
| 293 | - // range input 이벤트에서 드래그 앤 드롭과 충돌 방지 | |
| 294 | - range.addEventListener('mousedown', (e) => e.stopPropagation()); | |
| 295 | - range.addEventListener('mousemove', (e) => e.stopPropagation()); | |
| 296 | - range.addEventListener('mouseup', (e) => e.stopPropagation()); | |
| 297 | - range.oninput = () => this.changeSize(Number(range.value)); // 값 변환 | |
| 298 | - const fncCon = document.createElement('div'); | |
| 299 | - fncCon.classList.add('pencil-ctrl-options', 'painterbar-fnc'); | |
| 300 | - this.ctrlCon.append(fncCon); | |
| 301 | - const undo = document.createElement('button'); | |
| 302 | - undo.type = "button"; | |
| 303 | - undo.classList.add('undo'); | |
| 304 | - undo.textContent = "undo"; | |
| 305 | - fncCon.append(undo); | |
| 306 | - undo.addEventListener('click', this.undo); | |
| 307 | - const redo = document.createElement('button'); | |
| 308 | - redo.type = "button"; | |
| 309 | - redo.classList.add('redo'); | |
| 310 | - redo.textContent = "redo"; | |
| 311 | - fncCon.append(redo); | |
| 312 | - redo.addEventListener('click', this.redo); | |
| 313 | - this.eraseObj = document.createElement('input'); | |
| 314 | - this.eraseObj.type = "checkbox"; | |
| 315 | - this.eraseObj.id = "colouredPencils_eraser"; | |
| 316 | - this.eraseObj.classList.add('clear'); | |
| 317 | - this.eraseObj.checked = this.isErasing; | |
| 318 | - fncCon.append(this.eraseObj); | |
| 319 | - const eraserLB = document.createElement('label'); | |
| 320 | - eraserLB.setAttribute("for", this.eraseObj.id); | |
| 321 | - eraserLB.textContent = "지우개"; | |
| 322 | - fncCon.append(eraserLB); | |
| 323 | - this.eraseObj.onchange = () => { | |
| 324 | - this.switchEraser(); | |
| 325 | - }; | |
| 326 | - const reset = document.createElement('button'); | |
| 327 | - reset.type = 'button'; | |
| 328 | - reset.classList.add('reset'); | |
| 329 | - reset.textContent = 'reset'; | |
| 330 | - fncCon.append(reset); | |
| 331 | - reset.onclick = () => this.reset(); | |
| 332 | - // const save = document.createElement('button'); | |
| 333 | - // save.type = 'button'; | |
| 334 | - // save.classList.add('save'); | |
| 335 | - // save.textContent = 'save'; | |
| 336 | - // fncCon.append(save); | |
| 337 | - // save.onclick = () => this.save(); | |
| 338 | - const colorDiv = document.createElement('div'); | |
| 339 | - colorDiv.classList.add('pencil-ctrl-options', 'painterbar-color'); | |
| 340 | - colorDiv.setAttribute('data-opt', this.coloursArr[this.coloursSelect]); | |
| 341 | - this.ctrlCon.append(colorDiv); | |
| 342 | - this.coloursArr.forEach((el, i) => { | |
| 343 | - const radio = document.createElement('input'); | |
| 344 | - radio.type = "radio"; | |
| 345 | - radio.name = "colouredPencils_colour"; | |
| 346 | - radio.id = "colouredPencils_colour_" + i; | |
| 347 | - radio.value = i.toString(); | |
| 348 | - if (i === this.coloursSelect) | |
| 349 | - radio.checked = true; | |
| 350 | - colorDiv.append(radio); | |
| 351 | - const label = document.createElement('label'); | |
| 352 | - label.setAttribute("for", radio.id); | |
| 353 | - label.textContent = el; | |
| 354 | - label.style.backgroundColor = el; | |
| 355 | - if (el === '#ffffff') | |
| 356 | - label.classList.add('white'); | |
| 357 | - colorDiv.append(label); | |
| 358 | - radio.onchange = () => { | |
| 359 | - this.changeColour(i); | |
| 360 | - }; | |
| 361 | - }); | |
| 362 | - const close = document.createElement('button'); | |
| 363 | - close.type = "button"; | |
| 364 | - close.classList.add('btn-viewer-painter-close'); | |
| 365 | - close.textContent = "색연필 닫기"; | |
| 366 | - this.ctrlCon.append(close); | |
| 367 | - close.onclick = () => { | |
| 368 | - this.save(); | |
| 369 | - this.closeDrawing(); | |
| 370 | - }; | |
| 371 | - this.updatePreview(); | |
| 372 | - }; | |
| 373 | - this.undo = () => { | |
| 374 | - if (this.undoStack.length === 0) | |
| 375 | - return; | |
| 376 | - const paths = this.undoStack.pop(); | |
| 377 | - if (!paths) | |
| 378 | - return; | |
| 379 | - Array.isArray(paths) ? paths.forEach(path => path.remove()) : {}; | |
| 380 | - this.redoStack.push(paths); | |
| 381 | - }; | |
| 382 | - this.redo = () => { | |
| 383 | - if (this.redoStack.length === 0) | |
| 384 | - return; | |
| 385 | - const paths = this.redoStack.pop(); | |
| 386 | - if (!paths) | |
| 387 | - return; | |
| 388 | - // Check if drawingGroup and maskingGroup are not null | |
| 389 | - if (!this.drawingGroup || !this.maskingGroup) | |
| 390 | - return; | |
| 391 | - Array.isArray(paths) | |
| 392 | - ? paths.forEach(path => { | |
| 393 | - if (path.dataset.use === 'draw') { | |
| 394 | - this.drawingGroup.append(path); // Add null check with "!" to ensure not null | |
| 395 | - } | |
| 396 | - else { | |
| 397 | - this.maskingGroup.append(path); // Add null check | |
| 398 | - } | |
| 399 | - }) | |
| 400 | - : {}; | |
| 401 | - this.undoStack.push(paths); | |
| 402 | - }; | |
| 403 | - this.switchEraser = () => { | |
| 404 | - this.isErasing = !this.isErasing; | |
| 405 | - if (this.eraseObj) { | |
| 406 | - this.eraseObj.checked = this.isErasing; | |
| 407 | - } | |
| 408 | - this.updatePreview(); | |
| 409 | - }; | |
| 410 | - this.reset = () => { | |
| 411 | - this.undoStack = []; | |
| 412 | - this.redoStack = []; | |
| 413 | - if (this.maskingGroup) { | |
| 414 | - const maskPaths = this.maskingGroup.querySelectorAll('path'); | |
| 415 | - maskPaths.forEach(path => path.remove()); | |
| 416 | - } | |
| 417 | - if (this.drawingGroup) { | |
| 418 | - const drawPaths = this.drawingGroup.querySelectorAll('path'); | |
| 419 | - drawPaths.forEach(path => path.remove()); | |
| 420 | - } | |
| 421 | - }; | |
| 422 | - this.save = () => { | |
| 423 | - if (!this.drawingGroup) | |
| 424 | - return; | |
| 425 | - const paths = this.drawingGroup.querySelectorAll('path'); | |
| 426 | - const drawingData = []; | |
| 427 | - paths.forEach(path => { | |
| 428 | - const pathData = [ | |
| 429 | - path.getAttribute('stroke'), | |
| 430 | - parseFloat(path.getAttribute('stroke-width') || '0'), | |
| 431 | - path.getAttribute('d'), | |
| 432 | - ]; | |
| 433 | - drawingData.push(pathData); | |
| 434 | - }); | |
| 435 | - if (drawingData.length === 0) { | |
| 436 | - if (this.loadedDrawingData) { | |
| 437 | - // 그린 데이터가 없는데 로드된 데이터가 있으면 삭제 | |
| 438 | - this.deleteDrawingData(); | |
| 439 | - } | |
| 440 | - return; | |
| 441 | - } | |
| 442 | - if (this.maskingGroup) { | |
| 443 | - const maskPaths = this.maskingGroup.querySelectorAll('path'); | |
| 444 | - const maskData = []; | |
| 445 | - maskPaths.forEach(path => { | |
| 446 | - const maskPathData = [ | |
| 447 | - path.getAttribute('stroke'), | |
| 448 | - parseFloat(path.getAttribute('stroke-width') || '0'), | |
| 449 | - path.getAttribute('d'), | |
| 450 | - ]; | |
| 451 | - maskData.push(maskPathData); | |
| 452 | - }); | |
| 453 | - this.saveDrawingData({ drawingData, maskData }); | |
| 454 | - } | |
| 455 | - }; | |
| 456 | - this.changeSize = (size) => { | |
| 457 | - this.size = Number(size); // Ensure the size is converted to a number | |
| 458 | - this.updatePreview(); | |
| 459 | - }; | |
| 460 | - this.changeColour = (key) => { | |
| 461 | - this.coloursSelect = key; | |
| 462 | - if (this.isErasing) { | |
| 463 | - this.switchEraser(); | |
| 464 | - } | |
| 465 | - this.updatePreview(); | |
| 466 | - }; | |
| 467 | - this.updatePreview = () => { | |
| 468 | - if (this.previewObj) { | |
| 469 | - this.previewObj.style.width = `${this.size * 0.1}rem`; | |
| 470 | - this.previewObj.style.height = `${this.size * 0.1}rem`; | |
| 471 | - if (this.coloursArr[this.coloursSelect] === '#ffffff') { | |
| 472 | - this.previewObj.classList.add('white'); | |
| 473 | - } | |
| 474 | - else { | |
| 475 | - this.previewObj.classList.remove('white'); | |
| 476 | - } | |
| 477 | - if (this.isErasing) { | |
| 478 | - this.previewObj.classList.add('erase'); | |
| 479 | - } | |
| 480 | - else { | |
| 481 | - this.previewObj.classList.remove('erase'); | |
| 482 | - this.previewObj.style.backgroundColor = `${this.coloursArr[this.coloursSelect]}`; | |
| 483 | - } | |
| 484 | - } | |
| 485 | - }; | |
| 486 | - this.closeDrawing = () => { | |
| 487 | - var _a; | |
| 488 | - if (this.ctrlCon) { | |
| 489 | - this.ctrlCon.remove(); | |
| 490 | - this.ctrlCon = null; | |
| 491 | - } | |
| 492 | - if (this.drawingGroup) | |
| 493 | - this.drawingGroup = null; | |
| 494 | - if (this.maskingGroup) | |
| 495 | - this.maskingGroup = null; | |
| 496 | - if (this.svg && this.targetCon) { | |
| 497 | - this.targetCon.removeChild(this.svg); | |
| 498 | - this.svg = null; | |
| 499 | - } | |
| 500 | - this.isErasing = false; | |
| 501 | - this.btnInit.classList.remove('is-active'); | |
| 502 | - (_a = this.targetCon) === null || _a === void 0 ? void 0 : _a.classList.remove('is-active'); | |
| 503 | - // resize 이벤트 리스너 제거 | |
| 504 | - window.removeEventListener('resize', this.onResize); | |
| 505 | - // beforeunload 이벤트 리스너 제거 | |
| 506 | - // window.removeEventListener('beforeunload', this.beforeUnloadHandler); | |
| 507 | - }; | |
| 508 | - this.createDrawEvents = () => { | |
| 509 | - if (!this.svg) | |
| 510 | - return; | |
| 511 | - this.svg.addEventListener("mousedown", this.pressEventHandler); | |
| 512 | - this.svg.addEventListener("mousemove", this.dragEventHandler); | |
| 513 | - this.svg.addEventListener("mouseup", this.releaseEventHandler); | |
| 514 | - this.svg.addEventListener("mouseout", this.cancelEventHandler); | |
| 515 | - this.svg.addEventListener("touchstart", this.pressEventHandler); | |
| 516 | - this.svg.addEventListener("touchmove", this.dragEventHandler); | |
| 517 | - this.svg.addEventListener("touchend", this.releaseEventHandler); | |
| 518 | - this.svg.addEventListener("touchcancel", this.cancelEventHandler); | |
| 519 | - }; | |
| 520 | - // line 대신 path 사용으로 메모리 사용량 최적화 | |
| 521 | - this.releaseEventHandler = () => { | |
| 522 | - this.drawing = false; | |
| 523 | - if (this.drawPathElement) { | |
| 524 | - this.currentPath.push(this.drawPathElement); | |
| 525 | - } | |
| 526 | - if (this.eraserPathElement) { | |
| 527 | - this.currentPath.push(this.eraserPathElement); | |
| 528 | - } | |
| 529 | - if (this.currentPath.length > 0) { | |
| 530 | - this.undoStack.push(this.currentPath); | |
| 531 | - this.redoStack = []; | |
| 532 | - } | |
| 533 | - this.drawPathElement = null; // 현재 그리던 path 초기화 | |
| 534 | - this.eraserPathElement = null; // 현재 그리던 path 초기화 | |
| 535 | - this.currentPath = []; | |
| 536 | - // 드로잉이 끝나면 다시 기본 동작을 허용 | |
| 537 | - this.preventPageInteractions(true); | |
| 538 | - // 드로잉이 끝나면 다시 painterbar의 pointer-events 활성 | |
| 539 | - this.disablePointerEvents(this.ctrlCon, false); | |
| 540 | - }; | |
| 541 | - this.cancelEventHandler = () => { | |
| 542 | - this.releaseEventHandler(); | |
| 543 | - }; | |
| 544 | - this.pressEventHandler = (e) => { | |
| 545 | - var _a; | |
| 546 | - this.drawing = true; | |
| 547 | - const newPoint = this.getMousePosition(e); | |
| 548 | - // 드로잉 중에는 페이지에서 텍스트 선택 및 드래그가 되지 않도록 차단 | |
| 549 | - this.preventPageInteractions(false); | |
| 550 | - // 드로잉 중에는 painterbar의 pointer-events 비활성 | |
| 551 | - this.disablePointerEvents(this.ctrlCon, true); | |
| 552 | - // 드로잉 또는 지우기 동작 처리 | |
| 553 | - const strokeColor = this.isErasing ? 'black' : this.coloursArr[this.coloursSelect]; | |
| 554 | - this.startDrawing(strokeColor, this.size, this.isErasing, newPoint); | |
| 555 | - // 마스킹 그룹에 path 가 있을 경우, 마스킹 그룹에도 path 추가 | |
| 556 | - if (!this.isErasing && ((_a = this.maskingGroup) === null || _a === void 0 ? void 0 : _a.querySelector('path'))) { | |
| 557 | - this.startDrawing('white', this.size, true, newPoint); | |
| 558 | - } | |
| 559 | - }; | |
| 560 | - this.dragEventHandler = (e) => { | |
| 561 | - if (!this.drawing) | |
| 562 | - return; | |
| 563 | - const newPoint = this.getMousePosition(e); | |
| 564 | - // 새로운 path 요소 그리기 | |
| 565 | - if (this.drawPathElement) { | |
| 566 | - this.drawPath(`L ${newPoint.x} ${newPoint.y}`); | |
| 567 | - } | |
| 568 | - if (this.eraserPathElement) { | |
| 569 | - this.drawPath(`L ${newPoint.x} ${newPoint.y}`, true); | |
| 570 | - } | |
| 571 | - e.preventDefault(); | |
| 572 | - }; | |
| 573 | - // 새로운 path를 생성하고 시작 지점을 그리는 함수 | |
| 574 | - this.startDrawing = (strokeColor, strokeWidth, eraser, point) => { | |
| 575 | - this.createNewPath(strokeColor, strokeWidth, eraser); | |
| 576 | - this.drawPath(`M ${point.x} ${point.y} L ${point.x + 0.01} ${point.y}`, eraser); | |
| 577 | - }; | |
| 578 | - this.createNewPath = (strokeColor, strokeWidth, eraser = false) => { | |
| 579 | - var _a, _b; | |
| 580 | - // 새로운 path 요소 생성 | |
| 581 | - const pathElement = document.createElementNS(this.svgNS, 'path'); | |
| 582 | - pathElement.setAttribute('fill', 'none'); | |
| 583 | - pathElement.setAttribute('stroke', strokeColor); | |
| 584 | - pathElement.setAttribute('stroke-width', `${strokeWidth}`); | |
| 585 | - pathElement.setAttribute('stroke-linecap', 'round'); | |
| 586 | - pathElement.setAttribute('d', ''); | |
| 587 | - if (!eraser) { | |
| 588 | - pathElement.setAttribute('data-use', 'draw'); | |
| 589 | - (_a = this.drawingGroup) === null || _a === void 0 ? void 0 : _a.append(pathElement); | |
| 590 | - this.drawPathElement = pathElement; | |
| 591 | - } | |
| 592 | - else { | |
| 593 | - (_b = this.maskingGroup) === null || _b === void 0 ? void 0 : _b.append(pathElement); | |
| 594 | - this.eraserPathElement = pathElement; | |
| 595 | - } | |
| 596 | - }; | |
| 597 | - this.drawPath = (d, eraser = false) => { | |
| 598 | - const pathElement = eraser ? this.eraserPathElement : this.drawPathElement; | |
| 599 | - // 새로운 path 요소 그리기 | |
| 600 | - const od = pathElement.getAttribute('d'); | |
| 601 | - pathElement.setAttribute('d', `${od} ${d}`); | |
| 602 | - }; | |
| 603 | - this.getMousePosition = (e) => { | |
| 604 | - var _a; | |
| 605 | - if (!this.svg) | |
| 606 | - return new DOMPoint(0, 0); // Add a fallback value in case svg is null | |
| 607 | - const point = this.svg.createSVGPoint(); | |
| 608 | - point.x = e.changedTouches ? e.changedTouches[0].clientX : e.clientX; | |
| 609 | - point.y = e.changedTouches ? e.changedTouches[0].clientY : e.clientY; | |
| 610 | - return point.matrixTransform((_a = this.svg.getScreenCTM()) === null || _a === void 0 ? void 0 : _a.inverse()); // Convert screen coordinates to SVG coordinates | |
| 611 | - }; | |
| 612 | - // 페이지의 기본 선택 및 드래그 동작을 활성/비활성화하는 함수 | |
| 613 | - this.preventPageInteractions = (state) => { | |
| 614 | - if (state) { | |
| 615 | - document.onselectstart = null; // 텍스트 선택 차단 해제 | |
| 616 | - document.ondragstart = null; // 드래그 차단 해제 | |
| 617 | - } | |
| 618 | - else { | |
| 619 | - document.onselectstart = () => false; // 텍스트 선택 차단 | |
| 620 | - document.ondragstart = () => false; // 드래그 차단 | |
| 621 | - } | |
| 622 | - }; | |
| 623 | - // pointer-events를 활성/비활성화하는 함수 | |
| 624 | - this.disablePointerEvents = (target, state) => { | |
| 625 | - if (!target) | |
| 626 | - return; | |
| 627 | - target.style.pointerEvents = state ? 'none' : ''; | |
| 628 | - }; | |
| 629 | - // 데이터 가져오기 | |
| 630 | - this.loadDrawingData = () => { | |
| 631 | - if (this.loadDataUrl === '') { | |
| 632 | - // 세션스토리지에서 데이터 가져오기 | |
| 633 | - const userKey = this.dataParam.userKey; | |
| 634 | - const contentsKey = this.dataParam.contentsKey; | |
| 635 | - if (userKey && contentsKey) { | |
| 636 | - const storedData = sessionStorage.getItem(`drawingData-${userKey}-${contentsKey}`); | |
| 637 | - if (storedData) { | |
| 638 | - const parsedData = JSON.parse(storedData); | |
| 639 | - const { drawingData, maskData } = parsedData; | |
| 640 | - // 드로잉 데이터를 복원 | |
| 641 | - if (drawingData && this.drawingGroup) { | |
| 642 | - drawingData.forEach((line) => { | |
| 643 | - this.createNewPath(line[0], line[1]); | |
| 644 | - this.drawPath(line[2]); | |
| 645 | - }); | |
| 646 | - } | |
| 647 | - // 마스크 데이터를 복원 | |
| 648 | - if (maskData && this.maskingGroup) { | |
| 649 | - maskData.forEach((line) => { | |
| 650 | - this.createNewPath(line[0], line[1], true); | |
| 651 | - this.drawPath(line[2], true); | |
| 652 | - }); | |
| 653 | - } | |
| 654 | - this.loadedDrawingData = true; | |
| 655 | - console.log('Data loaded from session storage'); | |
| 656 | - } | |
| 657 | - else { | |
| 658 | - this.loadedDrawingData = false; | |
| 659 | - console.log('No data found in session storage'); | |
| 660 | - } | |
| 661 | - } | |
| 662 | - else { | |
| 663 | - console.error('userKey or contentsKey is missing in dataParam'); | |
| 664 | - } | |
| 665 | - return; | |
| 666 | - } | |
| 667 | - // fetch(this.loadDataUrl, { | |
| 668 | - // method: 'GET', | |
| 669 | - // headers: { | |
| 670 | - // 'Content-Type': 'application/json', | |
| 671 | - // } | |
| 672 | - // }) | |
| 673 | - fetch(this.loadDataUrl, { | |
| 674 | - method: 'POST', | |
| 675 | - headers: { | |
| 676 | - 'Content-Type': 'application/json', | |
| 677 | - }, | |
| 678 | - body: JSON.stringify(this.dataParam), | |
| 679 | - }) | |
| 680 | - .then(response => response.json()) | |
| 681 | - .then(data => { | |
| 682 | - const { drawingData, maskData } = data; | |
| 683 | - if (!drawingData || drawingData && drawingData.length === 0) { | |
| 684 | - this.loadedDrawingData = false; | |
| 685 | - return; | |
| 686 | - } | |
| 687 | - | |
| 688 | - if (drawingData && this.drawingGroup) { | |
| 689 | - drawingData.forEach((line) => { | |
| 690 | - this.createNewPath(line[0], line[1]); | |
| 691 | - this.drawPath(line[2]); | |
| 692 | - }); | |
| 693 | - } | |
| 694 | - if (maskData && this.maskingGroup) { | |
| 695 | - maskData.forEach((line) => { | |
| 696 | - this.createNewPath(line[0], line[1], true); | |
| 697 | - this.drawPath(line[2], true); | |
| 698 | - }); | |
| 699 | - } | |
| 700 | - this.loadedDrawingData = true; | |
| 701 | - }) | |
| 702 | - .catch(error => console.error('Error loading drawing data:', error)); | |
| 703 | - }; | |
| 704 | - // 데이터 저장 | |
| 705 | - this.saveDrawingData = (data) => { | |
| 706 | - const params = Object.assign(Object.assign({}, this.dataParam), data); | |
| 707 | - console.log('save data :', params); | |
| 708 | - console.log(this.dataParam); | |
| 709 | - // saveDataUrl이 비어 있으면 세션스토리지에 저장 | |
| 710 | - if (this.saveDataUrl === '') { | |
| 711 | - if ('userKey' in params && 'contentsKey' in params) { | |
| 712 | - sessionStorage.setItem(`drawingData-${params['userKey']}-${params['contentsKey']}`, JSON.stringify(params)); | |
| 713 | - console.log('Data saved to session storage'); | |
| 714 | - } | |
| 715 | - else { | |
| 716 | - console.error('userKey or contentsKey is missing in params'); | |
| 717 | - } | |
| 718 | - return; | |
| 719 | - } | |
| 720 | - fetch(this.saveDataUrl, { | |
| 721 | - method: 'POST', | |
| 722 | - headers: { | |
| 723 | - 'Content-Type': 'application/json', | |
| 724 | - }, | |
| 725 | - body: JSON.stringify(params), | |
| 726 | - }).then(response => this.reset()) | |
| 727 | - .catch(error => console.error('Error saving drawing data:', error)); | |
| 728 | - }; | |
| 729 | - // 저장된 데이터 삭제 | |
| 730 | - this.deleteDrawingData = () => { | |
| 731 | - const params = Object.assign(Object.assign({}, this.dataParam)); | |
| 732 | - // deleteDataUrl이 비어 있으면 세션스토리지를 확인하여 삭제 | |
| 733 | - if (this.deleteDataUrl === '') { | |
| 734 | - if ('userKey' in params && 'contentsKey' in params) { | |
| 735 | - sessionStorage.removeItem(`drawingData-${params['userKey']}-${params['contentsKey']}`); | |
| 736 | - console.log('Data deleted to session storage'); | |
| 737 | - } | |
| 738 | - else { | |
| 739 | - console.error('userKey or contentsKey is missing in params'); | |
| 740 | - } | |
| 741 | - return; | |
| 742 | - } | |
| 743 | - fetch(this.deleteDataUrl, { | |
| 744 | - method: 'POST', | |
| 745 | - headers: { | |
| 746 | - 'Content-Type': 'application/json', | |
| 747 | - }, | |
| 748 | - body: JSON.stringify(this.dataParam), | |
| 749 | - }) | |
| 750 | - .then(response => { | |
| 751 | - // 먼저 text로 받아서 확인 | |
| 752 | - return response.text().then(text => { | |
| 753 | - console.log("서버 응답 내용:\n", text); | |
| 754 | - try { | |
| 755 | - return JSON.parse(text); | |
| 756 | - } catch (err) { | |
| 757 | - console.error("⚠ JSON 파싱 실패: ", err); | |
| 758 | - throw new Error("서버에서 JSON이 아닌 HTML 응답을 받음"); | |
| 759 | - } | |
| 760 | - }); | |
| 761 | - }) | |
| 762 | - .then(data => { | |
| 763 | - console.log("정상적으로 파싱된 JSON:", data); | |
| 764 | - }) | |
| 765 | - .catch(error => { | |
| 766 | - console.error("Fetch 처리 중 에러:", error); | |
| 767 | - }); | |
| 768 | - }; | |
| 769 | - // 창 닫기 이벤트에서 save() 호출 | |
| 770 | - this.beforeUnloadHandler = (event) => { | |
| 771 | - this.save(); | |
| 772 | - event.preventDefault(); | |
| 773 | - // // 경고 메시지 표시 (선택 사항) | |
| 774 | - // const confirmationMessage = '색연필로 작성한 내용이 저장되지 않을 수 있습니다.'; | |
| 775 | - // event.returnValue = confirmationMessage; | |
| 776 | - // return confirmationMessage; | |
| 777 | - }; | |
| 778 | - this.btnInit = btnInit; | |
| 779 | - this.targetArr = objArr; | |
| 780 | - this.coloursArr = coloursArr.map(c => c.toLowerCase()); | |
| 781 | - this.coloursSelect = coloursSelect - 1; | |
| 782 | - this.baseSize = { x: -800, y: -450, w: 1600, h: 900 }; | |
| 783 | - brushMax = brushMax > 60 ? 60 : brushMax; | |
| 784 | - this.sizeRange = { | |
| 785 | - min: brushMin, | |
| 786 | - max: brushMax | |
| 787 | - }; | |
| 788 | - this.size = Math.round((brushMin + brushMax) / 3); | |
| 789 | - this.saveDataUrl = saveDataURL; | |
| 790 | - this.loadDataUrl = loadDataURL; | |
| 791 | - this.deleteDataUrl = deleteDataURL; | |
| 792 | - this.dataParam = dataParam; | |
| 793 | - this.btnInit.onclick = () => { | |
| 794 | - var _a, _b; | |
| 795 | - if (this.btnInit.classList.contains('is-active')) { | |
| 796 | - this.save(); | |
| 797 | - this.closeDrawing(); | |
| 798 | - } | |
| 799 | - else { | |
| 800 | - this.detectTargetObj(); | |
| 801 | - const svg = document.createElementNS(this.svgNS, 'svg'); | |
| 802 | - this.svg = svg; | |
| 803 | - this.svg.id = 'painterSvg'; | |
| 804 | - this.svg.setAttribute("preserveAspectRatio", "xMidYMid meet"); | |
| 805 | - (_a = this.targetCon) === null || _a === void 0 ? void 0 : _a.append(this.svg); | |
| 806 | - this.createDrawingGroup(); // 드로잉 그룹 생성 | |
| 807 | - this.createMaskingGroup(); // 마스크 생성 | |
| 808 | - this.uiUpdate(); | |
| 809 | - this.createDrawEvents(); | |
| 810 | - this.loadDrawingData(); | |
| 811 | - this.onResize(); | |
| 812 | - // Listen for scroll event | |
| 813 | - (_b = this.targetCon) === null || _b === void 0 ? void 0 : _b.addEventListener('scroll', this.onScroll); | |
| 814 | - } | |
| 815 | - }; | |
| 816 | - const ifrObj = document.querySelector('.iframe-area iframe'); | |
| 817 | - this.ifrObj = ifrObj; | |
| 818 | - (_a = this.ifrObj) === null || _a === void 0 ? void 0 : _a.addEventListener('load', this.onIfrLoad); | |
| 819 | - window.addEventListener('resize', this.onResize); | |
| 820 | - // 창을 닫거나 새로고침할 때 save() 호출 | |
| 821 | - // window.addEventListener('beforeunload', this.beforeUnloadHandler); | |
| 822 | - //window.onbeforeunload = this.beforeUnloadHandler; | |
| 823 | - document.addEventListener("visibilitychange", (event) => { | |
| 824 | - if (document.visibilityState === 'hidden') { | |
| 825 | - this.beforeUnloadHandler(event); // 사용자가 페이지를 떠나면 저장 | |
| 826 | - } | |
| 827 | - }); | |
| 828 | - } | |
| 829 | - // Getter for dataParam | |
| 830 | - getDataParam() { | |
| 831 | - return this.dataParam; | |
| 832 | - } | |
| 833 | - // Setter for dataParam | |
| 834 | - setDataParam(newDataParam) { | |
| 835 | - this.dataParam = Object.assign(Object.assign({}, this.dataParam), newDataParam); // Merge with the existing dataParam | |
| 836 | - } | |
| 837 | - saveDrawing() { | |
| 838 | - this.save(); | |
| 839 | - } | |
| 840 | -} | |
| 841 | -// Create the instance and attach it to the window object | |
| 842 | -export function initColouredPenciles() { | |
| 843 | - window.addEventListener('DOMContentLoaded', () => { | |
| 844 | - const btnInit = document.querySelector('.btn-painter-toggle'); // 색연필 활성 버튼 | |
| 845 | - if (!btnInit) { | |
| 846 | - // console.error('.btn-painter-toggle 요소를 찾을 수 없습니다.'); | |
| 847 | - return; | |
| 848 | - } | |
| 849 | - const objArr = ['viewer-painter']; // 색연필 콘테이너 클래스명 - 여러개 지정 가능, 앞에 위치한 클래스명의 엘리먼트 우선 | |
| 850 | - const colorArray = ["#222222", "#fd4418", "#ffce00", "#00d56a", "#2f77ff", "#ffffff"]; // 색상 팔레트 6색 | |
| 851 | - const defaultColor = 5; // 기본 선 굵기 | |
| 852 | - const brushMin = 0.5, brushMax = 43; // 최소, 최대 선 굵기 | |
| 853 | - const saveDataUrl = '/classroom/insertClassLessonWrtng.json'; // save api url | |
| 854 | - const loadDataURL = '/classroom/selectClassLessonWrtng.json'; // load api url | |
| 855 | - const deleteDataURL = '/classroom/deleteClassLessonWrtng.json'; // delete api url | |
| 856 | - // const loadDataURL = 'lineData.json'; // test load api url | |
| 857 | - /** dataParam은 외부에서 설정, 사용 환경에 따라 key, value 자유 설정 가능 */ | |
| 858 | - const dataParam = {}; | |
| 859 | - // 인스턴스 생성 후 전역 window 객체에 저장 | |
| 860 | - window.cpInstance = new colouredPenciles(btnInit, objArr, colorArray, defaultColor, brushMin, brushMax, saveDataUrl, loadDataURL, deleteDataURL, dataParam); | |
| 861 | - }); | |
| 862 | -} | |
| 863 | -initColouredPenciles(); |
--- resources/front/site/SITE_00000/js/custom/module/colouredPencilesSvg_pub.js
... | ... | @@ -1,852 +0,0 @@ |
| 1 | -/* | |
| 2 | -* Copyright (c) FOX EDU CO., LTD ALL RIGHT RESERVED. | |
| 3 | -* by flashkid | |
| 4 | -* 25.SEP.2024(WED) | |
| 5 | -*/ | |
| 6 | -export class colouredPenciles { | |
| 7 | - constructor(btnInit, objArr, coloursArr, coloursSelect = 1, brushMin = 0.5, brushMax = 10, saveDataURL = '', loadDataURL = '', deleteDataURL = '', dataParam = {}) { | |
| 8 | - var _a; | |
| 9 | - this.svgNS = "http://www.w3.org/2000/svg"; | |
| 10 | - this.ctrlCon = null; | |
| 11 | - this.targetCon = null; | |
| 12 | - this.targetArr = []; | |
| 13 | - this.ifrObj = null; | |
| 14 | - this.ifrDoc = null; | |
| 15 | - this.ifrContents = null; | |
| 16 | - this.ifrContentsType = "etc"; | |
| 17 | - this.svg = null; | |
| 18 | - this.eraseObj = null; | |
| 19 | - this.previewObj = null; | |
| 20 | - this.loadedDrawingData = false; | |
| 21 | - this.drawingGroup = null; | |
| 22 | - this.maskingGroup = null; | |
| 23 | - this.drawPathElement = null; | |
| 24 | - this.eraserPathElement = null; | |
| 25 | - this.undoStack = []; | |
| 26 | - this.redoStack = []; | |
| 27 | - this.currentPath = []; | |
| 28 | - this.drawing = false; | |
| 29 | - this.isErasing = false; | |
| 30 | - this.detectTargetObj = () => { | |
| 31 | - let tobj, i = 0; | |
| 32 | - while (!tobj && i <= this.targetArr.length) { | |
| 33 | - tobj = document.querySelector(`.${this.targetArr[i]}`); | |
| 34 | - i++; | |
| 35 | - } | |
| 36 | - if (tobj) { | |
| 37 | - this.targetCon = tobj; | |
| 38 | - this.targetCon.classList.add('painter-target'); | |
| 39 | - } | |
| 40 | - }; | |
| 41 | - // Function to handle the scroll event | |
| 42 | - this.onScroll = () => { | |
| 43 | - var _a; | |
| 44 | - if (['img', 'iframe', 'etc', 'unknown'].includes(this.ifrContentsType) && ((_a = this.ifrObj) === null || _a === void 0 ? void 0 : _a.contentWindow) && this.targetCon) { | |
| 45 | - this.ifrObj.contentWindow.scrollTo(this.targetCon.scrollLeft, this.targetCon.scrollTop); | |
| 46 | - } | |
| 47 | - else if (this.ifrContents && this.targetCon) { | |
| 48 | - this.ifrContents.scrollTo(this.targetCon.scrollLeft, this.targetCon.scrollTop); | |
| 49 | - } | |
| 50 | - }; | |
| 51 | - // 아이프레임 온로드 | |
| 52 | - this.onIfrLoad = () => { | |
| 53 | - var _a, _b, _c; | |
| 54 | - if (this.btnInit.classList.contains('is-active')) { | |
| 55 | - this.closeDrawing(); | |
| 56 | - this.onResize(); | |
| 57 | - } | |
| 58 | - const ifrDoc = ((_b = (_a = this.ifrObj) === null || _a === void 0 ? void 0 : _a.contentWindow) === null || _b === void 0 ? void 0 : _b.document) || ((_c = this.ifrObj) === null || _c === void 0 ? void 0 : _c.contentDocument); | |
| 59 | - if (!ifrDoc) | |
| 60 | - return; | |
| 61 | - this.ifrDoc = ifrDoc; | |
| 62 | - // ifrContents 선언 | |
| 63 | - this.ifrContents = this.ifrDoc.querySelector('#contents2') || this.ifrDoc.querySelector('.viewer .viewer-body .viewer-cont .quiz-viewer .quiz-viewer-body') || null; | |
| 64 | - if (this.ifrContents.classList.contains('quiz-viewer-body')) { | |
| 65 | - this.ifrContentsType = 'quiz'; | |
| 66 | - this.setBaseSize({ | |
| 67 | - w: this.ifrContents.querySelector('.quiz-viewer-cont').clientWidth, | |
| 68 | - h: this.ifrContents.scrollHeight | |
| 69 | - }); | |
| 70 | - this.setBaseSize({ | |
| 71 | - x: -this.ifrContents.querySelector('.quiz-viewer-cont').clientWidth / 2, | |
| 72 | - y: 0 | |
| 73 | - }); | |
| 74 | - } | |
| 75 | - else if (this.ifrContents.id === 'contents2') { | |
| 76 | - const video = this.ifrContents.querySelector('video'); | |
| 77 | - const img = this.ifrContents.querySelector('img'); | |
| 78 | - const iframe = this.ifrContents.querySelector('iframe'); | |
| 79 | - const youtube = this.ifrContents.querySelector('input#tempCntntsUrl'); | |
| 80 | - if (video) { | |
| 81 | - // 비디오 태그가 있을 경우 | |
| 82 | - this.ifrContentsType = 'video'; | |
| 83 | - this.setBaseSize({ x: -video.videoWidth / 2, y: -video.videoHeight / 2, w: video.videoWidth, h: video.videoHeight }); | |
| 84 | - } | |
| 85 | - else if (img) { | |
| 86 | - // 이미지 태그가 있을 경우 | |
| 87 | - this.ifrContentsType = 'img'; | |
| 88 | - this.setBaseSize({ x: -img.naturalWidth / 2, y: -img.naturalHeight / 2, w: img.naturalWidth, h: img.naturalHeight }); | |
| 89 | - } | |
| 90 | - else if (iframe) { | |
| 91 | - // 아이프레임이 있을 경우 | |
| 92 | - this.ifrContentsType = 'iframe'; | |
| 93 | - this.setBaseSize({ x: -iframe.clientWidth / 2, y: 0, w: iframe.clientWidth, h: iframe.scrollHeight }); | |
| 94 | - } | |
| 95 | - else if (youtube) { | |
| 96 | - // 유튜브 프레임이 들어온 경우 | |
| 97 | - this.ifrContentsType = 'youtube'; | |
| 98 | - this.setBaseSize({ x: -960, y: -540, w: 1920, h: 1080 }); | |
| 99 | - } | |
| 100 | - else { | |
| 101 | - this.ifrContentsType = 'etc'; | |
| 102 | - this.setBaseSize({ w: this.ifrContents.clientWidth, h: this.ifrContents.scrollHeight }); | |
| 103 | - this.setBaseSize({ | |
| 104 | - x: -this.ifrContents.clientWidth / 2, | |
| 105 | - y: 0 | |
| 106 | - }); | |
| 107 | - } | |
| 108 | - } | |
| 109 | - else { | |
| 110 | - this.ifrContentsType = 'unknown'; | |
| 111 | - this.setBaseSize({ w: this.ifrContents.clientWidth, h: this.ifrContents.scrollHeight }); | |
| 112 | - this.setBaseSize({ | |
| 113 | - x: -this.ifrContents.clientWidth / 2, | |
| 114 | - y: 0 | |
| 115 | - }); | |
| 116 | - } | |
| 117 | - }; | |
| 118 | - this.onResize = () => { | |
| 119 | - if (!this.ifrDoc || !this.svg || !this.ifrContents) | |
| 120 | - return; | |
| 121 | - const hasYScroll = this.ifrContents.scrollHeight > this.ifrContents.clientHeight; | |
| 122 | - if (hasYScroll) { | |
| 123 | - switch (this.ifrContentsType) { | |
| 124 | - case 'img': | |
| 125 | - // this.svg.style.height = `${this.ifrContents.scrollHeight}px`; | |
| 126 | - // this.setViewBox(this.baseSize); | |
| 127 | - // break; | |
| 128 | - case 'quiz': | |
| 129 | - this.svg.style.height = `${this.ifrContents.scrollHeight}px`; | |
| 130 | - this.setViewBox(this.baseSize); | |
| 131 | - break; | |
| 132 | - case 'iframe': | |
| 133 | - case 'unknown': | |
| 134 | - case 'etc': | |
| 135 | - this.setViewBox({ x: -this.ifrContents.clientWidth / 2, w: this.ifrContents.clientWidth, h: this.ifrContents.scrollHeight }); | |
| 136 | - break; | |
| 137 | - case 'video': | |
| 138 | - case 'youtube': | |
| 139 | - default: | |
| 140 | - this.setViewBox(this.baseSize); | |
| 141 | - break; | |
| 142 | - } | |
| 143 | - } | |
| 144 | - else { | |
| 145 | - this.svg.style.height = ``; | |
| 146 | - switch (this.ifrContentsType) { | |
| 147 | - case 'quiz': | |
| 148 | - this.svg.style.height = `${this.ifrContents.scrollHeight}px`; | |
| 149 | - this.setViewBox({ h: this.ifrContents.scrollHeight }); | |
| 150 | - break; | |
| 151 | - case 'iframe': | |
| 152 | - case 'unknown': | |
| 153 | - case 'etc': | |
| 154 | - this.setViewBox({ x: -this.ifrContents.clientWidth / 2, w: this.ifrContents.clientWidth, h: this.ifrContents.scrollHeight }); | |
| 155 | - break; | |
| 156 | - case 'img': | |
| 157 | - case 'video': | |
| 158 | - case 'youtube': | |
| 159 | - default: | |
| 160 | - this.setViewBox(this.baseSize); | |
| 161 | - break; | |
| 162 | - } | |
| 163 | - } | |
| 164 | - }; | |
| 165 | - this.setBaseSize = (newBaseSize) => { | |
| 166 | - this.baseSize = Object.assign(Object.assign({}, this.baseSize), newBaseSize); // Merge with the existing baseSize | |
| 167 | - }; | |
| 168 | - this.setViewBox = (newSize) => { | |
| 169 | - const newBaseSize = Object.assign(Object.assign({}, this.baseSize), newSize); | |
| 170 | - if (!this.svg) | |
| 171 | - return; | |
| 172 | - this.svg.setAttribute("viewBox", `${newBaseSize.x} ${newBaseSize.y} ${newBaseSize.w} ${newBaseSize.h}`); | |
| 173 | - }; | |
| 174 | - this.createDrawingGroup = () => { | |
| 175 | - var _a; | |
| 176 | - const drawingGroup = document.createElementNS(this.svgNS, 'g'); | |
| 177 | - (_a = this.svg) === null || _a === void 0 ? void 0 : _a.append(drawingGroup); | |
| 178 | - this.drawingGroup = drawingGroup; | |
| 179 | - }; | |
| 180 | - this.createMaskingGroup = () => { | |
| 181 | - var _a, _b; | |
| 182 | - const maskingGroup = document.createElementNS(this.svgNS, 'mask'); | |
| 183 | - maskingGroup.id = 'eraser-mask'; | |
| 184 | - (_a = this.svg) === null || _a === void 0 ? void 0 : _a.append(maskingGroup); | |
| 185 | - this.maskingGroup = maskingGroup; | |
| 186 | - (_b = this.drawingGroup) === null || _b === void 0 ? void 0 : _b.setAttribute('mask', 'url(#eraser-mask)'); | |
| 187 | - // 흰색 배경 (지우개 영역은 투명) | |
| 188 | - const maskRect = document.createElementNS(this.svgNS, "rect"); | |
| 189 | - maskRect.setAttribute("fill", "white"); | |
| 190 | - maskRect.setAttribute("x", `${this.baseSize.x * 100}`); | |
| 191 | - maskRect.setAttribute("y", `${this.baseSize.y * 100}`); | |
| 192 | - maskRect.setAttribute("width", `${this.baseSize.w * 100}`); | |
| 193 | - maskRect.setAttribute("height", `${this.baseSize.h * 100}`); | |
| 194 | - this.maskingGroup.appendChild(maskRect); | |
| 195 | - }; | |
| 196 | - // 콘트롤 패널 드래그 앤 드롭 기능 추가 | |
| 197 | - // this.enableDrag = (element) => { | |
| 198 | - // let offsetX = 0; | |
| 199 | - // let offsetY = 0; | |
| 200 | - // let isDragging = false; | |
| 201 | - // const onMouseDown = (e) => { | |
| 202 | - // var _a; | |
| 203 | - // e.preventDefault(); | |
| 204 | - // isDragging = true; | |
| 205 | - // // 현재 요소의 fixed 위치 계산 | |
| 206 | - // const rect = element.getBoundingClientRect(); | |
| 207 | - // const targetConRect = (_a = this.targetCon) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect(); | |
| 208 | - // if (!targetConRect) | |
| 209 | - // return; | |
| 210 | - | |
| 211 | - // // hyo: painterbar 위치 주석처리 | |
| 212 | - // // 마우스 포인터와 요소의 **오른쪽** 및 **위쪽** 경계와의 거리 계산 | |
| 213 | - // // offsetX = rect.right - e.clientX; | |
| 214 | - // // offsetY = e.clientY - rect.top; | |
| 215 | - // // // 요소의 스타일을 고정 위치에 맞추어 설정 | |
| 216 | - // // element.style.right = `${window.innerWidth - rect.right}px`; // right 기준으로 위치 지정 | |
| 217 | - // // element.style.top = `${rect.top}px`; | |
| 218 | - // // element.style.left = 'auto'; // left 속성 제거 | |
| 219 | - // // element.style.transform = 'none'; // 기존 translateY 제거 | |
| 220 | - // // element.style.transition = 'none'; | |
| 221 | - // }; | |
| 222 | - // const onMouseMove = (e) => { | |
| 223 | - // var _a; | |
| 224 | - // if (!isDragging) | |
| 225 | - // return; | |
| 226 | - // e.preventDefault(); | |
| 227 | - // // painter-target 영역의 크기 가져오기 | |
| 228 | - // const containerRect = (_a = this.targetCon) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect(); | |
| 229 | - // if (!containerRect) | |
| 230 | - // return; | |
| 231 | - | |
| 232 | - // // hyo: painterbar 위치 주석처리 | |
| 233 | - // // 새로운 위치 계산 | |
| 234 | - // // let right = window.innerWidth - e.clientX - offsetX; | |
| 235 | - // // let y = e.clientY - offsetY; | |
| 236 | - // // // 요소의 x, y 위치가 painter-target 영역을 넘지 않도록 제한 | |
| 237 | - // // right = Math.max(0, Math.min(right, window.innerWidth - containerRect.left - element.offsetWidth)); | |
| 238 | - // // y = Math.max(containerRect.top, Math.min(y, containerRect.bottom - element.offsetHeight)); | |
| 239 | - // // // 요소의 위치 업데이트 (오른쪽 기준) | |
| 240 | - // // element.style.right = `${right}px`; | |
| 241 | - // // element.style.top = `${y}px`; | |
| 242 | - // }; | |
| 243 | - // const onMouseUp = () => { | |
| 244 | - // isDragging = false; | |
| 245 | - // element.style.transition = ''; | |
| 246 | - // }; | |
| 247 | - // // 이벤트 리스너 등록 | |
| 248 | - // element.addEventListener('mousedown', onMouseDown); | |
| 249 | - // document.addEventListener('mousemove', onMouseMove); | |
| 250 | - // document.addEventListener('mouseup', onMouseUp); | |
| 251 | - // }; | |
| 252 | - this.uiUpdate = () => { | |
| 253 | - if (!this.targetCon) | |
| 254 | - return; // targetCon이 null인지 확인 | |
| 255 | - this.btnInit.classList.add('is-active'); | |
| 256 | - this.targetCon.classList.add('is-active'); | |
| 257 | - this.ctrlCon = document.createElement('aside'); | |
| 258 | - this.ctrlCon.classList.add('painterbar'); | |
| 259 | - this.targetCon.append(this.ctrlCon); | |
| 260 | - // 드래그 기능 활성화 | |
| 261 | - //this.enableDrag(this.ctrlCon); | |
| 262 | - const minBtn = document.createElement('button'); | |
| 263 | - minBtn.type = "button"; | |
| 264 | - minBtn.classList.add('btn-painterbar-size'); | |
| 265 | - minBtn.textContent = '색연필 최소화'; | |
| 266 | - this.ctrlCon.append(minBtn); | |
| 267 | - minBtn.onclick = () => { | |
| 268 | - var _a; | |
| 269 | - (_a = this.ctrlCon) === null || _a === void 0 ? void 0 : _a.classList.toggle('min'); // null 체크 | |
| 270 | - }; | |
| 271 | - const previewCon = document.createElement('div'); | |
| 272 | - previewCon.classList.add('pencil-ctrl-options', 'pencil-preview'); | |
| 273 | - previewCon.style.width = `${this.sizeRange['max'] * 0.1 + 0.2}rem`; | |
| 274 | - previewCon.style.height = `${this.sizeRange['max'] * 0.1 + 0.2}rem`; | |
| 275 | - this.ctrlCon.append(previewCon); | |
| 276 | - this.previewObj = document.createElement('div'); | |
| 277 | - previewCon.append(this.previewObj); | |
| 278 | - const sizeCon = document.createElement('div'); | |
| 279 | - sizeCon.classList.add('pencil-ctrl-options', 'painterbar-stroke'); | |
| 280 | - this.ctrlCon.append(sizeCon); | |
| 281 | - const range = document.createElement('input'); | |
| 282 | - range.type = "range"; | |
| 283 | - range.min = this.sizeRange['min'].toString(); | |
| 284 | - range.max = this.sizeRange['max'].toString(); | |
| 285 | - range.step = "0.1"; | |
| 286 | - range.setAttribute("orient", "vertical"); | |
| 287 | - range.value = this.size.toString(); | |
| 288 | - sizeCon.append(range); | |
| 289 | - // range input 이벤트에서 드래그 앤 드롭과 충돌 방지 | |
| 290 | - range.addEventListener('mousedown', (e) => e.stopPropagation()); | |
| 291 | - range.addEventListener('mousemove', (e) => e.stopPropagation()); | |
| 292 | - range.addEventListener('mouseup', (e) => e.stopPropagation()); | |
| 293 | - range.oninput = () => this.changeSize(Number(range.value)); // 값 변환 | |
| 294 | - const fncCon = document.createElement('div'); | |
| 295 | - fncCon.classList.add('pencil-ctrl-options', 'painterbar-fnc'); | |
| 296 | - this.ctrlCon.append(fncCon); | |
| 297 | - const undo = document.createElement('button'); | |
| 298 | - undo.type = "button"; | |
| 299 | - undo.classList.add('undo'); | |
| 300 | - undo.textContent = "undo"; | |
| 301 | - fncCon.append(undo); | |
| 302 | - undo.addEventListener('click', this.undo); | |
| 303 | - const redo = document.createElement('button'); | |
| 304 | - redo.type = "button"; | |
| 305 | - redo.classList.add('redo'); | |
| 306 | - redo.textContent = "redo"; | |
| 307 | - fncCon.append(redo); | |
| 308 | - redo.addEventListener('click', this.redo); | |
| 309 | - this.eraseObj = document.createElement('input'); | |
| 310 | - this.eraseObj.type = "checkbox"; | |
| 311 | - this.eraseObj.id = "colouredPencils_eraser"; | |
| 312 | - this.eraseObj.classList.add('clear'); | |
| 313 | - this.eraseObj.checked = this.isErasing; | |
| 314 | - fncCon.append(this.eraseObj); | |
| 315 | - const eraserLB = document.createElement('label'); | |
| 316 | - eraserLB.setAttribute("for", this.eraseObj.id); | |
| 317 | - eraserLB.textContent = "지우개"; | |
| 318 | - fncCon.append(eraserLB); | |
| 319 | - this.eraseObj.onchange = () => { | |
| 320 | - this.switchEraser(); | |
| 321 | - }; | |
| 322 | - const reset = document.createElement('button'); | |
| 323 | - reset.type = 'button'; | |
| 324 | - reset.classList.add('reset'); | |
| 325 | - reset.textContent = 'reset'; | |
| 326 | - fncCon.append(reset); | |
| 327 | - reset.onclick = () => this.reset(); | |
| 328 | - // const save = document.createElement('button'); | |
| 329 | - // save.type = 'button'; | |
| 330 | - // save.classList.add('save'); | |
| 331 | - // save.textContent = 'save'; | |
| 332 | - // fncCon.append(save); | |
| 333 | - // save.onclick = () => this.save(); | |
| 334 | - const colorDiv = document.createElement('div'); | |
| 335 | - colorDiv.classList.add('pencil-ctrl-options', 'painterbar-color'); | |
| 336 | - colorDiv.setAttribute('data-opt', this.coloursArr[this.coloursSelect]); | |
| 337 | - this.ctrlCon.append(colorDiv); | |
| 338 | - this.coloursArr.forEach((el, i) => { | |
| 339 | - const radio = document.createElement('input'); | |
| 340 | - radio.type = "radio"; | |
| 341 | - radio.name = "colouredPencils_colour"; | |
| 342 | - radio.id = "colouredPencils_colour_" + i; | |
| 343 | - radio.value = i.toString(); | |
| 344 | - if (i === this.coloursSelect) | |
| 345 | - radio.checked = true; | |
| 346 | - colorDiv.append(radio); | |
| 347 | - const label = document.createElement('label'); | |
| 348 | - label.setAttribute("for", radio.id); | |
| 349 | - label.textContent = el; | |
| 350 | - label.style.backgroundColor = el; | |
| 351 | - if (el === '#ffffff') | |
| 352 | - label.classList.add('white'); | |
| 353 | - colorDiv.append(label); | |
| 354 | - radio.onchange = () => { | |
| 355 | - this.changeColour(i); | |
| 356 | - }; | |
| 357 | - }); | |
| 358 | - const close = document.createElement('button'); | |
| 359 | - close.type = "button"; | |
| 360 | - close.classList.add('btn-viewer-painter-close'); | |
| 361 | - close.textContent = "색연필 닫기"; | |
| 362 | - this.ctrlCon.append(close); | |
| 363 | - close.onclick = () => { | |
| 364 | - this.save(); | |
| 365 | - this.closeDrawing(); | |
| 366 | - }; | |
| 367 | - this.updatePreview(); | |
| 368 | - }; | |
| 369 | - this.undo = () => { | |
| 370 | - if (this.undoStack.length === 0) | |
| 371 | - return; | |
| 372 | - const paths = this.undoStack.pop(); | |
| 373 | - if (!paths) | |
| 374 | - return; | |
| 375 | - Array.isArray(paths) ? paths.forEach(path => path.remove()) : {}; | |
| 376 | - this.redoStack.push(paths); | |
| 377 | - }; | |
| 378 | - this.redo = () => { | |
| 379 | - if (this.redoStack.length === 0) | |
| 380 | - return; | |
| 381 | - const paths = this.redoStack.pop(); | |
| 382 | - if (!paths) | |
| 383 | - return; | |
| 384 | - // Check if drawingGroup and maskingGroup are not null | |
| 385 | - if (!this.drawingGroup || !this.maskingGroup) | |
| 386 | - return; | |
| 387 | - Array.isArray(paths) | |
| 388 | - ? paths.forEach(path => { | |
| 389 | - if (path.dataset.use === 'draw') { | |
| 390 | - this.drawingGroup.append(path); // Add null check with "!" to ensure not null | |
| 391 | - } | |
| 392 | - else { | |
| 393 | - this.maskingGroup.append(path); // Add null check | |
| 394 | - } | |
| 395 | - }) | |
| 396 | - : {}; | |
| 397 | - this.undoStack.push(paths); | |
| 398 | - }; | |
| 399 | - this.switchEraser = () => { | |
| 400 | - this.isErasing = !this.isErasing; | |
| 401 | - if (this.eraseObj) { | |
| 402 | - this.eraseObj.checked = this.isErasing; | |
| 403 | - } | |
| 404 | - this.updatePreview(); | |
| 405 | - }; | |
| 406 | - this.reset = () => { | |
| 407 | - this.undoStack = []; | |
| 408 | - this.redoStack = []; | |
| 409 | - if (this.maskingGroup) { | |
| 410 | - const maskPaths = this.maskingGroup.querySelectorAll('path'); | |
| 411 | - maskPaths.forEach(path => path.remove()); | |
| 412 | - } | |
| 413 | - if (this.drawingGroup) { | |
| 414 | - const drawPaths = this.drawingGroup.querySelectorAll('path'); | |
| 415 | - drawPaths.forEach(path => path.remove()); | |
| 416 | - } | |
| 417 | - }; | |
| 418 | - this.save = () => { | |
| 419 | - if (!this.drawingGroup) | |
| 420 | - return; | |
| 421 | - const paths = this.drawingGroup.querySelectorAll('path'); | |
| 422 | - const drawingData = []; | |
| 423 | - paths.forEach(path => { | |
| 424 | - const pathData = [ | |
| 425 | - path.getAttribute('stroke'), | |
| 426 | - parseFloat(path.getAttribute('stroke-width') || '0'), | |
| 427 | - path.getAttribute('d'), | |
| 428 | - ]; | |
| 429 | - drawingData.push(pathData); | |
| 430 | - }); | |
| 431 | - if (drawingData.length === 0) { | |
| 432 | - if (this.loadedDrawingData) { | |
| 433 | - // 그린 데이터가 없는데 로드된 데이터가 있으면 삭제 | |
| 434 | - this.deleteDrawingData(); | |
| 435 | - } | |
| 436 | - return; | |
| 437 | - } | |
| 438 | - if (this.maskingGroup) { | |
| 439 | - const maskPaths = this.maskingGroup.querySelectorAll('path'); | |
| 440 | - const maskData = []; | |
| 441 | - maskPaths.forEach(path => { | |
| 442 | - const maskPathData = [ | |
| 443 | - path.getAttribute('stroke'), | |
| 444 | - parseFloat(path.getAttribute('stroke-width') || '0'), | |
| 445 | - path.getAttribute('d'), | |
| 446 | - ]; | |
| 447 | - maskData.push(maskPathData); | |
| 448 | - }); | |
| 449 | - this.saveDrawingData({ drawingData, maskData }); | |
| 450 | - } | |
| 451 | - }; | |
| 452 | - this.changeSize = (size) => { | |
| 453 | - this.size = Number(size); // Ensure the size is converted to a number | |
| 454 | - this.updatePreview(); | |
| 455 | - }; | |
| 456 | - this.changeColour = (key) => { | |
| 457 | - this.coloursSelect = key; | |
| 458 | - if (this.isErasing) { | |
| 459 | - this.switchEraser(); | |
| 460 | - } | |
| 461 | - this.updatePreview(); | |
| 462 | - }; | |
| 463 | - this.updatePreview = () => { | |
| 464 | - if (this.previewObj) { | |
| 465 | - this.previewObj.style.width = `${this.size * 0.1}rem`; | |
| 466 | - this.previewObj.style.height = `${this.size * 0.1}rem`; | |
| 467 | - if (this.coloursArr[this.coloursSelect] === '#ffffff') { | |
| 468 | - this.previewObj.classList.add('white'); | |
| 469 | - } | |
| 470 | - else { | |
| 471 | - this.previewObj.classList.remove('white'); | |
| 472 | - } | |
| 473 | - if (this.isErasing) { | |
| 474 | - this.previewObj.classList.add('erase'); | |
| 475 | - } | |
| 476 | - else { | |
| 477 | - this.previewObj.classList.remove('erase'); | |
| 478 | - this.previewObj.style.backgroundColor = `${this.coloursArr[this.coloursSelect]}`; | |
| 479 | - } | |
| 480 | - } | |
| 481 | - }; | |
| 482 | - this.closeDrawing = () => { | |
| 483 | - var _a; | |
| 484 | - if (this.ctrlCon) { | |
| 485 | - this.ctrlCon.remove(); | |
| 486 | - this.ctrlCon = null; | |
| 487 | - } | |
| 488 | - if (this.drawingGroup) | |
| 489 | - this.drawingGroup = null; | |
| 490 | - if (this.maskingGroup) | |
| 491 | - this.maskingGroup = null; | |
| 492 | - if (this.svg && this.targetCon) { | |
| 493 | - this.targetCon.removeChild(this.svg); | |
| 494 | - this.svg = null; | |
| 495 | - } | |
| 496 | - this.isErasing = false; | |
| 497 | - this.btnInit.classList.remove('is-active'); | |
| 498 | - (_a = this.targetCon) === null || _a === void 0 ? void 0 : _a.classList.remove('is-active'); | |
| 499 | - // resize 이벤트 리스너 제거 | |
| 500 | - window.removeEventListener('resize', this.onResize); | |
| 501 | - // beforeunload 이벤트 리스너 제거 | |
| 502 | - // window.removeEventListener('beforeunload', this.beforeUnloadHandler); | |
| 503 | - }; | |
| 504 | - this.createDrawEvents = () => { | |
| 505 | - if (!this.svg) | |
| 506 | - return; | |
| 507 | - this.svg.addEventListener("mousedown", this.pressEventHandler); | |
| 508 | - this.svg.addEventListener("mousemove", this.dragEventHandler); | |
| 509 | - this.svg.addEventListener("mouseup", this.releaseEventHandler); | |
| 510 | - this.svg.addEventListener("mouseout", this.cancelEventHandler); | |
| 511 | - this.svg.addEventListener("touchstart", this.pressEventHandler); | |
| 512 | - this.svg.addEventListener("touchmove", this.dragEventHandler); | |
| 513 | - this.svg.addEventListener("touchend", this.releaseEventHandler); | |
| 514 | - this.svg.addEventListener("touchcancel", this.cancelEventHandler); | |
| 515 | - }; | |
| 516 | - // line 대신 path 사용으로 메모리 사용량 최적화 | |
| 517 | - this.releaseEventHandler = () => { | |
| 518 | - this.drawing = false; | |
| 519 | - if (this.drawPathElement) { | |
| 520 | - this.currentPath.push(this.drawPathElement); | |
| 521 | - } | |
| 522 | - if (this.eraserPathElement) { | |
| 523 | - this.currentPath.push(this.eraserPathElement); | |
| 524 | - } | |
| 525 | - if (this.currentPath.length > 0) { | |
| 526 | - this.undoStack.push(this.currentPath); | |
| 527 | - this.redoStack = []; | |
| 528 | - } | |
| 529 | - this.drawPathElement = null; // 현재 그리던 path 초기화 | |
| 530 | - this.eraserPathElement = null; // 현재 그리던 path 초기화 | |
| 531 | - this.currentPath = []; | |
| 532 | - // 드로잉이 끝나면 다시 기본 동작을 허용 | |
| 533 | - this.preventPageInteractions(true); | |
| 534 | - // 드로잉이 끝나면 다시 painterbar의 pointer-events 활성 | |
| 535 | - this.disablePointerEvents(this.ctrlCon, false); | |
| 536 | - }; | |
| 537 | - this.cancelEventHandler = () => { | |
| 538 | - this.releaseEventHandler(); | |
| 539 | - }; | |
| 540 | - this.pressEventHandler = (e) => { | |
| 541 | - var _a; | |
| 542 | - this.drawing = true; | |
| 543 | - const newPoint = this.getMousePosition(e); | |
| 544 | - // 드로잉 중에는 페이지에서 텍스트 선택 및 드래그가 되지 않도록 차단 | |
| 545 | - this.preventPageInteractions(false); | |
| 546 | - // 드로잉 중에는 painterbar의 pointer-events 비활성 | |
| 547 | - this.disablePointerEvents(this.ctrlCon, true); | |
| 548 | - // 드로잉 또는 지우기 동작 처리 | |
| 549 | - const strokeColor = this.isErasing ? 'black' : this.coloursArr[this.coloursSelect]; | |
| 550 | - this.startDrawing(strokeColor, this.size, this.isErasing, newPoint); | |
| 551 | - // 마스킹 그룹에 path 가 있을 경우, 마스킹 그룹에도 path 추가 | |
| 552 | - if (!this.isErasing && ((_a = this.maskingGroup) === null || _a === void 0 ? void 0 : _a.querySelector('path'))) { | |
| 553 | - this.startDrawing('white', this.size, true, newPoint); | |
| 554 | - } | |
| 555 | - }; | |
| 556 | - this.dragEventHandler = (e) => { | |
| 557 | - if (!this.drawing) | |
| 558 | - return; | |
| 559 | - const newPoint = this.getMousePosition(e); | |
| 560 | - // 새로운 path 요소 그리기 | |
| 561 | - if (this.drawPathElement) { | |
| 562 | - this.drawPath(`L ${newPoint.x} ${newPoint.y}`); | |
| 563 | - } | |
| 564 | - if (this.eraserPathElement) { | |
| 565 | - this.drawPath(`L ${newPoint.x} ${newPoint.y}`, true); | |
| 566 | - } | |
| 567 | - e.preventDefault(); | |
| 568 | - }; | |
| 569 | - // 새로운 path를 생성하고 시작 지점을 그리는 함수 | |
| 570 | - this.startDrawing = (strokeColor, strokeWidth, eraser, point) => { | |
| 571 | - this.createNewPath(strokeColor, strokeWidth, eraser); | |
| 572 | - this.drawPath(`M ${point.x} ${point.y} L ${point.x + 0.01} ${point.y}`, eraser); | |
| 573 | - }; | |
| 574 | - this.createNewPath = (strokeColor, strokeWidth, eraser = false) => { | |
| 575 | - var _a, _b; | |
| 576 | - // 새로운 path 요소 생성 | |
| 577 | - const pathElement = document.createElementNS(this.svgNS, 'path'); | |
| 578 | - pathElement.setAttribute('fill', 'none'); | |
| 579 | - pathElement.setAttribute('stroke', strokeColor); | |
| 580 | - pathElement.setAttribute('stroke-width', `${strokeWidth}`); | |
| 581 | - pathElement.setAttribute('stroke-linecap', 'round'); | |
| 582 | - pathElement.setAttribute('d', ''); | |
| 583 | - if (!eraser) { | |
| 584 | - pathElement.setAttribute('data-use', 'draw'); | |
| 585 | - (_a = this.drawingGroup) === null || _a === void 0 ? void 0 : _a.append(pathElement); | |
| 586 | - this.drawPathElement = pathElement; | |
| 587 | - } | |
| 588 | - else { | |
| 589 | - (_b = this.maskingGroup) === null || _b === void 0 ? void 0 : _b.append(pathElement); | |
| 590 | - this.eraserPathElement = pathElement; | |
| 591 | - } | |
| 592 | - }; | |
| 593 | - this.drawPath = (d, eraser = false) => { | |
| 594 | - const pathElement = eraser ? this.eraserPathElement : this.drawPathElement; | |
| 595 | - // 새로운 path 요소 그리기 | |
| 596 | - const od = pathElement.getAttribute('d'); | |
| 597 | - pathElement.setAttribute('d', `${od} ${d}`); | |
| 598 | - }; | |
| 599 | - this.getMousePosition = (e) => { | |
| 600 | - var _a; | |
| 601 | - if (!this.svg) | |
| 602 | - return new DOMPoint(0, 0); // Add a fallback value in case svg is null | |
| 603 | - const point = this.svg.createSVGPoint(); | |
| 604 | - point.x = e.changedTouches ? e.changedTouches[0].clientX : e.clientX; | |
| 605 | - point.y = e.changedTouches ? e.changedTouches[0].clientY : e.clientY; | |
| 606 | - return point.matrixTransform((_a = this.svg.getScreenCTM()) === null || _a === void 0 ? void 0 : _a.inverse()); // Convert screen coordinates to SVG coordinates | |
| 607 | - }; | |
| 608 | - // 페이지의 기본 선택 및 드래그 동작을 활성/비활성화하는 함수 | |
| 609 | - this.preventPageInteractions = (state) => { | |
| 610 | - if (state) { | |
| 611 | - document.onselectstart = null; // 텍스트 선택 차단 해제 | |
| 612 | - document.ondragstart = null; // 드래그 차단 해제 | |
| 613 | - } | |
| 614 | - else { | |
| 615 | - document.onselectstart = () => false; // 텍스트 선택 차단 | |
| 616 | - document.ondragstart = () => false; // 드래그 차단 | |
| 617 | - } | |
| 618 | - }; | |
| 619 | - // pointer-events를 활성/비활성화하는 함수 | |
| 620 | - this.disablePointerEvents = (target, state) => { | |
| 621 | - if (!target) | |
| 622 | - return; | |
| 623 | - target.style.pointerEvents = state ? 'none' : ''; | |
| 624 | - }; | |
| 625 | - // 데이터 가져오기 | |
| 626 | - this.loadDrawingData = () => { | |
| 627 | - if (this.loadDataUrl === '') { | |
| 628 | - // 세션스토리지에서 데이터 가져오기 | |
| 629 | - const userKey = this.dataParam.userKey; | |
| 630 | - const contentsKey = this.dataParam.contentsKey; | |
| 631 | - if (userKey && contentsKey) { | |
| 632 | - this.reset(); | |
| 633 | - const storedData = sessionStorage.getItem(`drawingData-${userKey}-${contentsKey}`); | |
| 634 | - if (storedData) { | |
| 635 | - const parsedData = JSON.parse(storedData); | |
| 636 | - const { drawingData, maskData } = parsedData; | |
| 637 | - // 드로잉 데이터를 복원 | |
| 638 | - if (drawingData && this.drawingGroup) { | |
| 639 | - drawingData.forEach((line) => { | |
| 640 | - this.createNewPath(line[0], line[1]); | |
| 641 | - this.drawPath(line[2]); | |
| 642 | - }); | |
| 643 | - } | |
| 644 | - // 마스크 데이터를 복원 | |
| 645 | - if (maskData && this.maskingGroup) { | |
| 646 | - maskData.forEach((line) => { | |
| 647 | - this.createNewPath(line[0], line[1], true); | |
| 648 | - this.drawPath(line[2], true); | |
| 649 | - }); | |
| 650 | - } | |
| 651 | - this.loadedDrawingData = true; | |
| 652 | - console.log('Data loaded from session storage'); | |
| 653 | - } | |
| 654 | - else { | |
| 655 | - this.loadedDrawingData = false; | |
| 656 | - console.log('No data found in session storage'); | |
| 657 | - } | |
| 658 | - } | |
| 659 | - else { | |
| 660 | - console.error('userKey or contentsKey is missing in dataParam'); | |
| 661 | - } | |
| 662 | - return; | |
| 663 | - } | |
| 664 | - // fetch(this.loadDataUrl, { | |
| 665 | - // method: 'GET', | |
| 666 | - // headers: { | |
| 667 | - // 'Content-Type': 'application/json', | |
| 668 | - // } | |
| 669 | - // }) | |
| 670 | - fetch(this.loadDataUrl, { | |
| 671 | - method: 'POST', | |
| 672 | - headers: { | |
| 673 | - 'Content-Type': 'application/json', | |
| 674 | - }, | |
| 675 | - body: JSON.stringify(this.dataParam), | |
| 676 | - }) | |
| 677 | - .then(response => response.json()) | |
| 678 | - .then(data => { | |
| 679 | - const { drawingData, maskData } = data; | |
| 680 | - if (!drawingData || drawingData && drawingData.length === 0) { | |
| 681 | - // hyo: drawingData가 없으면 reset만 하고 종료 | |
| 682 | - this.reset(); | |
| 683 | - this.loadedDrawingData = false; | |
| 684 | - return; | |
| 685 | - } | |
| 686 | - | |
| 687 | - // hyo: 데이터가 있는 경우에만 초기화 후 복원 시작 | |
| 688 | - this.reset(); | |
| 689 | - | |
| 690 | - if (drawingData && this.drawingGroup) { | |
| 691 | - drawingData.forEach((line) => { | |
| 692 | - this.createNewPath(line[0], line[1]); | |
| 693 | - this.drawPath(line[2]); | |
| 694 | - }); | |
| 695 | - } | |
| 696 | - if (maskData && this.maskingGroup) { | |
| 697 | - maskData.forEach((line) => { | |
| 698 | - this.createNewPath(line[0], line[1], true); | |
| 699 | - this.drawPath(line[2], true); | |
| 700 | - }); | |
| 701 | - } | |
| 702 | - this.loadedDrawingData = true; | |
| 703 | - }) | |
| 704 | - .catch(error => console.error('Error loading drawing data:', error)); | |
| 705 | - }; | |
| 706 | - // 데이터 저장 | |
| 707 | - this.saveDrawingData = (data) => { | |
| 708 | - const params = Object.assign(Object.assign({}, this.dataParam), data); | |
| 709 | - console.log('save data :', params); | |
| 710 | - console.log(this.dataParam); | |
| 711 | - // saveDataUrl이 비어 있으면 세션스토리지에 저장 | |
| 712 | - if (this.saveDataUrl === '') { | |
| 713 | - if ('userKey' in params && 'contentsKey' in params) { | |
| 714 | - sessionStorage.setItem(`drawingData-${params['userKey']}-${params['contentsKey']}`, JSON.stringify(params)); | |
| 715 | - console.log('Data saved to session storage'); | |
| 716 | - } | |
| 717 | - else { | |
| 718 | - console.error('userKey or contentsKey is missing in params'); | |
| 719 | - } | |
| 720 | - return; | |
| 721 | - } | |
| 722 | - fetch(this.saveDataUrl, { | |
| 723 | - method: 'POST', | |
| 724 | - headers: { | |
| 725 | - 'Content-Type': 'application/json', | |
| 726 | - }, | |
| 727 | - body: JSON.stringify(params), | |
| 728 | - }) | |
| 729 | - .catch(error => console.error('Error saving drawing data:', error)); | |
| 730 | - }; | |
| 731 | - // 저장된 데이터 삭제 | |
| 732 | - this.deleteDrawingData = () => { | |
| 733 | - const params = this.dataParam; | |
| 734 | - // deleteDataUrl이 비어 있으면 세션스토리지를 확인하여 삭제 | |
| 735 | - if (this.deleteDataUrl === '') { | |
| 736 | - if ('userKey' in params && 'contentsKey' in params) { | |
| 737 | - sessionStorage.removeItem(`drawingData-${params['userKey']}-${params['contentsKey']}`); | |
| 738 | - console.log('Data deleted to session storage'); | |
| 739 | - } | |
| 740 | - else { | |
| 741 | - console.error('userKey or contentsKey is missing in params'); | |
| 742 | - } | |
| 743 | - return; | |
| 744 | - } | |
| 745 | - fetch(this.deleteDataUrl, { | |
| 746 | - method: 'POST', | |
| 747 | - headers: { | |
| 748 | - 'Content-Type': 'application/json', | |
| 749 | - }, | |
| 750 | - body: JSON.stringify(this.dataParam), | |
| 751 | - }) | |
| 752 | - .then(response => response.json()) | |
| 753 | - .then(data => { | |
| 754 | - console.log(data); | |
| 755 | - }) | |
| 756 | - .catch(error => console.error('Error loading drawing data:', error)); | |
| 757 | - }; | |
| 758 | - // 창 닫기 이벤트에서 save() 호출 | |
| 759 | - this.beforeUnloadHandler = (event) => { | |
| 760 | - this.save(); | |
| 761 | - event.preventDefault(); | |
| 762 | - // // 경고 메시지 표시 (선택 사항) | |
| 763 | - // const confirmationMessage = '색연필로 작성한 내용이 저장되지 않을 수 있습니다.'; | |
| 764 | - // event.returnValue = confirmationMessage; | |
| 765 | - // return confirmationMessage; | |
| 766 | - }; | |
| 767 | - this.btnInit = btnInit; | |
| 768 | - this.targetArr = objArr; | |
| 769 | - this.coloursArr = coloursArr.map(c => c.toLowerCase()); | |
| 770 | - this.coloursSelect = coloursSelect - 1; | |
| 771 | - this.baseSize = { x: -800, y: -450, w: 1600, h: 900 }; | |
| 772 | - brushMax = brushMax > 60 ? 60 : brushMax; | |
| 773 | - this.sizeRange = { | |
| 774 | - min: brushMin, | |
| 775 | - max: brushMax | |
| 776 | - }; | |
| 777 | - this.size = Math.round((brushMin + brushMax) / 3); | |
| 778 | - this.saveDataUrl = saveDataURL; | |
| 779 | - this.loadDataUrl = loadDataURL; | |
| 780 | - this.deleteDataUrl = deleteDataURL; | |
| 781 | - this.dataParam = dataParam; | |
| 782 | - this.btnInit.onclick = () => { | |
| 783 | - var _a, _b; | |
| 784 | - if (this.btnInit.classList.contains('is-active')) { | |
| 785 | - this.save(); | |
| 786 | - this.closeDrawing(); | |
| 787 | - } | |
| 788 | - else { | |
| 789 | - this.detectTargetObj(); | |
| 790 | - const svg = document.createElementNS(this.svgNS, 'svg'); | |
| 791 | - this.svg = svg; | |
| 792 | - this.svg.id = 'painterSvg'; | |
| 793 | - this.svg.setAttribute("preserveAspectRatio", "xMidYMid meet"); | |
| 794 | - (_a = this.targetCon) === null || _a === void 0 ? void 0 : _a.append(this.svg); | |
| 795 | - this.createDrawingGroup(); // 드로잉 그룹 생성 | |
| 796 | - this.createMaskingGroup(); // 마스크 생성 | |
| 797 | - this.uiUpdate(); | |
| 798 | - this.createDrawEvents(); | |
| 799 | - this.loadDrawingData(); | |
| 800 | - this.onResize(); | |
| 801 | - // Listen for scroll event | |
| 802 | - (_b = this.targetCon) === null || _b === void 0 ? void 0 : _b.addEventListener('scroll', this.onScroll); | |
| 803 | - } | |
| 804 | - }; | |
| 805 | - const ifrObj = document.querySelector('.iframe-area iframe'); | |
| 806 | - this.ifrObj = ifrObj; | |
| 807 | - (_a = this.ifrObj) === null || _a === void 0 ? void 0 : _a.addEventListener('load', this.onIfrLoad); | |
| 808 | - window.addEventListener('resize', this.onResize); | |
| 809 | - // 창을 닫거나 새로고침할 때 save() 호출 | |
| 810 | - // window.addEventListener('beforeunload', this.beforeUnloadHandler); | |
| 811 | - window.onbeforeunload = this.beforeUnloadHandler; | |
| 812 | - document.addEventListener("visibilitychange", (event) => { | |
| 813 | - if (document.visibilityState === 'hidden') { | |
| 814 | - this.beforeUnloadHandler(event); // 사용자가 페이지를 떠나면 저장 | |
| 815 | - } | |
| 816 | - }); | |
| 817 | - } | |
| 818 | - // Getter for dataParam | |
| 819 | - getDataParam() { | |
| 820 | - return this.dataParam; | |
| 821 | - } | |
| 822 | - // Setter for dataParam | |
| 823 | - setDataParam(newDataParam) { | |
| 824 | - this.dataParam = Object.assign(Object.assign({}, this.dataParam), newDataParam); // Merge with the existing dataParam | |
| 825 | - } | |
| 826 | - saveDrawing() { | |
| 827 | - this.save(); | |
| 828 | - } | |
| 829 | -} | |
| 830 | -// Create the instance and attach it to the window object | |
| 831 | -export function initColouredPenciles() { | |
| 832 | - window.addEventListener('DOMContentLoaded', () => { | |
| 833 | - const btnInit = document.querySelector('.btn-painter-toggle'); // 색연필 활성 버튼 | |
| 834 | - if (!btnInit) { | |
| 835 | - // console.error('.btn-painter-toggle 요소를 찾을 수 없습니다.'); | |
| 836 | - return; | |
| 837 | - } | |
| 838 | - const objArr = ['viewer-painter']; // 색연필 콘테이너 클래스명 - 여러개 지정 가능, 앞에 위치한 클래스명의 엘리먼트 우선 | |
| 839 | - const colorArray = ["#222222", "#fd4418", "#ffce00", "#00d56a", "#2f77ff", "#ffffff"]; // 색상 팔레트 6색 | |
| 840 | - const defaultColor = 5; // 기본 선 굵기 | |
| 841 | - const brushMin = 0.5, brushMax = 43; // 최소, 최대 선 굵기 | |
| 842 | - const saveDataUrl = ''; // save api url | |
| 843 | - const loadDataURL = ''; // load api url | |
| 844 | - const deleteDataURL = ''; // delete api url | |
| 845 | - // const loadDataURL = 'lineData.json'; // test load api url | |
| 846 | - /** dataParam은 외부에서 설정, 사용 환경에 따라 key, value 자유 설정 가능 */ | |
| 847 | - const dataParam = {}; | |
| 848 | - // 인스턴스 생성 후 전역 window 객체에 저장 | |
| 849 | - window.cpInstance = new colouredPenciles(btnInit, objArr, colorArray, defaultColor, brushMin, brushMax, saveDataUrl, loadDataURL, deleteDataURL, dataParam); | |
| 850 | - }); | |
| 851 | -} | |
| 852 | -initColouredPenciles(); |
--- resources/front/site/SITE_00000/js/custom/module/old_etc.js
... | ... | @@ -1,36 +0,0 @@ |
| 1 | -//선긋기 | |
| 2 | -var drawPath = function (idL, idR, startL, startT, endL, endT) { | |
| 3 | - matchCanvas | |
| 4 | - .append("line") | |
| 5 | - .style("stroke", "black") // colour the line | |
| 6 | - .style("stroke-width", 1) | |
| 7 | - .attr("x1", startLeft) // x position of the first end of the line | |
| 8 | - .attr("y1", startTop) // y position of the first end of the line | |
| 9 | - .attr("x2", endLeft) // x position of the second end of the line | |
| 10 | - .attr("y2", endTop) | |
| 11 | - .attr("class", idL + " " + idR) | |
| 12 | - .attr("data-left", idL) | |
| 13 | - .attr("data-right", idR) | |
| 14 | - | |
| 15 | - numLeft = null, | |
| 16 | - numRight = null; | |
| 17 | - | |
| 18 | - idLeft = 0; | |
| 19 | - idRight = 0; | |
| 20 | -} | |
| 21 | - | |
| 22 | -var viewEvaluationEvlInfo = function (el) { | |
| 23 | - let acsJsonList = JSON.parse($(el).closest(".info-fnc").find(".tempCodeList").val()); | |
| 24 | - let html = ""; | |
| 25 | - for (let a = 0; a < acsJsonList.length; a++) { | |
| 26 | - html += '<li>' + | |
| 27 | - '<ul class="info-list sm">' + | |
| 28 | - '<li>' + | |
| 29 | - '<cite>성취기준</cite>' + | |
| 30 | - '<div class="txt-area">[' + acsJsonList[a].acsCode + '] ' + acsJsonList[a].acsDc + '</div>' + | |
| 31 | - '</li>' + | |
| 32 | - '</ul></li>'; | |
| 33 | - } | |
| 34 | - $('#evaluationInfoUl').html(html); | |
| 35 | - popupOpen("evaluationInfoPopup"); | |
| 36 | -} |
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?