Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to use Vue3 to imitate Windows window

2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)05/31 Report--

This article introduces the knowledge of "how to use Vue3 to imitate Windows window". In the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!

I. Preface

The main requirement is to make a form that can be dragged and zoomed in and out, similar to the window of the system desktop, the function is draggable and then the width and height can be adjusted by dragging the mouse. After consulting a large number of blog posts, we intend to implement the custom instruction directive based on Vue. The instruction is easy to reference, and the function used does not need to be modified according to the usage scenario, so these two functions can be encapsulated in the instruction. Then based on these two instructions (v-drag, v-resize) to encapsulate a general form container component, the project framework is based on Vue3+TS, because TS is just getting started, so the basic any shuttle, I hope you do not laugh, students who are not familiar with TS can also look at the code to achieve a set of JS version, the main functions are JS basic skills, and framework, language is not related, as long as you can understand the implementation method Simple three swordsmen can also achieve this function. Let's move on to implementing this component.

II. Functional analysis

Figure 2 dom object properties

Event object Properties

Because it is the function of dragging and resizing elements, we need to know several attributes in the JS element, as shown in the figure above, and what we need to know is as follows:

Dom object Properties

OffsetTop: returns the distance from the upper boundary of the current element to the upper boundary of its parent element (offsetParent) [read-only]

OffsetLeft: returns the distance from the left boundary of the current element to the left boundary of its parent element (offsetParent) [read-only]

OffsetWidth: returns the width of the element, including padding+border-width [read-only]

OffsetHeight: returns the height of the element, including padding+border-width [read-only]

ClientWidth: returns the width of the element

ClientHeight: returns the height of the element

Event object Properties

OffsetX: Abscissa relative to the element

OffsetY: relative to the ordinate of the element

ClientX: Abscissa relative to the browser window

ClientY: ordinate relative to the browser window

PageX: relative to the Abscissa of the page

PageY: relative to the ordinate of the page

After you are familiar with these attributes, you can start to drag and resize. The main ideas are as follows:

V-drag mounts the instruction to the first child element, and then realizes it by listening for the event of the child element. The child element first obtains the parent element to facilitate subsequent operation. When the mouse is pressed and triggered, it starts to monitor the mouse movement event. When pressed, you need to record the coordinate values of the x-ray axis of the mouse position (relative to the position of the page). Then record the top,left value of the parent element before dragging, and then get the width and height of the window, subtract the width and height of the parent element itself, and calculate the maximum displacement distance that the parent element can move, beyond the distance can no longer move. Finally, the mouse displacement distance is calculated in real time through mousemove, and the changed displacement distance is updated to the parent element to realize the element movement function.

The v-resize instruction to adjust the width and height of an element is a bit complicated. You need to assign an name attribute to the element as resize. Binding the instruction cannot override the preset name value, and then determine that it is the element by the name attribute. Here, we first define some attribute data that need to be recorded. First, the attribute value of cursor. Cursor is a specified mouse-style attribute in css. There are 8 directions here, so list these attributes separately and map them to top, bottom, left and right. This is easy to understand and easy to operate. Then record the size, position, mouse-down position and change direction of the element before modification. After defining these variables, aggregate some special methods. The first is to obtain the orientation of the mouse and set an inner margin trigger calculation method by calculating the position of the mouse moving within the element. Here, the offset offset is set to 12px, when the mouse is 12px in the horizontal or vertical distance frame of the element. You can get the location of the mouse through getDirection. Then define a computedDistance method, which is used to calculate the distance of the mouse moving back and forth, the last is to calculate the encapsulation of the method of changing the size, the changeSize method obtains the distance of the mouse displacement, and then combined with the direction record value of the movement, the method calls to modify the size, and only half of the method is set to the minimum width and height. Here you can set it through css without writing in js, which can be seen in subsequent component packages. The same way to trigger is to enable the event when onmousedown is enabled. Here you will get whether it is in the range of 8 directions. If the data and orientation are recorded when the button is pressed, and the mobile calculation method is triggered, the data and method will be reset and resized when the mouse button is lifted and released. Mouse style control can be viewed separately, mainly for width and height adjustment has no effect, monitor 8 directions, and then modify the mouse style to make the interaction more friendly.

