本篇内容: 显示图片、自定义图形和画布自定义图形的学习使用
一、知识储备
1. 图片组件(Image)
- 可以展示jpg 、png 、svg 、gif等各格式的网络和本地资源文件图片的组件
- 接口调用
Image(src: string | Resource | media.PixelMap)
Image('images/view.jpg').width(200)//ets本次资源目录下
Image('https://www.example.com/example.JPG') // 网络图片实际使用时请替换为真实地址
Image($r('app.media.ic_hm_logo'))//resources/base/media目录下
Image($rawfile('snap'))//resources/rawfile目录下
Image('file://media/Photos/5').width(200)//手机媒体库中图片资源
//从手机媒体库中选择图片
try {
let photoSelectOptions = new picker.PhotoSelectOptions(); //媒体库选择器设置
photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE; //图片类型
photoSelectOptions.maxSelectNumber = 5; //最多 选取五张
let photoPicker = new picker.PhotoViewPicker; //初始化图片选择器
photoPicker.select(photoSelectOptions).then(photoSelectResult => { //选择结果回调
this.imgListData = photoSelectResult.photoUris;
console.error(`imgListData size is ${this.imgListData.length}`)
}).catch(err=>{
console.error(`select failed code is ${err.code}, msg : ${err.message}`)
})
} catch (err) {
console.error(`load photo failed ${err.code}, msg: ${err.message}`)
}
http.createHttp().request(this.imgUrl, (err, data) => {
if (err) {
console.error(`err is ${JSON.stringify(err)}`)
} else {
let code = data.responseCode;
if (ResponseCode.ResponseCode.OK == code) {
let res: any = data.result;
let imageSource = image.createImageSource(res)
let options = {
alphaTye: 0, //透明度
editable: false, //是否可编辑
pixelFormat: 3, //像素格式
scaleMode: 1, //缩略值
size: { height: 100, wight: 100 } //创建图片大小
}
imageSource.createPixelMap(options).then(pixelMap => {
this.image = pixelMap;
})
}
}
})
- 常用函数
- .objectFit(ImageFit.Fill)// 缩放类型
- .interpolation(ImageInterpolation.High)//图片插值,抗锯齿,使图片看着更清晰
- .renderMode(ImageRenderMode.Template) //图片渲染模式
- .objectRepeat(ImageRepeat.X)//设置图片在xy轴上重复显示
- .sourceSize({//设置图片解码尺寸
width: 65, height: 40
}) - .colorFilter([//添加滤镜效果
1,1,0,0,0,
0,1,0,0,0,
0,0,1,0,0,
0,0,0,1,0
]) - .syncLoad(true)//同步加载图片
- .onComplete(msg=>{ 加载成功和失败的事件回调
if (msg) {
console.error(widthVal = ${msg.width}\n height = ${msg.height} \n componentW = ${msg.componentWidth} \n status = ${msg.loadingStatus}
)
}
})
.onError(()=>{
console.error(‘err’)
})
2. 自定义图形(shape)
- 有两种创建形式,一种以Shape为父组件,一种是绘制组件单独使用
Shape() {
Rect().width(300).height(50)//以Shape作为父组件使用
}
Circle({ width: 150, height: 150 })//单独使用
- 绘制组件有七种类型:圆形(Circle)、椭圆形(Ellipse)、直线(Line)、拆线(Polyline)、多边形(Polygon)、路径(Path)、矩形(Rect)。
- 形状视口 viewPort
Shape() {
Rect({ width: 100, height: 100 }).fill(0xf7f7f7)
Circle({ width: 150, height: 150 }).fill(0x00c250)
}.viewPort({ x: 0, y: 0, width: 100, height: 100 })//根据这个视口与宽高比进行放大shape内的组件大小
.width(150)
.height(150)
Path()
.width(100)
.height(100)
.commands(`M${this._50} 0 L${this._50} ${this._50} L0 ${this._50} L${this._100} ${this._50} L${this._50} ${this._50} L${this._50} ${this._100} L${this._15} ${this._100} L${this._85} ${this._100} L${this._50} ${this._100} Z`)
.fillOpacity(0) //实心不填充颜色
.fill(Color.Red)
.stroke(Color.Red)
.strokeWidth(5)
Polyline()
.width(100)
.height(100)
.stroke(Color.Red)
.strokeWidth(5)
.points([[10, 10], [90, 10], [90, 90], [10, 90], [10, 10]])
.fillOpacity(0) //实心不填充颜色
3. 画布自定义图形(Canvas)
Canvas(this.context)
.width('100%')
.height('100%')
.backgroundColor(Color.Yellow)
.onReady(() => {
this.context.beginPath()
this.context.arc(110, 150, 50, 0, 6.28)
this.context.stroke()
this.context.font = '56px'
this.context.fillText('圆形', 120, 220)//画圆形
this.context.beginPath()
this.context.rect(110, 10, 210, 110)
this.context.stroke()
this.context.font = '46px'
this.context.fillText('矩形', 120, 20)//画矩形
this.context.beginPath()
this.context.ellipse(110, 250, 50, 100, Math.PI * 0.25, Math.PI * 0, Math.PI * 2)
this.context.stroke()//画椭圆形
let line = this.context.createLinearGradient(110, 500, 110, 600)
line.addColorStop(0, '#f00')
line.addColorStop(0.5, '#ff0')
line.addColorStop(1, '#f0f')
this.context.fillStyle = line
this.context.fillRect(110, 500, 100, 100)//渐变色
this.context.fillText('我是中华人民共和国的公民', 110, 500)//写字
this.offContext.drawImage(this.image, 110, 400, 130, 130)
let imageData = this.offContext.getImageData(150, 450, 130, 130)
this.offContext.putImageData(imageData, 130, 130)
let img = this.offContext.transferToImageBitmap();
this.context.transferFromImageBitmap(img)//.截图画图
let path = new Path2D();
path.moveTo(100, 100)
path.lineTo(100, 150)
path.lineTo(25, 150)
path.lineTo(175, 150)
path.lineTo(100, 150)
path.lineTo(100, 200)
path.lineTo(40, 200)
path.lineTo(160, 200)
path.lineTo(100, 200)
path.closePath()
this.context.strokeStyle = '#f00'
this.context.lineWidth = 5
this.context.stroke(path)
let path2 = new Path2D();
path2.moveTo(40, 215)
path2.lineTo(160, 215)
path2.lineTo(160, 335)
path2.lineTo(40, 335)
path2.lineTo(40, 215)
this.context.stroke(path2)//再次画个“吉”
二、 效果一览
三、 源码剖析
import http from '@ohos.net.http';
import ResponseCode from '@ohos.net.http';
import image from '@ohos.multimedia.image';
import picker from '@ohos.file.picker';
@Entry
@Component
struct MyRouter {
private tabsController: TabsController = new TabsController();
@State currentIndex: number = 0;
@Builder TabBuild(title: string, targetIndex: number, SelectedImg: Resource, normalImg: Resource) {
Column() { //自定义tab
Image(this.currentIndex == targetIndex ? SelectedImg : normalImg)
.size({ width: 25, height: 25 })
Text(title)
.fontSize(16).fontColor(this.currentIndex == targetIndex ? 0x00c250 : 0x333333)
}
.width('100%')
.height(48)
.justifyContent(FlexAlign.Center)
.onClick(() => {
console.error(`targetIndex is ${targetIndex}`)
this.currentIndex = targetIndex;
this.tabsController.changeIndex(this.currentIndex)
})
}
@State image: PixelMap = undefined; //创建PixelMap状态变量
private imgUrl: string = 'https://img1.baidu.com/it/u=3241660985,1063915045&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1194';
loadImg() { //获取网络美女图片
http.createHttp().request(this.imgUrl, (err, data) => {
if (err) {
console.error(`err is ${JSON.stringify(err)}`)
} else {
let code = data.responseCode;
if (ResponseCode.ResponseCode.OK == code) {
let res: any = data.result;
let imageSource = image.createImageSource(res)
let options = {
alphaTye: 0, //透明度
editable: false, //是否可编辑
pixelFormat: 3, //像素格式
scaleMode: 1, //缩略值
size: { height: 100, wight: 100 } //创建图片大小
}
imageSource.createPixelMap(options).then(pixelMap => {
this.image = pixelMap;
})
}
}
})
}
aboutToAppear() {
// this.context = getContext();
this.loadImg()
}
settings: RenderingContextSettings = new RenderingContextSettings(true)
context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
offContext: OffscreenCanvasRenderingContext2D = new OffscreenCanvasRenderingContext2D(600, 600, this.settings)
img: ImageBitmap = new ImageBitmap('../../../resources/base/media/ic_hm_logo.svg')
scroller: Scroller = new Scroller();
@State _50: number = vp2px(50)
@State _100: number = vp2px(100)
@State _85: number = vp2px(85)
@State _15: number = vp2px(15)
@State _: number = vp2px(50)
build() {
Tabs({
barPosition: BarPosition.End,
controller: this.tabsController
}) { //end start 首尾位置设置 controller 绑定tabs的控制器
TabContent() { //内容页面组件
MyNavigation().backgroundColor(0xf7f7f7)
}
.tabBar(this.TabBuild('首页', 0, $r('app.media.ic_hm_home_selected'), $r('app.media.ic_hm_home_normal')))
TabContent() {
Image(this.image).height('100%').width('100%')
}
.tabBar(this.TabBuild('直播', 1, $r('app.media.ic_hm_living_selected'), $r('app.media.ic_hm_living_normal')))
TabContent() {
Scroll(this.scroller) {
Column() {
Row() {
Image(this.imgUrl)
.width('30%')
.height('20%')
.border({ width: 1 })
.objectFit(ImageFit.Contain) //等比缩放,图片完全显示
.margin(15)
.overlay('Contain', { align: Alignment.Bottom, offset: { x: 0, y: 30 } })
.colorFilter([ //添加滤镜效果
1, 1, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0
])
.syncLoad(true) //同步加载图片
.onComplete(msg => {
if (msg) {
console.error(`widthVal = ${msg.width}\n height = ${msg.height} \n componentW = ${msg.componentWidth} \n status = ${msg.loadingStatus}`)
}
})
.onError(() => {
console.error('err')
})
Image(this.imgUrl)
.width('30%')
.height('20%')
.border({ width: 1 })
.objectFit(ImageFit.Cover) //等比缩放, 图片显示部分
.margin(15)
.overlay('Cover', { align: Alignment.Bottom, offset: { x: 0, y: 20 } })
Image(this.imgUrl)
.width('30%')
.height('20%')
.border({ width: 1 })
.objectFit(ImageFit.Auto) //自适应显示
.margin(15)
}
Row() {
Image(this.imgUrl)
.width('30%')
.height('20%')
.border({ width: 1 })
.objectFit(ImageFit.Fill) //填充显示,不保持等比缩放
.renderMode(ImageRenderMode.Template) //图片渲染模式
.margin(15)
.overlay('fill', { align: Alignment.Bottom, offset: { x: 0, y: 20 } })
Image(this.imgUrl)
.sourceSize({ //设置图片解码尺寸
width: 65, height: 40
})
.width('30%')
.height('20%')
.border({ width: 1 })
.objectFit(ImageFit.ScaleDown) //保持原宽高比 等比缩放或不变
.objectRepeat(ImageRepeat.XY) //设置图片在xy轴上重复显示
.margin(15)
.overlay('scaleDown', { align: Alignment.Bottom, offset: { x: 0, y: 30 } })
Image(this.imgUrl)
.width('30%')
.height('20%')
.border({ width: 1 })
.objectFit(ImageFit.None) //保持图片原尺寸
.interpolation(ImageInterpolation.High) //图片插值,抗锯齿,使图片看着更清晰
.margin(15)
.overlay("none", { align: Alignment.Bottom, offset: { x: 0, y: 30 } })
}
}
}.width('100%')
.height('100%')
}
.tabBar(this.TabBuild('朋友圈', 2, $r('app.media.ic_hm_friend_selected'), $r("app.media.ic_hm_friend_normal")))
TabContent() {
Canvas(this.context)
.width('100%')
.height('100%')
.backgroundColor(Color.Yellow)
.onReady(() => {
this.offContext.drawImage(this.image, 110, 400, 130, 130)
let imageData = this.offContext.getImageData(150, 450, 130, 130)
this.offContext.putImageData(imageData, 130, 130)
let img = this.offContext.transferToImageBitmap();
this.context.transferFromImageBitmap(img)//.截图画图
this.context.beginPath()
this.context.rect(110, 10, 210, 110)
this.context.stroke()
this.context.font = '46px'
this.context.fillText('矩形', 120, 20)//画矩形
this.context.beginPath()
this.context.arc(110, 150, 50, 0, 6.28)
this.context.stroke()
this.context.font = '56px'
this.context.fillText('圆形', 120, 220)//画圆形
this.context.beginPath()
this.context.ellipse(110, 250, 50, 100, Math.PI * 0.25, Math.PI * 0, Math.PI * 2)
this.context.stroke()//画椭圆形
let line = this.context.createLinearGradient(110, 500, 110, 600)
line.addColorStop(0, '#f00')
line.addColorStop(0.5, '#ff0')
line.addColorStop(1, '#f0f')
this.context.fillStyle = line
this.context.fillRect(110, 500, 100, 100)//渐变色
this.context.fillText('我是中华人民共和国的公民', 110, 500)//写字
let path = new Path2D();
path.moveTo(100, 100)
path.lineTo(100, 150)
path.lineTo(25, 150)
path.lineTo(175, 150)
path.lineTo(100, 150)
path.lineTo(100, 200)
path.lineTo(40, 200)
path.lineTo(160, 200)
path.lineTo(100, 200)
path.closePath()
this.context.strokeStyle = '#f00'
this.context.lineWidth = 5
this.context.stroke(path)
let path2 = new Path2D();
path2.moveTo(40, 215)
path2.lineTo(160, 215)
path2.lineTo(160, 335)
path2.lineTo(40, 335)
path2.lineTo(40, 215)
this.context.stroke(path2)//再次画个“吉”
})
}
.tabBar(this.TabBuild('画布', 3, $r('app.media.ic_hm_logo'), $r('app.media.icon')))
TabContent() {
Column() {
Text('原始尺寸Circle')
Circle({ width: 100, height: 100 }).fill(0x00c250)
Row({ space: 10 }) {
Column() {
Text('shape内放大的Circle')
Shape() {
Rect({ width: 100, height: 100 }).fill(0xf7f7f7)
Circle({ width: 150, height: 150 }).fill(0x00c250)
}.viewPort({ x: 0, y: 0, width: 100, height: 100 }) //根据这个视口与宽高比进行放大shape内的组件大小
.width(150)
.height(150)
Path()
.width(100)
.height(100)
.commands(`M${this._50} 0 L${this._50} ${this._50} L0 ${this._50} L${this._100} ${this._50} L${this._50} ${this._50} L${this._50} ${this._100} L${this._15} ${this._100} L${this._85} ${this._100} L${this._50} ${this._100} Z`)
.fillOpacity(0) //实心不填充颜色
.fill(Color.Red)
.stroke(Color.Red)
.strokeWidth(5)
Polyline()
.width(100)
.height(100)
.stroke(Color.Red)
.strokeWidth(5)
.points([[10, 10], [90, 10], [90, 90], [10, 90], [10, 10]])
.fillOpacity(0) //实心不填充颜色
}
}
}
}
.tabBar(this.TabBuild('我的', 4, $r('app.media.ic_hm_my_selected'), $r('app.media.ic_hm_my_normal')))
}
.vertical(false) //tabs垂直与横向设置
.scrollable(false) //禁止页面滑动
.barMode((BarMode.Fixed)) //Fixed 固定 Scrollable 可以滑动,当tab多时用
.onChange((index) => { //页面滑动监听
console.error(`this is ${index}`)
this.currentIndex = index;
// this.tabsController.changeIndex(this.currentIndex)
})
}
}
@Component
struct MyNavigation {
private arr: number[] = [1, 2, 3];
build() {
Column() {
Navigation() {
TextInput({ placeholder: '请输入...' })
.width('90%')
.height(40)
.backgroundColor('#ffffff')
List({ space: 12 }) {
ForEach(this.arr, item => {
ListItem() {
NavRouter() {
Text("NavRouter" + item)
.width('100%')
.height(72)
.backgroundColor(Color.White)
.borderRadius(36)
.fontSize(16)
.fontWeight(500)
.textAlign(TextAlign.Center)
NavDestination() {
Text(`NavDestinationContent${item}`)
}
.title(`NavDestinationTitle${item}`)
}
}
})
}
}
.title('主标题')
.mode(NavigationMode.Stack)
.titleMode(NavigationTitleMode.Mini)
.menus([
{ value: "", icon: './../../../resources/base/media/icon.png', action: () => {
} },
{ value: "", icon: './../../../resources/base/media/icon.png', action: () => {
} }
])
.toolBar({ items: [
{ value: 'func', icon: './../../../resources/base/media/icon.png', action: () => {
} },
{ value: 'func', icon: './../../../resources/base/media/icon.png', action: () => {
} }
] })
}
}
}