zhangbo1_wd

下拉刷新组件,对接人日动画,待优化细节

  1 +import lottie, { AnimationItem } from '@ohos/lottie';
1 import { PullToRefresh, PullToRefreshConfigurator } from '@ohos/pulltorefresh'; 2 import { PullToRefresh, PullToRefreshConfigurator } from '@ohos/pulltorefresh';
2 -import { LazyDataSource } from 'wdKit'; 3 +import { LazyDataSource, Logger } from 'wdKit/Index';
3 4
4 @Component 5 @Component
5 -export struct CustomPullToRefresh { 6 +export struct CustomPullToRefresh {
6 @Link alldata: Object[] | LazyDataSource<Object>; 7 @Link alldata: Object[] | LazyDataSource<Object>;
7 scroller: Scroller = new Scroller(); 8 scroller: Scroller = new Scroller();
8 @BuilderParam customList: () => void; 9 @BuilderParam customList: () => void;
@@ -12,28 +13,238 @@ export struct CustomPullToRefresh { @@ -12,28 +13,238 @@ export struct CustomPullToRefresh {
12 } 13 }
13 ///是否存在上拉更多 14 ///是否存在上拉更多
14 @Prop @Watch('hasMoreChange') hasMore: boolean = true 15 @Prop @Watch('hasMoreChange') hasMore: boolean = true
15 - refreshConfigurator: PullToRefreshConfigurator = new PullToRefreshConfigurator().setHasLoadMore(this.hasMore); 16 + refreshConfigurator: PullToRefreshConfigurator = new PullToRefreshConfigurator()
  17 + .setHasLoadMore(this.hasMore)
  18 + .setAnimDuration(500);
  19 + private refreshSettings: RenderingContextSettings = new RenderingContextSettings(true)
  20 + private refreshContext: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.refreshSettings)
  21 + private loadMoreSettings: RenderingContextSettings = new RenderingContextSettings(true)
  22 + private loadMoreContext: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.loadMoreSettings)
  23 + private refreshAnimation: AnimationItem | null = null;
  24 + private loadMoreAnimation: AnimationItem | null = null;
  25 + private refreshAnimName: string = "refresh";
  26 + private refreshingAnimName: string = "refreshing";
  27 + private loadMoreAnimName: string = "loadMore";
  28 + private refreshAnimationDestroy = true
  29 + // refresh-1,refreshing-2,refreshed-3,idle-4
  30 + @State @Watch('stateChange') private refreshState: number = 4;
  31 +
