文章目录
- 存在问题 曲线救国
- 新建一个工程,创建JS卡片,工程结构如下: 观察工程结构与HarmonyOS开发大致一样,区别在于FormAbility是交给JS侧来管理的,因为OpenHarmony的开发没有用到JAVA,同样的卡片样式是在widget里面进行管理。 那我们要做一个动态的卡片,只需要在样式里面添加image组件,放一张gif图就行了。但似乎不会主动循环?开个定时器刷新路径,似乎因为是同一张图片并不会主动进行刷新。 于是我陷入的沉思… 我想到了两种解决方法: 存放两张一样的gif图,开个定时器来回切换。 分解成帧动画,逐帧切换。这样更麻烦了,但是反过来想,如果封装得好,我们就可以自行DIY动画了,比如定格动画。
- 这里我们只针对卡片的动态实现,至于服务卡片细节工程结构与其含义,咱不如直接看官网文档,这里参考HarmonyOS官网和OpenHarmony官网都行,两者基本同步。
HarmonyOS官网FA卡片讲解。
OpenHarmony官网FA卡片讲解。
- 这里可以分析出,我们在widget中对卡片样式进行设计,在FormAbility中对卡片进行操作就行了,本案例只针对卡片设计,所以暂时不管MainAbility了,让他就是HelloWorld就行。
-
- 我在最初构建卡片的时候选择22,44大小的卡片,这里其实随便就行。 然后按照之前曲线救国的思路,这里只需要一个image组件就好了,同时为了方便测试,给图片路径开个通路,方便JS侧进行图片切换。 <div> <div id="wrapper"> <image id="image" src="/common/dog/{{ pic_num }}.png" style="background-color: white;" ></image> </div></div>
- 这里可以对卡片的变量进行初始化,和一些具体的事件定义(卡片可以赋予一些事件绑定,比如点击卡片打开应用)。但我并没有在官网找到json中更多方法实现的方式,所以我也不会,但是至少我们可以给hml样式的变量赋初值。 { "data": { "mini": false, "pic_num": "1" }, "actions": { }} 我们也可以拍摄一张一张的图片,来逐帧播放形成动画。这里是为了测试效果,那么素材就是从动图里面找。这里我们需要把动图分解成一张一张的图片,分别标记为1,2,3,4…, 然后在JS侧一张一张的切换得到动态的效果,也就是逐帧动画实现。
- 在这里我们实现动态的效果。
- 这里我们首先,选择一张动图并且把他分解成一帧一帧的图片,标记好后存放起来。
比如这张:
然后我们利用一些工具进行分解。这里推荐一个在线工具,可以进行图片分解,并且标注了每帧图片持续多少秒,便于后面的制作。
传送门。
于是我们就得到了,10帧的图片。每帧持续0.09s,也就是90ms。
- 首先,我们先了解Form.js的基本结构: 接口名 描述 onCreate(want: Want): formBindingData.FormBindingData 卡片提供方接收创建卡片的通知接口。 onCastToNormal(formId: string): void 卡片提供方接收临时卡片转常态卡片的通知接口。 onUpdate(formId: string): void 卡片提供方接收更新卡片的通知接口。 onVisibilityChange(newStatus: { [key: string]: number }): void 卡片提供方接收修改可见性的通知接口。 onEvent(formId: string, message: string): void 卡片提供方接收处理卡片事件的通知接口。 onDestroy(formId: string): void 卡片提供方接收销毁卡片的通知接口。 onAcquireFormState?(want: Want): formInfo.FormState 卡片提供方接收查询卡片状态的通知接口。 那么,曲线救国的思路是: 在onCreate方法中,来回切换同一个GIF图。 在onCreate方法中,开一个定时器一帧一帧切换图片,合成动图。这个方式似乎可以帮助我们自由拼接图片,或者制作自己的动画?切换图片,就是要更新卡片,那么怎么更新卡片呢?
- onUpdate(formId) { // 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要覆写该方法以支持数据更新 console.log('FormAbility onUpdate'); let obj = { "title": "titleOnUpdate", "detail": "detailOnUpdate" }; let formData = formBindingData.createFormBindingData(obj); formProvider.updateForm(formId, formData).catch((error) => { console.log('FormAbility updateForm, error:' + JSON.stringify(error)); }); }, 分析官网给出的教程,可以得到以下信息: formId,每个卡片的"身份证"。 obj中的数据就是我们在卡片的hml文件中开的通路,我们这里只有一条就是{{pic_num}}, 卡片数据绑定类,formBindingData,我们需要把修改好的obj信息绑定在其中。 卡片管理和更新的类,formProvider,通过传入卡片ID和卡片数据进入updateForm方法中,就可以更新卡片了。
- 方法1: 来回切换 import formBindingData from '@ohos.application.formBindingData';import formInfo from '@ohos.application.formInfo';import formProvider from '@ohos.application.formProvider';var obj={ "pic_num":"1"}var formData=formBindingData.createFormBindingData(obj);export default { onCreate(want) { // Called to return a FormBindingData object. // 获取卡片ID let formId = want.parameters["ohos.extra.param.key.form_identity"]; obj.pic_num="temp"; formData=formBindingData.createFormBindingData(obj); //设置一个计时器, 来回切换同一张GIF图 let flag=0; setInterval(()=>{ if(flag==0){ //两个一模一样的GIF图,只是名字不同来回切换罢了 obj.pic_num="temp"; flag=1; } else{ obj.pic_num="temp1"; flag=0; } formData = formBindingData.createFormBindingData(obj); //更新卡片 formProvider.updateForm(formId,formData).catch((err)=>{ console.info("yzj"+JSON.stringify(err)); }) //以GIF时长作为间隔时间。 },2400) return formData; }, onCastToNormal(formId) { // Called when the form provider is notified that a temporary form is successfully // converted to a normal form. console.info("yzj: onCastToNormal"); }, onUpdate(formId) { // Called to notify the form provider to update a specified form. }, onVisibilityChange(newStatus) { // Called when the form provider receives form events from the system. }, onEvent(formId, message) { // Called when a specified message event defined by the form provider is triggered. }, onDestroy(formId) { // Called to notify the form provider that a specified form has been destroyed. }, onAcquireFormState(want) { // Called to return a {@link FormState} object. return formInfo.FormState.READY; }} 方法2:帧动画这里用到 2.3.1分解的动图来做素材,在逐帧播放。这样似乎绕了一个大圈,那其实反而可以变成一个生产方式。我们可以自由选择图片逐帧播放形成动画,比如制作定格动画。那就可以变成定格动画卡片了。通常一帧为1/12秒,差不多为83ms,这里只是用GIF分解出来的图片进行测试,所以时间间隔遵从了GIF本身的帧。 import formBindingData from '@ohos.application.formBindingData';import formInfo from '@ohos.application.formInfo';import formProvider from '@ohos.application.formProvider';var obj={ "pic_num":"1"}var formData=formBindingData.createFormBindingData(obj);export default { onCreate(want) { // Called to return a FormBindingData object. // 获取卡片ID let formId = want.parameters["ohos.extra.param.key.form_identity"]; obj.pic_num="1"; formData=formBindingData.createFormBindingData(obj); //设置一个计时器,循环播放图片形成动画。 //从第一张图片开始,这里的动图分解后只有10帧,那么我们的上限就是10 let i=1; setInterval(()=>{ //如果已经播放到第10帧了,那么切换回第一帧,实现循环播放 if(i>10){ i=1; } //修改pic_num的值,递增播放 obj.pic_num=i.toString(); //绑定数据 formData = formBindingData.createFormBindingData(obj); //更新卡片 formProvider.updateForm(formId,formData).catch((err)=>{ console.info("yzj"+JSON.stringify(err)); }) //播放下一帧 i++; //每帧持续90ms,这里是根据实际情况设定的 },90); return formData; }, onCastToNormal(formId) { // Called when the form provider is notified that a temporary form is successfully // converted to a normal form. console.info("yzj: onCastToNormal"); }, onUpdate(formId) { // Called to notify the form provider to update a specified form. }, onVisibilityChange(newStatus) { // Called when the form provider receives form events from the system. }, onEvent(formId, message) { // Called when a specified message event defined by the form provider is triggered. }, onDestroy(formId) { // Called to notify the form provider that a specified form has been destroyed. }, onAcquireFormState(want) { // Called to return a {@link FormState} object. return formInfo.FormState.READY; }}
- 两种方式产生的效果差不多。
- 重启板子后,需要重新开启卡片,否则会静止不动。 关闭应用本身后,卡片只会循环一次。
- 应该是放一张GIF就能结束的事情,但是现在似乎有一点bug?曲线救国能够勉强实现功能,但换个角度想这样做或许能自制动图,定格动画? 文章相关附件可以点击下面的原文链接前往下载: https://ost.51cto.com/resource/2211 https://ost.51cto.com/resource/2212 想了解更多关于开源的内容,请访问: 51CTO 开源基础软件社区 https://ost.51cto.com。

- 存在问题

- 曲线救国

新建一个工程,创建JS卡片,工程结构如下:

观察工程结构与HarmonyOS开发大致一样,区别在于FormAbility是交给JS侧来管理的,因为OpenHarmony的开发没有用到JAVA,同样的卡片样式是在widget里面进行管理。
那我们要做一个动态的卡片,只需要在样式里面添加image组件,放一张gif图就行了。
但似乎不会主动循环?开个定时器刷新路径,似乎因为是同一张图片并不会主动进行刷新。

于是我陷入的沉思…
我想到了两种解决方法:
- 存放两张一样的gif图,开个定时器来回切换。
- 分解成帧动画,逐帧切换。这样更麻烦了,但是反过来想,如果封装得好,我们就可以自行DIY动画了,比如定格动画。
这里我们只针对卡片的动态实现,至于服务卡片细节工程结构与其含义,咱不如直接看官网文档,这里参考HarmonyOS官网和OpenHarmony官网都行,两者基本同步。

这里可以分析出,我们在widget中对卡片样式进行设计,在FormAbility中对卡片进行操作就行了,本案例只针对卡片设计,所以暂时不管MainAbility了,让他就是HelloWorld就行。
我在最初构建卡片的时候选择22,44大小的卡片,这里其实随便就行。
然后按照之前曲线救国的思路,这里只需要一个image组件就好了,同时为了方便测试,给图片路径开个通路,方便JS侧进行图片切换。
<div>
<div id="wrapper">
<image id="image" src="/common/dog/{{ pic_num }}.png" style="background-color: white;" ></image>
</div>
</div>
这里可以对卡片的变量进行初始化,和一些具体的事件定义(卡片可以赋予一些事件绑定,比如点击卡片打开应用)。但我并没有在官网找到json中更多方法实现的方式,所以我也不会,但是至少我们可以给hml样式的变量赋初值。
{
"data": {
"mini": false,
"pic_num": "1"
},
"actions": {
}
}
我们也可以拍摄一张一张的图片,来逐帧播放形成动画。这里是为了测试效果,那么素材就是从动图里面找。这里我们需要把动图分解成一张一张的图片,分别标记为1,2,3,4…, 然后在JS侧一张一张的切换得到动态的效果,也就是逐帧动画实现。
在这里我们实现动态的效果。
这里我们首先,选择一张动图并且把他分解成一帧一帧的图片,标记好后存放起来。
比如这张:

然后我们利用一些工具进行分解。这里推荐一个在线工具,可以进行图片分解,并且标注了每帧图片持续多少秒,便于后面的制作。
传送门。
于是我们就得到了,10帧的图片。每帧持续0.09s,也就是90ms。

首先,我们先了解Form.js的基本结构:
|
接口名 |
描述 |
|
onCreate(want: Want): formBindingData.FormBindingData |
卡片提供方接收创建卡片的通知接口。 |
|
onCastToNormal(formId: string): void |
卡片提供方接收临时卡片转常态卡片的通知接口。 |
|
onUpdate(formId: string): void |
卡片提供方接收更新卡片的通知接口。 |
|
onVisibilityChange(newStatus: { [key: string]: number }): void |
卡片提供方接收修改可见性的通知接口。 |
|
onEvent(formId: string, message: string): void |
卡片提供方接收处理卡片事件的通知接口。 |
|
onDestroy(formId: string): void |
卡片提供方接收销毁卡片的通知接口。 |
|
onAcquireFormState?(want: Want): formInfo.FormState |
卡片提供方接收查询卡片状态的通知接口。 |
那么,曲线救国的思路是:
- 在onCreate方法中,来回切换同一个GIF图。
- 在onCreate方法中,开一个定时器一帧一帧切换图片,合成动图。这个方式似乎可以帮助我们自由拼接图片,或者制作自己的动画?
切换图片,就是要更新卡片,那么怎么更新卡片呢?
onUpdate(formId) {
// 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要覆写该方法以支持数据更新
console.log('FormAbility onUpdate');
let obj = {
"title": "titleOnUpdate",
"detail": "detailOnUpdate"
};
let formData = formBindingData.createFormBindingData(obj);
formProvider.updateForm(formId, formData).catch((error) => {
console.log('FormAbility updateForm, error:' + JSON.stringify(error));
});
},
// 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要覆写该方法以支持数据更新
console.log('FormAbility onUpdate');
let obj = {
"title": "titleOnUpdate",
"detail": "detailOnUpdate"
};
let formData = formBindingData.createFormBindingData(obj);
formProvider.updateForm(formId, formData).catch((error) => {
console.log('FormAbility updateForm, error:' + JSON.stringify(error));
});
},
分析官网给出的教程,可以得到以下信息:
- formId,每个卡片的"身份证"。
- obj中的数据就是我们在卡片的hml文件中开的通路,我们这里只有一条就是{{pic_num}},
- 卡片数据绑定类,formBindingData,我们需要把修改好的obj信息绑定在其中。
- 卡片管理和更新的类,formProvider,通过传入卡片ID和卡片数据进入updateForm方法中,就可以更新卡片了。
- 方法1: 来回切换
import formBindingData from '@ohos.application.formBindingData';
import formInfo from '@ohos.application.formInfo';
import formProvider from '@ohos.application.formProvider';
var obj={
"pic_num":"1"
}
var formData=formBindingData.createFormBindingData(obj);
export default {
onCreate(want) {
// Called to return a FormBindingData object.
// 获取卡片ID
let formId = want.parameters["ohos.extra.param.key.form_identity"];
obj.pic_num="temp";
formData=formBindingData.createFormBindingData(obj);
//设置一个计时器, 来回切换同一张GIF图
let flag=0;
setInterval(()=>{
if(flag==0){
//两个一模一样的GIF图,只是名字不同来回切换罢了
obj.pic_num="temp";
flag=1;
}
else{
obj.pic_num="temp1";
flag=0;
}
formData = formBindingData.createFormBindingData(obj);
//更新卡片
formProvider.updateForm(formId,formData).catch((err)=>{
console.info("yzj"+JSON.stringify(err));
})
//以GIF时长作为间隔时间。
},2400)
return formData;
},
onCastToNormal(formId) {
// Called when the form provider is notified that a temporary form is successfully
// converted to a normal form.
console.info("yzj: onCastToNormal");
},
onUpdate(formId) {
// Called to notify the form provider to update a specified form.
},
onVisibilityChange(newStatus) {
// Called when the form provider receives form events from the system.
},
onEvent(formId, message) {
// Called when a specified message event defined by the form provider is triggered.
},
onDestroy(formId) {
// Called to notify the form provider that a specified form has been destroyed.
},
onAcquireFormState(want) {
// Called to return a {@link FormState} object.
return formInfo.FormState.READY;
}
}
- 方法2:帧动画
这里用到 2.3.1分解的动图来做素材,在逐帧播放。这样似乎绕了一个大圈,那其实反而可以变成一个生产方式。我们可以自由选择图片逐帧播放形成动画,比如制作定格动画。那就可以变成定格动画卡片了。通常一帧为1/12秒,差不多为83ms,这里只是用GIF分解出来的图片进行测试,所以时间间隔遵从了GIF本身的帧。
import formBindingData from '@ohos.application.formBindingData';
import formInfo from '@ohos.application.formInfo';
import formProvider from '@ohos.application.formProvider';
var obj={
"pic_num":"1"
}
var formData=formBindingData.createFormBindingData(obj);
export default {
onCreate(want) {
// Called to return a FormBindingData object.
// 获取卡片ID
let formId = want.parameters["ohos.extra.param.key.form_identity"];
obj.pic_num="1";
formData=formBindingData.createFormBindingData(obj);
//设置一个计时器,循环播放图片形成动画。
//从第一张图片开始,这里的动图分解后只有10帧,那么我们的上限就是10
let i=1;
setInterval(()=>{
//如果已经播放到第10帧了,那么切换回第一帧,实现循环播放
if(i>10){
i=1;
}
//修改pic_num的值,递增播放
obj.pic_num=i.toString();
//绑定数据
formData = formBindingData.createFormBindingData(obj);
//更新卡片
formProvider.updateForm(formId,formData).catch((err)=>{
console.info("yzj"+JSON.stringify(err));
})
//播放下一帧
i++;
//每帧持续90ms,这里是根据实际情况设定的
},90);
return formData;
},
onCastToNormal(formId) {
// Called when the form provider is notified that a temporary form is successfully
// converted to a normal form.
console.info("yzj: onCastToNormal");
},
onUpdate(formId) {
// Called to notify the form provider to update a specified form.
},
onVisibilityChange(newStatus) {
// Called when the form provider receives form events from the system.
},
onEvent(formId, message) {
// Called when a specified message event defined by the form provider is triggered.
},
onDestroy(formId) {
// Called to notify the form provider that a specified form has been destroyed.
},
onAcquireFormState(want) {
// Called to return a {@link FormState} object.
return formInfo.FormState.READY;
}
}
import formInfo from '@ohos.application.formInfo';
import formProvider from '@ohos.application.formProvider';
var obj={
"pic_num":"1"
}
var formData=formBindingData.createFormBindingData(obj);
export default {
onCreate(want) {
// Called to return a FormBindingData object.
// 获取卡片ID
let formId = want.parameters["ohos.extra.param.key.form_identity"];
obj.pic_num="temp";
formData=formBindingData.createFormBindingData(obj);
//设置一个计时器, 来回切换同一张GIF图
let flag=0;
setInterval(()=>{
if(flag==0){
//两个一模一样的GIF图,只是名字不同来回切换罢了
obj.pic_num="temp";
flag=1;
}
else{
obj.pic_num="temp1";
flag=0;
}
formData = formBindingData.createFormBindingData(obj);
//更新卡片
formProvider.updateForm(formId,formData).catch((err)=>{
console.info("yzj"+JSON.stringify(err));
})
//以GIF时长作为间隔时间。
},2400)
return formData;
},
onCastToNormal(formId) {
// Called when the form provider is notified that a temporary form is successfully
// converted to a normal form.
console.info("yzj: onCastToNormal");
},
onUpdate(formId) {
// Called to notify the form provider to update a specified form.
},
onVisibilityChange(newStatus) {
// Called when the form provider receives form events from the system.
},
onEvent(formId, message) {
// Called when a specified message event defined by the form provider is triggered.
},
onDestroy(formId) {
// Called to notify the form provider that a specified form has been destroyed.
},
onAcquireFormState(want) {
// Called to return a {@link FormState} object.
return formInfo.FormState.READY;
}
}
这里用到 2.3.1分解的动图来做素材,在逐帧播放。这样似乎绕了一个大圈,那其实反而可以变成一个生产方式。我们可以自由选择图片逐帧播放形成动画,比如制作定格动画。那就可以变成定格动画卡片了。通常一帧为1/12秒,差不多为83ms,这里只是用GIF分解出来的图片进行测试,所以时间间隔遵从了GIF本身的帧。
import formInfo from '@ohos.application.formInfo';
import formProvider from '@ohos.application.formProvider';
var obj={
"pic_num":"1"
}
var formData=formBindingData.createFormBindingData(obj);
export default {
onCreate(want) {
// Called to return a FormBindingData object.
// 获取卡片ID
let formId = want.parameters["ohos.extra.param.key.form_identity"];
obj.pic_num="1";
formData=formBindingData.createFormBindingData(obj);
//设置一个计时器,循环播放图片形成动画。
//从第一张图片开始,这里的动图分解后只有10帧,那么我们的上限就是10
let i=1;
setInterval(()=>{
//如果已经播放到第10帧了,那么切换回第一帧,实现循环播放
if(i>10){
i=1;
}
//修改pic_num的值,递增播放
obj.pic_num=i.toString();
//绑定数据
formData = formBindingData.createFormBindingData(obj);
//更新卡片
formProvider.updateForm(formId,formData).catch((err)=>{
console.info("yzj"+JSON.stringify(err));
})
//播放下一帧
i++;
//每帧持续90ms,这里是根据实际情况设定的
},90);
return formData;
},
onCastToNormal(formId) {
// Called when the form provider is notified that a temporary form is successfully
// converted to a normal form.
console.info("yzj: onCastToNormal");
},
onUpdate(formId) {
// Called to notify the form provider to update a specified form.
},
onVisibilityChange(newStatus) {
// Called when the form provider receives form events from the system.
},
onEvent(formId, message) {
// Called when a specified message event defined by the form provider is triggered.
},
onDestroy(formId) {
// Called to notify the form provider that a specified form has been destroyed.
},
onAcquireFormState(want) {
// Called to return a {@link FormState} object.
return formInfo.FormState.READY;
}
}
两种方式产生的效果差不多。

- 重启板子后,需要重新开启卡片,否则会静止不动。
- 关闭应用本身后,卡片只会循环一次。



应该是放一张GIF就能结束的事情,但是现在似乎有一点bug?曲线救国能够勉强实现功能,但换个角度想这样做或许能自制动图,定格动画?
文章相关附件可以点击下面的原文链接前往下载:
https://ost.51cto.com/resource/2211
https://ost.51cto.com/resource/2212