admin管理员组

文章数量:1405156

Hi i'm facing the very challenging & interesting problem of scroll during selection of items with mouse drag in both direction i,e up and down

here is a screen shot

Here is my code : =/src/overridden/Drag-select.vue

Drag-select.vue is the file where drag selection logic is written.

which fires change when files selection gets changed.

I receive those change event here <drag-select-container @change="dragSelect($event)">

Edit 1: after IVO GELO ment

I have added inside drag() function

   try{
      let containerEl = document.querySelector('#wrapping_container');
      let container = containerEl.getBoundingClientRect();
      if(box.top > (container.top )){
          containerEl.scrollTop = box.top - 50;
          return true;
      }
    }catch(e){
      console.log(e);
    } 

Edit code here: =/src/overridden/Drag-select.vue

It is very interesting and challenging problem so

Please help me thanks in advance!!

Hi i'm facing the very challenging & interesting problem of scroll during selection of items with mouse drag in both direction i,e up and down

here is a screen shot

Here is my code : https://codesandbox.io/s/select-ivwq8j?file=/src/overridden/Drag-select.vue

Drag-select.vue is the file where drag selection logic is written.

which fires change when files selection gets changed.

I receive those change event here <drag-select-container @change="dragSelect($event)">

Edit 1: after IVO GELO ment

I have added inside drag() function

   try{
      let containerEl = document.querySelector('#wrapping_container');
      let container = containerEl.getBoundingClientRect();
      if(box.top > (container.top )){
          containerEl.scrollTop = box.top - 50;
          return true;
      }
    }catch(e){
      console.log(e);
    } 

Edit code here: https://codesandbox.io/s/select-ivwq8j?file=/src/overridden/Drag-select.vue

It is very interesting and challenging problem so

Please help me thanks in advance!!

Share Improve this question edited Apr 6, 2022 at 10:27 EaBengaluru asked Apr 4, 2022 at 11:20 EaBengaluruEaBengaluru 892 gold badges30 silver badges73 bronze badges 3
  • Have you tried dom-AutoScroller - codepen.io/michaelbowlin/pen/GxVwvM – IVO GELOV Commented Apr 4, 2022 at 17:25
  • @IVOGELOV, I'm ready to use any other plugin to work with Drag-Select. What i meant is it must work together – EaBengaluru Commented Apr 4, 2022 at 17:37
  • dom-AutoScroller only needs to know whether there is a drag operation at the moment its callback is being run - so that it can scroll the container. The scrolling will happen with configurable speed and the distance from the container edges is also configurable. – IVO GELOV Commented Apr 6, 2022 at 6:22
Add a ment  | 

2 Answers 2

Reset to default 5 +100

I remend you use DragSelect js library.

Working Demo

https://codesandbox.io/s/select-forked-tnmnwk?file=/src/ponents/HelloWorld.vue

mounted() {
  const vm = this;

  const ds = new DragSelect({
    selectables: document.querySelectorAll(".selectable-nodes"),
    area: document.getElementById("area"),
    draggability: false,
  });

  ds.subscribe("elementselect", function ({ item }) {
    vm.selectedItems.push();
  });

  ds.subscribe("elementunselect", function ({ item }) {
    const index = vm.selectedItems.indexOf(item.getAttribute("customAttribute"));
    if (index > -1) {
      vm.selectedItems.splice(index, 1);
    }
  });
}

I found a solution for your question. I rewrite your code in a pletely different way. Here is a demo that you can test it. Actually it contains two main ponent. Parent ponent that is called "HelloCompo" and its code es here:

HelloCompo:

<template>
  <!-- This is a ponent that uses "MyDrag" ponent. -->
  <div id="wrapping_container" class="hello">
    <!-- Here we insert "MyDrag" ponent that emits custom "change" event when the selection of element is changed according to user drag. -->
    <my-drag @change="dragSelect">
      <div
          class="item"
          :class="{ selected: ( index >= minMax[1] && index <= minMax[0] ) }"
          :key="item"
          v-for="(item, index) in [1, 2, 3, 4,5,6,7,8,9,10,11,12,13,14,15,16]"
      >
        {{ item }}
      </div>
    </my-drag>
  </div>
