Vue requirements: realize the function of free positioning of signatures/signatures on the page (essence: drag and drop of elements on the page)

Table of Contents

Chapter 1 Effect Display

Chapter 2 Understanding Tools

2.1 draggable

2.1.1 Understanding draggable

2.1.2 draggable method

2.1.3 Use examples to understand methods

Chapter 3 Effect Realization

3.1 Implementation ideas

3.2 Code implementation

3.2.1 Points involved

3.2.2 Source generation

Chapter 1 Effect Display

  • Effect description: Click on the signature and seal in the left column to initially display them on the right, and then drag to determine their position.

Chapter 2 Understanding Tools

2.1 draggable

2.1.1 Understanding draggable

  • The draggable attribute is new to HTML5.
  • Internet Explorer 9+, Firefox, Opera, Chrome, and Safari browsers support the draggable attribute, but IE8 and below do not support it.
  • The draggable attribute specifies whether the element is draggable
  • usage:
<element draggable="true|false|auto">
true Specifies that the element is draggable.
false Specifies that the element is not draggable.
auto Use the browser’s default features.

2.1.2 draggable method

  • drag: During the dragging process, it is continuously triggered on the dragged node (every few hundred milliseconds).
  • dragstart: When the user starts dragging, it is triggered on the dragged node. The target attribute of this event is the dragged node. Usually in the listening function of this event, the drag data is specified. (commonly used)
  • dragend: Triggered on the dragged node when dragging ends (release the mouse button or press the ESC key). The target attribute of this event is the dragged node. It is triggered on the same node as the dragstart event. No matter whether the drag crosses the window or is canceled midway, the dragend event will always be triggered. (commonly used)
  • dragenter: When dragging into the current node, it is triggered once on the current node. The target attribute of this event is the current node. Usually, in the listening function of this event, you should specify whether the dragged data is allowed to be dropped on the current node. If the current node does not have a listening function for this event, or the listening function does not perform any operation, it means that data is not allowed to be dropped on the current node. The visual display of dragging into the current node is also set in the listening function of this event.
  • dragover: When dragging above the current node, it is continuously triggered on the current node (every few hundred milliseconds). The target attribute of this event is the current node. The difference between this event and the dragenter event is that the dragenter event is triggered when entering the node, and then as long as the node is not left, the dragover event will continue to be triggered.
  • dragleave: When the drag operation leaves the scope of the current node, it is triggered on the current node. The target attribute of this event is the current node. If you want to visually display the current node of the drag-and-drop operation, set it in the listener function of this event.
  • drop: When the dragged node or selected text is released to the target node, it is triggered on the target node. Note that if the current node does not allow drop, this event will not be triggered even if the mouse button is released over the node. If the user presses the ESC key to cancel this operation, this event will not be triggered. The listening function of this event is responsible for fetching the drag data and performing related processing.

2.1.3 Use examples to understand methods

<div class="back_box" ref="back_box">
  <div
    v-if="signShow"
    class="drag_box"
    draggable="true"
    @drag="drag($event)"
    @dragstart="dragstart($event)"
    @dragend="dragend($event)"
    @dragenter="dragenter($event)"
    @dragover="dragover($event)"
    @dragleave="dragleave($event)"
    @drop="drop($event)"
    :style="`left:${elLeft}px;top:${elTop}px`"
  >
</div>

drag(e){
  console.log('drag', e)
},
//Drag start event
dragstart (e) {
  console.log('dragstart', e)
},
//Drag completion event
dragend (e) {
  console.log('dragend', e)
},
dragenter (e) {
  console.log('dragenter', e)
},
dragover (e) {
  console.log('dragover', e)
},
dragleave (e) {
  console.log('dragleave', e)
},
  • See the effect

  • Looking at the output on the right, it is obvious that when the editor starts dragging, the dragstart function is called, and then drag-> dragenter->dragover is called. This process represents the dragging process. It enters the node and is still in the range above the node. The reason why the subsequent output changes from drag->dragover to drag->drag… is also during the dragging process. , after the node moves from the range above the original node to outside the range, only drag is triggered, and dragover is not triggered. Finally, release the mouse, drag is over, and dragend is triggered.
  • As for why drop is not triggered, please see the example in the document, as follows. (The editor’s understanding is that this event should be used in the stored target template, not itself)

HTML draggable attribute | Newbie tutorial

  • The parameters of the event (the editor will not explain them one by one, clientX and clientY are used here to calculate the offset)

Chapter 3 Effect Realization

3.1 Implementation Idea

  • Three elements: signature, signature and template
  • Page layout, preliminary style control, editor’s style: (For convenience, the pictures used in the editor’s template –> remember to replace them when using the source code)

  • Click the signature on the left, and the corresponding signature will appear in the template on the right. The same is true for signatures. Use positioning to make both the signature and the signature in the upper left corner of the template.
  • Record the initial position when dragging starts
  • After dragging, the position will be recorded again when the mouse is released.
  • Calculate the offset using the difference between two positions
  • Finally, use the positioning top and left again to fix the signature and seal to the template.

3.2 Code Implementation

3.2.1 Points involved

  • Use dragstart and dragend to record the initial position and end position of the dragged element
  • Initialize the page using the aspect ratio (normal screen width and height 1920*1080, initialize according to needs)
  • Learn about
  1. clientX: When a mouse event occurs (whether it is onclick, omousemovenmouseover, etc.), the mouse position for the browser (here is the valid area of the browser)
  2. clientY: When a mouse event occurs, the position of the mouse relative to the y-axis of the browser (here is the valid area of the browser);
  3. screenX: When a mouse event occurs, the position of the mouse relative to the x-axis of the monitor screen;
  4. screenY: When the mouse event occurs, the position of the mouse relative to the y-axis of the monitor screen;
  5. offsetX: When a mouse event occurs, the position of the mouse relative to the x-axis of the event source (the event source here is the previous positioned parent tag)
  6. offsetY: When a mouse event occurs, the position of the mouse relative to the event source y-axis (the event source here is the previous positioned parent tag)

