diff --git a/examples/ImageAnimator/entry/src/main/ets/pages/example/ImageAnimatorExample004.ets b/examples/ImageAnimator/entry/src/main/ets/pages/example/ImageAnimatorExample004.ets new file mode 100644 index 0000000000000000000000000000000000000000..6d2c60a6ae7d0fcd08c0243ab3423d09f955d202 --- /dev/null +++ b/examples/ImageAnimator/entry/src/main/ets/pages/example/ImageAnimatorExample004.ets @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// 该 Demo 旨在全面测试 ImageAnimator 组件在动态修改状态参数、播放属性及回调逻辑时的行为一致性 +@Entry +@Component +struct ImageAnimatorDemo01 { + // 动画状态:初始、运行、暂停、停止 + @State state: AnimationStatus = AnimationStatus.Initial + // 是否反向播放 + @State reverse: boolean = false + // 循环次数:-1表示无限循环 + @State iterations: number = 1 + // 回调状态标识 + @State callBackFlag: string = 'Null' + // 填充模式:动画结束后显示的状态 + @State fillMode: FillMode = FillMode.Forwards + + build() { + Column({ space: 10 }) { + // 图片动画组件 + ImageAnimator() + // 设置动画图片资源 + .images([ + { src: $r('app.media.ic_gif') }, + { src: $r('app.media.no2') }, + { src: $r('app.media.no3') }, + { src: $r('app.media.no4') }, + { src: $r('app.media.no5') } + ]) + // 设置圆角 + .borderRadius(10) + // 设置动画持续时间(毫秒) + .duration(5000) + // 设置动画状态 + .state(this.state) + // 设置是否反向播放 + .reverse(this.reverse) + // 设置填充模式 + .fillMode(this.fillMode) + // 设置循环次数 + .iterations(this.iterations) + // 设置组件尺寸和边距 + .width(340).height(240).margin({ top: 100 }) + // 动画开始回调 + .onStart(() => { + this.callBackFlag = 'ImageAnimator Start' + console.info('ImageAnimator Start') + }) + // 动画暂停回调 + .onPause(() => { + this.callBackFlag = 'ImageAnimator Pause' + console.info('ImageAnimator Pause') + }) + // 动画重复回调 + .onRepeat(() => { + this.callBackFlag = 'ImageAnimator Repeat' + console.info('ImageAnimator Repeat') + }) + // 动画取消回调 + .onCancel(() => { + this.callBackFlag = 'ImageAnimator Cancel' + console.info('ImageAnimator Cancel') + }) + // 动画完成回调 + .onFinish(() => { + this.callBackFlag = 'ImageAnimator Finish' + console.info('ImageAnimator Finish') + }) + + // 动画控制按钮行 + Row() { + // 开始动画按钮 + Button('start').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Running + }).margin(5) + + // 暂停动画按钮 + Button('pause').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Paused // 显示当前帧图片 + }).margin(5) + + // 停止动画按钮 + Button('stop').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Stopped // 显示动画的起始帧图片 + }).margin(5) + + // 重置动画按钮 + Button('Initial').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Initial // 显示动画的起始帧图片 + }).margin(5) + } + + // 动画设置按钮行 + Row() { + // 切换反向播放按钮 + Button('reverse' + this.reverse).width(100).padding(5).onClick(() => { + this.reverse = !this.reverse + }).margin(5) + + // 设置单次播放按钮 + Button('once').width(100).padding(5).onClick(() => { + this.iterations = 1 + }).margin(5) + + // 设置无限循环按钮 + Button('infinite').width(100).padding(5).onClick(() => { + this.iterations = -1 // 无限循环播放 + }).margin(5) + } + + // 显示当前动画状态信息 + Text(this.callBackFlag).fontWeight(FontWeight.Bolder) + Text('This reversed ' + this.reverse).fontWeight(FontWeight.Bolder) + Text('This fillMode ' + this.fillMode).fontWeight(FontWeight.Bolder) + Text('This Iteration ' + this.iterations).fontWeight(FontWeight.Bolder) + } + .width('100%') + .height('100%') + } +} \ No newline at end of file diff --git a/examples/ImageAnimator/entry/src/main/ets/pages/example/ImageAnimatorExample005.ets b/examples/ImageAnimator/entry/src/main/ets/pages/example/ImageAnimatorExample005.ets new file mode 100644 index 0000000000000000000000000000000000000000..3a2bd83a015cb39aac048bdb75ff80397e3e2cbf --- /dev/null +++ b/examples/ImageAnimator/entry/src/main/ets/pages/example/ImageAnimatorExample005.ets @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// 该 Demo 旨在全面测试 ImageAnimator 组件在动态修改状态参数、播放属性及回调逻辑时的行为一致性 +@Entry +@Component +struct ImageAnimatorDemo02 { + // 动画状态:初始、运行、暂停、停止 + @State state: AnimationStatus = AnimationStatus.Initial + // 是否反向播放 + @State reverse: boolean = true + // 循环次数:-1表示无限循环 + @State iterations: number = 1 + // 回调状态标识 + @State callBackFlag: string = 'Null' + // 填充模式:动画结束后显示的状态 + @State fillMode: FillMode = FillMode.None + + build() { + Column({ space: 10 }) { + // 图片动画组件 + ImageAnimator() + // 设置动画图片资源 + .images([ + { src: $r('app.media.no1') }, + { src: $r('app.media.no2') }, + { src: $r('app.media.no3') }, + { src: $r('app.media.no4') }, + { src: $r('app.media.no5') } + ]) + // 设置圆角 + .borderRadius(10) + // 设置动画持续时间(毫秒) + .duration(5000) + // 设置动画状态 + .state(this.state) + // 设置是否反向播放 + .reverse(this.reverse) + // 设置填充模式 + .fillMode(this.fillMode) + // 设置循环次数 + .iterations(this.iterations) + // 设置组件尺寸和边距 + .width(340).height(240).margin({ top: 100 }) + // 动画开始回调 + .onStart(() => { + this.callBackFlag = 'ImageAnimator Start' + console.info('ImageAnimator Start') + }) + // 动画暂停回调 + .onPause(() => { + this.callBackFlag = 'ImageAnimator Pause' + console.info('ImageAnimator Pause') + }) + // 动画重复回调 + .onRepeat(() => { + this.callBackFlag = 'ImageAnimator Repeat' + console.info('ImageAnimator Repeat') + }) + // 动画取消回调 + .onCancel(() => { + this.callBackFlag = 'ImageAnimator Cancel' + console.info('ImageAnimator Cancel') + }) + // 动画完成回调 + .onFinish(() => { + this.callBackFlag = 'ImageAnimator Finish' + console.info('ImageAnimator Finish') + }) + + // 动画控制按钮行 + Row() { + // 开始动画按钮 + Button('start').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Running + }).margin(5) + + // 暂停动画按钮 + Button('pause').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Paused // 显示当前帧图片 + }).margin(5) + + // 停止动画按钮 + Button('stop').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Stopped // 显示动画的起始帧图片 + }).margin(5) + + // 重置动画按钮 + Button('Initial').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Initial // 显示动画的起始帧图片 + }).margin(5) + } + + // 动画设置按钮行 + Row() { + // 切换反向播放按钮 + Button('reverse' + this.reverse).width(100).padding(5).onClick(() => { + this.reverse = !this.reverse + }).margin(5) + + // 设置单次播放按钮 + Button('once').width(100).padding(5).onClick(() => { + this.iterations = 1 + }).margin(5) + + // 设置无限循环按钮 + Button('infinite').width(100).padding(5).onClick(() => { + this.iterations = -1 // 无限循环播放 + }).margin(5) + } + + // 显示当前动画状态信息 + Text(this.callBackFlag).fontWeight(FontWeight.Bolder) + Text('This reversed ' + this.reverse).fontWeight(FontWeight.Bolder) + Text('This fillMode ' + this.fillMode).fontWeight(FontWeight.Bolder) + Text('This Iteration ' + this.iterations).fontWeight(FontWeight.Bolder) + } + .width('100%') + .height('100%') + } +} \ No newline at end of file diff --git a/examples/ImageAnimator/entry/src/main/ets/pages/example/ImageAnimatorExample006.ets b/examples/ImageAnimator/entry/src/main/ets/pages/example/ImageAnimatorExample006.ets new file mode 100644 index 0000000000000000000000000000000000000000..2e8d74137c9d45689ed9e45d4f7833206eccc16a --- /dev/null +++ b/examples/ImageAnimator/entry/src/main/ets/pages/example/ImageAnimatorExample006.ets @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// 该 Demo 通过在可滚动页面中集成 ImageAnimator 组件,全面测试动画组件在动态控制状态参数(如播放 / 暂停 / 反向)、 +// 自动监控不可见区域功能、以及多轮播循环场景下的行为稳定性,验证其与滚动交互的兼容性及事件回调的准确性 +@Entry +@Component +struct ImageAnimatorDemo03 { + // 滚动控制器 + scroller: Scroller = new Scroller() + // 动画状态:运行、暂停、停止、初始 + @State state: AnimationStatus = AnimationStatus.Running + // 是否反向播放 + @State reverse: boolean = false + // 循环次数:-1表示无限循环 + @State iterations: number = 100 + // 是否自动监控不可见区域 + @State autoMonitorInvisibleArea: boolean = false + // 回调状态标识 + @State callBackFlag: string = 'Null' + // 用于生成列表项的数据数组 + private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + + build() { + Stack({ alignContent: Alignment.TopStart }) { + // 可滚动区域 + Scroll(this.scroller) { + Column() { + // 图片动画组件 + ImageAnimator() + // 设置动画图片资源 + .images([ + { src: $r('app.media.no0') }, + { src: $r('app.media.no1') }, + { src: $r('app.media.no2') }, + { src: $r('app.media.no3') }, + { src: $r('app.media.no4') }, + { src: $r('app.media.no5') }, + { src: $r('app.media.no6') } + ]) + // 设置圆角 + .borderRadius(10) + // 设置是否自动监控不可见区域 + .monitorInvisibleArea(this.autoMonitorInvisibleArea) + // 设置裁剪 + .clip(true) + // 设置动画持续时间(毫秒) + .duration(4000) + // 设置动画状态 + .state(this.state) + // 设置是否反向播放 + .reverse(this.reverse) + // 设置填充模式:动画结束后保持最后一帧 + .fillMode(FillMode.Forwards) + // 设置循环次数 + .iterations(this.iterations) + // 设置组件尺寸和边距 + .width(340).height(240).margin({ top: 100 }) + // 动画开始回调 + .onStart(() => { + this.callBackFlag = 'ImageAnimator Start' + console.info('ImageAnimator Start') + }) + // 动画暂停回调 + .onPause(() => { + this.callBackFlag = 'ImageAnimator Pause' + console.info('ImageAnimator Pause') + }) + // 动画重复回调 + .onRepeat(() => { + this.callBackFlag = 'ImageAnimator Repeat' + console.info('ImageAnimator Repeat') + }) + // 动画取消回调 + .onCancel(() => { + this.callBackFlag = 'ImageAnimator Cancel' + console.info('ImageAnimator Cancel') + }) + // 动画完成回调 + .onFinish(() => { + this.callBackFlag = 'ImageAnimator Finish' + console.info('ImageAnimator Finish') + }) + + // 生成列表项 + ForEach(this.arr, (item: number) => { + Text(item.toString()) + .width('90%') + .height(150) + .backgroundColor(0xFFFFFF) + .borderRadius(15) + .fontSize(16) + .textAlign(TextAlign.Center) + .margin({ top: 10 }) + }, (item: number) => item.toString()) + } + .width('100%') + } + // 设置滚动属性 + .scrollable(ScrollDirection.Vertical) // 滚动方向纵向 + .scrollBar(BarState.On) // 滚动条常驻显示 + .scrollBarColor(Color.Gray) // 滚动条颜色 + .scrollBarWidth(10) // 滚动条宽度 + .friction(0.6) // 滚动摩擦系数 + .edgeEffect(EdgeEffect.None) // 边缘效果 + // 滚动监听事件 + .onWillScroll((xOffset: number, yOffset: number, scrollState: ScrollState) => { + console.info(`Scroll offset: x=${xOffset}, y=${yOffset}`) + }) + .onScrollEdge((side: Edge) => { + console.info('Scroll to the edge') + }) + .onScrollStop(() => { + console.info('Scroll stopped') + }) + + // 动画控制按钮组 + Button('start').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Running + }).margin({ top: 60, left: 20 }) + + Button('pause').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Paused // 显示当前帧图片 + }).margin({ top: 110, left: 20 }) + + Button('stop').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Stopped // 显示动画的起始帧图片 + }).margin({ top: 160, left: 20 }) + + Button('Initial').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Initial // 显示动画的起始帧图片 + }).margin({ top: 210, left: 20 }) + + Button('reverse').width(100).padding(5).onClick(() => { + this.reverse = !this.reverse + }).margin({ top: 260, left: 20 }) + + Button('once').width(100).padding(5).onClick(() => { + this.iterations = 1 + }).margin({ top: 310, left: 20 }) + + Button('infinite').width(100).padding(5).onClick(() => { + this.iterations = -1 // 无限循环播放 + }).margin({ top: 360, left: 20 }) + + Button(`Change AutoMonitorInvisibleArea: ${this.autoMonitorInvisibleArea}`) + .width(200).padding(5).onClick(() => { + this.autoMonitorInvisibleArea = !this.autoMonitorInvisibleArea + }).margin({ top: 410, left: 20 }) + + // 显示当前动画状态 + Text(this.callBackFlag).width(200).padding(5).margin({ top: 460, left: 20 }) + } + .width('100%') + .height('100%') + .backgroundColor(0xDCDCDC) + } +} \ No newline at end of file diff --git a/examples/ImageAnimator/entry/src/main/ets/pages/example/ImageAnimatorExample007.ets b/examples/ImageAnimator/entry/src/main/ets/pages/example/ImageAnimatorExample007.ets new file mode 100644 index 0000000000000000000000000000000000000000..01b0250cc78f8d7341335fe086184b95d2bfa5ee --- /dev/null +++ b/examples/ImageAnimator/entry/src/main/ets/pages/example/ImageAnimatorExample007.ets @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// 测试ImageAnimator组件中images为空的情况时,全面测试动画在状态切换(运行 / 暂停 / 停止)、反向播放、循环次数调整、 +// 填充模式变更及持续时间动态修改等场景下的行为一致性,验证各事件回调的触发准确性及属性动态更新的响应能力。 +@Entry +@Component +struct ImageAnimatorDemo04 { + // 动画状态:初始、运行、暂停、停止 + @State state: AnimationStatus = AnimationStatus.Initial + // 是否反向播放动画 + @State reverse: boolean = false + // 动画循环次数 + @State iterations: number = 1 + // 回调状态标识 + @State callBackFlag: string = 'Null' + // 动画填充模式:None、Forwards、Backwards、Both + @State fillMode: FillMode = FillMode.None + // 动画持续时间(毫秒) + @State duration: number = 5000 + + build() { + Column({ space: 10 }) { + // 图片动画组件 + ImageAnimator() + .images([ + { src: $r('app.media.no1') }, + { src: $r('app.media.no2') }, + { src: $r('app.media.no3') }, + { src: $r('app.media.no4') }, + { src: $r('app.media.no5') } + ]) + .borderRadius(10) // 圆角 + .duration(this.duration) // 动画持续时间 + .state(this.state) // 动画状态 + .reverse(this.reverse) // 是否反向播放 + .fillMode(this.fillMode) // 填充模式 + .iterations(this.iterations) // 循环次数 + .width(340).height(240).margin({ top: 100 }) // 尺寸和边距 + + // 动画事件回调 + .onStart(() => { + this.callBackFlag = 'ImageAnimator Start' + console.info('ImageAnimator Start') + }) + .onPause(() => { + this.callBackFlag = 'ImageAnimator Pause' + console.info('ImageAnimator Pause') + }) + .onRepeat(() => { + this.callBackFlag = 'ImageAnimator Repeat' + console.info('ImageAnimator Repeat') + }) + .onCancel(() => { + this.callBackFlag = 'ImageAnimator Cancel' + console.info('ImageAnimator Cancel') + }) + .onFinish(() => { + this.callBackFlag = 'ImageAnimator Finish' + console.info('ImageAnimator Finish') + }) + + // 动画控制按钮区域 + Row() { + Button('start').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Running + }).margin(5) + + Button('pause').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Paused // 暂停并显示当前帧 + }).margin(5) + + Button('stop').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Stopped // 停止并显示起始帧 + }).margin(5) + + Button('Initial').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Initial // 重置为初始状态 + }).margin(5) + } + + // 动画属性控制区域 + Row() { + Button('reverse').width(80).padding(5).onClick(() => { + this.reverse = !this.reverse // 切换反向播放状态 + }).margin(5) + + Button('1').width(40).padding(5).onClick(() => { + this.iterations = 1 // 设置循环次数为1 + }).margin(5) + + Button('-1').width(40).padding(5).onClick(() => { + this.iterations -= 1 // 减少循环次数 + }).margin(5) + + Button('+1').width(40).padding(5).onClick(() => { + this.iterations += 1 // 增加循环次数 + }).margin(5) + } + + // 填充模式控制区域 + Row() { + Button('None').width(50).padding(5).onClick(() => { + this.fillMode = FillMode.None + }).margin(5) + + Button('Forwards').width(100).padding(5).onClick(() => { + this.fillMode = FillMode.Forwards + }).margin(5) + + Button('Backwards').width(100).padding(5).onClick(() => { + this.fillMode = FillMode.Backwards + }).margin(5) + + Button('Both').width(50).padding(5).onClick(() => { + this.fillMode = FillMode.Both + }).margin(5) + } + + // 动画持续时间控制区域 + Row() { + Button('Dur - 0').width(100).padding(5).onClick(() => { + this.duration = 0 + }).margin(5) + + Button('Dur - 5000').width(100).padding(5).onClick(() => { + this.duration = 5000 + }).margin(5) + + Button('Dur - 2500').width(100).padding(5).onClick(() => { + this.duration = 2500 + }).margin(5) + } + + // 显示当前动画状态信息 + Text(this.callBackFlag).fontWeight(FontWeight.Bolder) + Text('This reversed ' + this.reverse).fontWeight(FontWeight.Bolder) + Text('This fillMode ' + this.fillMode).fontWeight(FontWeight.Bolder) + Text('This Duration ' + this.duration).fontWeight(FontWeight.Bolder) + Text('This Iteration ' + this.iterations).fontWeight(FontWeight.Bolder) + }.width('100%').height('100%') + } +} \ No newline at end of file diff --git a/examples/ImageAnimator/entry/src/main/ets/pages/example/ImageAnimatorExample008.ets b/examples/ImageAnimator/entry/src/main/ets/pages/example/ImageAnimatorExample008.ets new file mode 100644 index 0000000000000000000000000000000000000000..74529638696b2353029802fffc16202cb4e57730 --- /dev/null +++ b/examples/ImageAnimator/entry/src/main/ets/pages/example/ImageAnimatorExample008.ets @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ImageAnimator自动暂停功能测试组件, 全面测试动画在状态切换(运行 / 暂停 / 停止)、反向播放、循环次数调整、 +// 填充模式变更及持续时间动态修改等场景下的行为一致性 +@Entry +@Component +struct ImageAnimatorAutoPauseTestWithFa02 { + // 滚动控制器(虽然当前代码未使用,但初始化保证编译通过) + scroller: Scroller = new Scroller() + + // 动画状态控制(运行/暂停/停止/初始) + @State state: AnimationStatus = AnimationStatus.Running + + // 动画播放方向控制(是否反向) + @State reverse: boolean = false + + // 动画循环次数(-1表示无限循环) + @State iterations: number = 100 + + // 自动监控不可见区域标志位 + @State autoMonitorInvisibleArea: boolean = false + + // 测试用数组(当前代码未使用,但保留初始化) + private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + + // 裁剪标志位(当前代码未使用,但保留声明) + @State clipValue: boolean = false + + // 叠加层尺寸控制变量 + @State posX: number = 200 + @State posY: number = 200 + + // 自定义叠加层构建器(显示在动画组件中央) + @Builder OverlayNode() { + Column() { + Image($r('app.media.cat')) + .height(this.posX) + .width(this.posY) + Text('This is overlayNode').fontSize(20).fontColor(Color.White) + } + .width(this.posX) + .height(this.posY) + .alignItems(HorizontalAlign.Center) + } + + build() { + Stack({ alignContent: Alignment.TopStart }) { + // 图片动画组件主体 + ImageAnimator() + .images([ + { src: $r('app.media.no0') }, + { src: $r('app.media.no1') }, + { src: $r('app.media.no2') }, + { src: $r('app.media.no3') }, + { src: $r('app.media.no4') }, + { src: $r('app.media.no5') }, + { src: $r('app.media.no6') } + ]) + .height(200) + .width(200) + .borderRadius(10) + .monitorInvisibleArea(this.autoMonitorInvisibleArea) // 监控不可见区域自动暂停 + .clip(true) + .duration(4000) // 动画总时长4000ms + .state(this.state) // 动画状态控制 + .reverse(this.reverse) // 反向播放控制 + .fillMode(FillMode.Forwards) // 动画结束后保持最后一帧 + .iterations(this.iterations) // 循环次数 + .width(340) // 宽度340px + .height(240) // 高度240px + .margin({ top: 100 }) // 顶部边距100px + .id('TestImageAnimator') // 组件唯一标识 + .onStart(() => { + console.info('ImageAnimator Start') + }) + .onPause(() => { + console.info('ImageAnimator Pause') + }) + .onRepeat(() => { + console.info('ImageAnimator Repeat') + }) + .onCancel(() => { + console.info('ImageAnimator Cancel') + }) + .onFinish(() => { + console.info('ImageAnimator Finish') + }) + .overlay(this.OverlayNode(), { align: Alignment.Center }) // 中央叠加层 + + // 动画状态控制按钮组 + Button('start').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Running + }).margin({ top: 60, left: 20 }) + + Button('pause').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Paused // 暂停并显示当前帧 + }).margin({ top: 110, left: 20 }) + + Button('stop').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Stopped // 停止并显示起始帧 + }).margin({ top: 160, left: 20 }) + + Button('Initial').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Initial // 重置为初始状态 + }).margin({ top: 210, left: 20 }) + + // 动画属性控制按钮组 + Button('reverse').width(100).padding(5).onClick(() => { + this.reverse = !this.reverse // 切换反向播放状态 + }).margin({ top: 260, left: 20 }) + + Button('once').width(100).padding(5).onClick(() => { + this.iterations = 1 // 设置单次播放 + }).margin({ top: 310, left: 20 }) + + Button('infinite').width(100).padding(5).onClick(() => { + this.iterations = -1 // 设置无限循环 + }).margin({ top: 360, left: 20 }) + + // 不可见区域监控功能切换按钮 + Button('Change AutoMonitorInvisableArea ' + this.autoMonitorInvisibleArea) + .width(200).padding(5).onClick(() => { + this.autoMonitorInvisibleArea = !this.autoMonitorInvisibleArea; + }).margin({ top: 410, left: 20 }) + + // 叠加层尺寸控制按钮组 + Button('add posX ' + this.posX).width(200).padding(5).onClick(() => { + this.posX += 50; + }).margin({ top: 460, left: 20 }) + + Button('reduce posX ' + this.posX).width(200).padding(5).onClick(() => { + this.posX = Math.max(50, this.posX - 50); // 防止尺寸为负 + }).margin({ top: 510, left: 20 }) + + Button('add posY ' + this.posY).width(200).padding(5).onClick(() => { + this.posY += 50; + }).margin({ top: 560, left: 20 }) + + Button('reduce posY ' + this.posY).width(200).padding(5).onClick(() => { + this.posY = Math.max(50, this.posY - 50); // 防止尺寸为负 + }).margin({ top: 610, left: 20 }) + } + .width('100%') + .height('100%') + .backgroundColor(0xDCDCDC) // 浅灰色背景 + } +} \ No newline at end of file diff --git a/examples/ImageAnimator/entry/src/main/ets/pages/example/ImageAnimatorExample009.ets b/examples/ImageAnimator/entry/src/main/ets/pages/example/ImageAnimatorExample009.ets new file mode 100644 index 0000000000000000000000000000000000000000..366e9f7a562069937119eca091e95cbfaa091498 --- /dev/null +++ b/examples/ImageAnimator/entry/src/main/ets/pages/example/ImageAnimatorExample009.ets @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ImageAnimator自动暂停功能测试组件 + * 用于测试图片动画在不可见区域自动暂停的特性 + */ +@Entry +@Component +struct ImageAnimatorAutoPauseTestWithFa { + // 滚动控制器,用于实现页面滚动(当前未使用) + scroller: Scroller = new Scroller() + + // 动画状态管理 + @State state: AnimationStatus = AnimationStatus.Running + + // 动画播放方向控制 + @State reverse: boolean = false + + // 动画循环次数(-1表示无限循环) + @State iterations: number = 100 + + // 自动监控不可见区域标志 + @State autoMonitorInvisibleArea: boolean = false + + // 测试用数据数组(当前未使用) + private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + + // 裁剪功能控制标志 + @State clipValue: boolean = false + + build() { + Stack({ alignContent: Alignment.TopStart }) { + // 动画容器(带边框和裁剪功能) + Column() { + // 图片动画组件 + ImageAnimator() + .images([ // 动画帧列表 + { src: $r('app.media.no0') }, + { src: $r('app.media.no1') }, + { src: $r('app.media.no2') }, + { src: $r('app.media.no3') }, + { src: $r('app.media.no4') }, + { src: $r('app.media.no5') }, + { src: $r('app.media.no6') } + ]) + .borderRadius(10) // 圆角边框 + .monitorInvisibleArea(this.autoMonitorInvisibleArea) // 自动监控不可见区域 + .clip(true) // 启用裁剪 + .duration(4000) // 动画周期4秒 + .state(this.state) // 动画状态控制 + .reverse(this.reverse) // 反向播放控制 + .fillMode(FillMode.Forwards) // 动画结束后保持最终状态 + .iterations(this.iterations) // 循环次数 + .width(340).height(240) // 动画尺寸 + .margin({ top: 100 }) // 顶部边距 + .id('TestImageAnimator') // 组件ID + .position({x:100, y:300}) // 相对定位 + .onStart(() => { // 动画开始回调 + console.info('ImageAnimator Start') + }) + .onPause(() => { // 动画暂停回调 + console.info('ImageAnimator Pause') + }) + .onRepeat(() => { // 动画重复回调 + console.info('ImageAnimator Repeat') + }) + .onCancel(() => { // 动画取消回调 + console.info('ImageAnimator Cancel') + }) + .onFinish(() => { // 动画完成回调 + console.info('ImageAnimator Finish') + }) + } + .width(300).height(300) // 容器尺寸 + .borderWidth(2) // 边框宽度 + .clip(this.clipValue) // 容器裁剪控制 + + // 动画控制按钮区域 + Button('start').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Running // 启动动画 + }).margin({ top: 60, left: 20 }) + + Button('pause').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Paused // 暂停动画 + }).margin({ top: 110, left: 20 }) + + Button('stop').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Stopped // 停止动画 + }).margin({ top: 160, left: 20 }) + + Button('Initial').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Initial // 重置动画 + }).margin({ top: 210, left: 20 }) + + Button('reverse').width(100).padding(5).onClick(() => { + this.reverse = !this.reverse // 切换播放方向 + }).margin({ top: 260, left: 20 }) + + Button('once').width(100).padding(5).onClick(() => { + this.iterations = 1 // 设置单次播放 + }).margin({ top: 310, left: 20 }) + + Button('infinite').width(100).padding(5).onClick(() => { + this.iterations = -1 // 设置无限循环 + }).margin({ top: 360, left: 20 }) + + Button('Change AutoMonitorInvisableArea ' + this.autoMonitorInvisibleArea) + .width(200).padding(5).onClick(() => { + this.autoMonitorInvisibleArea = !this.autoMonitorInvisibleArea // 切换自动监控状态 + }).margin({ top: 410, left: 20 }) + + Button('change clip ' + this.clipValue) + .width(200).padding(5).onClick(() => { + this.clipValue = !this.clipValue // 切换裁剪状态 + }).margin({ top: 460, left: 20 }) + } + .width('100%').height('100%') // 页面占满屏幕 + .backgroundColor(0xDCDCDC) // 浅灰色背景 + } +} \ No newline at end of file diff --git a/examples/ImageAnimator/entry/src/main/ets/pages/example/ImageAnimatorExample010.ets b/examples/ImageAnimator/entry/src/main/ets/pages/example/ImageAnimatorExample010.ets new file mode 100644 index 0000000000000000000000000000000000000000..d93721da7dc649cd6e2b055b034948217402f1e5 --- /dev/null +++ b/examples/ImageAnimator/entry/src/main/ets/pages/example/ImageAnimatorExample010.ets @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * 图片动画自动暂停功能测试组件 + * 用于测试图片动画在不可见区域自动暂停的特性 + */ +@Entry +@Component +struct ImageAnimatorAutoPauseTest { + // 滚动控制器,用于管理页面滚动行为 + scroller: Scroller = new Scroller() + + // 动画状态管理(运行/暂停/停止/初始) + @State state: AnimationStatus = AnimationStatus.Running + + // 动画播放方向控制(正向/反向) + @State reverse: boolean = false + + // 动画循环次数(-1表示无限循环) + @State iterations: number = 100 + + // 自动监控不可见区域标志位 + @State autoMonitorInvisibleArea: boolean = false + + // 第一个动画的状态消息 + @State mes: string = 'Null' + + // 第二个动画的状态消息 + @State mes2: string = 'Null' + + // 生成填充内容的数组(用于测试滚动效果) + private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + + build() { + Stack({ alignContent: Alignment.TopStart }) { + // 可滚动区域,包含两个图片动画和填充内容 + Scroll(this.scroller) { + Column() { + // 第一个图片动画组件(正向播放) + ImageAnimator() + .images([ + { src: $r('app.media.no0') }, + { src: $r('app.media.no1') }, + { src: $r('app.media.no2') }, + { src: $r('app.media.no3') }, + { src: $r('app.media.no4') }, + { src: $r('app.media.no5') }, + { src: $r('app.media.no6') } + ]) + .borderRadius(10) // 圆角边框 + .monitorInvisibleArea(this.autoMonitorInvisibleArea) // 监控不可见区域 + .clip(true) // 启用裁剪 + .duration(4000) // 动画周期4秒 + .state(this.state) // 动画状态 + .reverse(this.reverse) // 播放方向 + .fillMode(FillMode.Forwards) // 动画结束后保持最终状态 + .iterations(this.iterations) // 循环次数 + .width(340).height(240) // 尺寸 + .margin({ top: 100 }) // 边距 + .onStart(() => { // 动画开始回调 + this.mes = 'Start' + console.info('ImageAnimator1 Start') + }) + .onPause(() => { // 动画暂停回调 + this.mes = 'Pause' + console.info('ImageAnimator1 Pause') + }) + .onRepeat(() => { // 动画重复回调 + console.info('ImageAnimator1 Repeat') + }) + .onCancel(() => { // 动画取消回调 + console.info('ImageAnimator1 Cancel') + }) + .onFinish(() => { // 动画完成回调 + console.info('ImageAnimator1 Finish') + }) + + // 第二个图片动画组件(反向播放,帧顺序相反) + ImageAnimator() + .images([ + { src: $r('app.media.no6') }, + { src: $r('app.media.no5') }, + { src: $r('app.media.no4') }, + { src: $r('app.media.no3') }, + { src: $r('app.media.no2') }, + { src: $r('app.media.no1') }, + { src: $r('app.media.no0') } + ]) + .borderRadius(10) // 圆角边框 + .monitorInvisibleArea(this.autoMonitorInvisibleArea) // 监控不可见区域 + .clip(true) // 启用裁剪 + .duration(4000) // 动画周期4秒 + .state(this.state) // 动画状态 + .reverse(this.reverse) // 播放方向 + .fillMode(FillMode.Forwards) // 动画结束后保持最终状态 + .iterations(this.iterations) // 循环次数 + .width(340).height(240) // 尺寸 + .margin({ top: 100 }) // 边距 + .onStart(() => { // 动画开始回调 + this.mes2 = 'Start' + console.info('ImageAnimator2 Start') + }) + .onPause(() => { // 动画暂停回调 + this.mes2 = 'Pause' + console.info('ImageAnimator2 Pause') + }) + .onRepeat(() => { // 动画重复回调 + console.info('ImageAnimator2 Repeat') + }) + .onCancel(() => { // 动画取消回调 + console.info('ImageAnimator2 Cancel') + }) + .onFinish(() => { // 动画完成回调 + console.info('ImageAnimator2 Finish') + }) + + // 生成填充内容,用于测试滚动效果 + ForEach(this.arr, (item: number) => { + Text(item.toString()) + .width('90%') + .height(150) + .backgroundColor(0xFFFFFF) + .borderRadius(15) + .fontSize(16) + .textAlign(TextAlign.Center) + .margin({ top: 10 }) + }, (item: number) => item.toString()) + }.width('100%') + } + .scrollable(ScrollDirection.Vertical) // 垂直滚动 + .scrollBar(BarState.On) // 显示滚动条 + .scrollBarColor(Color.Gray) // 滚动条颜色 + .scrollBarWidth(10) // 滚动条宽度 + .friction(0.6) // 滚动摩擦系数 + .edgeEffect(EdgeEffect.None) // 边缘效果 + .onWillScroll((xOffset: number, yOffset: number, scrollState: ScrollState) => { + console.info(`Scroll offset: x=${xOffset}, y=${yOffset}`) + }) + .onScrollEdge((side: Edge) => { + console.info('Scroll reached edge') + }) + .onScrollStop(() => { + console.info('Scroll stopped') + }) + + // 动画控制按钮区域 + Button('start').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Running // 启动动画 + }).margin({ top: 60, left: 20 }) + + Button('pause').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Paused // 暂停动画 + }).margin({ top: 110, left: 20 }) + + Button('stop').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Stopped // 停止动画 + }).margin({ top: 160, left: 20 }) + + Button('Initial').width(100).padding(5).onClick(() => { + this.state = AnimationStatus.Initial // 重置动画 + }).margin({ top: 210, left: 20 }) + + Button('reverse').width(100).padding(5).onClick(() => { + this.reverse = !this.reverse // 切换播放方向 + }).margin({ top: 260, left: 20 }) + + Button('once').width(100).padding(5).onClick(() => { + this.iterations = 1 // 设置单次播放 + }).margin({ top: 310, left: 20 }) + + Button('infinite').width(100).padding(5).onClick(() => { + this.iterations = -1 // 设置无限循环 + }).margin({ top: 360, left: 20 }) + + Button('Change AutoMonitorInvisableArea ' + this.autoMonitorInvisibleArea) + .width(200).padding(5).onClick(() => { + this.autoMonitorInvisibleArea = !this.autoMonitorInvisibleArea // 切换自动监控状态 + }).margin({ top: 410, left: 20 }) + + // 显示动画状态的按钮 + Button('Start or Pause : ' + this.mes) + .width(200).padding(5).margin({ top: 460, left: 20 }) + + Button('Start or Pause : ' + this.mes2) + .width(200).padding(5).margin({ top: 510, left: 20 }) + } + .width('100%').height('100%') // 占满整个屏幕 + .backgroundColor(0xDCDCDC) // 浅灰色背景 + } +} \ No newline at end of file diff --git a/examples/ImageAnimator/entry/src/main/ets/pages/example/ImageAnimatorExample011.ets b/examples/ImageAnimator/entry/src/main/ets/pages/example/ImageAnimatorExample011.ets new file mode 100644 index 0000000000000000000000000000000000000000..be43f5e9f770fe6f792e6f11378fa07dec5225b1 --- /dev/null +++ b/examples/ImageAnimator/entry/src/main/ets/pages/example/ImageAnimatorExample011.ets @@ -0,0 +1,598 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// 该 Demo 通过定义多组测试数据,全面验证 ImageAnimator 组件在原生属性与 Modifier 两种设置方式下,面对迭代次数、填充模式、 +// 尺寸固定、反向播放、持续时间、动画状态及图片资源等多维度属性组合时的行为一致性,支持动态切换测试用例以覆盖正常、边界及异常场景。 + +//引入需要的Modifier +import { ImageAnimatorModifier } from '@ohos.arkui.modifier'; +import router from '@ohos.router'; + + +//定义要测试的属性以及类型 +export interface TestAttributes { + iterations?: number; + fillMode?: FillMode; + fixedSize?: boolean; + reverse?: boolean; + duration?: number; + state?: AnimationStatus; + images?: Array; +} + +export interface Images { + width: number, + height: number, + src: string | Resource + top: number + left: number +} + +//初始化测试数据 +export const testData: TestAttributes[] = [ +//1 + { + iterations: 1, + fillMode: FillMode.None, + fixedSize: true, + reverse: true, + duration: 2000, + state: AnimationStatus.Initial, + images: [ + { + src: $r('app.media.flower') + }, + { + src: $r('app.media.ic_jpg') + }, + { + src: $r('app.media.mix_png') + }, + { + src: $r('app.media.ic_bmp') + } + ] + }, + //2 + { + iterations: 1, + fillMode: FillMode.None, + fixedSize: true, + reverse: true, + duration: 3000, + state: AnimationStatus.Running, + images: [ + { + src: $r('app.media.webp') + }, + { + src: $r('app.media.ic_svg') + }, + { + src: $r('app.media.ic_gif') + }, + { + src: $r('app.media.app_icon') + } + ] + }, + //3 + { + images: [{ + src: $r('app.media.flower'), + width: 200, + height: 100, + top: 10, + left: 20 + }, + { + src: '/common/ic_gif.gif', + width: 300, + height: 200, + top: 0, + left: 0 + }, + { + src: $r('app.media.app_icon'), + width: '50vp', + height: '50vp', + top: '50vp', + left: '50vp' + }, + { + src: $r('app.media.ic_svg'), + width: 100, + height: 100, + top: -10, + left: -10 + }, + { + src: '/common/ic_bmp.bmp', + width: 100, + height: 300, + top: -10, + left: -10 + }], + state: AnimationStatus.Initial, + duration: 2000, + reverse: false, + fixedSize: true, + fillMode: FillMode.Forwards, + iterations: 2, + }, + // 4 验证images里面全部设置了duration是否走images自身设置的值;验证fixedSize为false + // images中图片大小是否走自己设置的宽高坐标值;验证reverse设置为true播放效果;验证iterations为负数时效果 + { + images: [{ + src: $r('app.media.png_525_539'), + width: 100, + height: 200, + top: 0, + left: 0, + duration: 6000 + }, + { + src: $r('app.media.jpeg_690_669'), + width: '50vp', + height: '100vp', + top: '10vp', + left: '20vp', + duration: 6000 + }, + { + src: '/common/ic_png2.png', + width: 200, + height: 100, + top: -40, + left: -30, + duration: 2000 + }], + state: AnimationStatus.Running, + duration: 2000, //不生效 + reverse: true, + fixedSize: false, + fillMode: FillMode.Backwards, + iterations: -1, + }, + //5 预期只播放5张图? + { + images: [{ + src: '/common/png_256_256.png', + width: '120vp', + height: '350vp', + top: '40vp', + left: '30vp', + duration: 2000 + }, + { + src: $r('app.media.png_525_539'), + width: 100, + height: 200, + top: 10, + left: 20, + duration: 6000 + }, + { + src: $r('app.media.jpeg_690_669'), + width: '50vp', + height: '100vp', + top: '5vp', + left: '5vp', + duration: -6000 + }, //duration走默认值0,不播放 + { + src: '/common/ic_png2.png', + width: 200, + height: 300, + top: 40, + left: 30, + duration: 20 + }, + // duration值很小,播放吗? + { + src: '/common/jpg_240_160.jpg', + width: -300, + height: -100, + top: 40, + left: 30, + duration: 2000 + }, //width、height为负数,走默认值0,不显示? + { + src: $r('app.media.ic_public_refresh'), + width: 300, + height: 120, + top: -400, + left: -30, + duration: 2000 + }, + { + src: $r('app.media.png_608_317'), + width: 350, + height: 0, + top: 40, + left: 30, + duration: 6000 + }, //height为0,不显示? + { + src: '/common/ic_png.png', + width: '250vp', + height: 50, + top: '20%', + left: '30%', + duration: 6000 + }, + { + src: $r('app.media.alt_svg'), + width: 0, + height: '250vp', + top: 40, + left: 30, + duration: 6000 + }, //width为0,不显示? + { + src: '/common/yellowperson.jpg', + width: '100%', + height: '20%', + top: 40, + left: 30, + duration: 6000 + }], + state: AnimationStatus.Paused, + duration: 2000, //不生效 + reverse: true, + fixedSize: false, + fillMode: FillMode.None, + iterations: 3, + }, + // 6:验证iterations为0时效果 + { + images: [{ + src: '/common/ic_png2.png', + width: 300, + height: 100, + top: 40, + left: 30, + duration: 2000 + }, + { + src: $r('app.media.png_525_539'), + width: 100, + height: 200, + top: 10, + left: 20, + duration: 6000 + }, + { + src: $r('app.media.jpeg_690_669'), + width: '50vp', + height: '100vp', + top: 10, + left: 20, + duration: 6000 + }], + state: AnimationStatus.Running, + duration: 2000, //不生效 + reverse: true, + fixedSize: false, + fillMode: FillMode.Backwards, + iterations: 0, + }, + //7:验证duration为0时效果 + { + images: [{ + src: $r('app.media.png_608_317'), + width: 300, + height: 100, + top: 40, + left: 30 + }, + { + src: $r('app.media.png_525_539'), + width: 100, + height: 200, + top: 10, + left: 20 + }, + { + src: $r('app.media.jpeg_690_669'), + width: '50vp', + height: '100vp', + top: 10, + left: 20 + }, + { + src: '/common/ic_png2.png', + width: 300, + height: 100, + top: 40, + left: 30 + }, + { + src: '/common/jpg_240_160.jpg', + width: 300, + height: 100, + top: 40, + left: 30 + }, + { + src: '/common/png_256_256.png', + width: 300, + height: 100, + top: 40, + left: 30 + }, + { + src: $r('app.media.ic_public_refresh'), + width: 300, + height: 100, + top: 40, + left: 30 + }, + { + src: '/common/ic_png.png', + width: 300, + height: 100, + top: 40, + left: 30 + }, + { + src: $r('app.media.alt_svg'), + width: 300, + height: 100, + top: 40, + left: 30 + }, + { + src: '/common/yellowperson.jpg', + width: 300, + height: 100, + top: 40, + left: 30 + }], + state: AnimationStatus.Stopped, + duration: 0, //不播放 + reverse: false, + fixedSize: true, + fillMode: FillMode.Both, + iterations: 1, + }, + //8:验证fillMode为Both时效果 + { + images: [{ + src: '/common/ic_png2.png', + width: 300, + height: 100, + top: 40, + left: 30, + duration: 2000 + }, + { + src: $r('app.media.png_525_539'), + width: 100, + height: 200, + top: 10, + left: 20, + duration: 6000 + }, + { + src: $r('app.media.jpeg_690_669'), + width: '50vp', + height: '100vp', + top: 10, + left: 20, + duration: 6000 + }], + state: AnimationStatus.Initial, + duration: 2000, //不生效 + reverse: true, + fixedSize: false, + fillMode: FillMode.Both, + iterations: 1.5, + }, + //9:验证动态切换undefined + { + images: [{ + src: $r('app.media.png_525_539'), + width: 100, + height: 200, + top: 10, + left: 20, + duration: 6000 + }, + { + src: $r('app.media.jpeg_690_669'), + width: '50vp', + height: '100vp', + top: 10, + left: 20, + duration: 6000 + }, + { + src: '/common/ic_png2.png', + width: 300, + height: 100, + top: 40, + left: 30, + duration: 2000 + }], + state: undefined, + duration: undefined, //预期走默认值1000 + reverse: undefined, //预期不反转播放 + fixedSize: undefined, //预期图片按照组件大小显示 + fillMode: undefined, //预期效果为forwards + iterations: undefined, //预期只播放1次 + }, +] + + +//修改基类 +class CustomModifier extends ImageAnimatorModifier { + applyNormalAttribute(instance: ImageAnimatorAttribute): void { + super.applyNormalAttribute?.(instance); + } + + applyChange(testData: TestAttributes): void { + this + .iterations(testData.iterations) + .fillMode(testData.fillMode) + .fixedSize(testData.fixedSize) + .reverse(testData.reverse) + .duration(testData.duration) + .state(testData.state) + .images(testData.images) + } +} + +@Component +struct ModifierComponent { + @Link customModifier: ImageAnimatorModifier + + build() { + Column({ space: 10 }) { + ImageAnimator() + .width(200) + .height(150) + .attributeModifier(this.customModifier as CustomModifier) + } + } +} + +@Component +struct MyCustomComponent { + @Prop testData: TestAttributes + + build() { + //需要替换成自己要测试的组件并设置属性 + Column({ space: 10 }) { + ImageAnimator() + .width(200) + .height(150) + .iterations(this.testData.iterations) + .fillMode(this.testData.fillMode) + .fixedSize(this.testData.fixedSize) + .reverse(this.testData.reverse) + .duration(this.testData.duration) + .state(this.testData.state) + .images(this.testData.images) + } + } +} + +@Entry +@Component +struct ImageAnimatorLevel1_2 { + @State index: number = 0 + @State testData: TestAttributes[] = testData + @State customModifier: ImageAnimatorAttribute = new CustomModifier() + .iterations(this.testData[this.index].iterations) + .fillMode(this.testData[this.index].fillMode) + .fixedSize(this.testData[this.index].fixedSize) + .reverse(this.testData[this.index].reverse) + .duration(this.testData[this.index].duration) + .state(this.testData[this.index].state) + .images(this.testData[this.index].images) + + @Builder + nativeBuilder() { + Column() { + //这里是原生属性UI + MyCustomComponent({ + testData: this.testData[this.index] + }) + } + } + + @Builder + modifierBuilder() { + Column() { + //这里是Modifier属性UI + ModifierComponent({ customModifier: this.customModifier }) + } + } + + build() { + Column() { + Row() { + Text('原生属性') + .height(20) + .fontWeight(FontWeight.Bold) + + Blank().width('5%') + + Text('Modifier') + .height(20) + .fontWeight(FontWeight.Bold) + } + + Row() { + Column() { + this.nativeBuilder() + } + .width('50%') + .height('50%') + .key('testArea0') + .clip(true) + + + Column() { + this.modifierBuilder() + } + .width('50%') + .height('50%') + .key('testArea1') + .clip(true) + } + .key('testArea') + .width('100%') + .height('50%') + .justifyContent(FlexAlign.Center) + + //更新数据按钮 + Column() { + Text(`${this.index + 1} / ${this.testData?.length}`) + Row({ space: 2 }) { + Text(`${this.testData.length}`).key('ImageAnimatorLevel12_cycles1') + Button('Index++') + .margin({ left: 10, right: 10 }) + .onClick(() => { + this.index = (this.index + 1) % (this.testData?.length); + (this.customModifier as CustomModifier).applyChange(this.testData[this.index]); + }).key('indexLeft') + + Button('Index--') + .margin({ left: 10, right: 10 }) + .onClick(() => { + if (this.index <= 0) { + this.index = this.testData?.length - 1 + } else { + this.index-- + } + (this.customModifier as CustomModifier).applyChange(this.testData[this.index]); + }).key('indexRight') + }.padding({ top: 20, bottom: 100 }) + } + .height('40%') + .alignItems(HorizontalAlign.Center) + .position({ x: 0, y: '70%' }) + .width('100%') + }.width('100%') + .height('100%') + } + + pageTransition() { + PageTransitionEnter({ type: RouteType.None, duration: 0 }) + PageTransitionExit({ type: RouteType.None, duration: 0 }) + } +} \ No newline at end of file