진짜 최종의_최종의_최종.psd 같지만 하여간...
깃허브 : https://github.com/OhIkmyeong/move_modal
여기서 modal 폴더에 들어가시면 소스 있습니다.
얻어갈 수 있는것 :
- fetch()를 이용한 비동기로 html을 include하기
- class 문법으로 drag 가능한 modal 만들기
- mousedown시 mousemove 이벤트 달기
- mouseup시 mousemove 이벤트 지워주기
- 모달을 드래그 하는 도중, 모달 내의 텍스트까지 긁히는 현상 방지하기 (user-select:none/auto)
HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Move Modals...</title>
<link rel="stylesheet" type="text/css" href="./css/reset.css"/>
<link rel="stylesheet" type="text/css" href="./css/modal.css"/>
<script src="./js/main.js" type="module"></script>
</head>
<body>
<header>
<h1>Move Modals...!</h1>
<h3>Remove MouseMove Event When MouseUp Occurs!!!</h3>
<div>
<button class="btn-open-md" data-modal-open="modal_1">open modal 001</button>
<button class="btn-open-md" data-modal-open="modal_2">open modal 002</button>
<button class="btn-open-md" data-modal-open="modal_3">open modal 003</button>
</div>
</header>
<main>
<div class="box">Lorem ipsum dolor sit amet consectetur, adipisicing elit. Aperiam expedita amet saepe? Sit explicabo expedita alias? Quas numquam ducimus saepe.</div>
<div class="box">Lorem ipsum dolor sit amet consectetur, adipisicing elit. Aperiam expedita amet saepe? Sit explicabo expedita alias? Quas numquam ducimus saepe.</div>
<div class="box">Lorem ipsum dolor sit amet consectetur, adipisicing elit. Aperiam expedita amet saepe? Sit explicabo expedita alias? Quas numquam ducimus saepe.</div>
</main>
<!-- [MODALS] -->
<div class="wrap-modal" data-include="./include/modal_1.html" data-modal="modal_1"></div>
<div class="wrap-modal off" data-include="./include/modal_2.html" data-modal="modal_2"></div>
<div class="wrap-modal off" data-include="./include/modal_3.html" data-modal="modal_3"></div>
</body>
</html>
|
cs |
modal.css
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
@charset "utf-8";
.wrap-modal{
position:fixed;
top:0;left:0;
transition:opacity .3s;}
.wrap-modal.off{
opacity:0;
pointer-events:none;user-select:none;}
.md-bg{
position:fixed;
top:0;left:0;
width:100%;height:100vh;
background:rgba(0,0,0,.1);
backdrop-filter:blur(10px);
}
.modal{
position:fixed;
top:0;left:0;
min-width:300px; max-width:50vw;
background:#fff;
border:2px solid black;
box-shadow:
-5px -5px 5px rgba(255,255,255,.5),
5px 5px 10px rgba(0,0,0,.2);}
.md-head{
display:flex; flex-flow:row nowrap;
justify-content:space-between; align-items:center;
position:relative;
padding:.5em .5em .5em 1em;
background:#000;
font-size:14px; color:#fff;
user-select:none;
cursor:grab;}
.btn-close-md{
display:block; position:relative;
width:24px; aspect-ratio:1/1;
background:rgba(255,255,255,.2);
border:none;}
.btn-close-md::before,
.btn-close-md::after{
content:'';display:block;position:absolute;
top:50%;left:50%;
width:2px; height:15px;
background:red;
pointer-events:none;user-select:none;}
.btn-close-md::before{
transform:translate(-50%,-50%) rotate(45deg);}
.btn-close-md::after{
transform:translate(-50%,-50%) rotate(-45deg);}
.md-body{padding:1rem;}
/* [위치] */
.wrap-modal[data-modal="modal_1"] .modal{transform:translate(30vw,10vh);}
.wrap-modal[data-modal="modal_2"] .modal{transform:translate(10vw,20vh);}
.wrap-modal[data-modal="modal_3"] .modal{transform:translate(40vw,50vh);}
|
cs |
include/modal_sample.html
1
2
3
4
5
6
7
8
9
10
|
<div class="md-bg"></div>
<div class="modal">
<section class="md-head">
<p class="md-title">Modal 003</p>
<button class="btn-close-md" title="close modal" data-modal-close="modal_3"></button>
</section>
<section class="md-body">
<h3>BBO-WAP!</h3>
</section>
</div><!-- modal -->
|
cs |
main.js
1
2
3
|
import { Common } from "./func/common.js";
Common();
|
cs |
func/common.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
import { Include } from "./include.js";
import { Modal } from "./modal.js";
export async function Common(){
const INCLUDE = new Include();
const MODAL = new Modal();
await INCLUDE.init();
MODAL.init();
window.addEventListener('click',e=>{
const target = e.target;
// OPEN
if(target.dataset.modalOpen){MODAL.open(target);}
// CLOSE
if(target.dataset.modalClose){MODAL.close(target);}
});
}//Common
|
cs |
func/include.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
export class Include{
async init(){
const $$dom = document.querySelectorAll('[data-include]');
for(let $dom of $$dom){
const data = await this.fetch_html($dom);
$dom.innerHTML = data;
$dom.removeAttribute('data-include');
}//for
}//init
fetch_html($dom){
const url = $dom.dataset.include;
return fetch(url).then(res=>res.text());
}//fetch_html
}//class-Include
|
cs |
func/modal.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
export class Modal{
constructor(){}//constructor
init(){
const $$wrap = document.querySelectorAll('[data-modal]');
$$wrap.forEach($wrap => { new Draggable($wrap);});
}
open(target){
const mdName = target.dataset.modalOpen;
const $wrap = document.querySelector(`[data-modal="${mdName}"]`);
const $bg = $wrap.getElementsByClassName('md-bg')?.[0];
if($bg){
document.body.style.overflowY = 'hidden';
}//if
$wrap.classList.remove('off');
}//open
close(target){
const mdName = target.dataset.modalClose;
const $wrap = document.querySelector(`[data-modal="${mdName}"]`);
document.body.style.overflowY = 'auto';
$wrap.classList.add('off');
}//close
}//class-Modal
/* ----------- 모달 드래그 ------------ */
/* 윈도우 사이즈 */
const WINDOW_SIZE = {
wid : window.innerWidth,
hei : window.innerHeight
};
/* 윈도우 사이즈 업데이트 */
function update_window_size(){
WINDOW_SIZE.wid = window.innerWidth;
WINDOW_SIZE.hei = window.innerHeight;
}//update_window_size
/* 윈도우 드래그 해제 */
function clear_selection(){
if (window.getSelection().empty) {
// Chrome
window.getSelection().empty();
}else if(window.getSelection().removeAllRanges){
// Firefox
window.getSelection().removeAllRanges();
}
//https://w3c.github.io/selection-api
//https://stackoverflow.com/questions/3169786/clear-text-selection-with-javascript
}//clear_selection
class Draggable{
constructor($wrap){
this.$modal = $wrap.getElementsByClassName('modal')[0];
this.$header = this.$modal.getElementsByClassName('md-head')[0];
this.$body = this.$modal.getElementsByClassName('md-body')[0];
this.is_draggable = false;
this.SIZE = {
wid : this.$modal.getBoundingClientRect().width,
hei : this.$modal.getBoundingClientRect().height}
this.LIMIT = {
right : WINDOW_SIZE.wid - (this.SIZE.wid / 2),
bottom : WINDOW_SIZE.hei - (this.SIZE.hei / 2)}
this.POS = {
last : {x:null, y:null},
final : {x:null, y:null}}
//실행
this.init();
}//constructor
/* 실행 시작 */
init(){
if(!this.$header){return;}
this.$header.addEventListener('mousedown', this.ready_to_drag);
this.$modal.addEventListener('mouseup',this.stop_drag);
window.addEventListener('mouseleave',this.stop_drag);
}//init
/* 드래그 준비 */
ready_to_drag = (e) =>{
this.is_draggable = true;
this.POS.last.x = e.clientX - this.$modal.getBoundingClientRect().left;
this.POS.last.y = e.clientY - this.$modal.getBoundingClientRect().top;
/* update limit size.. */
update_window_size();
this.LIMIT.right = WINDOW_SIZE.wid - (this.SIZE.wid / 2);
this.LIMIT.bottom = WINDOW_SIZE.hei - (this.SIZE.hei / 2);
/* add Event */
window.addEventListener('mousemove', this.on_drag);
}//ready_to_drag
/* 드래그 중 */
on_drag = (e) => {
if(!this.is_draggable){return;}
this.$body.style.userSelect = 'none';
const currX = e.clientX - this.POS.last.x;
const currY = e.clientY - this.POS.last.y;
/* X축 */
if(currX <= (this.SIZE.wid / -2)){
this.POS.final.x = (this.SIZE.wid / -2);
}else if(currX > this.LIMIT.right){
this.POS.final.x = this.LIMIT.right;
}else{
this.POS.final.x = currX;}
/* Y축 */
if(currY <= 0){
this.POS.final.y = 0;
}else if(currY > this.LIMIT.bottom){
this.POS.final.y = this.LIMIT.bottom;
}else{
this.POS.final.y = currY;}
/* 최종 css 적용 */
const {final:{x,y}} = this.POS;
this.$modal.style.transform = `translate(${x}px,${y}px)`;
}//on_drag
/* 드래그 끝 */
stop_drag = () => {
this.is_draggable = false;
this.$body.style.userSelect = 'auto';
// clear_selection();
this.POS.last.x = this.$modal.getBoundingClientRect().left;
this.POS.last.y = this.$modal.getBoundingClientRect().top;
//removeEvent
window.removeEventListener('mousemove', this.on_drag);
}//stop_drag
}//class-Draggable
|
cs |
'CSS&JS > ⚡Thinkers' 카테고리의 다른 글
Table Builder (0) | 2022.07.08 |
---|---|
[JS]바닐라 자바스크립트로 24시간 쿠키 모달 끄기/켜기 설정하기 (0) | 2022.04.22 |
[JS] thead의 th를 누르면 그 항목에 맞춰 table sort 되게 하기(2) (0) | 2022.04.01 |
[JS] thead의 th를 누르면 그 항목에 맞춰 table sort 되게 하기 (0) | 2022.03.31 |
promise의 finally를 이용한 로딩화면 (0) | 2022.03.02 |