3.2.2 Source Code

<template>
  <div class="page">
    <div class="left">
      <div class="title">Signature</div>
      <div class="img" @click="signShow = true">
        <img src="./img/sign.png" alt="" style="height: 50px; border: 1px solid #eee;">
      </div>
      <div class="title">Signature</div>
      <div class="img" @click="sign2Show = true">
        <img src="./img/sign3.png" alt="">
      </div>
    </div>
    <div class="right">
      <div class="drag">
        <div class="back_box" ref="back_box">
          <div
            v-if="signShow"
            class="drag_box"
            draggable="true"
            @dragstart="dragstart($event)"
            @dragend="dragend($event)"
            :style="`left:${elLeft}px;top:${elTop}px`"
          >
          </div>
          <div
            v-if="sign2Show"
            class="drag_box2"
            draggable="true"
            @dragstart="sign2dragstart($event)"
            @dragend="sign2dragend($event)"
            :style="`left:${elLeft2}px;top:${elTop2}px`"
          >
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
  
<script>
export default {
  name: 'HelloWorld',
  data () {
    return {
      initWidth: 0, // The width-adaptive value of the parent element
      initHeight: 0, // The height-adaptive value of the parent element
      startclientX: 0, // X-axis position of the element from the browser before dragging
      startclientY: 0, // Y-axis position of the element from the browser before dragging
      elLeft: 0, //Left offset of the element
      elTop: 0, // Right offset of the element,
      startclientX2: 0, // X-axis position of the element from the browser before dragging
      startclientY2: 0, // Y-axis position of the element from the browser before dragging
      elLeft2: 0, //Left offset of the element
      elTop2: 0, // Right offset of element,
      signShow: false,
      sign2Show: false,
    }
  },
  components: {
  },
  methods: {
    // Page initialization
    initBodySize () {
      this.initWidth = this.$refs.back_box.clientWidth // Get the width of the parent element
      this.initHeight = this.initWidth * (1080 / 1920) // Calculate the height according to the width to achieve self-adaptation
    },
    //Drag start event
    dragstart (e) {
      this.startclientX = e.clientX //Record the initial position of the dragged element
      this.startclientY = e.clientY
    },
    //Drag completion event
    dragend (e) {
      let x = e.clientX - this.startclientX // Calculate offset
      let y = e.clientY - this.startclientY
      this.elLeft + = x // Implement dragging elements to move with offset
      this.elTop + = y
    },
    //Drag start event
    sign2dragstart (e) {
      this.startclientX2 = e.clientX //Record the initial position of the drag element
      this.startclientY2 = e.clientY
    },
    //Drag completion event
    sign2dragend (e) {
      let x = e.clientX - this.startclientX2 // Calculate offset
      let y = e.clientY - this.startclientY2
      this.elLeft2 + = x // Implement dragging elements to move with offset
      this.elTop2 + = y
    }
  },
  mounted () {
    this.initBodySize()
  }
}
</script>
  
<style lang="less" scoped>
.page{
    width: 100vw;
    height: 100vh;
    background-color: #fff;
    display: flex;
    justify-content: flex-start;

    .left{
      width: 20%;
      height: 100%;
      cursor: pointer;
      border-right: 1px solid #eee;

      .title{
        font-size: 14px;
        margin: 16px;
      }

      .img{
        margin: 16px 50px 16px 16px;
        
        img{
          height: 100px;
        }
      }
    }

    .right{
      width: 80%;
      position: relative;
      height: 100%;

      .back_box {
        background-image: url('./img/bg.png');
        background-size: 100% 100%;
        background-repeat: no-repeat;
        width: 70%;
        height: 60%;
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        margin: auto;
      }

      .drag_box {
        width: 150px;
        height: 50px;
        background-image: url('./img/sign.png');
        background-size: 100% 100%;
        background-repeat: no-repeat;
        position: absolute;
        z-index: 10;
      }
      .drag_box2 {
        width: 150px;
        height: 150px;
        background-image: url('./img/sign2.png');
        background-size: 100% 100%;
        background-repeat: no-repeat;
        position: absolute;
        z-index: 10;
      }
    }
}
</style>

Chapter 4 Extensions

  • If everyone also needs to apply this requirement, if the backend does PDF, the frontend and backend should first determine the same template
  • What the front-end needs to do is provide the location of the signature and seal to the back-end (note that the positions passed by the front-end must be proportional. If the front-end template is used by the user to reduce the window, the provided position will not be accurate). The owner of the signature needs to be provided to the back end
  • If the back-end does not take the signed and sealed image itself, it also needs to provide its path to the back-end; if the user can manually control the width and height of the image, the front-end also needs to process the width and height of the image. , obtain the width and height and then provide it to the backend – here the editor has two methods: one is to zoom the image through the mouse wheel to control the image size (onwheel); the other is input control, which the editor will talk about in subsequent articles.
  • If you directly let the front end generate pdf, reading this article may be helpful:

Requirements: The front-end generates a template and stamped pdf file (automatic download of a single file + compressed package download of multiple files and window.print()), and processes a pdf with multiple pages (to ensure that pictures, tables, and text can be fully displayed, Truncation problem solution)_?VE?’s blog-CSDN blog