diff --git a/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/class-names.js b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/class-names.js
new file mode 100644
index 0000000000000000000000000000000000000000..7a3a99f1c71c68f33e4bb495abf737e73b187d85
--- /dev/null
+++ b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/class-names.js
@@ -0,0 +1,29 @@
+const hasOwn = {}.hasOwnProperty;
+
+export function classNames() {
+ const classes = [];
+
+ for (let i = 0; i < arguments.length; i++) {
+ const arg = arguments[i];
+ if (!arg) continue;
+
+ const argType = typeof arg;
+
+ if (argType === 'string' || argType === 'number') {
+ classes.push(arg);
+ } else if (Array.isArray(arg) && arg.length) {
+ const inner = classNames.apply(null, arg);
+ if (inner) {
+ classes.push(inner);
+ }
+ } else if (argType === 'object') {
+ for (const key in arg) {
+ if (hasOwn.call(arg, key) && arg[key]) {
+ classes.push(key);
+ }
+ }
+ }
+ }
+
+ return classes.join(' ');
+};
diff --git a/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/create.js b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/create.js
new file mode 100644
index 0000000000000000000000000000000000000000..6d76447b98365f3e24231a087b582475b75db2fc
--- /dev/null
+++ b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/create.js
@@ -0,0 +1,40 @@
+import { basic } from '../mixins/basic';
+import { observe } from '../mixins/observer/index';
+
+export function create(sfc) {
+ // map props to properties
+ if (sfc.props) {
+ sfc.properties = sfc.props;
+ delete sfc.props;
+ }
+
+ // map mixins to behaviors
+ if (sfc.mixins) {
+ sfc.behaviors = sfc.mixins;
+ delete sfc.mixins;
+ }
+
+ // map classes to externalClasses
+ sfc.externalClasses = sfc.classes || [];
+ delete sfc.classes;
+
+ // add default externalClasses
+ sfc.externalClasses.push('custom-class');
+
+ // add default behaviors
+ sfc.behaviors = sfc.behaviors || [];
+ sfc.behaviors.push(basic);
+
+ // add default options
+ sfc.options = sfc.options || {};
+ sfc.options.multipleSlots = true;
+ sfc.options.addGlobalClass = true;
+
+ // map field to form-field behavior
+ if (sfc.field) {
+ sfc.behaviors.push('wx://form-field');
+ }
+
+ observe(sfc);
+ Component(sfc);
+};
diff --git a/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/index.wxss b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/index.wxss
new file mode 100644
index 0000000000000000000000000000000000000000..c3925180745f28c13970f07950eb59901c586ecb
--- /dev/null
+++ b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/index.wxss
@@ -0,0 +1 @@
+.van-ellipsis{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.van-multi-ellipsis--l2{overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}.van-multi-ellipsis--l3{overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical}.van-clearfix::after{content:'';display:table;clear:both}.van-hairline,.van-hairline--bottom,.van-hairline--left,.van-hairline--right,.van-hairline--surround,.van-hairline--top,.van-hairline--top-bottom{position:relative}.van-hairline--bottom::after,.van-hairline--left::after,.van-hairline--right::after,.van-hairline--surround::after,.van-hairline--top-bottom::after,.van-hairline--top::after,.van-hairline::after{content:'';position:absolute;top:-50%;left:-50%;right:-50%;bottom:-50%;-webkit-transform:scale(.5);transform:scale(.5);pointer-events:none;box-sizing:border-box;border:0 solid #eee}.van-hairline--top::after{border-top-width:1px}.van-hairline--left::after{border-left-width:1px}.van-hairline--right::after{border-right-width:1px}.van-hairline--bottom::after{border-bottom-width:1px}.van-hairline--top-bottom::after{border-width:1px 0}.van-hairline--surround::after{border-width:1px}
\ No newline at end of file
diff --git a/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/style/clearfix.wxss b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/style/clearfix.wxss
new file mode 100644
index 0000000000000000000000000000000000000000..df65a0a492b47ba614a07432acedd7f0679be540
--- /dev/null
+++ b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/style/clearfix.wxss
@@ -0,0 +1 @@
+.van-clearfix::after{content:'';display:table;clear:both}
\ No newline at end of file
diff --git a/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/style/ellipsis.wxss b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/style/ellipsis.wxss
new file mode 100644
index 0000000000000000000000000000000000000000..94eba8e273614b71007e95e73728fb3a3c7ee6a8
--- /dev/null
+++ b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/style/ellipsis.wxss
@@ -0,0 +1 @@
+.van-ellipsis{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.van-multi-ellipsis--l2{overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}.van-multi-ellipsis--l3{overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical}
\ No newline at end of file
diff --git a/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/style/hairline.wxss b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/style/hairline.wxss
new file mode 100644
index 0000000000000000000000000000000000000000..8b81d62231822c36efb29271dbb8597b97f19a8e
--- /dev/null
+++ b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/style/hairline.wxss
@@ -0,0 +1 @@
+.van-hairline,.van-hairline--bottom,.van-hairline--left,.van-hairline--right,.van-hairline--surround,.van-hairline--top,.van-hairline--top-bottom{position:relative}.van-hairline--bottom::after,.van-hairline--left::after,.van-hairline--right::after,.van-hairline--surround::after,.van-hairline--top-bottom::after,.van-hairline--top::after,.van-hairline::after{content:'';position:absolute;top:-50%;left:-50%;right:-50%;bottom:-50%;-webkit-transform:scale(.5);transform:scale(.5);pointer-events:none;box-sizing:border-box;border:0 solid #eee}.van-hairline--top::after{border-top-width:1px}.van-hairline--left::after{border-left-width:1px}.van-hairline--right::after{border-right-width:1px}.van-hairline--bottom::after{border-bottom-width:1px}.van-hairline--top-bottom::after{border-width:1px 0}.van-hairline--surround::after{border-width:1px}
\ No newline at end of file
diff --git a/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/style/mixins/clearfix.wxss b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/style/mixins/clearfix.wxss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/style/mixins/ellipsis.wxss b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/style/mixins/ellipsis.wxss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/style/mixins/hairline.wxss b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/style/mixins/hairline.wxss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/style/var.wxss b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/style/var.wxss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/utils.js b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/utils.js
new file mode 100644
index 0000000000000000000000000000000000000000..71ddfc644263c041976bc5c6bc300257074b8277
--- /dev/null
+++ b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/common/utils.js
@@ -0,0 +1,13 @@
+function isDef(value) {
+ return value !== undefined && value !== null;
+}
+
+function isObj(x) {
+ const type = typeof x;
+ return x !== null && (type === 'object' || type === 'function');
+}
+
+export {
+ isObj,
+ isDef
+};
diff --git a/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/mixins/basic.js b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/mixins/basic.js
new file mode 100644
index 0000000000000000000000000000000000000000..bad37d454d9a416b9bf1b041b5a891c029797878
--- /dev/null
+++ b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/mixins/basic.js
@@ -0,0 +1,28 @@
+import { classNames } from '../common/class-names';
+
+export const basic = Behavior({
+ methods: {
+ classNames,
+
+ $emit() {
+ this.triggerEvent.apply(this, arguments);
+ },
+
+ getRect(selector, all) {
+ return new Promise((resolve, reject) => {
+ wx.createSelectorQuery()
+ .in(this)[all ? 'selectAll' : 'select'](selector)
+ .boundingClientRect(rect => {
+ if (all && Array.isArray(rect) && rect.length) {
+ resolve(rect);
+ }
+
+ if (!all && rect) {
+ resolve(rect);
+ }
+ })
+ .exec();
+ });
+ }
+ }
+});
diff --git a/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/mixins/button.js b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/mixins/button.js
new file mode 100644
index 0000000000000000000000000000000000000000..fd15aea83399771ca297633494886507df8bc004
--- /dev/null
+++ b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/mixins/button.js
@@ -0,0 +1,50 @@
+export const button = Behavior({
+ properties: {
+ loading: Boolean,
+ openType: String,
+ appParameter: String,
+ sendMessageTitle: String,
+ sendMessagePath: String,
+ sendMessageImg: String,
+ showMessageCard: String,
+ hoverStopPropagation: Boolean,
+ hoverStartTime: {
+ type: Number,
+ value: 20
+ },
+ hoverStayTime: {
+ type: Number,
+ value: 70
+ },
+ lang: {
+ type: String,
+ value: 'en'
+ },
+ sessionFrom: {
+ type: String,
+ value: ''
+ }
+ },
+
+ methods: {
+ bindgetuserinfo(event = {}) {
+ this.$emit('getuserinfo', event.detail);
+ },
+
+ bindcontact(event = {}) {
+ this.$emit('contact', event.detail);
+ },
+
+ bindgetphonenumber(event = {}) {
+ this.$emit('getphonenumber', event.detail);
+ },
+
+ bindopensetting(event = {}) {
+ this.$emit('opensetting', event.detail);
+ },
+
+ binderror(event = {}) {
+ this.$emit('error', event.detail);
+ }
+ }
+});
diff --git a/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/mixins/observer/behavior.js b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/mixins/observer/behavior.js
new file mode 100644
index 0000000000000000000000000000000000000000..9162ef043df4e000872a27f4b857b710acf87df4
--- /dev/null
+++ b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/mixins/observer/behavior.js
@@ -0,0 +1,36 @@
+export const behavior = Behavior({
+ created() {
+ if (!this.$options) {
+ return;
+ }
+
+ const cache = {};
+ const { setData } = this;
+ const { computed } = this.$options();
+ const keys = Object.keys(computed);
+
+ const calcComputed = () => {
+ const needUpdate = {};
+ keys.forEach(key => {
+ const value = computed[key].call(this);
+
+ if (cache[key] !== value) {
+ cache[key] = needUpdate[key] = value;
+ }
+ });
+
+ return needUpdate;
+ };
+
+ Object.defineProperty(this, 'setData', { writable: true });
+
+ this.setData = (data, callback) => {
+ data && setData.call(this, data, callback);
+ setData.call(this, calcComputed());
+ };
+ },
+
+ attached() {
+ this.setData();
+ }
+});
diff --git a/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/mixins/observer/index.js b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/mixins/observer/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..0cba0aecb477753399ac5876f245d85fd91d92f1
--- /dev/null
+++ b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/mixins/observer/index.js
@@ -0,0 +1,14 @@
+import { behavior } from './behavior';
+import { observeProps } from './props';
+
+export function observe(sfc) {
+ if (sfc.computed) {
+ sfc.behaviors.push(behavior);
+ sfc.methods = sfc.methods || {};
+ sfc.methods.$options = () => sfc;
+
+ if (sfc.properties) {
+ observeProps(sfc.properties);
+ }
+ }
+}
diff --git a/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/mixins/observer/props.js b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/mixins/observer/props.js
new file mode 100644
index 0000000000000000000000000000000000000000..9f7923f64509dcefc7cf8c7782bcafc45e6dabfc
--- /dev/null
+++ b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/mixins/observer/props.js
@@ -0,0 +1,25 @@
+export function observeProps(props) {
+ if (!props) {
+ return;
+ }
+
+ Object.keys(props).forEach(key => {
+ let prop = props[key];
+ if (prop === null || !prop.type) {
+ prop = { type: prop };
+ }
+
+ let { observer } = prop;
+ prop.observer = function() {
+ if (observer) {
+ if (typeof observer === 'string') {
+ observer = this[observer];
+ }
+ observer.apply(this, arguments);
+ }
+ this.setData();
+ };
+
+ props[key] = prop;
+ });
+}
diff --git a/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/mixins/touch.js b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/mixins/touch.js
new file mode 100644
index 0000000000000000000000000000000000000000..ae2cb78e3211d7f5920e717e9d117f316182df9c
--- /dev/null
+++ b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/mixins/touch.js
@@ -0,0 +1,22 @@
+export const touch = Behavior({
+ methods: {
+ touchStart(event) {
+ this.direction = '';
+ this.deltaX = 0;
+ this.deltaY = 0;
+ this.offsetX = 0;
+ this.offsetY = 0;
+ this.startX = event.touches[0].clientX;
+ this.startY = event.touches[0].clientY;
+ },
+
+ touchMove(event) {
+ const touch = event.touches[0];
+ this.deltaX = touch.clientX - this.startX;
+ this.deltaY = touch.clientY - this.startY;
+ this.offsetX = Math.abs(this.deltaX);
+ this.offsetY = Math.abs(this.deltaY);
+ this.direction = this.offsetX > this.offsetY ? 'horizontal' : this.offsetX < this.offsetY ? 'vertical' : '';
+ }
+ }
+});
diff --git a/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/mixins/transition.js b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/mixins/transition.js
new file mode 100644
index 0000000000000000000000000000000000000000..e1a5ab872abe1055a2b9a98f7dbaa7c0db545181
--- /dev/null
+++ b/examples/wxcomponents-template/wxcomponents/vant-weapp/dist/mixins/transition.js
@@ -0,0 +1,54 @@
+export const transition = function(showDefaultValue) {
+ return Behavior({
+ properties: {
+ customStyle: String,
+ show: {
+ value: showDefaultValue,
+ type: Boolean,
+ observer(value) {
+ if (value) {
+ this.show();
+ } else {
+ this.setData({
+ type: 'leave'
+ });
+ }
+ }
+ },
+ duration: {
+ type: Number,
+ value: 300
+ }
+ },
+
+ data: {
+ type: '',
+ inited: false,
+ display: false
+ },
+
+ attached() {
+ if (this.data.show) {
+ this.show();
+ }
+ },
+
+ methods: {
+ show() {
+ this.setData({
+ inited: true,
+ display: true,
+ type: 'enter'
+ });
+ },
+
+ onAnimationEnd() {
+ if (!this.data.show) {
+ this.setData({
+ display: false
+ });
+ }
+ }
+ }
+ });
+};
diff --git a/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/animation-group/index.js b/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/animation-group/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..a857c44339232b5464c0c1a58c049789795a8ebd
--- /dev/null
+++ b/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/animation-group/index.js
@@ -0,0 +1,407 @@
+const ENTER = 'enter'
+const ENTERING = 'entering'
+const ENTERED = 'entered'
+const EXIT = 'exit'
+const EXITING = 'exiting'
+const EXITED = 'exited'
+const UNMOUNTED = 'unmounted'
+
+const TRANSITION = 'transition'
+const ANIMATION = 'animation'
+
+const TIMEOUT = 1000 / 60
+
+const defaultClassNames = {
+ enter: '', // 进入过渡的开始状态,在过渡过程完成之后移除
+ enterActive: '', // 进入过渡的结束状态,在过渡过程完成之后移除
+ enterDone: '', // 进入过渡的完成状态
+ exit: '', // 离开过渡的开始状态,在过渡过程完成之后移除
+ exitActive: '', // 离开过渡的结束状态,在过渡过程完成之后移除
+ exitDone: '', // 离开过渡的完成状态
+}
+
+Component({
+ externalClasses: ['wux-class'],
+ data: {
+ animateCss: '', // 动画样式
+ animateStatus: EXITED, // 动画状态,可选值 entering、entered、exiting、exited
+ isMounting: false, // 是否首次挂载
+ },
+ properties: {
+ // 触发组件进入或离开过渡的状态
+ in: {
+ type: Boolean,
+ value: false,
+ observer(newVal) {
+ if (this.data.isMounting) {
+ this.updated(newVal)
+ }
+ },
+ },
+ // 过渡的类名
+ classNames: {
+ type: null,
+ value: defaultClassNames,
+ },
+ // 过渡持续时间
+ duration: {
+ type: null,
+ value: null,
+ },
+ // 过渡动效的类型
+ type: {
+ type: String,
+ value: TRANSITION,
+ },
+ // 首次挂载时是否触发进入过渡
+ appear: {
+ type: Boolean,
+ value: false,
+ },
+ // 是否启用进入过渡
+ enter: {
+ type: Boolean,
+ value: true,
+ },
+ // 是否启用离开过渡
+ exit: {
+ type: Boolean,
+ value: true,
+ },
+ // 首次进入过渡时是否懒挂载组件
+ mountOnEnter: {
+ type: Boolean,
+ value: true,
+ },
+ // 离开过渡完成时是否卸载组件
+ unmountOnExit: {
+ type: Boolean,
+ value: true,
+ },
+ },
+ methods: {
+ /**
+ * 监听过渡或动画的回调函数
+ */
+ addEventListener() {
+ const { animateStatus } = this.data
+ const { enter, exit } = this.getTimeouts()
+
+ if (animateStatus === ENTERING && !enter && this.data.enter) {
+ this.performEntered()
+ }
+
+ if (animateStatus === EXITING && !exit && this.data.exit) {
+ this.performExited()
+ }
+ },
+ /**
+ * 会在 WXSS transition 或 wx.createAnimation 动画结束后触发
+ */
+ onTransitionEnd() {
+ if (this.data.type === TRANSITION) {
+ this.addEventListener()
+ }
+ },
+ /**
+ * 会在一个 WXSS animation 动画完成时触发
+ */
+ onAnimationEnd() {
+ if (this.data.type === ANIMATION) {
+ this.addEventListener()
+ }
+ },
+ /**
+ * 更新组件状态
+ * @param {String} nextStatus 下一状态,ENTERING 或 EXITING
+ * @param {Boolean} mounting 是否首次挂载
+ */
+ updateStatus(nextStatus, mounting = false) {
+ if (nextStatus !== null) {
+ this.cancelNextCallback()
+ this.isAppearing = mounting
+
+ if (nextStatus === ENTERING) {
+ this.performEnter()
+ } else {
+ this.performExit()
+ }
+ }
+ },
+ /**
+ * 进入过渡
+ */
+ performEnter() {
+ const { className, activeClassName } = this.getClassNames(ENTER)
+ const { enter } = this.getTimeouts()
+ const enterParams = {
+ animateStatus: ENTER,
+ animateCss: className,
+ }
+ const enteringParams = {
+ animateStatus: ENTERING,
+ animateCss: `${className} ${activeClassName}`,
+ }
+
+ // 若已禁用进入过渡,则更新状态至 ENTERED
+ if (!this.isAppearing && !this.data.enter) {
+ return this.performEntered()
+ }
+
+ // 第一阶段:设置进入过渡的开始状态,并触发 ENTER 事件
+ // 第二阶段:延迟一帧后,设置进入过渡的结束状态,并触发 ENTERING 事件
+ // 第三阶段:若已设置过渡的持续时间,则延迟指定时间后触发进入过渡完成 performEntered,否则等待触发 onTransitionEnd 或 onAnimationEnd
+ this.safeSetData(enterParams, () => {
+ this.triggerEvent('change', { animateStatus: ENTER })
+ this.triggerEvent(ENTER, { isAppearing: this.isAppearing })
+
+ // 由于有些时候不能正确的触发动画完成的回调,具体原因未知
+ // 所以采用延迟一帧的方式来确保可以触发回调
+ this.delayHandler(TIMEOUT, () => {
+ this.safeSetData(enteringParams, () => {
+ this.triggerEvent('change', { animateStatus: ENTERING })
+ this.triggerEvent(ENTERING, { isAppearing: this.isAppearing })
+
+ if (enter) {
+ this.delayHandler(enter, this.performEntered)
+ }
+ })
+ })
+ })
+ },
+ /**
+ * 进入过渡完成
+ */
+ performEntered() {
+ const { doneClassName } = this.getClassNames(ENTER)
+ const enteredParams = {
+ animateStatus: ENTERED,
+ animateCss: doneClassName,
+ }
+
+ // 第三阶段:设置进入过渡的完成状态,并触发 ENTERED 事件
+ this.safeSetData(enteredParams, () => {
+ this.triggerEvent('change', { animateStatus: ENTERED })
+ this.triggerEvent(ENTERED, { isAppearing: this.isAppearing })
+ })
+ },
+ /**
+ * 离开过渡
+ */
+ performExit() {
+ const { className, activeClassName } = this.getClassNames(EXIT)
+ const { exit } = this.getTimeouts()
+ const exitParams = {
+ animateStatus: EXIT,
+ animateCss: className,
+ }
+ const exitingParams = {
+ animateStatus: EXITING,
+ animateCss: `${className} ${activeClassName}`,
+ }
+
+ // 若已禁用离开过渡,则更新状态至 EXITED
+ if (!this.data.exit) {
+ return this.performExited()
+ }
+
+ // 第一阶段:设置离开过渡的开始状态,并触发 EXIT 事件
+ // 第二阶段:延迟一帧后,设置离开过渡的结束状态,并触发 EXITING 事件
+ // 第三阶段:若已设置过渡的持续时间,则延迟指定时间后触发离开过渡完成 performExited,否则等待触发 onTransitionEnd 或 onAnimationEnd
+ this.safeSetData(exitParams, () => {
+ this.triggerEvent('change', { animateStatus: EXIT })
+ this.triggerEvent(EXIT)
+
+ this.delayHandler(TIMEOUT, () => {
+ this.safeSetData(exitingParams, () => {
+ this.triggerEvent('change', { animateStatus: EXITING })
+ this.triggerEvent(EXITING)
+
+ if (exit) {
+ this.delayHandler(exit, this.performExited)
+ }
+ })
+ })
+ })
+ },
+ /**
+ * 离开过渡完成
+ */
+ performExited() {
+ const { doneClassName } = this.getClassNames(EXIT)
+ const exitedParams = {
+ animateStatus: EXITED,
+ animateCss: doneClassName,
+ }
+
+ // 第三阶段:设置离开过渡的完成状态,并触发 EXITED 事件
+ this.safeSetData(exitedParams, () => {
+ this.triggerEvent('change', { animateStatus: EXITED })
+ this.triggerEvent(EXITED)
+
+ // 判断离开过渡完成时是否卸载组件
+ if (this.data.unmountOnExit) {
+ this.setData({ animateStatus: UNMOUNTED }, () => {
+ this.triggerEvent('change', { animateStatus: UNMOUNTED })
+ })
+ }
+ })
+ },
+ /**
+ * 获取指定状态下的类名
+ * @param {String} type 过渡类型,enter 或 exit
+ */
+ getClassNames(type) {
+ const { classNames } = this.data
+ const className = typeof classNames !== 'string' ? classNames[type] : `${classNames}-${type}`
+ const activeClassName = typeof classNames !== 'string' ? classNames[`${type}Active`] : `${classNames}-${type}-active`
+ const doneClassName = typeof classNames !== 'string' ? classNames[`${type}Done`] : `${classNames}-${type}-done`
+
+ return {
+ className,
+ activeClassName,
+ doneClassName,
+ }
+ },
+ /**
+ * 获取过渡持续时间
+ */
+ getTimeouts() {
+ const { duration } = this.data
+
+ if (duration !== null && typeof duration === 'object') {
+ return {
+ enter: duration.enter,
+ exit: duration.exit,
+ }
+ } else if (typeof duration === 'number') {
+ return {
+ enter: duration,
+ exit: duration,
+ }
+ }
+
+ return {}
+ },
+ /**
+ * 属性值 in 被更改时的响应函数
+ * @param {Boolean} newVal 触发组件进入或离开过渡的状态
+ */
+ updated(newVal) {
+ let { animateStatus } = this.pendingData || this.data
+ let nextStatus = null
+
+ if (newVal) {
+ if (animateStatus === UNMOUNTED) {
+ animateStatus = EXITED
+ this.setData({ animateStatus: EXITED }, () => {
+ this.triggerEvent('change', { animateStatus: EXITED })
+ })
+ }
+ if (animateStatus !== ENTER && animateStatus !== ENTERING && animateStatus !== ENTERED) {
+ nextStatus = ENTERING
+ }
+ } else {
+ if (animateStatus === ENTER || animateStatus === ENTERING || animateStatus === ENTERED) {
+ nextStatus = EXITING
+ }
+ }
+
+ this.updateStatus(nextStatus)
+ },
+ /**
+ * safeSetData
+ * @param {Object} nextData 数据对象
+ * @param {Function} callback 回调函数
+ */
+ safeSetData(nextData, callback) {
+ this.pendingData = Object.assign({}, this.data, nextData)
+ callback = this.setNextCallback(callback)
+
+ this.setData(nextData, () => {
+ this.pendingData = null
+ callback()
+ })
+ },
+ /**
+ * 设置下一回调函数
+ * @param {Function} callback 回调函数
+ */
+ setNextCallback(callback) {
+ let active = true
+
+ this.nextCallback = (event) => {
+ if (active) {
+ active = false
+ this.nextCallback = null
+
+ callback.call(this, event)
+ }
+ }
+
+ this.nextCallback.cancel = () => {
+ active = false
+ }
+
+ return this.nextCallback
+ },
+ /**
+ * 取消下一回调函数
+ */
+ cancelNextCallback() {
+ if (this.nextCallback !== null) {
+ this.nextCallback.cancel()
+ this.nextCallback = null
+ }
+ },
+ /**
+ * 延迟一段时间触发回调
+ * @param {Number} timeout 延迟时间
+ * @param {Function} handler 回调函数
+ */
+ delayHandler(timeout, handler) {
+ if (timeout) {
+ this.setNextCallback(handler)
+ setTimeout(this.nextCallback, timeout)
+ }
+ },
+ /**
+ * 点击事件
+ */
+ onTap() {
+ this.triggerEvent('click')
+ },
+ },
+ created() {
+ this.nextCallback = null
+ },
+ attached() {
+ let animateStatus = null
+ let appearStatus = null
+
+ if (this.data.in) {
+ if (this.data.appear) {
+ animateStatus = EXITED
+ appearStatus = ENTERING
+ } else {
+ animateStatus = ENTERED
+ }
+ } else {
+ if (this.data.unmountOnExit || this.data.mountOnEnter) {
+ animateStatus = UNMOUNTED
+ } else {
+ animateStatus = EXITED
+ }
+ }
+
+ // 由于小程序组件首次挂载时 observer 事件总是优先于 attached 事件
+ // 所以使用 isMounting 来强制优先触发 attached 事件
+ this.safeSetData({ animateStatus, isMounting: true }, () => {
+ this.triggerEvent('change', { animateStatus })
+ this.updateStatus(appearStatus, true)
+ })
+ },
+ detached() {
+ this.cancelNextCallback()
+ },
+})
\ No newline at end of file
diff --git a/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/animation-group/index.json b/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/animation-group/index.json
new file mode 100644
index 0000000000000000000000000000000000000000..fba482a42bfc985e328144eb7f5cc005680b7c57
--- /dev/null
+++ b/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/animation-group/index.json
@@ -0,0 +1,3 @@
+{
+ "component": true
+}
\ No newline at end of file
diff --git a/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/animation-group/index.wxml b/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/animation-group/index.wxml
new file mode 100644
index 0000000000000000000000000000000000000000..6c646d7cc28f845983e5d6a447071568d0767b57
--- /dev/null
+++ b/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/animation-group/index.wxml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/animation-group/index.wxss b/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/animation-group/index.wxss
new file mode 100644
index 0000000000000000000000000000000000000000..9468c86bcc3ca255e06ff27432cbecde7c27b140
--- /dev/null
+++ b/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/animation-group/index.wxss
@@ -0,0 +1,250 @@
+.wux-animate--fadeIn-enter {
+ -webkit-transition: opacity .3s;
+ transition: opacity .3s;
+ opacity: 0;
+}
+.wux-animate--fadeIn-enter-active,
+.wux-animate--fadeIn-enter-done {
+ opacity: 1;
+}
+.wux-animate--fadeIn-exit {
+ -webkit-transition: opacity .3s;
+ transition: opacity .3s;
+ opacity: 1;
+}
+.wux-animate--fadeIn-exit-active,
+.wux-animate--fadeIn-exit-done {
+ opacity: 0;
+}
+.wux-animate--fadeInDown-enter {
+ -webkit-transition: opacity .3s, -webkit-transform .3s;
+ transition: opacity .3s, -webkit-transform .3s;
+ transition: opacity .3s, transform .3s;
+ transition: opacity .3s, transform .3s, -webkit-transform .3s;
+ opacity: 0;
+ -webkit-transform: translate3d(0, -100%, 0);
+ transform: translate3d(0, -100%, 0);
+}
+.wux-animate--fadeInDown-enter-active,
+.wux-animate--fadeInDown-enter-done {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+}
+.wux-animate--fadeInDown-exit {
+ -webkit-transition: opacity .3s, -webkit-transform .3s;
+ transition: opacity .3s, -webkit-transform .3s;
+ transition: opacity .3s, transform .3s;
+ transition: opacity .3s, transform .3s, -webkit-transform .3s;
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+}
+.wux-animate--fadeInDown-exit-active,
+.wux-animate--fadeInDown-exit-done {
+ opacity: 0;
+ -webkit-transform: translate3d(0, -100%, 0);
+ transform: translate3d(0, -100%, 0);
+}
+.wux-animate--fadeInLeft-enter {
+ -webkit-transition: opacity .3s, -webkit-transform .3s;
+ transition: opacity .3s, -webkit-transform .3s;
+ transition: opacity .3s, transform .3s;
+ transition: opacity .3s, transform .3s, -webkit-transform .3s;
+ opacity: 0;
+ -webkit-transform: translate3d(-100%, 0, 0);
+ transform: translate3d(-100%, 0, 0);
+}
+.wux-animate--fadeInLeft-enter-active,
+.wux-animate--fadeInLeft-enter-done {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+}
+.wux-animate--fadeInLeft-exit {
+ -webkit-transition: opacity .3s, -webkit-transform .3s;
+ transition: opacity .3s, -webkit-transform .3s;
+ transition: opacity .3s, transform .3s;
+ transition: opacity .3s, transform .3s, -webkit-transform .3s;
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+}
+.wux-animate--fadeInLeft-exit-active,
+.wux-animate--fadeInLeft-exit-done {
+ opacity: 0;
+ -webkit-transform: translate3d(-100%, 0, 0);
+ transform: translate3d(-100%, 0, 0);
+}
+.wux-animate--fadeInRight-enter {
+ -webkit-transition: opacity .3s, -webkit-transform .3s;
+ transition: opacity .3s, -webkit-transform .3s;
+ transition: opacity .3s, transform .3s;
+ transition: opacity .3s, transform .3s, -webkit-transform .3s;
+ opacity: 0;
+ -webkit-transform: translate3d(100%, 0, 0);
+ transform: translate3d(100%, 0, 0);
+}
+.wux-animate--fadeInRight-enter-active,
+.wux-animate--fadeInRight-enter-done {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+}
+.wux-animate--fadeInRight-exit {
+ -webkit-transition: opacity .3s, -webkit-transform .3s;
+ transition: opacity .3s, -webkit-transform .3s;
+ transition: opacity .3s, transform .3s;
+ transition: opacity .3s, transform .3s, -webkit-transform .3s;
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+}
+.wux-animate--fadeInRight-exit-active,
+.wux-animate--fadeInRight-exit-done {
+ opacity: 0;
+ -webkit-transform: translate3d(100%, 0, 0);
+ transform: translate3d(100%, 0, 0);
+}
+.wux-animate--fadeInUp-enter {
+ -webkit-transition: opacity .3s, -webkit-transform .3s;
+ transition: opacity .3s, -webkit-transform .3s;
+ transition: opacity .3s, transform .3s;
+ transition: opacity .3s, transform .3s, -webkit-transform .3s;
+ opacity: 0;
+ -webkit-transform: translate3d(0, 100%, 0);
+ transform: translate3d(0, 100%, 0);
+}
+.wux-animate--fadeInUp-enter-active,
+.wux-animate--fadeInUp-enter-done {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+}
+.wux-animate--fadeInUp-exit {
+ -webkit-transition: opacity .3s, -webkit-transform .3s;
+ transition: opacity .3s, -webkit-transform .3s;
+ transition: opacity .3s, transform .3s;
+ transition: opacity .3s, transform .3s, -webkit-transform .3s;
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
+}
+.wux-animate--fadeInUp-exit-active,
+.wux-animate--fadeInUp-exit-done {
+ opacity: 0;
+ -webkit-transform: translate3d(0, 100%, 0);
+ transform: translate3d(0, 100%, 0);
+}
+.wux-animate--slideInUp-enter {
+ -webkit-transition: -webkit-transform .3s;
+ transition: -webkit-transform .3s;
+ transition: transform .3s;
+ transition: transform .3s, -webkit-transform .3s;
+ -webkit-transform: translate3d(0, 100%, 0);
+ transform: translate3d(0, 100%, 0);
+ visibility: visible;
+}
+.wux-animate--slideInUp-enter-active,
+.wux-animate--slideInUp-enter-done {
+ -webkit-transform: translateZ(0);
+ transform: translateZ(0);
+}
+.wux-animate--slideInUp-exit {
+ -webkit-transition: -webkit-transform .3s;
+ transition: -webkit-transform .3s;
+ transition: transform .3s;
+ transition: transform .3s, -webkit-transform .3s;
+ -webkit-transform: translateZ(0);
+ transform: translateZ(0);
+}
+.wux-animate--slideInUp-exit-active,
+.wux-animate--slideInUp-exit-done {
+ -webkit-transform: translate3d(0, 100%, 0);
+ transform: translate3d(0, 100%, 0);
+ visibility: visible;
+}
+.wux-animate--slideInDown-enter {
+ -webkit-transition: -webkit-transform .3s;
+ transition: -webkit-transform .3s;
+ transition: transform .3s;
+ transition: transform .3s, -webkit-transform .3s;
+ -webkit-transform: translate3d(0, -100%, 0);
+ transform: translate3d(0, -100%, 0);
+ visibility: visible;
+}
+.wux-animate--slideInDown-enter-active,
+.wux-animate--slideInDown-enter-done {
+ -webkit-transform: translateZ(0);
+ transform: translateZ(0);
+}
+.wux-animate--slideInDown-exit {
+ -webkit-transition: -webkit-transform .3s;
+ transition: -webkit-transform .3s;
+ transition: transform .3s;
+ transition: transform .3s, -webkit-transform .3s;
+ -webkit-transform: translateZ(0);
+ transform: translateZ(0);
+}
+.wux-animate--slideInDown-exit-active,
+.wux-animate--slideInDown-exit-done {
+ -webkit-transform: translate3d(0, -100%, 0);
+ transform: translate3d(0, -100%, 0);
+ visibility: visible;
+}
+.wux-animate--slideInLeft-enter {
+ -webkit-transition: -webkit-transform .3s;
+ transition: -webkit-transform .3s;
+ transition: transform .3s;
+ transition: transform .3s, -webkit-transform .3s;
+ -webkit-transform: translate3d(-100%, 0, 0);
+ transform: translate3d(-100%, 0, 0);
+ visibility: visible;
+}
+.wux-animate--slideInLeft-enter-active,
+.wux-animate--slideInLeft-enter-done {
+ -webkit-transform: translateZ(0);
+ transform: translateZ(0);
+}
+.wux-animate--slideInLeft-exit {
+ -webkit-transition: -webkit-transform .3s;
+ transition: -webkit-transform .3s;
+ transition: transform .3s;
+ transition: transform .3s, -webkit-transform .3s;
+ -webkit-transform: translateZ(0);
+ transform: translateZ(0);
+}
+.wux-animate--slideInLeft-exit-active,
+.wux-animate--slideInLeft-exit-done {
+ -webkit-transform: translate3d(-100%, 0, 0);
+ transform: translate3d(-100%, 0, 0);
+ visibility: visible;
+}
+.wux-animate--slideInRight-enter {
+ -webkit-transition: -webkit-transform .3s;
+ transition: -webkit-transform .3s;
+ transition: transform .3s;
+ transition: transform .3s, -webkit-transform .3s;
+ -webkit-transform: translate3d(100%, 0, 0);
+ transform: translate3d(100%, 0, 0);
+ visibility: visible;
+}
+.wux-animate--slideInRight-enter-active,
+.wux-animate--slideInRight-enter-done {
+ -webkit-transform: translateZ(0);
+ transform: translateZ(0);
+}
+.wux-animate--slideInRight-exit {
+ -webkit-transition: -webkit-transform .3s;
+ transition: -webkit-transform .3s;
+ transition: transform .3s;
+ transition: transform .3s, -webkit-transform .3s;
+ -webkit-transform: translateZ(0);
+ transform: translateZ(0);
+}
+.wux-animate--slideInRight-exit-active,
+.wux-animate--slideInRight-exit-done {
+ -webkit-transform: translate3d(100%, 0, 0);
+ transform: translate3d(100%, 0, 0);
+ visibility: visible;
+}
diff --git a/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/backdrop/index.js b/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/backdrop/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..60ef4aeca7df8fdf46f38be3785bb5ab36bd1f48
--- /dev/null
+++ b/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/backdrop/index.js
@@ -0,0 +1,43 @@
+import baseBehavior from '../helpers/baseBehavior'
+
+Component({
+ behaviors: [baseBehavior],
+ externalClasses: ['wux-class'],
+ properties: {
+ transparent: {
+ type: Boolean,
+ value: false,
+ },
+ },
+ methods: {
+ /**
+ * 保持锁定
+ */
+ retain() {
+ if (typeof this.backdropHolds !== 'number' || !this.backdropHolds) {
+ this.backdropHolds = 0
+ }
+
+ this.backdropHolds = this.backdropHolds + 1
+
+ if (this.backdropHolds === 1) {
+ this.$$setData({ in: true })
+ }
+ },
+ /**
+ * 释放锁定
+ */
+ release() {
+ if (this.backdropHolds === 1) {
+ this.$$setData({ in: false })
+ }
+ this.backdropHolds = Math.max(0, this.backdropHolds - 1)
+ },
+ /**
+ * 点击事件
+ */
+ onClick() {
+ this.triggerEvent('click')
+ },
+ },
+})
\ No newline at end of file
diff --git a/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/backdrop/index.json b/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/backdrop/index.json
new file mode 100644
index 0000000000000000000000000000000000000000..9b9e7abfc6cffb4f868e60bf1b19ad7ec6e853ef
--- /dev/null
+++ b/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/backdrop/index.json
@@ -0,0 +1,6 @@
+{
+ "component": true,
+ "usingComponents": {
+ "wux-animation-group": "../animation-group/index"
+ }
+}
\ No newline at end of file
diff --git a/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/backdrop/index.wxml b/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/backdrop/index.wxml
new file mode 100644
index 0000000000000000000000000000000000000000..66be6180b3c58caf9bb6a7971287a94b4fca4a62
--- /dev/null
+++ b/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/backdrop/index.wxml
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/backdrop/index.wxss b/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/backdrop/index.wxss
new file mode 100644
index 0000000000000000000000000000000000000000..edfc7968f1e37e302f179406ae198a55ebda56d7
--- /dev/null
+++ b/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/backdrop/index.wxss
@@ -0,0 +1,15 @@
+.wux-backdrop {
+ background: rgba(0, 0, 0, 0.4);
+}
+.wux-backdrop,
+.wux-backdrop--transparent {
+ position: fixed;
+ z-index: 1000;
+ top: 0;
+ right: 0;
+ left: 0;
+ bottom: 0;
+}
+.wux-backdrop--transparent {
+ background: transparent;
+}
diff --git a/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/helpers/baseBehavior.js b/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/helpers/baseBehavior.js
new file mode 100644
index 0000000000000000000000000000000000000000..dd04f8153807ccd1ed04a6da9e1a55deb7dacedd
--- /dev/null
+++ b/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/helpers/baseBehavior.js
@@ -0,0 +1,77 @@
+/**
+ * Simple bind, faster than native
+ *
+ * @param {Function} fn
+ * @param {Object} ctx
+ * @return {Function}
+ */
+const bind = (fn, ctx) => {
+ return (...args) => {
+ return args.length ? fn.apply(ctx, args) : fn.call(ctx)
+ }
+}
+
+/**
+ * Object assign
+ */
+const assign = (...args) => Object.assign({}, ...args)
+
+export default Behavior({
+ properties: {
+ visible: {
+ type: Boolean,
+ value: false,
+ },
+ },
+ methods: {
+ /**
+ * 合并参数并绑定方法
+ *
+ * @param {Object} opts 参数对象
+ * @param {Object} fns 方法挂载的属性
+ */
+ $$mergeOptionsAndBindMethods(opts = {}, fns = this.fns) {
+ const options = Object.assign({}, opts)
+
+ for (const key in options) {
+ if (options.hasOwnProperty(key) && typeof options[key] === 'function') {
+ fns[key] = bind(options[key], this)
+ delete options[key]
+ }
+ }
+
+ return options
+ },
+ /**
+ * Promise setData
+ * @param {Array} args 参数对象
+ */
+ $$setData(...args) {
+ const params = assign({}, ...args)
+
+ return new Promise((resolve) => {
+ this.setData(params, resolve)
+ })
+ },
+ /**
+ * 延迟指定时间执行回调函数
+ * @param {Function} callback 回调函数
+ * @param {Number} timeout 延迟时间
+ */
+ $$requestAnimationFrame(callback = () => {}, timeout = 1000 / 60) {
+ return new Promise((resolve) => setTimeout(resolve, timeout)).then(callback)
+ },
+ },
+ /**
+ * 组件生命周期函数,在组件实例进入页面节点树时执行
+ */
+ created() {
+ this.fns = {}
+ },
+ /**
+ * 组件生命周期函数,在组件实例被从页面节点树移除时执行
+ */
+ detached() {
+ this.fns = {}
+ },
+})
\ No newline at end of file
diff --git a/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/helpers/colors.js b/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/helpers/colors.js
new file mode 100644
index 0000000000000000000000000000000000000000..3490eff72ad5c2ddc73f16b027c265ad21ece3f1
--- /dev/null
+++ b/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/helpers/colors.js
@@ -0,0 +1,18 @@
+export const colors = {
+ 'light': '#ddd',
+ 'stable': '#b2b2b2',
+ 'positive': '#387ef5',
+ 'calm': '#11c1f3',
+ 'balanced': '#33cd5f',
+ 'energized': '#ffc900',
+ 'assertive': '#ef473a',
+ 'royal': '#886aea',
+ 'dark': '#444',
+}
+
+export const isPresetColor = (color) => {
+ if (!color) {
+ return false
+ }
+ return colors[color] ? colors[color] : color
+}
\ No newline at end of file
diff --git a/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/helpers/gestures.js b/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/helpers/gestures.js
new file mode 100644
index 0000000000000000000000000000000000000000..908ee845eeaa4b7d9882e8bd783f5ba6806aa2e2
--- /dev/null
+++ b/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/helpers/gestures.js
@@ -0,0 +1,38 @@
+/**
+ * 获取触摸点位置信息
+ */
+export const getTouchPoints = (e, index = 0) => {
+ const { pageX: x, pageY: y } = e.touches[index] || e.changedTouches[index]
+ return {
+ x,
+ y,
+ }
+}
+
+/**
+ * 获取触摸点个数
+ */
+export const getPointsNumber = (e) => e.touches && e.touches.length || e.changedTouches && e.changedTouches.length
+
+/**
+ * 判断是否为同一点
+ */
+export const isEqualPoints = (p1, p2) => p1.x === p2.x && p1.y === p2.y
+
+/**
+ * 判断是否为相近的两点
+ */
+export const isNearbyPoints = (p1, p2, DOUBLE_TAP_RADIUS = 25) => {
+ const xMove = Math.abs(p1.x - p2.x)
+ const yMove = Math.abs(p1.y - p2.y)
+ return xMove < DOUBLE_TAP_RADIUS & yMove < DOUBLE_TAP_RADIUS
+}
+
+/**
+ * 获取两点之间的距离
+ */
+export const getPointsDistance = (p1, p2) => {
+ const xMove = Math.abs(p1.x - p2.x)
+ const yMove = Math.abs(p1.y - p2.y)
+ return Math.sqrt(xMove * xMove + yMove * yMove)
+}
\ No newline at end of file
diff --git a/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/helpers/mergeOptionsToData.js b/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/helpers/mergeOptionsToData.js
new file mode 100644
index 0000000000000000000000000000000000000000..632556bd228b3610ba20e690f1bca8c52226b685
--- /dev/null
+++ b/examples/wxcomponents-template/wxcomponents/wux-weapp/dist/helpers/mergeOptionsToData.js
@@ -0,0 +1,17 @@
+/**
+ * 过滤对象的函数属性
+ * @param {Object} opts
+ */
+const mergeOptionsToData = (opts = {}) => {
+ const options = Object.assign({}, opts)
+
+ for (const key in options) {
+ if (options.hasOwnProperty(key) && typeof options[key] === 'function') {
+ delete options[key]
+ }
+ }
+
+ return options
+}
+
+export default mergeOptionsToData
\ No newline at end of file