本节首先解决@Extend装饰器无法多页面复用的问题,介绍通过自定义组件实现标题和正文的样式复用。然后介绍图片展示组件Image和帧动画组件ImageAnimator,并同时讲解如何对图片应用图像效果及实现共享元素转场。
3.3.1 建立多页面可复用的组件
在《3.1.1 Text组件》这节中,我们使用了@Extend装饰器定义了title和body,实现快速定义并复用组件的自定义样式。但是,@Extend装饰器只能应用在定义该装饰器的页面里,无法通过export共享给其它页面调用。同时,如果在其它页面定义了同名的装饰器,如,另一个页面也定义了一个名为"title"或"body"的装饰器,会编译异常,提示重复的定义。
本小节建立两个自定义组件,解决@Extend装饰器的上述不足,实现多页面复用。其中:
@Component装饰的struct表示该结构体具有组件化能力,能够成为一个独立的组件,这种类型的组件也称为自定义组件。
本小节不打算展开对@Component装饰器的讲解,更多的声明式语法将在《第8章 基于TS扩展的声明式开发规范》中讲解。
在index.ets页面底部加入如下代码:
在下一小节演示如何引用并使用标题组件和正文组件。
3.3.2 Image组件
图片组件Image,用来渲染展示图片。
首先,建立ImageSample.ets页面,用于展示本小节的效果,初始化代码如下:
1.接口:Image(src: string | PixelMap)
src:string|PixelMap 图片的URI,支持本地图片和网络路径,支持使用媒体PixelMap对象。
PixelMap:图像像素类,用于读取或写入图像数据以及获取图像信息。在调用PixelMap的方法前,需要先通过createPixelMap创建一个PixelMap实例。关于创建PixelMap实例的方法并不适合初学者学习,这里暂时不展开讨论,后面会有专门的章节详细讲解。本专栏教程是由浅入深、循序渐进、水到渠成的。
这里先演示如何通过接口引用App本地图片。图片格式支持“png/jpg/gif/svg”,图片文件可以存放在media媒体目录、rawfile目录或自己创建的“/common/images”目录。图片格式和图片存放目录没有对应关系,下面演示做了个随机分配,便于用最少代码做更全面的演示。
在上述初始化代码的”TODO“处加入如下代码:
在手机浅色模式和平板深色模式下效果如下:

使用网络路径
如果图片来源于网络,接口还是和上面一样,但是需要在config.json文件中“js”节点的后面加入如下配置:
如下图所示:

然后在ImageSample.ets页面加入如下代码:
效果如下:

2.属性
.alt(string | PixelMap): 加载时显示的占位图。支持本地图片和网络路径,支持使用媒体PixelMap对象。
示例代码:
在媒体文件夹下放入alt.png图片,浅色模式和深色模式下效果如下:

. objectFit (ImageFit): 设置图片的缩放类型。
示例代码:
在手机和平板上,图片表现如下:

. objectRepeat (ImageRepeat): 设置图片的重复样式。
示例代码:
运行效果:

. interpolation (ImageInterpolation): 设置图片的插值效果,仅针对图片放大插值。
对于一些较小的图片,或老照片,插值计算有助于提升画质。示例代码:
可以看到,原图放大后有马赛克,而高插值的方式就很平滑了。效果对比如下:

. renderMode (ImageRenderMode): 设置图片渲染的模式。 相当于PS中对图片做去色处理,可将彩色图片变为灰度图片。
示例代码:
效果如下:

.sourceSize({width: number,height: number}): 设置图片解码尺寸,将原始图片解码成指定尺寸的图片,number类型单位为px。
一张原本尺寸为1200x750像素的图片,指定解码尺寸为150x100像素的照片后,如果图片展示尺寸超过该尺寸,就会模糊,示例代码如下:
解码为小尺寸和原图的效果对比如下:

