Browse Source

feat: 增加拖拽组件

jerry 5 years ago
parent
commit
8155653cb9

+ 10 - 0
src/config.json

@@ -603,6 +603,16 @@
       "sort": "3",
       "sort": "3",
       "showDemo": false,
       "showDemo": false,
       "author": "szg2008"
       "author": "szg2008"
+    },
+    {
+      "version": "1.0.0",
+      "name": "Drag",
+      "chnName": "拖拽",
+      "desc": "实现可拖拽的任意元素",
+      "type": "component",
+      "sort": "5",
+      "showDemo": true,
+      "author": "张宇"
     }
     }
   ]
   ]
 }
 }

+ 4 - 1
src/nutui.js

@@ -118,6 +118,8 @@ import SubSideNavBar from "./packages/subsidenavbar/index.js";
 import "./packages/subsidenavbar/subsidenavbar.scss";
 import "./packages/subsidenavbar/subsidenavbar.scss";
 import SideNavBarItem from "./packages/sidenavbaritem/index.js";
 import SideNavBarItem from "./packages/sidenavbaritem/index.js";
 import "./packages/sidenavbaritem/sidenavbaritem.scss";
 import "./packages/sidenavbaritem/sidenavbaritem.scss";
+import Drag from "./packages/drag/index.js";
+import "./packages/drag/drag.scss";
 
 
 const packages = {
 const packages = {
   Cell,
   Cell,
@@ -177,7 +179,8 @@ const packages = {
   TimeLineItem: TimeLineItem,
   TimeLineItem: TimeLineItem,
   SideNavBar: SideNavBar,
   SideNavBar: SideNavBar,
   SubSideNavBar: SubSideNavBar,
   SubSideNavBar: SubSideNavBar,
-  SideNavBarItem: SideNavBarItem
+  SideNavBarItem: SideNavBarItem,
+  Drag: Drag
 };
 };
 
 
 const components = {};
 const components = {};

+ 74 - 0
src/packages/drag/demo.vue

@@ -0,0 +1,74 @@
+<template>
+  <div class="demo-list">
+    <nut-noticebar :closeMode="true" v-if="!isMobile"
+      >此 Demo 仅能在移动端浏览器体验,建议在 Android 或 iOS 设备上体验。
+    </nut-noticebar>
+    <h4>基本用法</h4>
+    <nut-drag name="demo1" :style="{ top: '100px', left: '8px' }">
+      <div class="touch-dom">基本用法</div>
+    </nut-drag>
+    <h4 :style="{ top: '150px' }">限制拖拽方向</h4>
+    <nut-drag name="demo2" direction="x" :style="{ top: '200px', left: '8px' }">
+      <div class="touch-dom">只能在X轴拖动</div>
+    </nut-drag>
+    <h4 :style="{ top: '250px' }">自动吸边</h4>
+    <nut-drag
+      name="demo3"
+      direction="x"
+      :attract="true"
+      :style="{ top: '300px', left: '8px' }"
+    >
+      <div class="touch-dom">拖动我</div>
+    </nut-drag>
+    <h4 :style="{ top: '350px' }">限制拖动边界</h4>
+    <div class="drag-boundary"></div>
+    <nut-drag
+      :boundary="{ top: 401, left: 9, bottom: bottom(), right: right() }"
+      name="demo4"
+      :attract="true"
+      :style="{ top: '400px', left: '8px' }"
+    >
+      <div class="touch-dom">拖动我</div>
+    </nut-drag>
+  </div>
+</template>
+
+<script>
+export default {
+  components: {},
+  data() {
+    return {};
+  },
+  methods: {
+    right() {
+      return document.documentElement.clientWidth - 300 - 9;
+    },
+    bottom() {
+      return document.documentElement.clientHeight - 601;
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.touch-dom {
+  height: 30px;
+  padding: 10px;
+  line-height: 30px;
+  text-align: center;
+  color: #fff;
+  background: red;
+  overflow: hidden;
+}
+h4 {
+  position: absolute;
+}
+.drag-boundary {
+  position: absolute;
+  top: 400px;
+  left: 8px;
+  width: 300px;
+  height: 200px;
+  border: 1px solid red;
+}
+</style>

+ 0 - 0
src/packages/drag/doc.md


+ 9 - 0
src/packages/drag/drag.scss

@@ -0,0 +1,9 @@
+.nut-drag {
+  position: fixed;
+  display: inline-block;
+  top: 0;
+  right: 0;
+  z-index: 9997 !important;
+  width: fit-content;
+  height: fit-content;
+}

+ 206 - 0
src/packages/drag/drag.vue

@@ -0,0 +1,206 @@
+<template>
+  <div class="nut-drag" @touchstart="touchStart($event)">
+    <slot></slot>
+  </div>
+</template>
+<script>
+import requestAniFrame from "../../utils/raf.js";
+/**
+ * @module drag
+ * @description 拖拽组件,用于页面中需要拖拽的元素
+ * @vue-prop {Boolean} [attract=false] - 拖拽元素是否需要自动吸边
+ * @vue-prop {String} [direction='all'] - 拖拽元素的拖拽方向
+ * @vue-prop {Number | String} [zIndex=11] - 拖拽元素的堆叠顺序
+ * @vue-prop {Object} [boundary={top: 0,left: 0,right: 0,bottom: 0}] - 拖拽元素的拖拽边界
+ * @vue-data {Number} elWidth 拖拽元素的宽度
+ * @vue-data {Number} elHeight 拖拽元素的高度
+ * @vue-data {Number} screenWidth 屏幕的宽度
+ * @vue-data {Number} screenHeight 屏幕的高度
+ * @vue-data {Number} startTop 拖拽元素距离顶部的距离
+ * @vue-data {Number} startLeft 拖拽元素距离左侧的距离
+ * @vue-data {Object} position 鼠标点击的位置,包含距离x轴和y轴的距离
+ */
+export default {
+  name: "nut-drag",
+  props: {
+    attract: {
+      type: Boolean,
+      default: false,
+    },
+    direction: {
+      type: String,
+      default: "all",
+    },
+    zIndex: {
+      type: [Number, String],
+      default: 11,
+    },
+    boundary: {
+      type: Object,
+      default: function() {
+        return {
+          top: 0,
+          left: 0,
+          right: 0,
+          bottom: 0,
+        };
+      },
+    },
+  },
+  data() {
+    return {
+      elWidth: 0,
+      elHeight: 0,
+      screenWidth: 0,
+      screenHeight: 0,
+      startTop: 0,
+      startLeft: 0,
+      position: { x: 0, y: 0 },
+    };
+  },
+  methods: {
+    /**
+     * 获取拖拽元素的属性和屏幕的宽高,初始化拖拽元素的位置
+     */
+    getElementInfo() {
+      const el = this.$el;
+      const domElem = document.documentElement;
+      this.elWidth = el.offsetWidth;
+      this.elHeight = el.offsetHeight;
+      this.screenWidth = domElem.clientWidth;
+      this.screenHeight = domElem.clientHeight;
+      el.style.zIndex = this.zIndex;
+      if (this.boundary.left) {
+        el.style.left = this.boundary.left + "px";
+      } else {
+        el.style.right = this.boundary.right + "px";
+      }
+      if (this.boundary.top) {
+        el.style.top = this.boundary.top + "px";
+      } else {
+        el.style.bottom = this.boundary.bottom + "px";
+      }
+    },
+    touchStart(e) {
+      const target = e.currentTarget;
+      this.startTop = target.offsetTop; // 元素距离顶部的距离
+      this.startLeft = target.offsetLeft; // 元素距离左侧的距离
+      this.position.x = e.touches[0].clientX; // 鼠标点击的x轴的距离
+      this.position.y = e.touches[0].clientY; // 鼠标点击的y轴的距离
+      this.$el.addEventListener("touchmove", this.touchMove, false);
+      this.$el.addEventListener("touchend", this.touchEnd, false);
+    },
+    touchMove(e) {
+      e.preventDefault();
+      const target = e.currentTarget;
+      if (e.targetTouches.length == 1) {
+        const touch = e.targetTouches[0];
+        this.nx = touch.clientX - this.position.x;
+        this.ny = touch.clientY - this.position.y;
+        this.xPum = this.startLeft + this.nx;
+        this.yPum = this.startTop + this.ny;
+        const rightLocation =
+          this.screenWidth - this.elWidth - this.boundary.right;
+        // 限制左右拖拽边界
+        if (Math.abs(this.xPum) > rightLocation) {
+          this.xPum = rightLocation;
+        } else if (this.xPum <= this.boundary.left) {
+          this.xPum = this.boundary.left;
+        }
+        // 限制上下拖拽边界
+        if (this.yPum < this.boundary.top) {
+          this.yPum = this.boundary.top;
+        } else if (
+          this.yPum >
+          this.screenHeight - this.elHeight - this.boundary.bottom
+        ) {
+          this.yPum = this.screenHeight - this.elHeight - this.boundary.bottom;
+        }
+        if (this.direction != "y") {
+          target.style.left = this.xPum + "px";
+        }
+        if (this.direction != "x") {
+          target.style.top = this.yPum + "px";
+        }
+      }
+    },
+    touchEnd(e) {
+      const target = e.currentTarget;
+      const touch = e.changedTouches[0];
+      let currX = touch.clientX;
+      const rightLocation =
+        this.screenWidth - this.elWidth - this.boundary.right;
+      if (currX > rightLocation) {
+        currX = rightLocation;
+        // console.log('往右划出边界');
+      } else if (currX < this.boundary.left) {
+        currX = this.boundary.left;
+        // console.log('往左划出边界');
+      } else {
+        currX =
+          currX < this.screenWidth / 2 ? this.boundary.left : rightLocation;
+        // console.log('在边界内滑动');
+      }
+      if (this.direction != "y" && this.attract) {
+        if (currX < this.screenWidth / 2) {
+          this.goLeft(target);
+        } else {
+          this.goRight(target, rightLocation);
+        }
+      }
+      if (this.direction != "x") {
+        target.style.top = this.yPum + "px";
+      }
+    },
+    goLeft(target) {
+      if (this.boundary.left) {
+        if (target.style.left.split("px")[0] > this.boundary.left) {
+          target.style.left = target.style.left.split("px")[0] - 10 + "px";
+          requestAniFrame(() => {
+            this.goLeft(target);
+          });
+        } else {
+          target.style.left = `${this.boundary.left}px`;
+        }
+      } else {
+        if (target.style.left.split("px")[0] > 10) {
+          target.style.left = target.style.left.split("px")[0] - 10 + "px";
+          requestAniFrame(() => {
+            this.goLeft(target);
+          });
+        } else {
+          target.style.left = "0px";
+        }
+      }
+    },
+    goRight(target, rightLocation) {
+      if (rightLocation - parseInt(target.style.left.split("px")[0]) > 10) {
+        target.style.left =
+          parseInt(target.style.left.split("px")[0]) + 10 + "px";
+        requestAniFrame(() => {
+          this.goRight(target, rightLocation);
+        });
+      } else {
+        target.style.left = rightLocation + "px";
+      }
+    },
+  },
+  mounted() {
+    this.getElementInfo();
+  },
+  activated() {
+    if (this.keepAlive) {
+      this.keepAlive = false;
+    }
+  },
+  deactivated() {
+    this.keepAlive = true;
+    this.$el.removeEventListener("touchmove", this.handleScroll, false);
+    this.$el.removeEventListener("touchend", this.handleScroll, false);
+  },
+  destroyed() {
+    this.$el.removeEventListener("touchmove", this.handleScroll, false);
+    this.$el.removeEventListener("touchend", this.handleScroll, false);
+  },
+};
+</script>

+ 8 - 0
src/packages/drag/index.js

@@ -0,0 +1,8 @@
+import Drag from './drag.vue';
+import './drag.scss';
+
+Drag.install = function(Vue) {
+  Vue.component(Drag.name, Drag);
+};
+
+export default Drag

+ 1 - 0
types/nutui.d.ts

@@ -78,3 +78,4 @@ export declare class TimeLineItem extends UIComponent {}
 export declare class SideNavBar extends UIComponent {}
 export declare class SideNavBar extends UIComponent {}
 export declare class SubSideNavBar extends UIComponent {}
 export declare class SubSideNavBar extends UIComponent {}
 export declare class SideNavBarItem extends UIComponent {}
 export declare class SideNavBarItem extends UIComponent {}
+export declare class Drag extends UIComponent {}