標(biāo)簽(空格分隔): 小程序
小程序的一套腳本語言雹熬,結(jié)合 WXML宁赤,可以構(gòu)建出頁面的結(jié)構(gòu)
WXS 的運(yùn)行環(huán)境和其他 JavaScript 代碼是隔離的,WXS 中不能調(diào)用其他 JavaScript 文件中定義的函數(shù),也不能調(diào)用小程序提供的API
頁面渲染
<!--wxml-->
<wxs module="m1">
var msg = "hello world";
module.exports.message = msg;
</wxs>
<view> {{m1.message}} </view>
數(shù)據(jù)處理
// page.js
Page({
data: {
array: [1, 2, 3, 4, 5, 1, 2, 3, 4]
}
})
<!--wxml-->
<!-- 下面的 getMax 函數(shù)萎羔,接受一個數(shù)組,且返回數(shù)組中最大的元素的值 -->
<wxs module="m1">
var getMax = function(array) {
var max = undefined;
for (var i = 0; i < array.length; ++i) {
max = max === undefined ?
array[i] :
(max >= array[i] ? max : array[i]);
}
return max;
}
module.exports.getMax = getMax;
</wxs>
<!-- 調(diào)用 wxs 里面的 getMax 函數(shù)寺董,參數(shù)為 page.js 里面的 array -->
<view> {{m1.getMax(array)}} </view>
WXS響應(yīng)事件
背景:一次 touchmove 的響應(yīng)需要經(jīng)過 2 次的邏輯層和渲染層的通信以及一次渲染覆糟,通信的耗時比較大。為了減少通信的次數(shù)遮咖,讓事件在視圖層(Webview)響應(yīng)滩字。
使用 WXS 函數(shù)用來響應(yīng)小程序事件,目前只能響應(yīng)內(nèi)置組件的事件御吞,不支持自定義組件事件
WXS 函數(shù)的除了純邏輯的運(yùn)算麦箍,還可以通過封裝好的ComponentDescriptor 實(shí)例來訪問以及設(shè)置組件的 class 和樣式,對于交互動畫陶珠,設(shè)置 style 和 class 足夠了
var wxsFunction = function(event, ownerInstance) {
var instance = ownerInstance.selectComponent('.classSelector') // 返回組件的實(shí)例
instance.setStyle({
"font-size": "14px" // 支持rpx
})
instance.getDataset()
instance.setClass(className)
// ...
return false // 不往上冒泡挟裂,相當(dāng)于調(diào)用了同時調(diào)用了stopPropagation和preventDefault
}
其中入?yún)?event 是小程序事件對象基礎(chǔ)上多了 event.instance 來表示觸發(fā)事件的組件的 ComponentDescriptor 實(shí)例。ownerInstance 表示的是觸發(fā)事件的組件所在的組件的 ComponentDescriptor 實(shí)例揍诽,如果觸發(fā)事件的組件是在頁面內(nèi)的诀蓉,ownerInstance 表示的是頁面實(shí)例。
方法 | 參數(shù) | 描述 |
---|---|---|
selectComponent | selector對象 | 返回組件的 ComponentDescriptor 實(shí)例暑脆。 |
selectAllComponents | selector對象數(shù)組 | 返回組件的 ComponentDescriptor 實(shí)例數(shù)組渠啤。 |
setStyle | Object/string | 設(shè)置組件樣式,支持rpx添吗。設(shè)置的樣式優(yōu)先級比組件 wxml 里面定義的樣式高沥曹。不能設(shè)置最頂層頁面的樣式。 |
addClass/removeClass/ hasClass | string | 設(shè)置組件的 class碟联。設(shè)置的 class 優(yōu)先級比組件 wxml 里面定義的 class 高妓美。不能設(shè)置最頂層頁面的 class。 |
getDataset | 無 | 返回當(dāng)前組件/頁面的 dataset 對象 |
callMethod | (funcName:string, args:object) | 調(diào)用當(dāng)前組件/頁面在邏輯層(App Service)定義的函數(shù)鲤孵。funcName表示函數(shù)名稱壶栋,args表示函數(shù)的參數(shù)。 |
requestAnimationFrame | Function | 和原生 requestAnimationFrame 一樣普监。用于設(shè)置動畫委刘。 |
getState | 無 | 返回一個object對象,當(dāng)有局部變量需要存儲起來后續(xù)使用的時候用這個方法鹰椒。 |
triggerEvent | (eventName, detail) | 和組件的triggerEvent一致锡移。 |
WXS 運(yùn)行在視圖層(Webview),里面的邏輯畢竟能做的事件比較少漆际,需要有一個機(jī)制和邏輯層(App Service)開發(fā)者的代碼通信淆珊,上面的
callMethod
是 WXS 里面調(diào)用邏輯層(App Service)開發(fā)者的代碼的方法
使用方法
WXML定義事件:
<wxs module="test" src="./test.wxs"></wxs>
<view change:prop="{{test.propObserver}}" prop="{{propValue}}" bindtouchmove="{{test.touchmove}}" class="movable"></view>
上面的change:prop(屬性前面帶change:前綴)是在 prop 屬性被設(shè)置的時候觸發(fā) WXS 函數(shù),值必須用{{}}括起來奸汇。類似 Component 定義的 properties 里面的 observer 屬性施符,在setData({propValue: newValue})調(diào)用之后會觸發(fā)往声。
注意:WXS函數(shù)必須用{{}}括起來。當(dāng) prop 的值被設(shè)置 WXS 函數(shù)就會觸發(fā)戳吝,而不只是值發(fā)生改變浩销,所以在頁面初始化的時候會調(diào)用一次WxsPropObserver的函數(shù)。
WXS文件test.wxs里面定義并導(dǎo)出事件處理函數(shù)和屬性改變觸發(fā)的函數(shù):
module.exports = {
touchmove: function(event, instance) {
console.log('log event', JSON.stringify(event))
},
propObserver: function(newValue, oldValue, ownerInstance, instance) {
console.log('prop observer', newValue, oldValue)
}
}
示例:
/* wxs文件 */
swipeDirection = 0; //是否觸發(fā)水平滑動 0:未觸發(fā) 1:觸發(fā)水平滑動 2:觸發(fā)垂直滑動
var touchstart = function(event, ownerInstance) {
var ins = event.instance
var st = ins.getState()
st.isMoving = true
st.startX = event.touches[0].clientX;
st.startY = event.touches[0].clientY;
var _data = event.currentTarget.dataset,
_loc = _data.loc,
_twoIndex = _data.index;
st.index = _loc;
st.twoIndex = _twoIndex;
}
var touchmove = function(event, ownerInstance) {
var ins = event.instance
var st = ins.getState()
if (!st.isMoving) return
if (event.touches.length == 1) {
if (swipeDirection === 2) {
return;
}
var pagex = event.touches[0].pageX - st.startX
var movex = pagex > 0 ? Math.min(st.delBtnWidth, pagex) : Math.max(-st.delBtnWidth, pagex)
// //已觸發(fā)垂直滑動听哭,由scroll-view處理滑動操作;
// //手指起始點(diǎn)位置與移動期間的差值
var pagey = event.touches[0].pageY - st.startY;
// //未觸發(fā)滑動方向
if (swipeDirection === 0) {
//觸發(fā)垂直操作
if (Math.abs(pagey) > 20) {
swipeDirection = 2;
return;
}
//觸發(fā)水平操作
if (Math.abs(movex) > 4) {
swipeDirection = 1;
ownerInstance.callMethod('scrollYFn', {
scrollY: false
})
} else {
// return;
}
}
// 往回滑動的情況
var goods = ownerInstance.selectAllComponents('.good-con')
goods.map(function(item) {
var itemOut = item.getState().out
if (itemOut) {
item.setStyle({
'transform': 'translateX(0px)',
'transition': 'transform 0.4s'
})
}
})
if (st.out) {
// 已經(jīng)是劃出來了慢洋,還要往左滑動,忽略
if (movex < 0) return
ins.setStyle({
'transform': 'translateX(' + (st.transformx + movex) + 'px)',
'transition': ''
})
return
}
if (movex > 0) movex = 0
st.transformx = movex
ins.setStyle({
'transform': 'translateX(' + movex + 'px)',
'transition': ''
})
ownerInstance.callMethod('wxsTouchmove')
return
}
}
var touchend = function(event, ownerInstance) {
var ins = event.instance
var st = ins.getState()
if (!st.isMoving) return
st.isMoving = false
swipeDirection = 0;
if (event.changedTouches.length == 1) {
//手指移動結(jié)束后水平位置
var endX = event.changedTouches[0].clientX;
var endY = event.changedTouches[0].clientY;
//觸摸開始與結(jié)束陆盘,手指移動的距離
var disX = st.startX - endX;
var disY = st.startY - endY;
var delBtnWidth = st.delBtnWidth;
// if ((Math.abs(disY) > 10 && disX < delBtnWidth) || disX<0) {
if (Math.abs(event.changedTouches[0].pageX - st.startX) < st.throttle || event.changedTouches[0].pageX - st.startX > 0) {
st.out = false
ins.setStyle({
'transform': 'translateX(0px)',
'transition': 'transform 0.4s'
})
ownerInstance.callMethod('scrollYFn', {
scrollY: true
})
return;
}
//如果距離小于刪除按鈕的1/2普筹,不顯示刪除按鈕
var txtStyle = disX > delBtnWidth / 2 ? "-" + delBtnWidth : "0";
ins.setStyle({
'transform': 'translateX(' + (txtStyle) + 'px)',
'transition': 'transform 0.4s'
})
st.out = true;
st.transformx = -Math.abs(txtStyle)
if (disX <= delBtnWidth / 2) {
ownerInstance.callMethod('scrollYFn', {
scrollY: true
})
}
}
ownerInstance.callMethod('show')
}
var hideButton = function(event, ownerInstance) {
var goods = ownerInstance.selectAllComponents('.good-con')
goods.map(function(item) {
if (item.hasClass('touchmove')) {
item.setStyle({
'transform': 'translateX(0px)',
'transition': 'transform 500ms'
})
}
})
st.transformx = 0
ownerInstance.callMethod('buttonTapByWxs', {
id: event.currentTarget.dataset.id
})
return false
}
var delBtnWidthFn = function(newVal, oldVal, ownerInstance, ins) {
var st = ins.getState()
st.transformx = 0
st.delBtnWidth = newVal;
st.throttle = 40 // 固定值
}
module.exports = {
touchstart: touchstart,
touchmove: touchmove,
touchend: touchend,
hideButton: hideButton,
delBtnWidthFn: delBtnWidthFn
}
//wxml
<wxs src="./../common/slideview.wxs" module="slideHander"></wxs>
//...
<view class="good-con flex" bindtouchstart="{{slideHander.touchstart}}" bindtouchmove="{{slideHander.touchmove}}" bindtouchend="{{slideHander.touchend}}" delbtnwidth="{{delBtnWidth}}" change:delbtnwidth="{{slideHander.delBtnWidthFn}}"></view>