Browse Source

feat: 新增gesture手势组件

dsj 5 years ago
parent
commit
8982420834

+ 1 - 0
package.json

@@ -51,6 +51,7 @@
     "@nutui/cli": "^0.2.2",
     "babel-plugin-istanbul": "^6.0.0",
     "gsap": "^3.2.6",
+    "hammerjs": "^2.0.8",
     "husky": "^3.0.0",
     "vue-lazyload": "^1.3.3",
     "vue-qr": "^2.2.1"

+ 10 - 0
src/config.json

@@ -642,6 +642,16 @@
       "sort": "0",
       "showDemo": true,
       "author": "yumingming"
+    },
+    {
+      "version": "1.0.0",
+      "name": "Gesture",
+      "type": "directive",
+      "chnName": "手势组件",
+      "desc": "基于hammer.js封装,支持移动端各种手势操作",
+      "sort": "2",
+      "showDemo": true,
+      "author": "dsj"
     }
   ]
 }

+ 8 - 5
src/nutui.js

@@ -124,8 +124,10 @@ import './packages/drag/drag.scss'; // import VueQr from "./packages/qart/index.
 
 import Address from './packages/address/index.js';
 import './packages/address/address.scss';
-import CountUp from "./packages/countup/index.js";
-import "./packages/countup/countup.scss";
+import CountUp from './packages/countup/index.js';
+import './packages/countup/countup.scss';
+import Gesture from './packages/gesture/index.js';
+import './packages/gesture/gesture.scss';
 
 const packages = {
   Cell,
@@ -188,7 +190,8 @@ const packages = {
   SideNavBarItem: SideNavBarItem,
   Drag: Drag,
   Address: Address,
-  CountUp: CountUp
+  CountUp: CountUp,
+  Gesture: Gesture
 };
 
 const components = {};
@@ -216,7 +219,7 @@ pkgList.map(item => {
   }
 });
 
