1 | <template>
|
2 | |
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 | <div class="c-deformableBox" :style="style" ref="content">
|
12 |
|
13 | <template v-if="resizable">
|
14 | <div
|
15 | class="ctrl ctrl-v ctrl-t"
|
16 | v-if="canResizeDirection('top')"
|
17 | @mousedown.stop.left.prevent="handleResizeStart(['t'])"
|
18 | ></div>
|
19 | <div
|
20 | class="ctrl ctrl-h ctrl-r"
|
21 | v-if="canResizeDirection('right')"
|
22 | @mousedown.stop.left.prevent="handleResizeStart(['r'])"
|
23 | ></div>
|
24 | <div
|
25 | class="ctrl ctrl-v ctrl-b"
|
26 | v-if="canResizeDirection('bottom')"
|
27 | @mousedown.stop.left.prevent="handleResizeStart(['b'])"
|
28 | ></div>
|
29 | <div
|
30 | class="ctrl ctrl-h ctrl-l"
|
31 | v-if="canResizeDirection('left')"
|
32 | @mousedown.stop.left.prevent="handleResizeStart(['l'])"
|
33 | ></div>
|
34 | <div
|
35 | class="ctrl ctrl-c ctrl-t ctrl-r"
|
36 | v-if="canResizeDirection('top') && canResizeDirection('right')"
|
37 | @mousedown.stop.left.prevent="handleResizeStart(['t', 'r'])"
|
38 | ></div>
|
39 | <div
|
40 | class="ctrl ctrl-c ctrl-t ctrl-l"
|
41 | v-if="canResizeDirection('top') && canResizeDirection('left')"
|
42 | @mousedown.stop.left.prevent="handleResizeStart(['t', 'l'])"
|
43 | ></div>
|
44 | <div
|
45 | class="ctrl ctrl-c ctrl-b ctrl-r"
|
46 | v-if="canResizeDirection('bottom') && canResizeDirection('right')"
|
47 | @mousedown.stop.left.prevent="handleResizeStart(['b', 'r'])"
|
48 | ></div>
|
49 | <div
|
50 | class="ctrl ctrl-c ctrl-b ctrl-l"
|
51 | v-if="canResizeDirection('left') && canResizeDirection('right')"
|
52 | @mousedown.stop.left.prevent="handleResizeStart(['b', 'l'])"
|
53 | ></div>
|
54 | </template>
|
55 | <slot></slot>
|
56 | </div>
|
57 | </template>
|
58 | <script>
|
59 | export default {
|
60 | name: 'deformable-box',
|
61 | data() {
|
62 | return {
|
63 | w: undefined,
|
64 | h: undefined,
|
65 | resizeDirections: [],
|
66 | resizing: false
|
67 | };
|
68 | },
|
69 | props: {
|
70 |
|
71 | resizable: {
|
72 |
|
73 | validator(val) {
|
74 | if (typeof val === 'boolean') return val;
|
75 | return Array.isArray(val) &&
|
76 | val.every(each => ['top', 'left', 'right', 'bottom'].indexOf(each) > -1);
|
77 | },
|
78 | default: true
|
79 | },
|
80 |
|
81 | initWidth: Number,
|
82 |
|
83 | initHeight: Number,
|
84 |
|
85 | maxHeight: Number,
|
86 | minHeight: Number,
|
87 | maxWidth: Number,
|
88 | minWidth: Number
|
89 | },
|
90 | mounted() {
|
91 | this.init();
|
92 | },
|
93 | computed: {
|
94 | style() {
|
95 | if (this.w === 0 && this.h === 0) return {};
|
96 | const result = {};
|
97 |
|
98 | if (this.canResizeDirection('left') || this.canResizeDirection('right')) {
|
99 | result.width = `${this.w}px`;
|
100 | }
|
101 | if (this.canResizeDirection('top') || this.canResizeDirection('bottom')) {
|
102 | result.height = `${this.h}px`;
|
103 | }
|
104 | return result;
|
105 | }
|
106 | },
|
107 | methods: {
|
108 | init() {
|
109 | this.w = this.initWidth || this.$el.clientWidth;
|
110 | this.h = this.initHeight || this.$el.clientHeight;
|
111 | },
|
112 |
|
113 | canResizeDirection(direction) {
|
114 | if (typeof this.resizable === 'boolean') return this.resizable;
|
115 | return this.resizable.indexOf(direction) > -1;
|
116 | },
|
117 | handleResizeStart(directionArray) {
|
118 | this.resizeDirections = directionArray;
|
119 | this.resizing = true;
|
120 | this.$emit('resize-start');
|
121 | document.documentElement.addEventListener('mousemove', this.handleResize);
|
122 |
|
123 | document.documentElement.addEventListener(
|
124 | 'mouseup',
|
125 | this.handleResizeEnd,
|
126 | { once: true }
|
127 | );
|
128 | document.getElementById('monitor-iframe').addEventListener('mouseover', this.handleResizeEnd);
|
129 | },
|
130 | getElementLeft(el) {
|
131 | let actualLeft = el.offsetLeft;
|
132 | let parent = el.offsetParent;
|
133 | while (parent !== null) {
|
134 | actualLeft += parent.offsetLeft + parent.clientLeft;
|
135 | parent = parent.offsetParent;
|
136 | }
|
137 | return actualLeft;
|
138 | },
|
139 | getElementTop(el) {
|
140 | let actualTop = el.offsetTop;
|
141 | let parent = el.offsetParent;
|
142 | while (parent !== null) {
|
143 | actualTop += parent.offsetTop + parent.clientTop;
|
144 | parent = parent.offsetParent;
|
145 | }
|
146 | return actualTop;
|
147 | },
|
148 | handleResize(e) {
|
149 | console.log('resizing');
|
150 | if (this.resizeDirections.indexOf('r') > -1) {
|
151 | const widthAfterMove =
|
152 | e.pageX - this.getElementLeft(this.$refs.content);
|
153 | this.w = Math.max(Math.min(widthAfterMove, this.maxWidth || Infinity), this.minWidth || 1);
|
154 | }
|
155 | if (this.resizeDirections.indexOf('l') > -1) {
|
156 | const widthAfterMove =
|
157 | this.getElementLeft(this.$refs.content) + this.w - e.pageX;
|
158 | this.w = Math.max(Math.min(widthAfterMove, this.maxWidth || Infinity), this.minWidth || 1);
|
159 | }
|
160 |
|
161 | if (this.resizeDirections.indexOf('b') > -1) {
|
162 | const heightAfterMove =
|
163 | e.pageY - this.getElementTop(this.$refs.content);
|
164 | this.h = Math.max(Math.min(heightAfterMove, this.maxHeight || Infinity), this.minHeight || 1);
|
165 | }
|
166 | if (this.resizeDirections.indexOf('t') > -1) {
|
167 | const heightAfterMove =
|
168 | this.getElementTop(this.$refs.content) + this.h - e.pageY;
|
169 | this.h = Math.max(Math.min(heightAfterMove, this.maxHeight || Infinity), this.minHeight || 1);
|
170 | }
|
171 |
|
172 | this.$emit('resizing', {
|
173 | width: this.w,
|
174 | height: this.h
|
175 | });
|
176 | },
|
177 | handleResizeEnd() {
|
178 | this.resizeDirections = [];
|
179 | document.documentElement.removeEventListener(
|
180 | 'mousemove',
|
181 | this.handleResize
|
182 | );
|
183 | this.$emit('resize-end');
|
184 | }
|
185 | }
|
186 | };
|
187 | </script>
|
188 | <style lang="scss">
|
189 | .c-deformableBox {
|
190 | position: relative;
|
191 | & .ctrl {
|
192 | position: absolute;
|
193 |
|
194 | &-h {
|
195 | width: 4px;
|
196 | height: calc(100% + 4px);
|
197 | top: -2px;
|
198 | cursor: ew-resize;
|
199 | }
|
200 | &-v {
|
201 | width: calc(100% + 4px);
|
202 | height: 4px;
|
203 | left: -2px;
|
204 | cursor: ns-resize;
|
205 | }
|
206 |
|
207 | &-c {
|
208 | width: 4px;
|
209 | height: 4px;
|
210 | }
|
211 |
|
212 | &-t {
|
213 | top: -2px;
|
214 | }
|
215 | &-r {
|
216 | right: -2px;
|
217 | }
|
218 | &-b {
|
219 | bottom: -2px;
|
220 | }
|
221 | &-l {
|
222 | left: -2px;
|
223 | }
|
224 |
|
225 | &.ctrl-t.ctrl-l,
|
226 | &.ctrl-b.ctrl-r {
|
227 | cursor: nwse-resize;
|
228 | }
|
229 |
|
230 | &.ctrl-t.ctrl-r,
|
231 | &.ctrl-b.ctrl-l {
|
232 | cursor: nesw-resize;
|
233 | }
|
234 | }
|
235 | }
|
236 | </style>
|