作者:杨尚晓
前言
雷达扫描是一个比较有科技感的东西,对于科幻迷来说,科幻电影里基本都能看到的画面,一个大大的屏幕上,可以看到雷达扫描的绿幕效果。
下面我们使用三种方式来实现这样的雷达效果。
项目说明
- 工具版本:DevEco Studio 3.0 Release
- SDK版本:3.1.1.2(API Version 8 Release)
- 主要组件:canvas, Row, Image
效果
-
使用Image图片组件方式实现

-
使用canvas组件实现

-
使用Row组件的渐变属性实现

实现过程
需要注意,一个页面里只能有一个@Entry修饰符,所以,下面三种方法在预览的时候,需要注意注释只保留一个@Entry
1. 使用Image方法实现
使用Image组件的方法是最简单的,直接制作两张图片,一张底图,一张扫描的图


将两张图片通过叠加,将扫描的图片通过圆心宣旋转即可,下面使用代码来实现
整体比较简单,旋转主要用到了animation属性,这些在官网API文档可以查看。虽然使用Image组件实现比较简单,但是却是可以实现一些复杂的雷达UI。
2. 使用canvas实现
使用canvas实现的需要用到两个组件,第一个是Canvas组件,用来绘制底图网格,第二个是Row组件,使用角渐变属性实现旋转的扇形。
这里为什么不都使用canvas实现呢,找了一圈,canvas只有线性渐变和径向渐变,切没有角度渐变属性,所以,为了方便就用了row来实现吧。
下面直接上代码。
Row的渐变方式在下一个方法讲解,最终都还是使用animation属性动画实现扇形的旋转效果
3. 使用Row组件实现
使用Row组件实现的方法稍微复杂一些,这里用到了4个Row组件,其实对于前端童鞋来说,这里可能会比较好里一些,其实就是类似通过div和css来实现的,row组件是div,其属性是css样式。
废话不多说,直接上代码开搞
3.1 实现圆圈圈
首先使用径向渐变属性(radialGradient)来实现底部圈圈的效果
radialGradient属性有几个需要注意的值,radius是渐变的半径,这里使用30。
渐变颜色colors组用了三个数据,可以看出,前两个其实是透明度为0的。
可以理解为
- 第一个渐变颜色到第二个渐变颜色用了90%
- 第二个渐变颜色到第三个渐变颜色,用了10%(100%-90%)
- 也就是说在渐变半径为30的情况下,有90%是透明的,只有10%是透明到green颜色的,这样得到了一个圈圈
- 然后又设置了repeating属性(重复着色)为true
- 所以在半径为150的圆内,可以设置 150 / 3 = 5个圆圈圈
我们看看效果