16 build() { 32 build() {
17 - Column(){ 33 + Column() {
18 PullToRefresh({ 34 PullToRefresh({
19 - data:$alldata,  
20 - scroller:this.scroller,  
21 - refreshConfigurator:this.refreshConfigurator,  
22 - customList:()=>{  
23 - this.customList(); 35 + data: $alldata,
  36 + scroller: this.scroller,
  37 + refreshConfigurator: this.refreshConfigurator,
  38 + customList: () => {
  39 + this.customList();
24 }, 40 },
25 - onRefresh:()=>{  
26 - return new Promise<string>((resolve, reject) => {  
27 - this.onRefresh(resolve) 41 + onRefresh: () => {
  42 + return new Promise<string>((success, error) => {
  43 + new Promise<string>((resolve) => {
  44 + this.onRefresh(resolve)
  45 + }).then((text) => {
  46 + setTimeout(()=>{
  47 + // 延时500,展示第二段动画 TODO 是否去掉?
  48 + this.refreshState = 3
  49 + this.refreshDestroy()
  50 + setTimeout(() => {
  51 + success(text)
  52 + }, 500)
  53 + setTimeout(() => {
  54 + this.refreshState = 4
  55 + // 延时将状态改为空闲,组件closeRefresh做了延时,不能配置,这里配合延时
  56 + }, 1200)
  57 + },500)
  58 + })
28 }); 59 });
29 }, 60 },
30 - onLoadMore:()=>{ 61 + onLoadMore: () => {
31 return new Promise<string>((resolve, reject) => { 62 return new Promise<string>((resolve, reject) => {
32 - this.onLoadMore(resolve) 63 + this.onLoadMore(resolve)// TODO 加层promise处理,延长动画时间
33 }); 64 });
34 }, 65 },
35 - customLoad: null,  
36 - customRefresh: null, 66 + customLoad: () => {
  67 + this.customLoadMoreView()
  68 + },
  69 + // 可选项,自定义下拉刷新动画布局
  70 + customRefresh: () => {
  71 + this.customRefreshView()
  72 + },
  73 + // 可选项,下拉中回调
  74 + onAnimPullDown: (value, width, height) => {
  75 + this.refreshState = 1
  76 + // Logger.error('zzzz', 'onAnimPullDown, ' + value + ', ' + width + ', ' + height)
  77 + this.refreshAnim(value)
  78 + },
  79 + // 可选项,刷新中回调
  80 + onAnimRefreshing: (value, width, height) => {
  81 + if (this.refreshState < 2) {
  82 + this.refreshState = 2
  83 + }
  84 + // Logger.error('zzzz', 'onAnimRefreshing, ' + value + ', ' + width + ', ' + height)
  85 + this.refreshingAnim()
  86 + },
  87 + })
  88 + }
  89 + }
  90 +
  91 + /**
  92 + * 销毁所有动画
  93 + */
  94 + private refreshDestroy() {
  95 + lottie.destroy(this.refreshAnimName);
  96 + lottie.destroy(this.refreshingAnimName);
  97 + this.refreshAnimation?.removeEventListener('DOMLoaded')
  98 + this.refreshAnimation?.removeEventListener('destroy')
  99 + this.refreshAnimation = null
  100 + }
  101 +
  102 + @Builder
  103 + private customRefreshView() {
  104 + Stack({ alignContent: Alignment.Center }) {
  105 + Canvas(this.refreshContext)
  106 + .width(60)
  107 + .height(60)
  108 + .backgroundColor(Color.Transparent)
  109 + .onReady(() => {
  110 + // 可在此生命回调周期中加载动画,可以保证动画尺寸正确
  111 + //抗锯齿的设置
  112 + this.refreshContext.imageSmoothingEnabled = true;
  113 + this.refreshContext.imageSmoothingQuality = 'medium'
  114 + })
  115 + .onDisAppear(() => {
  116 + lottie.destroy(this.refreshAnimName);
  117 + lottie.destroy(this.refreshingAnimName);
  118 + })
  119 + .visibility((this.refreshState == 1 || this.refreshState == 2) ? Visibility.Visible : Visibility.Hidden)
  120 +
  121 + Text('已更新至最新')
  122 + .fontSize(14)
  123 + .textAlign(TextAlign.Center)
  124 + .fontColor('#676767')
  125 + .backgroundColor('#f6f6f6')
  126 + .borderRadius(20)
  127 + .padding({
  128 + left: 19,
  129 + right: 19,
  130 + top: 10,
  131 + bottom: 10
  132 + })
  133 + .visibility(this.refreshState == 3 ? Visibility.Visible : Visibility.Hidden)
  134 + }.padding({ top: 20 })
  135 + }
  136 +
  137 + @Builder
  138 + private customLoadMoreView() {
  139 + Row() {
  140 + Canvas(this.loadMoreContext)
  141 + .width(14)
  142 + .height(14)
  143 + .backgroundColor(Color.Transparent)
  144 + .onReady(() => {
  145 + // 可在此生命回调周期中加载动画,可以保证动画尺寸正确
  146 + //抗锯齿的设置
  147 + this.loadMoreContext.imageSmoothingEnabled = true;
  148 + this.loadMoreContext.imageSmoothingQuality = 'medium'
  149 + this.loadMoreAnimate()
  150 + })
  151 + .onDisAppear(() => {
  152 + // Logger.error('zzzz', 'CustomLoadMoreLayout onDisAppear')
  153 + lottie.destroy(this.loadMoreAnimName);
  154 + })
  155 +
  156 + Text('努力加载中')
  157 + .margin({
  158 + left: 7,
  159 + bottom: 1
  160 + })
  161 + .fontSize(14)
  162 + .fontColor('#888888')
  163 + .textAlign(TextAlign.Center)
  164 + }
  165 + .clip(true)
  166 + .width('100%')
  167 + .justifyContent(FlexAlign.Center)
  168 + .height(60)
  169 + }
  170 +
  171 + private refreshAnim(percent: number) {
  172 + if (this.refreshAnimation == null || this.refreshAnimation.name != this.refreshAnimName ||
  173 + this.refreshAnimationDestroy) {
  174 + this.refreshAnimation?.destroy(this.refreshAnimName)
  175 + this.refreshAnimation?.destroy(this.refreshingAnimName)
  176 + this.refreshAnimation = lottie.loadAnimation({
  177 + container: this.refreshContext,
  178 + renderer: 'canvas', // canvas 渲染模式
  179 + loop: 1,
  180 + autoplay: true,
  181 + name: this.refreshAnimName,
  182 + path: "lottie/refresh_step1.json", // 路径加载动画只支持entry/src/main/ets 文件夹下的相对路径
  183 + })
  184 + this.refreshAnimation?.goToAndStop(1)
  185 + this.addRefreshAnimListener()
  186 + }
  187 + this.refreshAnimation?.goToAndStop(this.getFramesByProgress(percent), true);
  188 + }
  189 +
  190 + /**
  191 + * 获取动画帧
  192 + *
  193 + * @param percent 百分比,如0.76
  194 + * @returns
  195 + */
  196 + private getFramesByProgress(percent: number): number {
  197 + if (this.refreshAnimation == null) {
  198 + return 1;
  199 + }
  200 + let total = this.refreshAnimation.totalFrames;
  201 + let frame = Math.floor(total * percent);
  202 + if (frame >= total - 1) {
  203 + frame = total - 1
  204 + }
  205 + return frame;
  206 + }
  207 +
  208 + private refreshingAnim() {
  209 + // Logger.error('zzzz', 'animate2, 1')
  210 + // 先销毁之前的动画
  211 + if (this.refreshAnimation == null || this.refreshAnimation.name != this.refreshingAnimName ||
  212 + this.refreshAnimationDestroy) {
  213 + this.refreshAnimation?.destroy(this.refreshAnimName)
  214 + this.refreshAnimation?.destroy(this.refreshingAnimName)
  215 + this.refreshAnimation = lottie.loadAnimation({
  216 + container: this.refreshContext,
  217 + renderer: 'canvas', // canvas 渲染模式
  218 + loop: 10,
  219 + autoplay: true,
  220 + name: this.refreshingAnimName,
  221 + path: "lottie/refresh_step2.json", // 路径加载动画只支持entry/src/main/ets 文件夹下的相对路径
  222 + })
  223 + this.addRefreshAnimListener()
  224 + }
  225 + // Logger.error('zzzz', 'animate2, 2')
  226 + }
  227 +
  228 + private addRefreshAnimListener() {
  229 + this.refreshAnimation?.addEventListener('DOMLoaded', (args: Object): void => {
  230 + // Logger.error('zzzz', "lottie DOMLoaded");
  231 + this.refreshAnimationDestroy = false
  232 + }); //动画加载完成,播放之前触发
  233 + this.refreshAnimation?.addEventListener('destroy', (args: Object): void => {
  234 + // Logger.error('zzzz', "lottie destroy");
  235 + this.refreshAnimationDestroy = true
  236 + });
  237 + }
  238 +
  239 + loadMoreAnimate() {
  240 + if (this.loadMoreAnimation == null) {
  241 + this.loadMoreAnimation = lottie.loadAnimation({
  242 + container: this.loadMoreContext,
  243 + renderer: 'canvas', // canvas 渲染模式
  244 + loop: 50,
  245 + autoplay: true,
  246 + name: this.loadMoreAnimName,
  247 + path: "lottie/loading_more.json", // 路径加载动画只支持entry/src/main/ets 文件夹下的相对路径
37 }) 248 })
38 } 249 }
39 } 250 }
@@ -41,4 +252,8 @@ export struct CustomPullToRefresh { @@ -41,4 +252,8 @@ export struct CustomPullToRefresh {
41 hasMoreChange() { 252 hasMoreChange() {
42 this.refreshConfigurator.setHasLoadMore(this.hasMore) 253 this.refreshConfigurator.setHasLoadMore(this.hasMore)
43 } 254 }
  255 +
  256 + stateChange() {
  257 + // Logger.error('zzzz', 'stateChange ' + this.refreshState)
  258 + }
44 } 259 }