1# Image 2 3图片组件,支持本地图片和网络图片的渲染展示。 4 5> **说明:** 6> 7> 该组件从API Version 7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 8 9 10## 需要权限 11 12使用网络图片时,需要申请权限ohos.permission.INTERNET。具体申请方式请参考[权限申请声明](../../security/accesstoken-guidelines.md)。 13 14 15## 子组件 16 17无 18 19 20## 接口 21 22Image(src: string | PixelMap | Resource) 23 24通过图片数据源获取图片,用于后续渲染展示。 25 26从API version 9开始,该接口支持在ArkTS卡片中使用。 27 28**参数:** 29 30| 参数名 | 参数类型 | 必填 | 参数描述 | 31| ---- | ---------------------------------------- | ---- | ---------------------------------------- | 32| src | string\| [PixelMap](../apis/js-apis-image.md#pixelmap7) \| [Resource](ts-types.md#resource类型) | 是 | 图片的数据源,支持本地图片和网络图片。<br/>当使用相对路径引用图片资源时,例如`Image("common/test.jpg")`,不支持跨包/跨模块调用该Image组件,建议使用`$r`方式来管理需全局使用的图片资源。<br/>\- 支持的图片格式包括png、jpg、bmp、svg和gif。<br/>\- 支持`Base64`字符串。格式`data:image/[png\|jpeg\|bmp\|webp];base64,[base64 data]`, 其中`[base64 data]`为`Base64`字符串数据。<br/>\- 支持`datashare://`路径前缀的字符串,用于访问通过data ability提供的图片路径。<br/>\- 支持file:///data/storage路径前缀的字符串,用于读取本应用安装目录下files文件夹下的图片资源。需要保证目录包路径下的文件有可读权限。<br/>**说明:**<br/>- ArkTS卡片上支持gif图片格式动效,但仅在显示时播放一次。<br/>- ArkTS卡片上不支持`http://`等网络相关路径前缀、`datashare://`路径前缀以及`file://data/storage`路径前缀的字符串<br/>- ArkTS卡片上不支持 [PixelMap](../apis/js-apis-image.md#pixelmap7)类型 | 33 34## 属性 35 36除支持[通用属性](ts-universal-attributes-size.md)外,还支持以下属性: 37 38| 名称 | 参数类型 | 描述 | 39| ------------------------ | ---------------------------------------- | ---------------------------------------- | 40| alt | string \| [Resource](ts-types.md#resource类型) | 加载时显示的占位图,支持本地图片。<br/>从API version 9开始,该接口支持在ArkTS卡片中使用。 | 41| objectFit | [ImageFit](ts-appendix-enums.md#imagefit) | 设置图片的缩放类型。<br/>默认值:ImageFit.Cover<br/>从API version 9开始,该接口支持在ArkTS卡片中使用。 | 42| objectRepeat | [ImageRepeat](ts-appendix-enums.md#imagerepeat) | 设置图片的重复样式。<br/>默认值:ImageRepeat.NoRepeat<br/>从API version 9开始,该接口支持在ArkTS卡片中使用。<br/>**说明:**<br/>svg类型图源不支持该属性。 | 43| interpolation | [ImageInterpolation](#imageinterpolation) | 设置图片的插值效果,即减轻低清晰度图片在放大显示的时候出现的锯齿问题,仅针对图片放大插值。<br/>默认值:ImageInterpolation.None<br/>从API version 9开始,该接口支持在ArkTS卡片中使用。<br/>**说明:**<br/>svg类型图源不支持该属性。<br/>PixelMap资源不支持该属性。 | 44| renderMode | [ImageRenderMode](#imagerendermode) | 设置图片渲染的模式。<br/>默认值:ImageRenderMode.Original<br/>从API version 9开始,该接口支持在ArkTS卡片中使用。<br/>**说明:**<br/>svg类型图源不支持该属性。 | 45| sourceSize | {<br/>width: number,<br/>height: number<br/>} | 设置图片裁剪尺寸,将原始图片解码成pixelMap,指定尺寸的图片,单位为px。<br/>从API version 9开始,该接口支持在ArkTS卡片中使用。<br/>**说明:**<br/>PixelMap资源和SVG图片不支持该属性。 | 46| matchTextDirection | boolean | 设置图片是否跟随系统语言方向,在RTL语言环境下显示镜像翻转显示效果。<br/>默认值:false<br/>从API version 9开始,该接口支持在ArkTS卡片中使用。 | 47| fitOriginalSize | boolean | 图片组件尺寸未设置时,其显示尺寸是否跟随图源尺寸。<br/>默认值:false<br/>从API version 9开始,该接口支持在ArkTS卡片中使用。 | 48| fillColor | [ResourceColor](ts-types.md#resourcecolor) | 填充颜色。设置的填充颜色会覆盖在图片上。仅对svg图源生效,设置后会替换svg图片的fill颜色。<br/>从API version 9开始,该接口支持在ArkTS卡片中使用。 | 49| autoResize | boolean | 是否需要在图片解码过程中对图源做resize操作,该操作会根据显示区域的尺寸决定用于绘制的图源尺寸,有利于减少内存占用。<br/>默认值:true<br/>从API version 9开始,该接口支持在ArkTS卡片中使用。 | 50| syncLoad<sup>8+</sup> | boolean | 设置是否同步加载图片,默认是异步加载。同步加载时阻塞UI线程,不会显示占位图。<br/>默认值:false<br/>从API version 9开始,该接口支持在ArkTS卡片中使用。 | 51| copyOption<sup>9+</sup> | [CopyOptions](ts-appendix-enums.md#copyoptions9) | 设置图片是否可复制(SVG图片不支持复制)。<br/>当copyOption设置为非CopyOptions.None时,支持使用长按、鼠标右击、快捷组合键'CTRL+C'等方式进行复制。<br/>默认值:CopyOptions.None<br/>该接口支持在ArkTS卡片中使用。 | 52| colorFilter<sup>9+</sup> | [ColorFilter](ts-types.md#colorfilter9) | 给图像设置颜色滤镜效果。<br/>该接口支持在ArkTS卡片中使用。 | 53| draggable<sup>9+</sup> | boolean | 设置默认拖拽效果。(不能和[onDragStart](ts-universal-events-drag-drop.md)事件同时使用。)<br/>默认值:false | 54 55> **说明:** 56> 57> 使用快捷组合键对Image组件复制的前提是,该组件必须处于获焦状态。将Image组件的属性focusable设置为true,即可使用TAB键将焦点切换到Image组件上,再将Image组件的focusOnTouch属性设置为true,即可实现点击获焦。 58> 图片设置svg图源时,支持的标签范围有限,目前支持的svg标签包括svg、rect、circle、ellipse、path、line、polyline、polygon。 59 60### ImageInterpolation 61 62从API version 9开始,该接口支持在ArkTS卡片中使用。 63 64| 名称 | 描述 | 65| ------ | ------------------------- | 66| None | 不使用插值图片数据。 | 67| High | 插值图片数据的使用率高,可能会影响图片渲染的速度。 | 68| Medium | 插值图片数据的使用率中。 | 69| Low | 插值图片数据的使用率低。 | 70 71### ImageRenderMode 72 73从API version 9开始,该接口支持在ArkTS卡片中使用。 74 75| 名称 | 描述 | 76| -------- | --------------------- | 77| Original | 按照原图进行渲染,包括颜色。 | 78| Template | 将图片渲染为模板图片,忽略图片的颜色信息。 | 79 80## 事件 81 82除支持[通用事件](ts-universal-events-click.md)外,还支持以下事件: 83 84| 名称 | 功能描述 | 85| ---------------------------------------- | ---------------------------------------- | 86| onComplete(callback: (event?: { width: number, height: number, componentWidth: number,<br> componentHeight: number, loadingStatus: number }) => void) | 图片成功加载时触发该回调,返回成功加载的图片尺寸。<br>- width:图片的宽,单位为像素。<br/>- height:图片的高,单位为像素。<br/>- componentWidth:组件的宽,单位为像素。<br/>- componentHeight:组件的高,单位为像素。<br/>- loadingStatus:图片加载成功的状态。<br/>从API version 9开始,该接口支持在ArkTS卡片中使用。<br/>**说明:** <br/>loadingStatus返回的状态值为0时,代表图片加载失败;返回的状态值为1时,代表图片加载成功。 | 87| onError(callback: (event?: { componentWidth: number, componentHeight: number , message<sup>9+</sup>: string }) => void) | 图片加载出现异常时触发该回调。<br>- componentWidth:组件的宽,单位为像素。<br/>- componentHeight:组件的高,单位为像素。<br/>从API version 9开始,该接口支持在ArkTS卡片中使用。 | 88| onFinish(event: () => void) | 当加载的源文件为带动效的svg图片时,当svg动效播放完成时会触发这个回调,如果动效为无限循环动效,则不会触发这个回调。<br/>从API version 9开始,该接口支持在ArkTS卡片中使用。 | 89 90## 示例 91 92### 图片加载 93 94加载显示不同类型的图片,并设置图片的缩放类型。 95 96```ts 97@Entry 98@Component 99struct ImageExample1 { 100 private on: string = 'www.example.com' 101 @State src: string = this.on 102 103 build() { 104 Column() { 105 Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Start }) { 106 Text('default').fontSize(16).fontColor(0xcccccc).height(30) 107 Row({ space: 5 }) { 108 Image($r('app.media.ic_png')) 109 .width(110).height(110).border({ width: 1 }) 110 .overlay('png', { align: Alignment.Bottom, offset: { x: 0, y: 20 } }) 111 Image($r('app.media.ic_gif')) 112 .width(110).height(110).border({ width: 1 }) 113 .overlay('gif', { align: Alignment.Bottom, offset: { x: 0, y: 20 } }) 114 Image($r('app.media.ic_svg')) 115 .width(110).height(110).border({ width: 1 }) 116 .overlay('svg', { align: Alignment.Bottom, offset: { x: 0, y: 20 } }) 117 } 118 Row({ space: 5 }) { 119 Image($r('app.media.img_example')) 120 .width(110).height(110).border({ width: 1 }) 121 .overlay('jpg', { align: Alignment.Bottom, offset: { x: 0, y: 20 } }) 122 Image(this.src) 123 .width(110).height(110).border({ width: 1 }) 124 .overlay('network', { align: Alignment.Bottom, offset: { x: 0, y: 20 } }) 125 }.margin({ top: 25, bottom: 10 }) 126 } 127 128 Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Start }) { 129 Text('objectFit').fontSize(16).fontColor(0xcccccc).height(30) 130 Row({ space: 5 }) { 131 Image($r('app.media.img_example')) 132 .border({ width: 1 }) 133 .objectFit(ImageFit.None).width(110).height(110) 134 .overlay('None', { align: Alignment.Bottom, offset: { x: 0, y: 20 } }) 135 Image($r('app.media.img_example')) 136 .border({ width: 1 }) 137 .objectFit(ImageFit.Fill).width(110).height(110) 138 .overlay('Fill', { align: Alignment.Bottom, offset: { x: 0, y: 20 } }) 139 Image($r('app.media.img_example')) 140 .border({ width: 1 }) 141 .objectFit(ImageFit.Cover).width(110).height(110) 142 .overlay('Cover', { align: Alignment.Bottom, offset: { x: 0, y: 20 } }) 143 } 144 Row({ space: 5 }) { 145 Image($r('app.media.img_example_w250')) 146 .border({ width: 1 }) 147 .objectFit(ImageFit.Contain).width(110).height(110) 148 .overlay('Contain', { align: Alignment.Bottom, offset: { x: 0, y: 20 } }) 149 Image($r('app.media.img_example_w250')) 150 .border({ width: 1 }) 151 .objectFit(ImageFit.ScaleDown).width(110).height(110) 152 .overlay('ScaleDown', { align: Alignment.Bottom, offset: { x: 0, y: 20 } }) 153 }.margin({ top: 25 }) 154 } 155 }.height(320).width(360).padding({ right: 10, top: 10 }) 156 } 157} 158``` 159 160 161 162 163 164### 网络图片 165 166加载网络图片时,默认网络超时是5分钟,建议使用alt配置加载时的占位图。如果需要更灵活的网络配置,可以使用SDK中提供的[HTTP](../../connectivity/http-request.md)工具包发送网络请求,接着将返回的数据解码为Image组件中的`PixelMap`,图片开发可参考[图片处理](../../media/image-overview.md)。代码如下。 167 168```tsx 169// @ts-nocheck 170import http from '@ohos.net.http' 171import ResponseCode from '@ohos.net.http' 172import image from '@ohos.multimedia.image' 173 174 175@Entry 176@Component 177struct Index { 178 179 // 先创建一个PixelMap状态变量用于接收网络图片 180 @State image: PixelMap = undefined 181 182 build() { 183 Column({space: 10}) { 184 Button("获取网络图片") 185 .onClick(() => { 186 this.httpRequest() 187 }) 188 Image(this.image).height(100).width(100) 189 } 190 .width('100%') 191 .height('100%') 192 .padding(10) 193 } 194 195 // 网络图片请求方法 196 private httpRequest() { 197 let httpRequest = http.createHttp() 198 199 httpRequest.request( 200 "https://www.example.com/xxx.png", // 请填写一个具体的网络图片地址 201 (error, data) => { 202 if(error) { 203 console.log("error code: " + error.code + ", msg: " + error.message) 204 } else { 205 let code = data.responseCode 206 if(ResponseCode.ResponseCode.OK == code) { 207 let imageSource = image.createImageSource(data.result) 208 let options = {alphaType: 0, // 透明度 209 editable: false, // 是否可编辑 210 pixelFormat: 3, // 像素格式 211 scaleMode: 1, // 缩略值 212 size: {height: 100, width: 100}} // 创建图片大小 213 imageSource.createPixelMap(options).then((pixelMap) => { 214 this.image = pixelMap 215 }) 216 } else { 217 console.log("response code: " + code) 218 } 219 } 220 } 221 ) 222 } 223} 224``` 225 226**说明**:网络图片加载的请求方式、超时、额外请求参数等配置可以参考HTTP工具包中关于[`request()`](../../reference/apis/js-apis-http.md)请求方法的细节。 227 228### 设置属性 229 230```ts 231@Entry 232@Component 233struct ImageExample2 { 234 235 build() { 236 Column({ space: 10 }) { 237 Text('renderMode').fontSize(12).fontColor(0xcccccc).width('96%').height(30) 238 Row({ space: 50 }) { 239 Image($r('app.media.img_example')) 240 .renderMode(ImageRenderMode.Original).width(100).height(100) 241 .border({ width: 1 }) 242 .overlay('Original', { align: Alignment.Bottom, offset: { x: 0, y: 20 } }) 243 Image($r('app.media.img_example')) 244 .renderMode(ImageRenderMode.Template).width(100).height(100) 245 .border({ width: 1 }) 246 .overlay('Template', { align: Alignment.Bottom, offset: { x: 0, y: 20 } }) 247 } 248 249 Text('alt').fontSize(12).fontColor(0xcccccc).width('96%').height(30) 250 Image('') 251 .alt($r('app.media.Image_none')) 252 .width(100).height(100).border({ width: 1 }) 253 254 Text('sourceSize').fontSize(12).fontColor(0xcccccc).width('96%') 255 Row({ space: 50 }) { 256 Image($r('app.media.img_example')) 257 .sourceSize({ 258 width: 150, 259 height: 150 260 }) 261 .objectFit(ImageFit.ScaleDown).width('25%').aspectRatio(1) 262 .border({ width: 1 }) 263 .overlay('w:150 h:150', { align: Alignment.Bottom, offset: { x: 0, y: 20 } }) 264 Image($r('app.media.img_example')) 265 .sourceSize({ 266 width: 200, 267 height: 200 268 }) 269 .objectFit(ImageFit.ScaleDown).width('25%').aspectRatio(1) 270 .border({ width: 1 }) 271 .overlay('w:200 h:200', { align: Alignment.Bottom, offset: { x: 0, y: 20 } }) 272 } 273 274 Text('objectRepeat').fontSize(12).fontColor(0xcccccc).width('96%').height(30) 275 Row({ space: 5 }) { 276 Image($r('app.media.ic_health_heart')) 277 .width(120).height(125).border({ width: 1 }) 278 .objectRepeat(ImageRepeat.XY).objectFit(ImageFit.ScaleDown) 279 .overlay('ImageRepeat.XY', { align: Alignment.Bottom, offset: { x: 0, y: 20 } }) 280 Image($r('app.media.ic_health_heart')) 281 .width(110).height(125).border({ width: 1 }) 282 .objectRepeat(ImageRepeat.Y).objectFit(ImageFit.ScaleDown) 283 .overlay('ImageRepeat.Y', { align: Alignment.Bottom, offset: { x: 0, y: 20 } }) 284 Image($r('app.media.ic_health_heart')) 285 .width(110).height(125).border({ width: 1 }) 286 .objectRepeat(ImageRepeat.X).objectFit(ImageFit.ScaleDown) 287 .overlay('ImageRepeat.X', { align: Alignment.Bottom, offset: { x: 0, y: 20 } }) 288 } 289 }.height(150).width('100%').padding({ right: 10 }) 290 } 291} 292``` 293 294 295 296### 事件调用 297 298```ts 299@Entry 300@Component 301struct ImageExample3 { 302 @State widthValue: number = 0 303 @State heightValue: number = 0 304 private on: Resource = $r('app.media.image_on') 305 private off: Resource = $r('app.media.image_off') 306 private on2off: Resource = $r('app.media.image_on2off') 307 private off2on: Resource = $r('app.media.image_off2on') 308 @State src: Resource = this.on 309 310 build() { 311 Column() { 312 Row({ space: 20 }) { 313 Column() { 314 Image($r('app.media.img_example1')) 315 .alt($r('app.media.ic_public_picture')) 316 .sourceSize({ 317 width: 900, 318 height: 900 319 }) 320 .objectFit(ImageFit.Cover) 321 .height(180).width(180) 322 // 图片加载完成后,获取图片尺寸。 323 .onComplete((msg: { width: number,height: number }) => { 324 this.widthValue = msg.width 325 this.heightValue = msg.height 326 }) 327 .onError(() => { 328 console.log('load image fail') 329 }) 330 .overlay('\nwidth: ' + String(this.widthValue) + ' height: ' + String(this.heightValue), { 331 align: Alignment.Bottom, 332 offset: { x: 0, y: 20 } 333 }) 334 } 335 // 为图片添加点击事件,点击完成后加载特定图片。 336 Image(this.src) 337 .width(120).height(120) 338 .onClick(() => { 339 if (this.src == this.on || this.src == this.off2on) { 340 this.src = this.on2off 341 } else { 342 this.src = this.off2on 343 } 344 }) 345 .onFinish(() => { 346 if (this.src == this.off2on) { 347 this.src = this.on 348 } else { 349 this.src = this.off 350 } 351 }) 352 } 353 }.width('100%') 354 } 355} 356``` 357 358 359 360### 渲染沙箱路径图片 361 362```ts 363import fileio from '@ohos.fileio'; 364import fs from '@ohos.file.fs'; 365import context from '@ohos.app.ability.common'; 366 367@Entry 368@Component 369struct LoadImageExample { 370 @State resourcesPath: string = '' 371 @State sandboxPath: string = '' 372 context: context.UIAbilityContext = getContext(this) as context.UIAbilityContext 373 374 build() { 375 Column() { 376 Button('读取沙箱图片') 377 .margin({ bottom: 10, top: 10 }) 378 .onClick(() => { 379 this.sandboxPath = this.context.getApplicationContext().filesDir + '/icon.png' 380 console.log(`读取沙箱图片=========>${this.sandboxPath}`) 381 let fd = fs.openSync(this.sandboxPath, 0o100) 382 console.log(`create file========>${fd}`) 383 let srcPath = this.context.bundleCodeDir + '/entry/resources/base/media/icon.png' 384 console.log('mySrcpath' + srcPath) 385 fileio.copyFileSync(srcPath, this.sandboxPath) // 复制图片到沙箱路径 386 this.sandboxPath = 'file://' + this.context.getApplicationContext().filesDir + '/icon.png' 387 }) 388 Button('读取资源图片') 389 .margin({ bottom: 10 }) 390 .onClick(() => { 391 this.resourcesPath = 'file://' + this.context.bundleCodeDir + '/entry/resources/base/media/icon.png' 392 }) 393 Text(`资源图片路径:${this.resourcesPath}`) 394 .fontSize(20) 395 .margin({ bottom: 10 }) 396 Image(this.resourcesPath) 397 .width(100) 398 .height(100) 399 .colorFilter([ 400 0.30, 0.59, 0.11, 0, 0, 401 0.30, 0.59, 0.11, 0, 0, 402 0.30, 0.59, 0.11, 0, 0, 403 0, 0, 0, 1.0, 0 404 ]) 405 Text(`沙箱图片路径:${this.sandboxPath}`) 406 .fontSize(20) 407 .margin({ bottom: 10 }) 408 Image(this.sandboxPath) 409 .width(100) 410 .height(100) 411 } 412 .width('100%').height('100%') 413 } 414} 415``` 416 417### 为图片增加滤镜 418 419```ts 420// xxx.ets 421@Entry 422@Component 423struct colorFilterExample { 424 @State colorFilterR: number = 0 425 @State colorFilterG: number = 0 426 @State colorFilterB: number = 0 427 @State colorFilterA: number = 0 428 429 build() { 430 Row() { 431 Column() { 432 Image($r('app.media.sky')) 433 .width(200) 434 .height(200) 435 Image($r('app.media.sky')) 436 .width(200) 437 .height(200) 438 .colorFilter([ 439 this.colorFilterR, 0, this.colorFilterR, 0, 0, 440 0, this.colorFilterG, this.colorFilterG, 0, 0, 441 this.colorFilterB, 0, this.colorFilterB, 0, 0, 442 0, 0, this.colorFilterA, 0, 0 443 ]) 444 445 Row() { 446 Text('R') 447 Slider({ 448 min: 0, 449 max: 1, 450 step: 0.01 451 }) 452 .onChange((valueR) => { 453 this.colorFilterR = valueR 454 }) 455 } 456 457 Row() { 458 Text('G') 459 Slider({ 460 min: 0, 461 max: 1, 462 step: 0.01 463 }) 464 .onChange((valueG) => { 465 this.colorFilterG = valueG 466 }) 467 } 468 469 Row() { 470 Text('B') 471 Slider({ 472 min: 0, 473 max: 1, 474 step: 0.01 475 }) 476 .onChange((valueB) => { 477 this.colorFilterB = valueB 478 }) 479 } 480 481 Row() { 482 Text('A') 483 Slider({ 484 min: 0, 485 max: 1, 486 step: 0.01 487 }) 488 .onChange((valueA) => { 489 this.colorFilterA = valueA 490 }) 491 } 492 }.width('90%').alignItems(HorizontalAlign.Center) 493 }.height('100%').width('100%').justifyContent(FlexAlign.Center) 494 } 495} 496``` 497 498