CSS&JS/⚡Thinkers

[JS] vanilla JavaScript로 jQuery ui의 Draggable과 Resizable 구현하기

arancia_ 2022. 9. 7. 13:30

데모 페이지 : https://ohikmyeong.github.io/resize_dom/

 

vanilla JS : Resizable

 

ohikmyeong.github.io

깃 허브 : https://github.com/OhIkmyeong/resize_dom

 

GitHub - OhIkmyeong/resize_dom: RESIZE DOM : DOM을 마우스다운+마우스무브 (드래그)로 Resize 가능하도록

RESIZE DOM : DOM을 마우스다운+마우스무브 (드래그)로 Resize 가능하도록. Contribute to OhIkmyeong/resize_dom development by creating an account on GitHub.

github.com

 

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
34
35
36
37
<!DOCTYPE html>
<html lang="en">
<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>vanilla JS : Resizable</title>
<style>
    *{margin:0;padding:0;box-sizing:border-box;}
    body{min-height:100vh;}
    img{width:100%;height:100%;pointer-events:none;}
    .resizable{
        position:absolute;top:0;left:0;background:#eee; width:500px; height:200px;
        min-width:100px;min-height:100px;
    }
    .resizable p{font-size:1rem;}
    .btn-resize{
        display:block;position:absolute;
        right:0;bottom:0;
        width:40px; aspect-ratio:1/1;
        background:#eee;
        border:2px solid black;
        cursor:nwse-resize;
    }
</style>
<script src="./js/main.js" type="module"></script>
</head>
<body>
    <div class="resizable modal" data-resizable="true" data-modal>
        <img src="https://source.unsplash.com/dqu6jrYxJ7I" alt="샘플 이미지"/>
    </div>
 
    <div class="resizable modal" data-resizable="true" data-modal>
        <img src="https://source.unsplash.com/xS9KgKrR3ac" alt="샘플 이미지"/>
    </div>
</body>
</html>
cs

main.js

1
2
3
4
5
6
import { Modal_Ctrl } from "./Modal.js";
// import { Resizable } from "./Resizable.js";
 
// const RESIZE = new Resizable();
const MODAL = new Modal_Ctrl();
MODAL.init();
cs

Modal.js (Draggable)

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
import { Resize } from "./Resizable.js";
 
export class Modal_Ctrl{
    /** 준비 */
    init(){
        //모달 드래그 관련
        const $$modal = document.querySelectorAll('[data-modal]');
        if(!$$modal.length){return;}
        for(let $modal of $$modal){ 
            $modal && new Draggable($modal);
        }//for
    }//init
}//class-Modal_Ctrl
 
/* ----------- 모달 드래그 ------------ */
/* 윈도우 사이즈 */
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
 
/** 윈도우 드래그 해제 
 * @deprecated 원래는 모달 드래그시 셀렉션(드래그 선택)되는 현상을 막으려고 만들어놨으나, 정작 모달 안의 input같은것들이 바로 포커스 해제 되는 문제가 발생하여 사용하지 않습니다.
 * @see https://w3c.github.io/selection-api
 * @see https://stackoverflow.com/questions/3169786/clear-text-selection-with-javascript
*/
export function clear_selection(){
    if (window.getSelection().empty) {  
        // Chrome
        window.getSelection().empty();
    }else if(window.getSelection().removeAllRanges){  
        // Firefox
        window.getSelection().removeAllRanges();
    }
}//clear_selection
 
/* CLASS - DRAGGABLE */
class Draggable{
    /** MODAL을 드래그 가능하게 만들어 줍니다.
     * @param $wrap DOM .wrap-modal
     */
    constructor($wrap){
        this.$modal = $wrap;
        console.log(this.$modal);
        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.RESIZE = new Resize(this); 
        this.init();
    }//constructor;
 
    /** 드래그 기능 실행 시작 */
    init(){
        this.$modal.addEventListener('mousedown'this.ready_to_drag, {once:true});
    }//init
 
