HomeDocs
跳到主要内容

sjs 响应事件

背景

有频繁用户交互的效果在小程序上表现是比较卡顿的,例如页面有 2 个元素 A 和 B,用户在 A 上做 touchmove 手势,要求 B 也跟随移动。一次 touchmove 事件的响应过程为:

  1. touchmove 事件从视图层(Webview)抛到逻辑层(App Service)
  2. 逻辑层(App Service)处理 touchmove 事件,再通过 setData 来改变 B 的位置

一次 touchmove 的响应需要经过 2 次的逻辑层和渲染层的通信以及一次渲染,通信的耗时比较大。此外 setData 渲染也会阻塞其它脚本执行,导致了整个用户交互的动画过程会有延迟。

实现方案

本方案基本的思路是减少通信的次数,让事件在视图层(Webview)响应。小程序的框架分为视图层(Webview)和逻辑层(App Service),这样分层的目的是管控,开发者的代码只能运行在逻辑层(App Service),而这个思路就必须要让开发者的代码运行在视图层(Webview),如下图所示的流程:

SJS 流程图

使用 sjs 函数用来响应小程序事件,目前只能响应内置组件的事件,不支持自定义组件事件。sjs 函数的除了纯逻辑的运算,还可以通过封装好的 ComponentDescriptor 实例来访问以及设置组件的 class 和样式,对于交互动画,设置 style 和 class 足够了。sjs 函数的例子如下:

var sjsFunction = function (event, ownerInstance) {
var instance = ownerInstance.selectComponent(".classSelector"); // 返回组件的实例
instance.setStyle({
"font-size": "14px",
});
instance.getDataset();
instance.setClass(className);
// ...
return false; // 不往上冒泡,相当于调用了同时调用了 stopPropagation 和preventDefault
};

其中入参 event 是小程序事件对象基础上多了 event.instance 来表示触发事件的组件的 ComponentDescriptor 实例。ownerInstance 表示的是触发事件的组件所在的组件的 ComponentDescriptor 实例,如果触发事件的组件是在页面内的,ownerInstance 表示的是页面实例。

ComponentDescriptor 的定义如下:

方法参数描述
selectComponentselector 对象,只能是当前触发事件元素的子元素返回组件的 ComponentDescriptor 实例。
setStyleObject/string设置组件样式。设置的样式优先级比组件 dlt 里面定义的样式高。不能设置最顶层页面的样式。
addClass/removeClass/hasClassstring设置组件的 class。设置的 class 优先级比组件 dlt 里面定义的 class 高。不能设置最顶层页面的 class。
getDataSet返回当前组件/页面的 dataset 对象
callMethod(funcName:string, args:object)调用当前组件/页面在逻辑层(App Service)定义的函数。funcName 表示函数名称,args 表示函数的参数。
requestAnimationFrameFunction和原生 requestAnimationFrame 一样。用于设置动画。
getComputedStyleArray.<string> 参数与 SelectorQuery 的 computedStyle 一致。
setTimeout(Function, Number)与原生 setTimeout 一致。用于创建定时器。
clearTimeoutNumber与原生 clearTimeout 一致。用于清除定时器。
getBoundingClientRect返回值与 SelectorQuery 的 boundingClientRect 一致。

sjs 运行在视图层(Webview),里面的逻辑毕竟能做的事件比较少,需要有一个机制和逻辑层(App Service)开发者的代码通信,上面的 callMethod 是 sjs 里面调用逻辑层(App Service)开发者的代码的方法。

使用方法

  • dlt 定义事件
<import-sjs module="test" src="./test.sjs"></import-sjs>
<view bind:touchmove="{{test.touchmove}}" class="movable"></view>

注意: sjs 函数必须用{{}}括起来。

  • sjs 文件 test.sjs 里面定义并导出事件处理函数和属性改变触发的函数:
export default {
touchmove: function (event, instance) {
var touch = event.touches[0] || event.changedTouches[0];
var pageX = touch.pageX;
var pageY = touch.pageY;
var left = pageX - startX + lastLeft;
var top = pageY - startY + lastTop;
startX = pageX;
startY = pageY;
lastLeft = left;
lastTop = top;

ins.selectComponent(".movable").setStyle({
left: left + "px",
top: top + "px",
});
},
};
Privacy agreementDeveloper agreementcontact us: developer_service.mi@transsion.com © 2024 MiniApp. All Rights Reserved.