",
+ "version": "<%= version %>",
+ "description": "<%= description %>",
+ "main": "./lib/index.js",
+ "author": "<%= author %>",
+ "license": "<%= license %>",
+ "devDependencies": {},
+ "dependencies": {}
+}
diff --git a/packages/swipe/src/input.js b/packages/swipe/src/input.js
new file mode 100755
index 000000000..065ea6dd2
--- /dev/null
+++ b/packages/swipe/src/input.js
@@ -0,0 +1,116 @@
+import { EventEmitter, extend, bindEvents, removeEvents } from './utils';
+
+function Input(host, options) {
+ EventEmitter.apply(this, arguments);
+
+ this.isStarting = false;
+ this.startPt = null;
+ this.endPt = null;
+ this.isDeaf = false;
+
+ this.options = extend({
+ listenMoving: false
+ }, options);
+
+ this.host = host;
+ this.onTouchStart = this.onTouchStart.bind(this);
+ this.onTouchMove = this.onTouchMove.bind(this);
+ this.onTouchEnd = this.onTouchEnd.bind(this);
+
+ this.bind(this.host);
+}
+
+Input.prototype = Object.create(new EventEmitter());
+
+extend(Input.prototype, {
+ bind: function(host) {
+
+ bindEvents(host, 'touchstart mousedown', this.onTouchStart);
+ if (this.options.listenMoving) {
+ bindEvents(window, 'touchmove mousemove', this.onTouchMove);
+ }
+ bindEvents(window, 'touchend mouseup touchcancel', this.onTouchEnd);
+ },
+
+ onTouchStart: function(e) {
+ if (this.isDeaf || this.isStarting) {
+ return;
+ }
+ this.isStarting = true;
+ this.orgDirection = null;
+ this.startPt = this.pointerEventToXY(e);
+ },
+
+ onTouchMove: function(e) {
+ if (!this.isStarting) {
+ return;
+ }
+ this.caculate(e);
+ },
+
+ onTouchEnd: function(e) {
+ if (!this.isStarting) {
+ return;
+ }
+ this.isStarting = false;
+ this.caculate(e, true);
+ },
+
+ caculate: function(e, isEnd) {
+ var distY, distX;
+ this.endPt = this.pointerEventToXY(e);
+
+ distY = this.startPt.y - this.endPt.y;
+ distX = this.startPt.x - this.endPt.x;
+
+ if (distY) {
+ this.emit(distY > 0 ? 'up' : 'down', distY, isEnd, e);
+ }
+ if (distX) {
+ this.emit(distX > 0 ? 'left' : 'right', distX, isEnd, e);
+ }
+
+ if (this.orgDirection == null) {
+ this.orgDirection = Math.abs(distX) > Math.abs(distY);
+ }
+
+ this.emit('move', {x: distX, y: distY}, isEnd, e, {orgDirection: this.orgDirection});
+ },
+
+ pointerEventToXY: function(e) {
+ var out = {x: 0, y: 0};
+ var type = e.type;
+ if (e.originalEvent) {
+ e = e.originalEvent;
+ }
+ if (['touchstart', 'touchmove', 'touchend', 'touchcancel'].indexOf(type) > -1) {
+ var touch = e.touches[0] || e.changedTouches[0];
+ out.x = touch.pageX;
+ out.y = touch.pageY;
+ } else if (
+ ['mousedown', 'mouseup', 'mousemove', 'mouseover', 'mouseout', 'mouseenter', 'mouseleave'].indexOf(type) > -1
+ ) {
+ out.x = e.pageX;
+ out.y = e.pageY;
+ }
+ return out;
+ },
+
+ deaf: function() {
+ this.isDeaf = true;
+ },
+
+ undeaf: function() {
+ this.isDeaf = false;
+ },
+
+ destroy: function() {
+ removeEvents(this.host, 'touchstart mousedown', this.onTouchStart);
+ if (this.options.listenMoving) {
+ removeEvents(window, 'touchmove mousemove', this.onTouchMove);
+ }
+ removeEvents(window, 'touchend mouseup touchcancel', this.onTouchEnd);
+ }
+});
+
+export default Input;
diff --git a/packages/swipe/src/scroll.js b/packages/swipe/src/scroll.js
new file mode 100755
index 000000000..7a8be2bb8
--- /dev/null
+++ b/packages/swipe/src/scroll.js
@@ -0,0 +1,147 @@
+import { EventEmitter, extend } from './utils'
+
+const setElementsStyles = (elems, styles) => {
+ Array.prototype.forEach.call(elems, item => {
+ extend(item.style, styles)
+ })
+}
+
+function Scroll(wrapElem, options) {
+ EventEmitter.apply(this, arguments);
+ this.wrapElem = wrapElem;
+ this.wrapSize = {
+ width: () => wrapElem.clientWidth,
+ height: () => wrapElem.clientHeight
+ };
+
+ this.options = extend({
+ loop: true,
+ autoPlay: false,
+ startIndex: 0
+ }, options);
+ this.init.apply(this, arguments);
+}
+
+Scroll.prototype = Object.create(new EventEmitter());
+extend(Scroll.prototype, {
+ init: function() {
+ this.update();
+ },
+
+ getCurrentDist: function() {
+ return this.mCache.currentDist;
+ },
+
+ update: function() {
+ const oldPages = this.pages
+ this.pages = this.wrapElem.querySelectorAll('.swp-page');
+ if (oldPages && oldPages.length === this.pages.length) {
+ const isSame = Array.prototype.every.call(this.pages, (elem, index) => {
+ return this.pages[index] === oldPages[index]
+ })
+ if (isSame) {
+ return
+ }
+ }
+ var defaultStyle = {
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ width: '100%',
+ height: '100%',
+ display: 'block',
+ '-webkit-transform': 'translate3d(-9999px, 0, 0)'
+ };
+ setElementsStyles(this.pages, defaultStyle);
+ this.mCache = {
+ dist: 0,
+ offsetPage: 0
+ };
+
+ this.setCurrentPage(0);
+ this.movePage(this.options.startIndex * this.wrapSize.width(), true);
+ },
+
+ renderPage: function(dist = 0, currentOffsetPage = 0) {
+ var wrapWidth = this.wrapSize.width();
+ var offset = currentOffsetPage * wrapWidth - dist;
+ var page;
+ var leftPage;
+ var rightPage;
+ var leftOffset = offset - wrapWidth;
+ var rightOffset = offset + wrapWidth;
+
+ page = this.getCurrentPage();
+ if (page) {
+ page.style['-webkit-transform'] = 'translate3d(' + offset + 'px, 0, 0)';
+ }
+
+ leftPage = this.pages[this.mapLoopPage(currentOffsetPage - 1)];
+ if (leftPage) {
+ if (Math.abs(leftOffset) <= wrapWidth) {
+ leftPage.style['-webkit-transform'] = 'translate3d(' + leftOffset + 'px, 0, 0)';
+ } else {
+ if (this.pages.length > 2) {
+ leftPage.style['-webkit-transform'] = 'translate3d(-9999px, 0, 0)';
+ }
+ }
+ }
+
+ rightPage = this.pages[this.mapLoopPage(currentOffsetPage + 1)];
+ if (rightPage) {
+ if (Math.abs(rightOffset) <= wrapWidth) {
+ rightPage.style['-webkit-transform'] = 'translate3d(' + rightOffset + 'px, 0, 0)';
+ } else {
+ if (this.pages.length > 2) {
+ rightPage.style['-webkit-transform'] = 'translate3d(-9999px, 0, 0)';
+ }
+ }
+ }
+ },
+
+ movePage: function(dist, isEnd) {
+ var currentOffsetPage;
+
+ this.mCache.currentDist = dist + this.mCache.dist;
+ if (isEnd) {
+ this.mCache.dist += dist;
+ }
+
+ currentOffsetPage = Math.round(this.mCache.currentDist / this.wrapSize.width()) || 0;
+
+ if (currentOffsetPage !== this.mCache.offsetPage) {
+ this.setCurrentPage(currentOffsetPage);
+
+ // 翻页
+ this.emit('pageChangeEnd', this.getCurrentPage()
+ , this.currentIndex, this.mCache.offsetPage);
+ this.mCache.offsetPage = currentOffsetPage;
+ }
+
+ this.renderPage(this.mCache.currentDist, currentOffsetPage);
+ },
+
+ getCurrentPage: function() {
+ return this.pages[this.currentIndex];
+ },
+
+ mapLoopPage: function(num) {
+ if (this.options.loop) {
+ var direction = num < 0 ? -1 : 1;
+ var l = this.pages.length;
+ return Math.abs(l + direction * Math.abs(num) % l) % l;
+ } else {
+ if (num >= this.pages.length || num < 0) {
+ return this.pages.length;
+ } else {
+ return num;
+ }
+ }
+ },
+
+ setCurrentPage: function(num) {
+ this.currentIndex = this.mapLoopPage(num);
+ }
+});
+
+export default Scroll;
diff --git a/packages/swipe/src/spring_dummy.js b/packages/swipe/src/spring_dummy.js
new file mode 100755
index 000000000..9d68751d6
--- /dev/null
+++ b/packages/swipe/src/spring_dummy.js
@@ -0,0 +1,147 @@
+import { requestAnimationFrame, cancelAnimationFrame, EventEmitter, extend } from './utils'
+
+function SpringDummy(scroll, input, options) {
+ var wrapElem = scroll.wrapElem;
+ var self = this;
+ EventEmitter.apply(this, arguments);
+
+ this.scroll = scroll;
+ this.input = input;
+ this.input.on('move', this.movementReact.bind(this));
+ this.wrapSize = {
+ width: () => wrapElem.clientWidth,
+ height: () => wrapElem.clientHieght
+ };
+
+ this.options = extend({
+ intervalTween: 3000,
+ threshold: 20
+ }, options);
+
+ if (this.scroll.options.autoPlay) {
+ this.initMove();
+ }
+
+ this.on('bounceEnd', function() {
+ if (self.scroll.options.autoPlay) {
+ self.initMove();
+ }
+
+ self.input.undeaf();
+ }).on('bounceStart', function() {
+ self.input.deaf();
+ });
+
+}
+
+SpringDummy.prototype = Object.create(new EventEmitter());
+extend(SpringDummy.prototype, {
+
+ clearTransition: function() {
+ cancelAnimationFrame(this.transitionReq);
+ },
+
+ movementReact: function(pt, isEnd, e, extra) {
+ if (isEnd) {
+ this.launch(extra.orgDirection ? pt.x : 0);
+ }
+ this.clearMove();
+ },
+
+ launch: function(dist) {
+ var self = this;
+ var direction = dist / Math.abs(dist);
+ var addition = 0;
+ var w = self.wrapSize.width();
+ var tempOffsetPage = Math.round(dist / w);
+ var offsetPage = this.scroll.mCache.offsetPage;
+
+ // 翻到对应页
+ addition = w * tempOffsetPage;
+
+ // addition为0是原位置
+ if (addition === 0) {
+ if (Math.abs(dist) > self.options.threshold) {
+ // 翻到下一页
+ addition = w * direction;
+ }
+ }
+
+ if (!self.scroll.options.loop) {
+ if (offsetPage <= 0) {
+ if (Math.abs(dist) > self.options.threshold && direction > 0) {
+ addition = w * direction;
+ } else {
+ addition = w * (tempOffsetPage - offsetPage);
+ }
+ }
+
+ if (this.scroll.pages.length === 1) {
+ addition = 0;
+ } else if (offsetPage >= this.scroll.pages.length - 1) {
+ if (Math.abs(dist) > self.options.threshold && direction < 0) {
+ addition = w * direction;
+ } else {
+ addition = w * (tempOffsetPage - offsetPage + this.scroll.pages.length - 1);
+ }
+ }
+
+ }
+
+ this.initTween(addition - dist, 150, 'bounce');
+ },
+
+ initTween: function(dist, duration, eventName) {
+ if (dist === 0) {
+ return;
+ }
+ var elapse;
+ var self = this;
+ var startTime = new Date();
+
+ this.cancelTween();
+ this.emit(eventName + 'Start');
+
+ function round() {
+ elapse = new Date() - startTime;
+ if (elapse > duration) {
+ self.emit(eventName, {x: dist}, true);
+ self.emit(eventName + 'End');
+ return;
+ }
+
+ self.emit(eventName, {x: dist / duration * elapse}, false);
+ self.tweenRid = requestAnimationFrame(round);
+ }
+ round();
+ },
+
+ cancelTween: function() {
+ cancelAnimationFrame(this.tweenRid);
+ },
+
+ initMove: function() {
+ var self = this;
+ var scroll = this.scroll;
+ var intervalTween = self.options.intervalTween;
+
+ this.clearMove();
+
+ function round() {
+ if ((scroll.currentIndex === scroll.pages.length - 1) && !scroll.options.loop) {
+ self.initTween(-self.wrapSize.width() * (scroll.pages.length - 1), 200, 'autoPlay');
+ } else {
+ self.initTween(self.wrapSize.width(), 200, 'autoPlay');
+ }
+ self.moveTid = setTimeout(round, intervalTween);
+ }
+ self.moveTid = setTimeout(round, intervalTween);
+ },
+
+ clearMove: function() {
+ clearTimeout(this.moveTid);
+ }
+});
+
+export default SpringDummy;
+
diff --git a/packages/swipe/src/swipe-item.vue b/packages/swipe/src/swipe-item.vue
new file mode 100644
index 000000000..0a7ea92b5
--- /dev/null
+++ b/packages/swipe/src/swipe-item.vue
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
diff --git a/packages/swipe/src/swipe.vue b/packages/swipe/src/swipe.vue
new file mode 100644
index 000000000..83e641a6a
--- /dev/null
+++ b/packages/swipe/src/swipe.vue
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
diff --git a/packages/swipe/src/utils.js b/packages/swipe/src/utils.js
new file mode 100755
index 000000000..1bcbebaf2
--- /dev/null
+++ b/packages/swipe/src/utils.js
@@ -0,0 +1,70 @@
+'use strict';
+
+var extend = Object.assign.bind(Object);
+
+function EventEmitter() {
+ this.__events = {};
+}
+EventEmitter.prototype = {
+ on: function(name, cb) {
+ this.__events[name] || (this.__events[name] = []);
+ this.__events[name].push(cb);
+ return this;
+ },
+ emit: function(name) {
+ var arr = this.__events[name];
+ var argus = Array.prototype.slice.call(arguments, 1);
+ var self = this;
+ if (arr) {
+ arr.forEach(function(cb) {
+ cb.apply(self, argus);
+ })
+ }
+ },
+ removeListener: function(name, fn) {
+ if (this.__events[name] == undefined) {
+ return;
+ }
+ let index;
+ if (fn) {
+ index = this.__events[name].indexOf(fn);
+ if (index > 0) {
+ this.__events[name].splice(index, 1);
+ }
+ } else {
+ delete this.__events[name];
+ }
+ }
+};
+
+const requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
+ window.webkitRequestAnimationFrame || window.msRequestAnimationFrame ||
+ function(callback, element) {
+ return window.setTimeout(callback, 1000 / 60);
+ };
+
+const cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame ||
+ window.webkitCancelAnimationFrame || window.msRequestAnimationFrame ||
+ function(id) {
+ clearTimeout(id);
+ };
+
+const bindEvents = (elem, eventNames, fn) => {
+ eventNames = eventNames.split(/\s+/)
+ eventNames.forEach(eventName => elem.addEventListener(eventName, fn))
+}
+
+const removeEvents = (elem, eventNames, fn) => {
+ eventNames = eventNames.split(/\s+/)
+ eventNames.forEach(eventName => elem.removeEventListener(eventName, fn))
+}
+
+export {
+ extend,
+ EventEmitter,
+ requestAnimationFrame,
+ cancelAnimationFrame,
+ bindEvents,
+ removeEvents
+};
+
diff --git a/packages/zanui-css/src/index.css b/packages/zanui-css/src/index.css
index 9dbed8807..11f7b10de 100644
--- a/packages/zanui-css/src/index.css
+++ b/packages/zanui-css/src/index.css
@@ -25,3 +25,4 @@
@import './actionsheet.css';
@import './quantity.css';
@import './progress.css';
+@import './swipe.css';
diff --git a/packages/zanui-css/src/swipe.css b/packages/zanui-css/src/swipe.css
new file mode 100644
index 000000000..16a4debfc
--- /dev/null
+++ b/packages/zanui-css/src/swipe.css
@@ -0,0 +1,24 @@
+@component-namespace zan {
+ @b swipe {
+ position: relative;
+ overflow: hidden;
+ width: 100%;
+ }
+
+ @b swipe-item {
+ display: none;
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ text-align: center;
+
+ img {
+ max-width: 100%;
+ max-height: 100%;
+ }
+
+ &:first-child {
+ display: block;
+ }
+ }
+}
diff --git a/src/index.js b/src/index.js
index 04f0431c6..55ec5da76 100644
--- a/src/index.js
+++ b/src/index.js
@@ -30,6 +30,8 @@ import Row from '../packages/row/index.js';
import Actionsheet from '../packages/actionsheet/index.js';
import Quantity from '../packages/quantity/index.js';
import Progress from '../packages/progress/index.js';
+import Swipe from '../packages/swipe/index.js';
+import SwipeItem from '../packages/swipe-item/index.js';
const install = function(Vue) {
if (install.installed) return;
@@ -62,6 +64,8 @@ const install = function(Vue) {
Vue.component(Actionsheet.name, Actionsheet);
Vue.component(Quantity.name, Quantity);
Vue.component(Progress.name, Progress);
+ Vue.component(Swipe.name, Swipe);
+ Vue.component(SwipeItem.name, SwipeItem);
};
// auto install
@@ -103,5 +107,7 @@ module.exports = {
Row,
Actionsheet,
Quantity,
- Progress
+ Progress,
+ Swipe,
+ SwipeItem
};