├── .eslintrc ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── README.md ├── dist ├── vue-swipe.css └── vue-swipe.js ├── example ├── example.vue ├── index.html └── index.js ├── package-lock.json ├── package.json ├── src ├── index.js ├── swipe-item.vue └── swipe.vue ├── webpack.config.js └── webpack.example.config.js /.eslintrc: -------------------------------------------------------------------------------- 1 | parserOptions: 2 | sourceType: module 3 | 4 | env: 5 | es6: true 6 | browser: true 7 | 8 | extends: 9 | - 'eslint:recommended' 10 | - 'plugin:vue/essential' 11 | 12 | rules: 13 | quotes: [ 1, single ] 14 | linebreak-style: [ 1, unix ] 15 | semi: [ 1, always ] 16 | no-undef: [ 1 ] 17 | no-console: [ 0 ] 18 | no-unused-vars: [ 1 ] 19 | space-infix-ops: [ 1 ] 20 | no-multi-spaces: [ 1 ] 21 | no-fallthrough: [ 0 ] 22 | 'vue/require-v-for-key': 'off' 23 | 'vue/script-indent': [ 1, 2, { baseIndent: 1 }] 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | coverage 4 | .idea 5 | .jshintrc 6 | *.log -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | coverage 4 | .idea 5 | .jshintrc 6 | webpack.config.js 7 | webpack.base.js 8 | webpack.example.config.js -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | 6 | # [2.3.0](https://github.com/ElemeFE/vue-swipe/compare/v2.2.0...v2.3.0) (2017-11-29) 7 | 8 | 9 | ### Features 10 | 11 | * add propagation option ([14b4281](https://github.com/ElemeFE/vue-swipe/commit/14b4281)) 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-swipe 2 | 3 | vue-swipe is a touch slider for Vue.js. 4 | 5 | ## Install 6 | 7 | ```bash 8 | $ npm install vue-swipe 9 | ``` 10 | 11 | ## Import 12 | 13 | ### Import using module 14 | 15 | Import components to your project: 16 | 17 | ``` js 18 | require('vue-swipe/dist/vue-swipe.css'); 19 | 20 | // in ES6 modules 21 | import { Swipe, SwipeItem } from 'vue-swipe'; 22 | 23 | // in CommonJS 24 | const { Swipe, SwipeItem } = require('vue-swipe'); 25 | 26 | // in Global variable 27 | const { Swipe, SwipeItem } = VueSwipe; 28 | ``` 29 | 30 | And register components: 31 | 32 | ``` js 33 | Vue.component('swipe', Swipe); 34 | Vue.component('swipe-item', SwipeItem); 35 | ``` 36 | 37 | ### Import using script tag 38 | 39 | ``` html 40 | 41 | 42 | ``` 43 | 44 | ``` js 45 | const vueSwipe = VueSwipe.Swipe; 46 | const vueSwipeItem = VueSwipe.SwipeItem; 47 | 48 | new Vue({ 49 | el: 'body', 50 | components: { 51 | 'swipe': vueSwipe, 52 | 'swipe-item': vueSwipeItem 53 | } 54 | }); 55 | ``` 56 | 57 | ## Usage 58 | 59 | Work on a Vue instance: 60 | 61 | ```HTML 62 | 63 | 64 | 65 | 66 | 67 | ``` 68 | 69 | ```CSS 70 | .my-swipe { 71 | height: 200px; 72 | color: #fff; 73 | font-size: 30px; 74 | text-align: center; 75 | } 76 | 77 | .slide1 { 78 | background-color: #0089dc; 79 | color: #fff; 80 | } 81 | 82 | .slide2 { 83 | background-color: #ffd705; 84 | color: #000; 85 | } 86 | 87 | .slide3 { 88 | background-color: #ff2d4b; 89 | color: #fff; 90 | } 91 | ``` 92 | 93 | ## Options 94 | 95 | ### Props 96 | 97 | | Option | Type | Description | Default | 98 | | ----- | ----- | ----- | ----- | 99 | | speed | Number | Speed of animation | 300 | 100 | | defaultIndex | Number | Start swipe item index | 0 | 101 | | disabled | Boolean | Disable user drag swipe item | false | 102 | | auto | Number | Delay of auto slide | 3000 | 103 | | continuous | Boolean | Create an infinite slider without endpoints | true | 104 | | showIndicators | Boolean | Show indicators on slider bottom | true | 105 | | noDragWhenSingle | Boolean | Do not drag when there is only one swipe-item | true | 106 | | prevent | Boolean | `preventDefault` when touch start, useful for some lower version Android Browser (4.2, etc) | false | 107 | | propagation | Boolean | solve nesting | false | 108 | | disabled | Boolean | disabled user swipe item | false | 109 | 110 | ### Events 111 | 112 | | Event Name | Description | params | 113 | | ----- | ----- | ----- | 114 | | change | triggers when current swipe-item change | new swipe item Index, old swipe item Index | 115 | 116 | ## FAQ 117 | 118 | ### How to programminly swipe to an item? 119 | 120 | Use ref and call `goto()` method. 121 | 122 | ``` js 123 | this.$refs.swipe.goto(newIndex) 124 | ``` 125 | 126 | For more details, please refer to example code. 127 | 128 | ## Development 129 | 130 | Watching with hot-reload: 131 | 132 | ```bash 133 | $ npm run dev 134 | ``` 135 | 136 | Develop on real remote device: 137 | 138 | ```bash 139 | $ npm run remote-dev {{ YOUR IP ADDRESS }} 140 | ``` 141 | 142 | ## License 143 | 144 | MIT 145 | -------------------------------------------------------------------------------- /dist/vue-swipe.css: -------------------------------------------------------------------------------- 1 | .mint-swipe,.mint-swipe-items-wrap{overflow:hidden;position:relative;height:100%}.mint-swipe-items-wrap{-webkit-transform:translateZ(0);transform:translateZ(0)}.mint-swipe-items-wrap>div{position:absolute;-webkit-transform:translateX(-100%);transform:translateX(-100%);width:100%;height:100%;display:none}.mint-swipe-items-wrap>div.is-active{display:block;-webkit-transform:none;transform:none}.mint-swipe-indicators{position:absolute;bottom:10px;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.mint-swipe-indicator{width:8px;height:8px;display:inline-block;border-radius:100%;background:#000;opacity:.2;margin:0 3px}.mint-swipe-indicator.is-active{background:#fff} -------------------------------------------------------------------------------- /dist/vue-swipe.js: -------------------------------------------------------------------------------- 1 | module.exports=function(t){var e={};function n(i){if(e[i])return e[i].exports;var a=e[i]={i:i,l:!1,exports:{}};return t[i].call(a.exports,a,a.exports,n),a.l=!0,a.exports}return n.m=t,n.c=e,n.d=function(t,e,i){n.o(t,e)||Object.defineProperty(t,e,{configurable:!1,enumerable:!0,get:i})},n.r=function(t){Object.defineProperty(t,"__esModule",{value:!0})},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=9)}([function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.default={name:"mt-swipe-item",mounted:function(){this.$parent&&this.$parent.swipeItemCreated(this)},destroyed:function(){this.$parent&&this.$parent.swipeItemDestroyed(this)}}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var i=n(7),a=n(6);e.default={name:"mt-swipe",created:function(){this.dragState={}},data:function(){return{ready:!1,dragging:!1,userScrolling:!1,animating:!1,index:0,pages:[],timer:null,reInitTimer:null,noDrag:!1}},props:{speed:{type:Number,default:300},defaultIndex:{type:Number,default:0},disabled:{type:Boolean,default:!1},auto:{type:Number,default:3e3},continuous:{type:Boolean,default:!0},showIndicators:{type:Boolean,default:!0},noDragWhenSingle:{type:Boolean,default:!0},prevent:{type:Boolean,default:!1},propagation:{type:Boolean,default:!1}},methods:{swipeItemCreated:function(){var t=this;this.ready&&(clearTimeout(this.reInitTimer),this.reInitTimer=setTimeout(function(){t.reInitPages()},100))},swipeItemDestroyed:function(){var t=this;this.ready&&(clearTimeout(this.reInitTimer),this.reInitTimer=setTimeout(function(){t.reInitPages()},100))},translate:function(t,e,n,a){var s=this,r=arguments;if(n){this.animating=!0,t.style.webkitTransition="-webkit-transform "+n+"ms ease-in-out",setTimeout(function(){t.style.webkitTransform="translate3d("+e+"px, 0, 0)"},50);var o=!1,l=function(){o||(o=!0,s.animating=!1,t.style.webkitTransition="",t.style.webkitTransform="",a&&a.apply(s,r))};(0,i.once)(t,"webkitTransitionEnd",l),setTimeout(l,n+100)}else t.style.webkitTransition="",t.style.webkitTransform="translate3d("+e+"px, 0, 0)"},reInitPages:function(){var t=this,e=this.$children;this.noDrag=1===e.length&&this.noDragWhenSingle;var n=[];this.index=this.defaultIndex,e.forEach(function(e,i){n.push(e.$el),(0,a.removeClass)(e.$el,"is-active"),i===t.defaultIndex&&(0,a.addClass)(e.$el,"is-active")}),this.pages=n},doAnimate:function(t,e){var n=this;if(0!==this.$children.length&&(e||!(this.$children.length<2))){var i,s,r,o,l,u,d=this.speed||300,c=this.index,h=this.pages,f=h.length;e&&"goto"!==t?(i=e.prevPage,r=e.currentPage,s=e.nextPage,o=e.pageWidth,l=e.offsetLeft):(e=e||{},o=this.$el.clientWidth,r=h[c],"goto"===t?(i=e.prevPage,s=e.nextPage):(i=h[c-1],s=h[c+1]),this.continuous&&h.length>1&&(i||(i=h[h.length-1]),s||(s=h[0])),i&&(i.style.display="block",this.translate(i,-o)),s&&(s.style.display="block",this.translate(s,o)));var p=this.$children[c].$el;"prev"===t?(c>0&&(u=c-1),this.continuous&&0===c&&(u=f-1)):"next"===t?(c-1&&e.newIndex0&&n.translate(i,-1*o,d),s&&l<0&&n.translate(s,o,d)):(i&&n.translate(i,-1*o,d),s&&n.translate(s,o,d)))},10)}},next:function(){this.doAnimate("next")},prev:function(){this.doAnimate("prev")},goto:function(t){this.index!==t&&(t1&&(a||(a=this.$children[this.$children.length-1]),r||(r=this.$children[0])),n.prevPage=a?a.$el:null,n.dragPage=s?s.$el:null,n.nextPage=r?r.$el:null,n.prevPage&&(n.prevPage.style.display="block"),n.nextPage&&(n.nextPage.style.display="block")}},doOnTouchMove:function(t){if(!this.noDrag&&!this.disabled){var e=this.dragState,n=t.changedTouches?t.changedTouches[0]:t;e.currentLeft=n.pageX,e.currentTop=n.pageY,e.currentTopAbsolute=n.clientY;var i=e.currentLeft-e.startLeft,a=e.currentTopAbsolute-e.startTopAbsolute,s=Math.abs(i),r=Math.abs(a);if(s<5||s>=5&&r>=1.73*s)this.userScrolling=!0;else{this.userScrolling=!1,t.preventDefault();var o=(i=Math.min(Math.max(1-e.pageWidth,i),e.pageWidth-1))<0?"next":"prev";if(e.prevPage&&"prev"===o)this.translate(e.prevPage,i-e.pageWidth);else if(e.nextPage&&"next"===o)this.translate(e.nextPage,i+e.pageWidth);else{var l=e.pageWidth,u=i;i=-1/6/l*u*(Math.abs(u)-2*l)}this.translate(e.dragPage,i)}}},doOnTouchEnd:function(){if(!this.noDrag&&!this.disabled){var t=this.dragState,e=new Date-t.startTime,n=null,i=t.currentLeft-t.startLeft,a=t.currentTop-t.startTop,s=t.pageWidth,r=this.index,o=this.pages.length;if(e<300){var l=Math.abs(i)<5&&Math.abs(a)<5;(isNaN(i)||isNaN(a))&&(l=!0),l&&this.$children[this.index].$emit("tap")}e<300&&void 0===t.currentLeft||((e<300||Math.abs(i)>s/2)&&(n=i<0?"next":"prev"),this.continuous||(0===r&&"prev"===n||r===o-1&&"next"===n)&&(n=null),this.$children.length<2&&(n=null),this.doAnimate(n,{offsetLeft:i,pageWidth:t.pageWidth,prevPage:t.prevPage,currentPage:t.dragPage,nextPage:t.nextPage}),this.dragState={})}},dragStartEvent:function(t){this.prevent&&t.preventDefault(),this.propagation&&t.stopPropagation(),this.animating||(this.dragging=!0,this.userScrolling=!1,this.doOnTouchStart(t))},dragMoveEvent:function(t){this.dragging&&this.doOnTouchMove(t)},dragEndEvent:function(t){if(this.userScrolling)return this.dragging=!1,void(this.dragState={});this.dragging&&(this.doOnTouchEnd(t),this.dragging=!1)}},destroyed:function(){this.timer&&(clearInterval(this.timer),this.timer=null),this.reInitTimer&&(clearTimeout(this.reInitTimer),this.reInitTimer=null)},mounted:function(){var t=this;this.ready=!0,this.auto>0&&(this.timer=setInterval(function(){t.dragging||t.animating||t.next()},this.auto)),this.reInitPages();var e=this.$el;e.addEventListener("touchstart",this.dragStartEvent),e.addEventListener("touchmove",this.dragMoveEvent),e.addEventListener("touchend",this.dragEndEvent),e.addEventListener("mousedown",this.dragStartEvent),e.addEventListener("mousemove",this.dragMoveEvent),e.addEventListener("mouseup",this.dragEndEvent)}}},function(t,e,n){"use strict";function i(t,e,n,i,a,s,r,o){var l=typeof(t=t||{}).default;"object"!==l&&"function"!==l||(t=t.default);var u,d="function"==typeof t?t.options:t;if(e&&(d.render=e,d.staticRenderFns=n,d._compiled=!0),i&&(d.functional=!0),s&&(d._scopeId=s),r?(u=function(t){(t=t||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(t=__VUE_SSR_CONTEXT__),a&&a.call(this,t),t&&t._registeredComponents&&t._registeredComponents.add(r)},d._ssrRegister=u):a&&(u=o?function(){a.call(this,this.$root.$options.shadowRoot)}:a),u)if(d.functional){d._injectStyles=u;var c=d.render;d.render=function(t,e){return u.call(e),c(t,e)}}else{var h=d.beforeCreate;d.beforeCreate=h?[].concat(h,u):[u]}return{exports:t,options:d}}n.d(e,"a",function(){return i})},function(t,e,n){"use strict";n.d(e,"a",function(){return i}),n.d(e,"b",function(){return a});var i=function(){var t=this.$createElement;return(this._self._c||t)("div",{staticClass:"mint-swipe-item"},[this._t("default")],2)},a=[]},function(t,e,n){"use strict";n.d(e,"a",function(){return i}),n.d(e,"b",function(){return a});var i=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"mint-swipe"},[n("div",{ref:"wrap",staticClass:"mint-swipe-items-wrap"},[t._t("default")],2),t._v(" "),n("div",{directives:[{name:"show",rawName:"v-show",value:t.showIndicators,expression:"showIndicators"}],staticClass:"mint-swipe-indicators"},t._l(t.pages,function(e,i){return n("div",{key:i,staticClass:"mint-swipe-indicator",class:{"is-active":i===t.index}})}))])},a=[]},function(t,e,n){"use strict";n.r(e);var i=n(0),a=n.n(i);for(var s in i)"default"!==s&&function(t){n.d(e,t,function(){return i[t]})}(s);var r=n(3),o=n(2),l=Object(o.a)(a.a,r.a,r.b,!1,null,null,null);e.default=l.exports},function(t,e,n){"use strict";var i=function(t,e){if(!t||!e)return!1;if(-1!=e.indexOf(" "))throw new Error("className should not contain space.");return t.classList?t.classList.contains(e):(" "+t.className+" ").indexOf(" "+e+" ")>-1};t.exports={hasClass:i,addClass:function(t,e){if(t){for(var n=t.className,a=(e||"").split(" "),s=0,r=a.length;s 2 |
3 |

