3.3 Image组件和ImageAnimator组件 原创 精华
本节首先解决@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页面底部加入如下代码:
/**
* H8标题
*/
@Component
export struct H8 {
private text: string = ''
private color: any = $r('app.color.fgLevel1')
build() {
Text(this.text)
.fontSize($r('app.float.fontSizeH8'))
.fontWeight(Number($r('app.float.fontWeightH8')))
.fontColor(this.color)
.fontFamily('sans-serif-condensed, sans-serif')
}
}
/**
* 正文文本
*/
@Component
export struct TextBody1 {
private text: string = ''
private color: any = $r('app.color.fgLevel1')
build() {
Text(this.text)
.fontSize($r('app.float.fontSizeBody1'))
.fontWeight(Number($r('app.float.fontWeightBody1')))
.fontColor(this.color)
.fontFamily('sans-serif-condensed, sans-serif')
}
}
在下一小节演示如何引用并使用标题组件和正文组件。
3.3.2 Image组件
图片组件Image,用来渲染展示图片。
首先,建立ImageSample.ets页面,用于展示本小节的效果,初始化代码如下:
// 引用自定义组件
import {H8,TextBody1} from './index'
/**
* 3.3.1 Image组件
*/
@Entry
@Component
struct ImageSample {
build() {
Stack({ alignContent: Alignment.TopStart }){
Scroll() {
Column({space:8}){
// 返回首页
Row({space:8}){
Image($r("app.media.ic_back")).width(24).height(24)
Navigator({ target: 'pages/index', type: NavigationType.Back }) {
H8({text:'返回'}) // 调用自定义组件
}
}
// TODO
}.padding({left: $r('app.float.spaceLeft'), right: $r('app.float.spaceRight')}).alignItems(HorizontalAlign.Start)
}
}.width('100%').height('100%').backgroundColor($r("app.color.appBg")).padding({top:$r('app.float.spaceTop'), bottom:$r('app.float.spaceBottom')})
}
}
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“处加入如下代码:
/**
* 接口:Image(src: string | PixelMap)
* src:string|PixelMap 图片的URI,支持本地图片和网络路径,支持使用媒体PixelMap对象。
* PixelMap:图像像素类,用于读取或写入图像数据以及获取图像信息。在调用PixelMap的方法前,需要先通过createPixelMap创建一个PixelMap实例。
*/
H8({text:'1.接口'})
TextBody1({text:'png格式图片,media目录'})
Image($r("app.media.cover")) // media目录下的媒体资源
.width("100%")
.aspectRatio(1.5)
.borderRadius($r("app.float.radius_L"))
TextBody1({text:'gif格式图片,rawfile目录'})
Image($rawfile("ic_gif.gif")) // $rawfile资源要写文件后缀
.width("100%")
.aspectRatio(1.5)
.borderRadius($r("app.float.radius_L"))
TextBody1({text:'jpg格式图片,/common/images目录'})
Image("/common/images/banner.jpg") // /common/images目录下的图片
.width("100%")
.aspectRatio(3.5)
在手机浅色模式和平板深色模式下效果如下:
使用网络路径
如果图片来源于网络,接口还是和上面一样,但是需要在config.json文件中“js”节点的后面加入如下配置:
"reqPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
如下图所示:
然后在ImageSample.ets页面加入如下代码:
TextBody1({text:'svg格式图片,使用网络路径'})
Image("https://developer.huawei.com/images/new-content/Navigation/HW-LOGO.svg") // 使用网络路径的图片
.width(300).height(65) // Logo指定宽高,不进行自适应缩放
效果如下:
2.属性
.alt(string | PixelMap): 加载时显示的占位图。支持本地图片和网络路径,支持使用媒体PixelMap对象。
示例代码:
H8({text:'2.属性'})
/**
* .alt(string | PixelMap): 加载时显示的占位图。支持本地图片和网络路径,支持使用媒体PixelMap对象。
*/
TextBody1({text:'.alt(string | PixelMap): 加载时显示的占位图'})
Image('') // 这里使用网络路径的图片
.width("100%")
.aspectRatio(3.5)
.alt($r('app.media.alt')) // 加载时显示的占位图
在媒体文件夹下放入alt.png图片,浅色模式和深色模式下效果如下:
. objectFit (ImageFit): 设置图片的缩放类型。
示例代码:
/**
* .objectFit(ImageFit) 默认值Cover,设置图片的缩放类型。
* ImageFit.Cover 保持宽高比进行缩小或者放大,使得图片两边都大于或等于显示边界。
* ImageFit.Contain 保持宽高比进行缩小或者放大,使得图片完全显示在显示边界内。
* ImageFit.Fill 不保持宽高比进行放大缩小,使得图片填充满显示边界。
* ImageFit.None 保持原有尺寸显示。通常配合objectRepeat属性一起使用。
* ImageFit.ScaleDown 保持宽高比显示,图片缩小或者保持不变。
*/
TextBody1({text:'.objectFit(ImageFit):设置图片的缩放类型'})
Image($r("app.media.cover"))
.width('100%').height(120)
.objectFit(ImageFit.Cover)
Image($r("app.media.cover"))
.width('100%').height(120)
.objectFit(ImageFit.Contain)
Image($r("app.media.cover"))
.width('100%').height(120)
.objectFit(ImageFit.Fill)
Image($r("app.media.cover"))
.width('100%').height(120)
.objectFit(ImageFit.None)
Image($r("app.media.cover"))
.width('100%').height(120)
.objectFit(ImageFit.ScaleDown)
在手机和平板上,图片表现如下:
. objectRepeat (ImageRepeat): 设置图片的重复样式。
示例代码:
/**
* .objectRepeat (ImageRepeat): 默认值NoRepeat,设置图片的重复样式。
* ImageRepeat.X 只在水平轴上重复绘制图片。
* ImageRepeat.Y 只在竖直轴上重复绘制图片。
* ImageRepeat.XY 在两个轴上重复绘制图片。
* ImageRepeat.NoRepeat 不重复绘制图片。
* 注意:SVG类型图源不支持该属性。
*/
TextBody1({text:'. objectRepeat (ImageRepeat): 设置图片的重复样式'})
Image($r('app.media.icon'))
.width(115).height(115)
.border({ width: 1 }).borderStyle(BorderStyle.Dashed)
.objectFit(ImageFit.ScaleDown)
.objectRepeat(ImageRepeat.X) // 只在水平轴上重复绘制图片
Image($r('app.media.icon'))
.width(115).height(115)
.border({ width: 1 }).borderStyle(BorderStyle.Dashed)
.objectFit(ImageFit.ScaleDown)
.objectRepeat(ImageRepeat.Y) // 只在竖直轴上重复绘制图片
Image($r('app.media.icon'))
.width(115).height(115)
.border({ width: 1 }).borderStyle(BorderStyle.Dashed)
.objectFit(ImageFit.ScaleDown)
.objectRepeat(ImageRepeat.XY) // 在两个轴上重复绘制图片
Image($r('app.media.icon'))
.width(115).height(115)
.border({ width: 1 }).borderStyle(BorderStyle.Dashed)
.objectFit(ImageFit.ScaleDown)
.objectRepeat(ImageRepeat.NoRepeat) // 不重复绘制图片
运行效果:
. interpolation (ImageInterpolation): 设置图片的插值效果,仅针对图片放大插值。
对于一些较小的图片,或老照片,插值计算有助于提升画质。示例代码:
/**
* .interpolation (ImageInterpolation): 设置图片的插值效果,仅针对图片放大插值。默认值None。
* ImageInterpolation.None 不使用插值图片数据。
* ImageInterpolation.High 高度使用插值图片数据,可能会影响图片渲染的速度。
* ImageInterpolation.Medium 中度使用插值图片数据。
* ImageInterpolation.Low 低度使用插值图片数据。
* 注意:SVG类型图源不支持该属性;PixelMap资源不支持该属性。
*/
TextBody1({text:'.interpolation (ImageInterpolation): 设置图片的插值效果'})
Image('/common/images/photo.png')
.width(300)
.objectFit(ImageFit.Cover)
.interpolation (ImageInterpolation.None) // 不使用插值图片数据
Image('/common/images/photo.png')
.width(300)
.objectFit(ImageFit.Cover)
.interpolation (ImageInterpolation.High) // 高度使用插值图片数据
Image('/common/images/photo.png')
.width(300)
.objectFit(ImageFit.Cover)
.interpolation (ImageInterpolation.Medium) // 中度使用插值图片数据
Image('/common/images/photo.png')
.width(300)
.objectFit(ImageFit.Cover)
.interpolation (ImageInterpolation.Low) // 低度使用插值图片数据
可以看到,原图放大后有马赛克,而高插值的方式就很平滑了。效果对比如下:
. renderMode (ImageRenderMode): 设置图片渲染的模式。 相当于PS中对图片做去色处理,可将彩色图片变为灰度图片。
示例代码:
/**
* .renderMode(ImageRenderMode) 默认值Original,设置图片渲染的模式。
* ImageRenderMode.Original 按照原图进行渲染,包括颜色。
* ImageRenderMode.Template 将图像渲染为模板图像,忽略图片的颜色信息。
* 注意:SVG类型图源不支持该属性。
*/
TextBody1({text:'.renderMode(ImageRenderMode) 设置图片渲染的模式'})
Image("/common/images/banner.jpg")
.width("100%")
.aspectRatio(3.5)
.renderMode(ImageRenderMode.Original) // 按照原图进行渲染,包括颜色
Image("/common/images/banner.jpg")
.width("100%")
.aspectRatio(3.5)
.renderMode(ImageRenderMode.Template) // 忽略图片的颜色信息
效果如下:
.sourceSize({width: number,height: number}): 设置图片解码尺寸,将原始图片解码成指定尺寸的图片,number类型单位为px。
一张原本尺寸为1200x750像素的图片,指定解码尺寸为150x100像素的照片后,如果图片展示尺寸超过该尺寸,就会模糊,示例代码如下:
/**
* .sourceSize({width: number,height: number}) 设置图片解码尺寸,将原始图片解码成指定尺寸的图片,number类型单位为px。
* 注意:PixelMap资源不支持该属性。
*/
TextBody1({text:'.sourceSize({width: number,height: number}) 设置图片解码尺寸'})
Image($r("app.media.cover"))
.borderRadius($r("app.float.radius_L"))
.sourceSize({
width: 150,
height: 100
}) // 解码为小尺寸后显示模糊
Image($r("app.media.cover")) // 原图效果
.borderRadius($r("app.float.radius_L"))
解码为小尺寸和原图的效果对比如下:
对于.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动效播放完成时会触发这个回调,如果动效为无限循环动效,则不会触发这个回调。
示例代码:
/**
* 事件:
* 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动效播放完成时会触发这个回调,如果动效为无限循环动效,则不会触发这个回调。
*/
H8({text:'3.事件'})
Image("https://developer.huawei.com/images/new-content/Navigation/HW-LOGO.svg") // 使用网络路径的图片
.width(300).height(65)
.onComplete((msg: { width: number,height: number }) => {
// 可根据 msg.width和 msg.height的大小决定卡片显示风格等
})
.onError(() => {
console.log('加载图片失败')
})
.onFinish(() => {
//
})
4.图像效果
前面演示了通过Image组件的.renderMode(ImageRenderMode)实现图片的去色效果。在基于TS扩展的声明式开发范式中还有一些控制图像效果的属性,这些属性不仅可以用于Image组件,也可以用于其它组件。
示例代码如下:
/**
* 图像效果
* 属性:
* blur:number 为当前组件添加内容模糊效果,入参为模糊半径,模糊半径越大越模糊,为0时不模糊。
* backdropBlur:number 为当前组件添加背景模糊效果,入参为模糊半径,模糊半径越大越模糊,为0时不模糊。
* shadow:{radius: number,color?: Color,offsetX?: number,offsetY?: number}
* 为当前组件添加阴影效果,入参为模糊半径(必填)、阴影的颜色(可选,默认为灰色)、X轴的偏移量(可选,默认为0),Y轴的偏移量(可选,默认为0),偏移量单位为px。
* grayscale:number 默认值0.0,为当前组件添加灰度效果。值定义为灰度转换的比例,入参1.0则完全转为灰度图像,入参则0.0图像无变化,入参在0.0和1.0之间时,效果呈线性变化。(百分比)
* brightness:number 默认值1.0,为当前组件添加高光效果,入参为高光比例,值为1时没有效果,小于1时亮度变暗,0为全黑;大于1时亮度增加,数值越大亮度越大。
* saturate:number 默认值1.0,为当前组件添加饱和度效果,饱和度为颜色中的含色成分和消色成分(灰)的比例,入参为1时,显示原图像,大于1时含色成分越大,饱和度越大;小于1时消色成分越大,饱和度越小。(百分比)
* contrast:number 默认值1.0,为当前组件添加对比度效果,入参为对比度的值,值为1时,显示原图;大于1时,值越大对比度越高,图像越清晰醒目;小于1时,值越小对比度越低;当对比度为0时,图像变为全灰。(百分比)
* invert:number 默认值0,反转输入的图像。入参为图像反转的比例。值为1时完全反转。值为0则图像无变化。(百分比)
* sepia:number 默认值0,将图像转换为深褐色。入参为图像反转的比例。值为1则完全是深褐色的,值为0图像无变化。 (百分比)
* hueRotate:number 默认值0deg,为当前组件添加色相旋转效果,入参为旋转的角度值。当入参为0deg时图像无变化(默认值是0deg),入参没有最大值,超过360deg的值相当于又绕一圈。
*/
H8({text:'4.图像效果'})
TextBody1({text:'添加内容模糊效果'})
Image("/common/images/banner.jpg") // /common/images目录下的图片
.width("100%")
.aspectRatio(3.5)
.blur(3) // 为当前组件添加内容模糊效果
TextBody1({text:'背景模糊效果'})
Row().width('100%').height(240)
.backgroundImage('/common/images/banner.jpg')
.backgroundImageSize({ width: 1200, height: 800 })
.backgroundImagePosition(Alignment.Center)
.backdropBlur(3)
TextBody1({text:'添加阴影效果'})
Image("/common/images/banner.jpg").width('100%').aspectRatio(3.5)
.shadow({ radius: 10, color: Color.Gray, offsetX: 5, offsetY: 5 })
TextBody1({text:'添加灰度效果'})
Image("/common/images/lake.jpg").width('100%').aspectRatio(3.5)
.grayscale(0.6)
TextBody1({text:'添加高光效果'})
Image("/common/images/lake.jpg").width('100%').aspectRatio(3.5)
.brightness(2.0)
TextBody1({text:'添加饱和度效果'})
Image("/common/images/lake.jpg").width('100%').aspectRatio(3.5)
.saturate(2.0)
TextBody1({text:'添加对比度效果'})
Image("/common/images/lake.jpg").width('100%').aspectRatio(3.5)
.contrast(2.0)
TextBody1({text:'反转输入的图像'})
Image("/common/images/lake.jpg").width('100%').aspectRatio(3.5)
.invert(1)
TextBody1({text:'图像转换为深褐色'})
Image("/common/images/lake.jpg").width('100%').aspectRatio(3.5)
.sepia(1)
TextBody1({text:'色相旋转效果'})
Image("/common/images/lake.jpg").width('100%').aspectRatio(3.5)
.hueRotate(90)
上述代码的效果可以通过远程模拟器体验:
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 })
}
然后新建一个ImageDetailSample.ets页面,代码如下:
@Entry
@Component
struct ImageDetailSample {
build() {
Column({space:8}) {
Navigator({ target: 'pages/ImageSample', type: NavigationType.Back }) {
Image($r("app.media.cover"))
.width("100%")
.aspectRatio(1.5)
.borderRadius($r("app.float.radius_L"))
.sharedTransition('sharedId1') // 设置和上级页面相同的id
}
}
.width('100%')
.height('100%')
.padding({top: $r("app.float.spaceTop"), bottom:$r("app.float.spaceBottom"), left:$r("app.float.spaceLeft"), right: $r("app.float.spaceRight")})
.backgroundColor($r("app.color.appBg"))
}
}
建议在DevEco Studioi中通过本地预览器或远程模拟器体验流畅的转场动画效果,可以点击放大,也可以点击返回上级页面时缩小,运行效果如下:
3.3.3 ImageAnimator组件
在ArkUI eTS语言中,可通过Image组件展示gif动画,同时,提供了一个ImageAnimator组件,提供帧动画组件来实现逐帧播放图片的能力,可以配置需要播放的图片列表,每张图片可以配置时长。
新建ImageAnimatorSample.ets页面,初始化代码如下:
import {H8} from './index'
/**
* 3.3.3 ImageAnimator组件
*/
@Entry
@Component
struct ImageDetailSample {
build() {
Column({space:8}) {
// 返回首页
Row({space:8}){
Image($r("app.media.ic_back")).width(24).height(24)
Navigator({ target: 'pages/index', type: NavigationType.Back }) {
H8({text:'返回'})
}
}
// TODO
}
.width('100%')
.height('100%')
.padding({top: $r("app.float.spaceTop"), bottom:$r("app.float.spaceBottom"), left:$r("app.float.spaceLeft"), right: $r("app.float.spaceRight")})
.backgroundColor($r("app.color.appBg"))
.alignItems(HorizontalAlign.Start)
}
}
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(){"之前加入如下代码:
@State state: AnimationStatus = AnimationStatus.Initial
@State reverse: boolean = false
@State iterations: number = 1
代码位置如下图所示:
然后在"// 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
})
}
运行效果如下:
【本节源码:https://gitee.com/cloudev/harmonyos3/tree/master/3.0/BaseComponent 】
赞,老师讲解的非常详细
点赞,收藏,评论三连