3.2 实现十字架
实现十字架使用了两个Row组件,使用线性渐变属性(linearGradient)分别绘制了一横一竖的效果。
可以看到颜色组colors使用了5个颜色来实现,1,2,4,5的颜色都是透明的
- 第一个颜色到第二个颜色都是透明的,渐变范围是0到49%,
- 第二个颜色到第三个颜色渐变范围是50% - 49% = 1%,也就是在其50%(中间)的地方绘制了一个1%的green线条
- 第三个颜色到第四个颜色不变
- 第三个到第五个颜色也是透明,渐变范围也是50%
上面绘制好之后,我们通过angle属性将线条旋转90度得到一个十字架
Row()
.width(300)
.height(300)
.borderRadius(150)
.linearGradient({
angle: 0,
colors: [
['rgba(0,0,0,0)', 0],
['rgba(0,0,0,0)',0.49],
['green',0.5],
['rgba(0,0,0,0)',0.5],
['rgba(0,0,0,0)',1]
]
})
Row()
.width(300)
.height(300)
.borderRadius(150)
.linearGradient({
angle: 90,
colors: [
['rgba(0,0,0,0)', 0],
['rgba(0,0,0,0)',0.49],
['green',0.5],
['rgba(0,0,0,0)',0.5],
['rgba(0,0,0,0)',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.
来看看效果

3.3 实现旋转扇形
扇形也是用到Row组件,其角度渐变属性(sweepGradient)来实现的。
来看看效果

最后设置rotate旋转属性,设置旋转轴为z轴,角度angle为动态更新,这样animation动画属性才会更新。
最终代码
扇形的代码
这里添加一个扫描周边设备的效果,动态设置了一个数据源,通过ForEach来动态渲染
最终的代码
@Entry
@Component
struct Radar {
@State angle:number = 0;
@State scanData: any = []
@State w:number = 0;
@State h:number = 0;
@State opt:number = 1
@State flag:number = 0.4
aboutToAppear(){
setTimeout(()=>{
this.angle = 360
},200)
setTimeout(()=>{
animateTo({
duration: 1000,
curve: Curve.Linear,
iterations: -1,
playMode: PlayMode.AlternateReverse,
onFinish: () => {
console.info('play end')
}
}, () => {
this.opt = 0.3
})
},2000)
setTimeout(()=>{
this.scanData = [
{
id: 1,
x: 190,
y: 200,
name: '空调'
},
{
id: 1,
x: 80,
y: 240,
name: '插座'
},
]
setTimeout(()=>{
this.w = 1;
this.h = 1;
},200)
},1000)
}
build(){
Row(){
Stack(){
Row()
.width(300)
.height(300)
.borderRadius(150)
.radialGradient({
center: [150,150],
radius: 30,
colors: [
['rgba(0,0,0,0)', 0],
['rgba(0,0,0,0)', 0.9],
['green', 1],
],
repeating: true,
})
Row()
.width(300)
.height(300)
.borderRadius(150)
.linearGradient({
angle: 0,
colors: [
['rgba(0,0,0,0)', 0],
['rgba(0,0,0,0)',0.49],
['green',0.5],
['rgba(0,0,0,0)',0.5],
['rgba(0,0,0,0)',1]
]
})
Row()
.width(300)
.height(300)
.borderRadius(150)
.linearGradient({
angle: 90,
colors: [
['rgba(0,0,0,0)', 0],
['rgba(0,0,0,0)',0.49],
['green',0.5],
['rgba(0,0,0,0)',0.5],
['rgba(0,0,0,0)',1]
]
})
Row()
.width(300)
.height(300)
.borderRadius(150)
.sweepGradient({
center: [150,150],
start: 0,
end: 359,
colors: [
['rgba(0,0,0,0)',0],
['rgba(0,0,0,0)',this.flag],
['rgba(0,255,0,0.5)',1],
]
})
.rotate({
z: 1,
angle: this.angle
})
.animation({
duration: 2000,
iterations: -1,
curve: Curve.Linear
})
ForEach(this.scanData,(item: any) => {
Column(){
Image($r('app.media.icon'))
.width(28)
.height(28)
.backgroundColor('#fff')
.borderRadius(19)
Text(item.name)
.fontColor('#fff')
.margin({top: 5})
.fontSize(10)
}
.alignItems(HorizontalAlign.Center)
.position({x: item.x, y: item.y})
.scale({x: this.w, y: this.h})
.animation({
duration: 1000,
iterations: 1,
curve: Curve.Friction
})
.opacity(this.opt)
})
}
.width(300)
.height(300)
}
.justifyContent(FlexAlign.Center)
.width('100%')
.height('100%')
.backgroundColor(0x111111)
}
}
- 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.
来看看最终完整的效果

git地址
https://gitee.com/yango520/yg-radar
总结
通过这个雷达demo,特别是第三种方法,可以学到了颜色渐变属性中的三种渐变,线性渐变,径向渐变,和角度渐变。也可以学习属性动画的实现。
入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。
有了雷达就不怕丢东西了
效果非常逼真
经常能在电影军舰上看到这种雷达
三种实现方式,可以跟进应用场景来选择使用实现
不错不错,厉害呀
已参考到实际项目 很好用