对于.sourceSize属性的使用,我个人的看法是:如果原图较小,通过sourceSize解码为大尺寸,建议配合.interpolation (ImageInterpolation)属性来设置图片的插值,改善画质;如果原图较大,通过sourceSize解码为小尺寸,建议配合.objectFit(ImageFit.ScaleDown)属性显示为合适的尺寸,保证清晰度。
如有不同想法,欢迎留言讨论。
3.事件
1.onComplete(callback: (event?: { width: number, height: number, componentWidth: number, componentHeight: number, loadingStatus: number }) => void):图片成功加载时触发该回调,返回成功加载的图源尺寸。
2.onError(callback: (event?: { componentWidth: number, componentHeight: number }) => void):图片加载出现异常时触发该回调。
3.onFinish(callback: () => void) 当加载的源文件为带动效的svg图片时,当svg动效播放完成时会触发这个回调,如果动效为无限循环动效,则不会触发这个回调。
示例代码:
4.图像效果
前面演示了通过Image组件的.renderMode(ImageRenderMode)实现图片的去色效果。在基于TS扩展的声明式开发范式中还有一些控制图像效果的属性,这些属性不仅可以用于Image组件,也可以用于其它组件。
示例代码如下:
上述代码的效果可以通过远程模拟器体验:

5.共享元素转场
在图片列表页面给缩略图设置一个id,在放大显示图片的展示页也给图片设置一个相同的id, 利用共享元素转场,将当前页面的图片转场到详情页面。
属性:
. sharedTransition(id: string, options?: Object) : 两个页面的组件配置为同一个id,则转场过程中会进行共享元素转场,配置为空字符串时不会有共享元素转场效果。 其中options参数说明如下:
duration: number 单位为毫秒,默认动画时长为1000毫秒。
curve : Curve | Curves 默认曲线为线性
delay:number 默认值0,单位为毫秒,默认不延时播放。
示例代码:
首先在ImageSample.ets页面添加如下代码:
/**
* 5.共享元素转场
* 共享元素转场支持页面间的转场,如当前页面的图片转场至下一页面中。
*
* 属性:
* .sharedTransition(id: string,options?: Object)
* 两个页面的组件配置为同一个id,则转场过程中会进行共享元素转场,配置为空字符串时不会有共享元素转场效果。
* options参数说明:
* duration:number 默认值1000,单位为毫秒,默认动画时长为1000毫秒。
* curve:Curve | Curves 默认值Linear,默认曲线为线性
* Curve.Linear 表示动画从头到尾的速度都是相同的。
* Curve.Ease 表示动画以低速开始,然后加快,在结束前变慢,CubicBezier(0.25, 0.1, 0.25, 1.0)。
* Curve.EaseIn 表示动画以低速开始,CubicBezier(0.42, 0.0, 1.0, 1.0)。
* Curve.EaseOut 表示动画以低速结束,CubicBezier(0.0, 0.0, 0.58, 1.0)。
* Curve.EaseInOut 表示动画以低速开始和结束,CubicBezier(0.42, 0.0, 0.58, 1.0)。
* Curve.FastOutSlowIn 标准曲线,cubic-bezier(0.4, 0.0, 0.2, 1.0)。
* Curve.LinearOutSlowIn 减速曲线,cubic-bezier(0.0, 0.0, 0.2, 1.0)。
* Curve.FastOutLinearIn 加速曲线,cubic-bezier(0.4, 0.0, 1.0, 1.0)。
* Curve.ExtremeDeceleration 急缓曲线,cubic-bezier(0.0, 0.0, 0.0, 1.0)。
* Curve.Sharp 锐利曲线,cubic-bezier(0.33, 0.0, 0.67, 1.0)。
* Curve.Rhythm 节奏曲线,cubic-bezier(0.7, 0.0, 0.2, 1.0)。
* Curve.Smooth 平滑曲线,cubic-bezier(0.4, 0.0, 0.4, 1.0)。
* Curve.Friction 阻尼曲线,CubicBezier(0.2, 0.0, 0.2, 1.0)。
* delay:number 默认值0,单位为毫秒,默认不延时播放。
*/
H8({text:'5.共享元素转场'})
Navigator({ target: 'pages/ImageDetailSample', type: NavigationType.Push }) {
Image($r("app.media.cover"))
.width(100)
.aspectRatio(1.5)
.sharedTransition('sharedId1', { duration: 800, curve: Curve.Linear, delay: 100 })
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
然后新建一个ImageDetailSample.ets页面,代码如下:
建议在DevEco Studioi中通过本地预览器或远程模拟器体验流畅的转场动画效果,可以点击放大,也可以点击返回上级页面时缩小,运行效果如下:

3.3.3 ImageAnimator组件
在ArkUI eTS语言中,可通过Image组件展示gif动画,同时,提供了一个ImageAnimator组件,提供帧动画组件来实现逐帧播放图片的能力,可以配置需要播放的图片列表,每张图片可以配置时长。
新建ImageAnimatorSample.ets页面,初始化代码如下:
1.接口
ImageAnimator()
2.属性
.images(Array<{
src:string, //图片路径,图片格式为svg,png和jpg。
width?:Length, //图片宽度。
height?:Length, //图片高度。
top?:Length, //图片相对于组件左上角的纵向坐标。
left?:Length, //图片相对于组件左上角的横向坐标。
duration?:number //每一帧图片的播放时长,单位毫秒。
}>):设置图片帧信息集合。
.state(AnimationStatus):用于控制播放状态。
.duration(number):持续时间
.reverse(boolean):设置播放顺序。
.fixedSize(boolean):设置图片大小是否固定为组件大小。
.preDecode(number):是否启用预解码。
.fillMode(FillMode): 设置动画开始前和结束后的状态 。
.iterations(number): 默认播放一次,设置为-1时表示无限次播放。
3.事件
onStart() => void : 状态回调,动画开始播放时触发。
onPause() => void : 状态回调,动画暂停播放时触发。
onRepeat() => void : 状态回调,动画重新播放时触发。
onCancel() => void : 状态回调,动画取消播放时触发。
onFinish() => void : 状态回调,动画播放完成时触发。
示例代码如下:
首先,在代码"build(){"之前加入如下代码:
代码位置如下图所示:

然后在"// TODO"处加入如下代码:
/**
* 1.接口:ImageAnimator()
* 2.属性:
* images:Array<{
src:string, //图片路径,图片格式为svg,png和jpg。
width?:Length, //图片宽度。
height?:Length, //图片高度。
top?:Length, //图片相对于组件左上角的纵向坐标。
left?:Length, //图片相对于组件左上角的横向坐标。
duration?:number //每一帧图片的播放时长,单位毫秒。
}> 设置图片帧信息集合。每一帧的帧信息包含图片路径、图片大小、图片位置和图片播放时长信息。
* state:AnimationStatus 默认值Initial,默认为初始状态,用于控制播放状态。
* AnimationStatus.Initial 动画初始状态。
* AnimationStatus.Running 动画处于播放状态。
* AnimationStatus.Paused 动画处于暂停状态。
* AnimationStatus.Stopped 动画处于停止状态。
* duration:number 默认值1000,单位为毫秒,默认时长为1000ms;duration为0时,不播放图片;值的改变只会在下一次循环开始时生效;当images中设置了单独的duration后,该属性设置无效。
* reverse:boolean 默认值false,设置播放顺序。false表示从第1张图片播放到最后1张图片; true表示从最后1张图片播放到第1张图片。
* fixedSize:boolean 默认值true,设置图片大小是否固定为组件大小。 true表示图片大小与组件大小一致,此时设置图片的width 、height 、top 和left属性是无效的。false表示每一张图片的 width 、height 、top和left属性都要单独设置。
* preDecode:number 默认值0,是否启用预解码,默认值为0,即不启用预解码,如该值设为2,则播放当前页时会提前加载后面两张图片至缓存以提升性能。
* fillMode:FillMode 默认值Forwards,设置动画开始前和结束后的状态,可选值参见FillMode说明。
* FillMode.None 播放完成后恢复初始状态。
* FillMode.Forwards 播放完成后保持动画结束时的状态。
* FillMode.Backwards 在animation-delay所指定的一段时间内,在动画显示之前,应用开始属性值。
* FillMode.Both 向前和向后填充模式都被应用。
* iterations:number 默认值1,默认播放一次,设置为-1时表示无限次播放。
*
* 3.事件:
* onStart() => void 状态回调,动画开始播放时触发。
* onPause() => void 状态回调,动画暂停播放时触发。
* onRepeat() => void 状态回调,动画重新播放时触发。
* onCancel() => void 状态回调,动画取消播放时触发。
* onFinish() => void 状态回调,动画播放完成时触发。
*/
ImageAnimator()
.images([
{
src: '/common/images/01.jpg',
duration: 100,
width: 300,
height: 300,
top: 0,
left: 0
},
{
src: '/common/images/02.jpg',
duration: 100,
width: 300,
height: 300,
top: 0,
left: 0
},
{
src: '/common/images/03.jpg',
duration: 100,
width: 300,
height: 300,
top: 0,
left: 0
},
{
src: '/common/images/04.jpg',
duration: 100,
width: 300,
height: 300,
top: 0,
left: 0
},
{
src: '/common/images/05.jpg',
duration: 100,
width: 300,
height: 300,
top: 0,
left: 0
},
{
src: '/common/images/06.jpg',
duration: 100,
width: 300,
height: 300,
top: 0,
left: 0
},
{
src: '/common/images/07.jpg',
duration: 100,
width: 300,
height: 300,
top: 0,
left: 0
},
{
src: '/common/images/08.jpg',
duration: 100,
width: 300,
height: 300,
top: 0,
left: 0
},
{
src: '/common/images/09.jpg',
duration: 100,
width: 300,
height: 300,
top: 0,
left: 0
},
{
src: '/common/images/10.jpg',
duration: 100,
width: 300,
height: 300,
top: 0,
left: 0
},
{
src: '/common/images/11.jpg',
duration: 100,
width: 300,
height: 300,
top: 0,
left: 0
},
{
src: '/common/images/12.jpg',
duration: 100,
width: 300,
height: 300,
top: 0,
left: 0
}
]) // 设置图片帧信息集合
.state(this.state) // 用于控制播放状态
.reverse(this.reverse) // 设置播放顺序,
.fixedSize(false) // 设置图片大小是否固定为组件大小
.preDecode(2) // 是否启用预解码
.fillMode(FillMode.None) // 设置动画开始前和结束后的状态
.iterations(this.iterations) // 默认播放一次,设置为-1时表示无限次播放
.width(300).height(300)
.margin({top:100})
.onStart(() => { // 状态回调,动画开始播放时触发
console.info('动画开始播放')
})
.onPause(() => { // 状态回调,动画暂停播放时触发
console.info('动画暂停播放')
})
.onRepeat(() => { // 状态回调,动画重新播放时触发
console.info('动画重新播放')
})
.onCancel(() => { // 状态回调,动画取消播放时触发
console.info('动画取消播放')
})
.onFinish(() => { // 状态回调,动画播放完成时触发
console.info('动画播放完成')
})
Row() {
Button('开始').width(100).padding(5).onClick(() => {
this.state = AnimationStatus.Running
})
Button('暂停').width(100).padding(5).onClick(() => {
this.state = AnimationStatus.Paused
})
Button('停止').width(100).padding(5).onClick(() => {
this.state = AnimationStatus.Stopped
})
}
Row() {
Button('反向播放').width(100).padding(5).onClick(() => {
this.reverse = !this.reverse
})
Button('播放一次').width(100).padding(5).onClick(() => {
this.iterations = 1
})
Button('无限循环').width(100).padding(5).onClick(() => {
this.iterations = -1
})
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
- 175.
- 176.
- 177.
- 178.
运行效果如下:

【本节源码:https://gitee.com/cloudev/harmonyos3/tree/master/3.0/BaseComponent 】
赞,老师讲解的非常详细
点赞,收藏,评论三连