【ArkUI专栏-边框】OpenHarmony应用开发-如何实现循环流体边框动效
·
简介
在OpenHarmony应用开发中,设置循环流体边框动效是较为常见的场景。下文会通过详细示例来描述如何实现。
开发环境
DevEco Studio: DevEco Studio 6.0.1 Release(Build Version: 6.0.1.260)
系统: OpenHarmony 6.0.0.47
设备: DAYU200(rk3568)
最佳实践示例
@Entry
@Component
struct BorderTest1 {
build() {
Column() {
FlowBorder({
widthFlow: 260,
heightFlow: 140,
lineWidth: 4,
cornerRadius: 18,
color: '#00E5FF',
segLen: 36, // 亮段长度(调小就是“一小段在跑”)
speed: 2 // 每帧前进素
}) {
Text('内容区')
.fontSize(16)
.fontColor(Color.White)
}
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.backgroundColor('#111')
}
}
@Component
struct FlowBorder {
@Prop widthFlow: number = 240
@Prop heightFlow: number = 120
@Prop lineWidth: number = 3
@Prop cornerRadius: number = 16
@Prop color: string = '#00E5FF'
@Prop segLen: number = 36
@Prop speed: number = 2
@BuilderParam content?: () => void
private settings: RenderingContextSettings = new RenderingContextSettings(true)
private ctx: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
private timerId: number = -1
private dashOffset: number = 0
build() {
Stack() {
Column() {
if (this.content) this.content()
}
.width(this.widthFlow)
.height(this.heightFlow)
.padding(16)
.borderRadius(this.cornerRadius)
.clip(true)
Canvas(this.ctx)
.width(this.widthFlow)
.height(this.heightFlow)
.hitTestBehavior(HitTestMode.Transparent)
.onReady(() => this.start())
}
}
private start(): void {
this.stop()
this.dashOffset = 0
this.timerId = setInterval(() => {
const w = this.widthFlow
const h = this.heightFlow
const lw = this.lineWidth
// r 不能超过几何限制
const r = Math.max(0, Math.min(this.cornerRadius, (Math.min(w, h) - lw) / 2))
const rw = w - lw
const rh = h - lw
// 圆角矩形周长:直线部分 + 四个1/4圆 = 2πr
const perimeter = 2 * (rw + rh - 4 * r) + 2 * Math.PI * r
this.dashOffset = (this.dashOffset + this.speed) % perimeter
this.draw(perimeter, r)
}, 16)
}
private stop(): void {
if (this.timerId !== -1) {
clearInterval(this.timerId)
this.timerId = -1
}
}
private draw(perimeter: number, r: number): void {
const ctx = this.ctx
const w = this.widthFlow
const h = this.heightFlow
const lw = this.lineWidth
const x = lw / 2
const y = lw / 2
const rw = w - lw
const rh = h - lw
const segLen = Math.max(1, Math.min(this.segLen, perimeter))
const gapLen = Math.max(0, perimeter - segLen) // 关键:周期=perimeter,保证无缝
ctx.clearRect(0, 0, w, h)
// 底色边框(可选)
ctx.save()
ctx.beginPath()
this.roundRectPath(ctx, x, y, rw, rh, r)
ctx.lineWidth = lw
ctx.strokeStyle = 'rgba(0,229,255,0.15)'
ctx.setLineDash([])
ctx.stroke()
ctx.restore()
// 流动亮段(只有一段)
ctx.save()
ctx.beginPath()
this.roundRectPath(ctx, x, y, rw, rh, r)
ctx.lineWidth = lw
ctx.strokeStyle = this.color
ctx.lineCap = 'round'
ctx.lineJoin = 'round'
ctx.setLineDash([segLen, gapLen])
ctx.lineDashOffset = -this.dashOffset // 反方向就去掉负号
ctx.stroke()
ctx.restore()
}
// 用 arcTo 兼容地画圆角矩形路径(不依赖 roundRect)
private roundRectPath(ctx: CanvasRenderingContext2D,
x: number, y: number, w: number, h: number, r: number): void {
const rr = Math.max(0, Math.min(r, w / 2, h / 2))
ctx.moveTo(x + rr, y)
ctx.lineTo(x + w - rr, y)
ctx.arcTo(x + w, y, x + w, y + rr, rr)
ctx.lineTo(x + w, y + h - rr)
ctx.arcTo(x + w, y + h, x + w - rr, y + h, rr)
ctx.lineTo(x + rr, y + h)
ctx.arcTo(x, y + h, x, y + h - rr, rr)
ctx.lineTo(x, y + rr)
ctx.arcTo(x, y, x + rr, y, rr)
ctx.closePath()
}
}
更多推荐

所有评论(0)