Instruction encapsulation

V-drag and v-resize directives:

/ / directives.tsimport {App} from "vue"; import {throttle} from "@ / utils" / / the throttling function is no longer displayed. Do not remove it directly. Remove the style reference below and const directives = {drag: {mounted (el: any, binding: any, vnode: any) {/ / do not enable the instruction if false is passed, otherwise start if (! binding.value & & (binding.value?? ")! =") return if true undefined null is not passed / / drag and drop the implementation const odiv = el [XSS _ clean]; el.onmousedown = (eve: any) = > {odiv.style.zIndex = 1; / / the current drag displays eve = eve at the front | | window.event; const mx = eve.pageX; / / the coordinate of mouse click const my = eve.pageY; / / the coordinate of mouse click const dleft = odiv.offsetLeft / / initial position of the window const dtop = odiv.offsetTop; const clientWidth = document.documentElement.clientWidth; / / width of the page const oWidth = odiv.clientWidth; / / width of the window const maxX = clientWidth-oWidth; / / maximum distance that the x axis can move const clientHeight = document.documentElement.clientHeight; / / height of the page const oHeight = odiv.clientHeight; / / height of the window const maxY = clientHeight-oHeight / / the maximum distance that the y axis can move _ document.onmousemove = (e: any) = > {const x = e.pageX; const y = e.pageY; let left = x-mx + dleft; / / the new position after moving let top = y-my + dtop; / / the new position after moving if (left

< 0) left = 0; if (left >

MaxX) left = maxX; if (top

< 0) top = 0; if (top >

MaxY) top = maxY; odiv.style.left = left + "px"; odiv.style.top = top + "px"; odiv.style.marginLeft = 0; odiv.style.marginTop = 0;}; _ document.onmouseup = () = > {_ document.onmousemove = null;};} }, resize: {mounted (el: any, binding: any, vnode: any) {/ / if false is passed, the instruction is not enabled, otherwise, if true undefined null is not passed, start if (! binding.value & & (binding.value? ")! =") return / / set name to resize for the selected element binding name attribute to distinguish that only this element can be scaled el.name = "resize" / / eight directions correspond to const mouseDir = {top: "n-resize", / / Upper bottom: "s-resize", / / Lower left: "w-resize", / / left right: "e-resize", / / right topright: "ne-resize", / / Top right topleft: "nw-resize" / / Top left bottomleft: "sw-resize", / / bottom left bottomright: "se-resize" / / bottom right} / / record the original position size of the modified element and change direction const pos = {width: 0, height: 0, top: 0, left: 0, x: 0, y: 0, dir: ""}; / / get the mouse orientation const getDirection = (ev: any): string = > {let dir = "; const xP = ev.offsetX; const yP = ev.offsetY Const offset = 12; / / what is the inner margin that triggers / / calculates which azimuth if (yP)

< offset) dir += "top"; else if (yP >

Ev.toElement.clientHeight-offset) dir + = "bottom"; if (xP

< offset) dir += "left"; else if (xP >

Ev.toElement.clientWidth-offset) dir + = "right"; return dir;}; / / calculate the moving distance const computedDistance = (pre: any, cur: any): any = > {return [cur.x-pre.x, cur.y-pre.y];}; / / data reset const resetData = () = > {pos.width = 0; pos.height = 0 Pos.top = 0; pos.left = 0; pos.x = 0; pos.y = 0; pos.dir = ""; _ document.onmousemove = null;} / / change the size method const changeSize = (e: any) = > {/ / the difference between two points, calculate the mouse displacement value const [disX, disY] = computedDistance ({x: pos.x, y: pos.y}, {x: e.pageX, y: e.pageY}); const addWid = pos.width + disX; const subWid = pos.width-disX Const addHig = pos.height + disY; const subHig = pos.height-disY; const minX = 200; const minY = 200; / / change method const top = () > {if (subHig {el.style.height = addHig + "px";} / / under const left = () = > {if (subWid {el.style.width = addWid + "px";} / / right / / change orientation and its modification method mapping const doFn = {top, / / upper bottom, / / lower left, / / left right, / / right topright: () = > {top (); right () }, / / Top right topleft: () = > {top (); left ();}, / / Top left bottomleft: () = > {bottom (); left ();}, / / bottom left bottomright: () = > {bottom (); right () } / / bottom right}; doFn [pos.dir] ();}; / / change event triggered by mouse press el.onmousedown = (e: any) = > {if (e.target.name! = = "resize") return; let d = getDirection (e) / / size modification if (mouseDir [d]) {pos.width = el.clientWidth; pos.height = el.clientHeight; pos.top = el.offsetLeft; pos.x = e.pageX; pos.y = e.pageY; pos.dir = d when the position is four edges and four corners _ document.onmousemove = changeSize;} _ document.onmouseup = resetData;}; / * * Mouse style change * / const changeShowCursor = throttle ((e: any) = > {e.preventDefault (); el.style.cursor = "default"; / / restore the mouse default if (e.target.name! = = "resize") return first / / modify the mouse display effect let d = getDirection (e); / / determine the movement in a certain direction el.style.cursor = mouseDir [d] | | "default";}}); / / Throttle 0.2s el.onmousemove = changeShowCursor; / / listen for mouse events moving on the root element} Export default (app: App) = > {/ / bulk registration instruction Object.entries (directives). ForEach (([key, fn]) = > {app.directive (key, fn);});}

The above two instructions, mainly to get the element itself, use the native js method to manipulate the element, it is important to note that v-drag is bound to the first child element of the root element (resizing the parent element), while v-resize is the binding element itself (resizing the element itself). After completing the writing of the two instructions, you can register either locally or globally. Here I use the global registration method.

/ / main.ts global registration import {createApp} from "vue"; import App from ". / App.vue"; import registerDirectives from "@ / directives"; const app = createApp (App); registerDirectives (app); app.mount ("# app")

After the global registration instruction is completed, you can use these two instructions in the component, and then we write a more general pop-up window component, which can be opened and closed, and can be dragged and resized.

IV. General component encapsulation

Here the process of encapsulating components and Vue2 is not much different, but the preparation of components using Vue3 combined API writing, other aspects are basically the same, for vue css transition effect 2 and 3 versions have some differences, here please consult the Vue3 documentation, the rest is to define some properties that need to be modified, use props to receive, and set default values, so that components can be customized to modify and expand as far as possible.

The following is a pop-up window component encapsulated after using two instructions. When setting the form css style drag-dialog, it uses min-width: 200pxmitmin: 100vh. Here, through the restrictions on width and height, you do not have to use js to limit the resizing of the form. It was mentioned before when writing the v-resize instruction that js is used to control the minimum and maximum display range of the display form. Here, I think it is more convenient to write through css.

Import {ref} from "vue"; / / props incoming data type constraint interface Props {modelValue: boolean; / / controls whether the form is displayed or not width?: string; / / default width-set head height width best input variable height?: string; / / default height headHeight?: string; / / default control bar height headStyle?: string; / / control bar style mainStyle?: string / / main content area style resizeAble?: boolean | string; / / whether you can resize dragAble?: boolean by default | string; / / whether you can drag and drop default draggable closeShow?: boolean; / / turn off control display default does not display fullShow?: boolean / / full screen control display does not display} / * * component adjustment parameter default value * / const props = withDefaults (defineProps (), {modelValue: true, width: "500px", height: "60vh", headHeight: "35px", headStyle: ", mainStyle:", resizeAble: ", dragAble:", closeShow: false, fullShow: false}) / / form record data type constraint interface recordType {width: number; height: number; top: number; left: number; fill: boolean;} / / record original size const recordBox: recordType = {width: 0, height: 0, top: 0, left: 0, fill: false}; / / get window entity const dragWin: any = ref (null); / / event definition const emits = defineEmits (["update:modelValue"]) / * * method definition * / / Internal control window switch const controlDialog = () = > {emits ("update:modelValue",! props.modelValue);}; / / full-screen control const fullScreen = () = > {const tmp = dragWin.value; const style = dragWin.value.style / / if the wide style is zoomed out or enlarged manually, it means that it is not full screen. Set the status to false if (! style.width | | style.width! = = "100vw") {recordBox.fill = false;} / / full screen or restore if (recordBox.fill) {style.width = `$ {recordBox.width} px`; style.height = `$ {recordBox.height} px`; style.top = `$ {recordBox.top} px` Style.left = `${recordBox.left} px`;} else {/ / record the original style recordBox.width = tmp.offsetWidth; recordBox.height = tmp.offsetHeight; recordBox.top = tmp.offsetTop; recordBox.left = tmp.offsetLeft; / / full-screen style style.width = "100vw"; style.height = "100vh"; style.top = "0px"; style.left = "0px";} recordBox.fill =! recordBox.fill / / full screen status transition}; / * disable text selection * / .ban-select-font {- moz-user-select: none; / * Firefox * /-webkit-user-select: none; / * webkit browser * /-ms-user-select: none; / * IE10*/-khtml-user-select: none; / * early browser * / user-select: none;}. Drag-dialog {position: fixed Width: v-bind ("props.width"); height: v-bind ("props.height"); left: calc (50-v-bind ("props.width") / 2); top: calc (50-v-bind ("props.height") / 2); box-sizing: border-box; padding: 8px; overflow: hidden; color: # fff; min-width: 200px; min-height: 200px; max-width: 100vw; max-height: 100vh Background-color: # 313438cc;}. Drag-bar {width: 100%; cursor: move; height: v-bind ("props.headHeight"); border-bottom: 1px solid # fff; box-sizing: border-box; padding: 1px 2px 9px;} .drag-btn {width: 25px; height: 25px; float: right; cursor: pointer; margin-left: 5px; border-radius: 50%;} .drag-full {background-color: # 28c940b8 } .drag-full:hover {background-color: # 28c93f;} .drag-close {background-color: # f2473ec7;} .drag-close:hover {background-color: # f2473e;} .drag-main {width: 100%; height: calc (100%-v-bind ("props.headHeight")); box-sizing: border-box; overflow: auto; font-size: 13px; line-height: 1.6 } / * vue taps in and out of style * / .drag-win-enter-from,.drag-win-leave-to {opacity: 0; transform: scale (0);} .drag-win-enter-to,.drag-win-leave-from {opacity: 1;} .drag-win-enter-active,.drag-win-leave-active {transition: all 0.5s ease;}

There are still some problems in writing this component. For example, if the top and left properties are set when opening and closing, it will change back to the position defined during initialization. Here, you can refer to zooming in and out to record the location of the window and make a record of closing and opening the form. I did not write the relevant code here, which mainly has little impact on my project. So students in need can try how to write it for themselves (ps: mainly lazy).

After writing the component, you can reference and register, either globally or locally. Here I use local reference registration, and then write two small examples to use the encapsulated component. You can view the encapsulated props of the component, customize the configuration of the component through the properties inside, add or subtract the required functions, and then there are two style, one is the header style headStyle, and the other is the body style mainStyle. The outermost style can be adjusted directly by writing style when referencing, and then the width and height of the form are best adjusted by passing string variables, because it also involves the calculation of the specific location in the container where the form is located, and the default is to center both horizontally and vertically. Here is the reference code:

Example demonstration: {{btnName}} box controls me as the head I am the content area import IsDragDialog from "@ / components/IsDragDialog.vue"; / / because I am using script setup, the component will directly register import {computed} from "@ vue/reactivity"; import {ref} from "vue"; const show = ref (true); const box = ref (true); const control = () = > {show.value =! show.value;} Const btnName = computed (() = > {return show.value? `close window`: `Open window `;}); "how to use Vue3 to imitate Windows window" ends here. Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report