-const install = function (Vue, opts = {}) {
+const install = function(Vue, opts = {}) {
   if (install.installed) return;
 
   if (opts.locale) {
@@ -272,4 +275,4 @@ export default {
   ...filters,
   ...directives,
   ...methods
-};
+};

+ 80 - 0
src/packages/gesture/demo.vue

@@ -0,0 +1,80 @@
+<template>
+  <div class="container">
+    <div class="item" v-gesture:pan="test1" v-gesture:panend="test2" :style="{ left: left, top: top }">
+      <h4>基本操作</h4>
+      <p>平移</p>
+    </div>
+
+    <div class="item" v-gesture:pan.left="test3">
+      <h4>指定方向平移</h4>
+      <p>左移</p>
+    </div>
+
+    <div class="item" v-gesture:swipe.horizontal="test3">
+      <h4>指定方向滑动</h4>
+      <p>swipe</p>
+    </div>
+
+    <div class="item" v-gesture:press="test3" v-gesture:tap="test3" v-gesture:pan="test3">
+      <h4>绑定多个事件</h4>
+      <p>press、tap、pan</p>
+    </div>
+
+    <div class="show">{{ text }}</div>
+  </div>
+</template>
+<script>
+export default {
+  data() {
+    return {
+      left: 0,
+      top: 70,
+      posX: 0,
+      posY: 70,
+      text: ''
+    };
+  },
+  mounted() {},
+  methods: {
+    test1(e) {
+      this.left = parseInt(this.posX) + e.deltaX + 'px';
+      this.top = parseInt(this.posY) + e.deltaY + 'px';
+    },
+    test2(e) {
+      this.posX = this.left;
+      this.posY = this.top;
+    },
+    test3(e) {
+      this.text = 'x: ' + e.deltaX + ', y: ' + e.deltaY;
+    }
+  }
+};
+</script>
+
+<style>
+.item {
+  position: absolute;
+  left: 0;
+  width: 200px;
+  height: 80px;
+  background-color: #ddd;
+}
+
+@for $i from 1 to 20 {
+  .container .item:nth-child(#{$i}) {
+    top: $i * 90 + px;
+  }
+}
+.content {
+  position: absolute;
+  left: 0;
+  top: 200px;
+  width: 100px;
+  height: 200px;
+  background-color: red;
+}
+.show {
+  position: fixed;
+  bottom: 0;
+}
+</style>

+ 54 - 0
src/packages/gesture/doc.md

@@ -0,0 +1,54 @@
+# Gesture 手势组件
+基于[hammer.js](http://hammerjs.github.io/getting-started/)封装,支持移动端各种手势操作。包括'tap', 'pan', 'pinch', 'press', 'rotate', 'swipe'
+
+## 基本用法
+```html
+<div class="item" v-gesture:pan="test1" v-gesture:panend="test2" :style="{ left: left, top: top }">
+    平移
+</div>
+```
+## 指定方向平移
+```html
+<div class="item" v-gesture:pan.left="test3">
+    左移
+</div>
+```
+## 指定方向滑动
+```html
+<div class="item" v-gesture:swipe.horizontal="test3">
+    swipe
+</div>
+```
+## 绑定多个事件
+```html
+<div class="item" v-gesture:press="test3" v-gesture:tap="test3" v-gesture:pan="test3">
+    press、tap、pan
+</div>
+<script>
+export default {
+  data() {
+    return {
+      left: 0,
+      top: 70,
+      posX: 0,
+      posY: 70
+    };
+  },
+  mounted() {},
+  methods: {
+    test1(e) {
+      this.left = parseInt(this.posX) + e.deltaX + 'px';
+      this.top = parseInt(this.posY) + e.deltaY + 'px';
+    },
+    test2(e) {
+      this.posX = this.left;
+      this.posY = this.top;
+    },
+    test3(e) {
+      console.log(e.deltaX, e.deltaY);
+    }
+  }
+};
+</script>
+
+```

+ 3 - 0
src/packages/gesture/gesture.scss

@@ -0,0 +1,3 @@
+.nut-gesture{
+
+}

+ 147 - 0
src/packages/gesture/index.js

@@ -0,0 +1,147 @@
+import Hammer from 'hammerjs';
+
+const gestures = ['tap', 'pan', 'pinch', 'press', 'rotate', 'swipe'];
+const subGestures = [
+  'panstart',
+  'panend',
+  'panmove',
+  'pancancel',
+  'pinchstart',
+  'pinchmove',
+  'pinchend',
+  'pinchcancel',
+  'pinchin',
+  'pinchout',
+  'pressup',
+  'rotatestart',
+  'rotatemove',
+  'rotateend',
+  'rotatecancel'
+];
+const directions = ['up', 'down', 'left', 'right', 'horizontal', 'vertical', 'all'];
+function verifyDirection(options) {
+  var dir = options.direction;
+  if (typeof dir === 'string') {
+    var hammerDirection = 'DIRECTION_' + dir.toUpperCase();
+    if (directions.indexOf(dir) > -1 && Hammer.hasOwnProperty(hammerDirection)) {
+      options.direction = Hammer[hammerDirection];
+    } else {
+      console.warn('[vue-hammer] invalid direction: ' + dir);
+    }
+  }
+}
+function handleDirection(event, direction) {
+  let dirArray = new Set();
+  const relations = {
+    horizontal: ['left', 'right'],
+    vertical: ['up', 'down'],
+    all: ['left', 'right', 'up', 'down']
+  };
+  direction.forEach(dir => {
+    dir = dir.toLowerCase();
+    if (relations[dir]) {
+      dirArray = new Set([...dirArray, ...relations[dir]]);
+    } else {
+      dirArray.add(dir);
+    }
+  });
+
+  if (dirArray.size === 0) {
+    return event;
+  }
+  dirArray = [...dirArray].map(dir => {
+    return event + dir;
+  });
+  return dirArray.join(' ');
+}
+
+function update(el, binding) {
+  const mc = el.hammer;
+  const event = binding.arg;
+  const dirType = subGestures.find(subGes => subGes === event) ? event : handleDirection(event, el.storage[event].direction);
+  // 删除已绑定的事件
+  if (mc.handler) {
+    mc.off(dirType, mc.handler);
+  }
+  if (typeof binding.value !== 'function') {
+    mc.handler = null;
+    console.warn(binding.arg + '请传入function类型');
+  } else {
+    mc.on(dirType, (mc.handler = binding.value));
+  }
+}
+const Gesture = {
+  name: 'gesture',
+  bind: (el, binding) => {
+    if (!el.hammer) {
+      // 是否已初始化
+      el.hammer = new Hammer.Manager(el, { inputClass: Hammer.TouchMouseInput });
+      el.hammer.domEvents = true;
+    }
+
+    const elType = binding.arg; // pan panleft ...
+    const mc = el.hammer;
+    const direction = binding.modifiers; // 方向: {right: true}
+
+    el.storage = el.storage || {}; // 记录所有事件及其方向
+    el.storage[elType] = el.storage[elType] || {};
+    el.storage[elType].direction = el.storage[elType].direction || [];
+
+    // 存储传入的方向到对应事件下
+    Object.keys(direction).forEach(keyName => {
+      if (!el.storage[elType].direction.includes(keyName)) {
+        el.storage[elType].direction.push(keyName);
+      }
+    });
+
+    let recognizerType = gestures.find(gesture => gesture === elType); // 验证传入的事件合法性
+    if (!recognizerType) {
+      return;
+    }
+    let recognizer = mc.get(recognizerType); // 获取识别器实例
+    if (!recognizer) {
+      // 创建构造器
+      recognizer = new Hammer[recognizerType.charAt(0).toUpperCase() + recognizerType.slice(1)]();
+      // 同时识别多个手势
+      recognizer.recognizeWith(mc.recognizers);
+      // 向管理器添加新的识别器实例。
+      mc.add(recognizer);
+    }
+
+    // 有方向传入时默认取第一个进行设置
+    if (el.storage[recognizerType].direction.length >= 1) {
+      let options = {
+        direction: el.storage[recognizerType].direction[0]
+      };
+      verifyDirection(options);
+      recognizer.set(options);
+    }
+  },
+  inserted: (el, binding) => {
+    update(el, binding);
+  },
+  componentUpdated: (el, binding) => {
+    update(el, binding);
+  },
+  unbind: (el, binding) => {
+    const mc = el.hammer;
+    const event = binding.arg;
+    const dirType = subGestures.find(subGes => subGes === event) ? event : handleDirection(event, el.storage[event].direction);
+    if (mc.handler) {
+      el.hammer.off(dirType, mc.handler);
+    }
+    let eventkeys = Object.keys(el.hammer.handlers);
+    let isDestroy = true;
+    eventkeys.forEach(element => {
+      if (mc.handlers[element].length > 0) {
+        isDestroy = false;
+      }
+    });
+    if (isDestroy) {
+      el.hammer.destroy();
+      el.hammer = null;
+    }
+  }
+};
+
+export default Gesture;

+ 1 - 0
types/nutui.d.ts

@@ -82,3 +82,4 @@ export declare class Qart extends UIComponent {}
 export declare class Drag extends UIComponent {}
 export declare class Address extends UIComponent {}
 export declare class CountUp extends UIComponent {}
+export declare class Gesture extends UIComponent {}