Default

4 | 5 | Slide1 6 | Slide2 7 | Slide3 8 | 9 | 10 |
11 | 12 |

Change swipe

13 | 14 | Slide1 15 | Slide2 16 | Slide3 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 |

Drag single swipe

25 | 26 | SINGLE SLIDE 27 | 28 | 29 |
30 | 31 |

Default index

32 | 33 | Slide1 34 | Slide2 35 | Slide3 36 | 37 |
38 | 39 | 40 | 59 | 60 | 80 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | vue-swipe examples 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Example from './example.vue'; 3 | 4 | new Vue({ 5 | el: '#app', 6 | render: h => h(Example) 7 | }); 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-swipe", 3 | "version": "2.4.0", 4 | "description": "A touch slider for vue.js.", 5 | "main": "dist/vue-swipe.js", 6 | "scripts": { 7 | "dev": "webpack-dev-server --config ./webpack.example.config.js --inline --hot", 8 | "remote-dev": "npm run dev --host", 9 | "build": "webpack -p", 10 | "release": "standard-version & git push --follow-tags eleme master & npm publish", 11 | "prepare": "npm run build" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/ElemeFE/vue-swipe.git" 16 | }, 17 | "babel": { 18 | "presets": [ 19 | "env" 20 | ] 21 | }, 22 | "files": [ 23 | "dist", 24 | "src" 25 | ], 26 | "keywords": [ 27 | "swipe", 28 | "slider", 29 | "vue" 30 | ], 31 | "author": "long.zhang@ele.me", 32 | "license": "MIT", 33 | "devDependencies": { 34 | "babel-core": "^6.26.0", 35 | "babel-loader": "^7.1.4", 36 | "babel-preset-env": "^1.6.1", 37 | "css-loader": "^0.28.11", 38 | "eslint": "^4.19.1", 39 | "eslint-loader": "^2.0.0", 40 | "eslint-plugin-vue": "^4.4.0", 41 | "extract-text-webpack-plugin": "4.0.0-beta.0", 42 | "style-loader": "^0.20.3", 43 | "vue": "2.5.16", 44 | "vue-loader": "^14.2.2", 45 | "vue-template-compiler": "2.5.16", 46 | "webpack": "^4.5.0", 47 | "webpack-cli": "^2.0.14", 48 | "webpack-dev-server": "^3.1.3" 49 | }, 50 | "dependencies": { 51 | "wind-dom": "0.0.3" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import Swipe from './swipe.vue'; 2 | import SwipeItem from './swipe-item.vue'; 3 | 4 | export { Swipe, SwipeItem }; 5 | -------------------------------------------------------------------------------- /src/swipe-item.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 20 | -------------------------------------------------------------------------------- /src/swipe.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 61 | 62 | 558 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const { resolve } = require('path'); 2 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 3 | 4 | module.exports = { 5 | mode: 'production', 6 | entry: './src/', 7 | output: { 8 | library: 'VueSwipe', 9 | libraryTarget: 'commonjs2', 10 | filename: 'vue-swipe.js', 11 | path: resolve('dist'), 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | enforce: 'pre', 17 | test: /\.(js|vue)$/, 18 | loader: 'eslint-loader', 19 | exclude: /node_modules/ 20 | }, 21 | { 22 | test: /\.vue$/, 23 | loader: 'vue-loader', 24 | options: { 25 | extractCSS: true 26 | } 27 | }, 28 | { test: /\.js$/, 29 | loader: 'babel-loader' 30 | } 31 | ] 32 | }, 33 | plugins: [ 34 | new ExtractTextPlugin('vue-swipe.css') 35 | ] 36 | }; 37 | -------------------------------------------------------------------------------- /webpack.example.config.js: -------------------------------------------------------------------------------- 1 | const {resolve} = require('path'); 2 | const options = require('./webpack.config'); 3 | 4 | options.mode = 'development'; 5 | options.entry = './example/'; 6 | 7 | options.output.filename = 'example.js'; 8 | options.output.libraryTarget = 'var'; 9 | options.output.publicPath = '/dist/'; 10 | 11 | options.devServer = { 12 | contentBase: [ 13 | resolve(__dirname, "example"), 14 | ], 15 | publicPath: '/dist/' 16 | }; 17 | 18 | module.exports = options; 19 | --------------------------------------------------------------------------------