    /** 드래그 준비(mousedown)
     * @param e event
    */
    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);
 
        window.addEventListener('mousemove'this.on_drag);
        window.addEventListener('mouseup',this.stop_drag,{once:true});
        window.addEventListener('mouseleave',this.stop_drag,{once:true});
    }//ready_to_drag
    
    /** 드래그 중(mousemove) 
     * @param e event
    */
    on_drag = (e) => {
        if(!this.is_draggable){return;}
        if(this.RESIZE.IS_REISZE) return;
        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)`; 
 
        clear_selection();
    }//on_drag
 
    /** 드래그 끝(mouseup) */
    stop_drag = () => {
        this.is_draggable = false;
        this.POS.last.x = this.$modal.getBoundingClientRect().left;
        this.POS.last.y = this.$modal.getBoundingClientRect().top;
 
        window.removeEventListener('mousemove'this.on_drag);
        this.$modal.addEventListener('mousedown'this.ready_to_drag, {once:true});
    }//stop_drag
}//class-Draggable
cs

Resizable

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
export class Resizable{
    /** make DOM(data-resizable="true") Resizable */
    constructor(){
        this.$$dom = Array.from(document.querySelectorAll('[data-resizable]'));
        this.$$dom.forEach($dom => new Resize($dom));
    }//constructor
}//Resizable
 
export class Resize{
    constructor(DRAG){
        this.DRAG = DRAG;
        this.$dom = this.DRAG.$modal;
        this.$btn = null;
        this.IS_REISZE = false;
        this.POS_START = {x:null, y:null};
        this.init();
    }//constructor
 
    /** Init Resizable */
    init(){
        this.$btn = this.add_btn();
        this.$btn.addEventListener('mousedown'this.on_down,{once:true});
    }//init
 
    /** resize 가능한 버튼 추가 
     * @returns <button.btn-resize>
    */
    add_btn(){
        const $btn = document.createElement('BUTTON');
        $btn.classList.add('btn-resize');
        $btn.title = "사이즈조절";
        this.$dom.appendChild($btn);
        return $btn;
    }//add_btn
 
    on_down = (e) =>{
        this.IS_REISZE = true;
        this.POS_START.x = e.clientX;
        this.POS_START.y = e.clientY;
        window.addEventListener('mousemove'this.on_move);
        window.addEventListener('mouseup'this.cancel,{once:true});
    }//on_down
 
    cancel = () => {
        this.IS_REISZE = false;
        window.removeEventListener('mousemove'this.on_move);
        this.$btn.addEventListener('mousedown'this.on_down,{once:true});
    }//cancel
 
    on_move = e =>{
        if(!this.IS_REISZE) return;
        if(!this.DRAG.is_draggable) return;
        const {x,y} = this.POS_START;
        const nowX = e.clientX;
        const nowY = e.clientY;
        const wid = this.$dom.offsetWidth;
        const hei = this.$dom.offsetHeight;
        if(x < nowX){
            const per = (nowX - x);
            this.$dom.style.width = `${wid + per}px`;
        }
 
        if(x >= nowX){
            const per = (x - nowX);
            this.$dom.style.width = `${wid - per}px`;
        }
 
        if(y < nowY){
            const per = (nowY - y);
            this.$dom.style.height = `${hei + per}px`;
        }
 
        if(y >= nowY){
            const per = (y - nowY);
            this.$dom.style.height = `${hei - per}px`;
        }
        this.POS_START.x = nowX;
        this.POS_START.y = nowY;
    }//on_move
 
    get_size(size,per){}//get_size
}//Resize
cs

'CSS&JS > ⚡Thinkers' 카테고리의 다른 글

[CSS,JS] 타임 슬라이더  (0) 2022.10.11
[vanilla JS] 바닐라 자바스크립트로 무한 슬라이더 만들기. v1.0  (0) 2022.10.11
clipText  (0) 2022.07.25
Table Builder 2.0  (0) 2022.07.22
Table Builder  (0) 2022.07.08