</template>

<script>
import MyDrag from "./MyDrag";
export default {
  name: "HelloCompo",
  ponents: {MyDrag},
  data() {
    return {
      selectedItems: [],
    };
  },

  puted: {
    minMax: function () {
      /* This puted property uses data returned by "MyDrag" ponent to define the maximum and minimum range to accept "selected" class. */
      let max = -1;
      let min = -1;
      if (this.selectedItems.length > 0) {
        max = Math.max(...this.selectedItems);
        min = Math.min(...this.selectedItems);
      }
      return [max-1, min-1]
    }
  },

  methods: {
    dragSelect: function (selectedList) {
      // console.log(selectedList);
      /* this Method is used to set "selectedItems" data after each change in selected drag. */
      this.selectedItems = selectedList;
    }
  },
}
</script>

<style scoped>

.item {
  display: block;
  width: 230px;
  height: 130px;
  background: orange;
  margin-top: 9px;
  line-height: 23px;
  color: #fff;
}
.selected {
  background: red !important;
}
#wrapping_container{
  background:#e7e7e7;
}

</style>

And child ponent that is called "MyDrag":

MyDrag:

<template>
  <section id="parentAll">
    <!-- I used "@mousedown" and ... for calling methods instead of using all functions in mounted hook. -->
    <div class="minHr" ref="container" @mousedown="startDrag" @mouseup="endDrag" @mousemove="whileDrag">
      <slot></slot>
    </div>

    <!-- This canvas is shown only when the user is dragging on the page. -->
    <canvas ref="myCanvas" v-if="showCanvas" @mouseup="endDrag" @mousemove="whileDrag"></canvas>

  </section>
</template>

<script>
export default {
  name: "MyDrag",
  data() {
    return {
      dragStatus: false, // used for detecting mouse drag
      childrenArr: [], // used to store the information of children of 'ref="container"' that es from "slot"
      startEvent: null, // used to detect mouse position on mousedown
      endEvent: null, // used to detect mouse position on mouseup
      direction: "topBottom", // used to detect the direction of dragging
      selectedArr: [], // used to store the selected "divs" after dragging
      heightContainer: null, // used to detect the height of 'ref="container"' dynamically
      widthContainer: null, // used to detect the width of 'ref="container"' dynamically
      /* These data used to draw rectangle on canvas while the user is dragging */
      rect: {
        startX: null,
        startY: null,
        w: null,
        h: null
      },
      startDragData: {
        x: null,
        y: null
      },
      whileDragData: {
        x: null,
        y: null,
        CLY: null
      },
      showCanvas: false // used to show or hide <canvas></canvas>
    }
  },
  methods: {
    childrenInfo: function () {
      /* This method is called on "mounted()" hook to gather information about children of 'ref="container"' that es from <slot></slot> */
      const { container } = this.$refs;
      const stylesDiv = window.getComputedStyle(container, null);
      this.widthContainer = parseFloat( stylesDiv.getPropertyValue("width") );
      this.heightContainer = parseFloat( stylesDiv.getPropertyValue("height") );
      let children = container.childNodes;

      children.forEach((item, index) => {

        let childObj = {
          offsetTop: item.offsetParent.offsetTop + item.offsetTop,
          offsetHeight: item.offsetHeight
        }

        this.childrenArr.push(childObj);
      })
    },

    startDrag: function (event) {
      /* This method is called at mousedown and detect the click or right click. after that it sets some data like "showCanvas". */
        if(event.button === 0) {
          this.dragStatus = true;
          this.startEvent = event.pageY;
          this.startDragData.x = event.pageX;
          this.startDragData.y = event.pageY;
          this.showCanvas = false;
        }
    },

    whileDrag: async function (event) {
      /* This method is called when the user is dragging. Because I want to be confident about showing <canvas> before doing other parts of code, I used "async" function for this method. */
      if (this.dragStatus) {
        await this.showMethod();
        console.log("dragging");
        this.whileDragData.x = event.pageX;
        this.whileDragData.y = event.pageY;
        this.whileDragData.CLY = event.clientY
        await this.canvasMethod();
      } else {
        this.showCanvas = false;
      }
    },

    endDrag: function (event) {
      /* This method is called at mouseup. After that it calls other methods to calculate the "divs" that were selected by user. */
      if(event.button === 0) {
        console.log("end drag");
        this.dragStatus = false;
        this.showCanvas = false;
        this.endEvent = event.pageY;
        this.calculateDirection();
        this.calculateSelected();
      }
    },

    showMethod: function () {
      /* This method is used to set "showCanvas" data at proper time. */
      this.showCanvas = true;
    },

    calculateDirection: function () {
      /* This method is used to detect the direction of dragging. */
      if (this.startEvent <= this.endEvent) {
        this.direction = "topBottom";
      } else {
        this.direction = "bottomTop";
      }
    },

    calculateSelected: function () {
      /* This method is responsible to find out which "divs" were selected while the user was dragging. After that it emits "this.selectedArr" data to the parent ponent. */
      this.selectedArr = [];
      let endIndex = null;
      let startIndex = null;

      this.childrenArr.forEach( (item, index) => {

        if ( (item.offsetTop < this.endEvent) && ( (item.offsetTop + item.offsetHeight) > this.endEvent) ) {
          endIndex = index;
          console.log(endIndex);
        }

        if ( (item.offsetTop < this.startEvent) && ( (item.offsetTop + item.offsetHeight) > this.startEvent) ) {
          startIndex = index;
          console.log(startIndex);
        }

      });

      if( endIndex !== null ) {
        if (this.direction === "topBottom") {
          for (let i = startIndex; i <= endIndex; i++ ) {
            this.selectedArr.push(i+1);
          }
        } else {
          for (let i = startIndex; i >= endIndex; i-- ) {
            this.selectedArr.push(i+1);
          }
        }
      }

      this.$emit("change", this.selectedArr);
    },

    canvasMethod: function () {
      /* This method is used to show a rectangle when user drags on page. It also could understand that the user is near the top or bottom of page, and then it scrolls the page when the user is dragging. */
      const { myCanvas } = this.$refs;
      myCanvas.width = this.widthContainer;
      myCanvas.height = this.heightContainer;
      const html = document.documentElement;

      let ctx = myCanvas.getContext('2d');

      this.rect.startX = this.startDragData.x - myCanvas.offsetParent.offsetLeft;
      this.rect.startY = this.startDragData.y - myCanvas.offsetParent.offsetTop;

      this.rect.w = (this.whileDragData.x - myCanvas.offsetParent.offsetLeft) - this.rect.startX;
      this.rect.h = (this.whileDragData.y - myCanvas.offsetParent.offsetTop) - this.rect.startY ;

      if ( Math.abs(this.whileDragData.CLY - window.innerHeight) <  12) {
        console.log("near");
        html.scrollTop += 25;
      }
      if ( Math.abs(this.whileDragData.CLY) < 12 ) {
        html.scrollTop -= 25;
      }

      if ( (this.whileDragData.y > (myCanvas.offsetParent.offsetTop + myCanvas.offsetHeight) - 25) || (this.whileDragData.y < myCanvas.offsetParent.offsetTop + 25) ) {
        ctx.clearRect(0,0,myCanvas.width,myCanvas.height);
      }

      ctx.clearRect(0,0,myCanvas.width,myCanvas.height);
      ctx.setLineDash([6]);
      ctx.strokeRect(this.rect.startX, this.rect.startY, this.rect.w, this.rect.h);

    },

  },

  mounted() {
    this.childrenInfo();
  }
}
</script>

<style scoped>
.minHr {
  min-height: 900px;
}

#parentAll {
  position: relative;
}

#parentAll canvas {
  position: absolute;
  top: 0;
  left: 0;
}
</style>

I used a <canvas> to draw rectangle when the user is dragging. The main difference of my code with your is that it shows the selected items after the dragging process was finished. It works in both upward dragging and downward dragging and also when the user is want to continue dragging beyond the window area (scrolling).

本文标签: javascriptHow to scroll while selecting items with mouse in vueStack Overflow