feat(录音): RecordingOverlay 录音浮层(波形/上滑取消/太短,触摸穿透)
新增 entry/src/main/ets/components/RecordingOverlay.ets: - @Prop state: 'recording'|'cancel'|'tooShort' 三态展示 - recording 状态:7根波形柱 setInterval(120ms) 随机高度跳动动画 - cancel 状态:红底(#C0392B) + '✕' - tooShort 状态:暗底 + '!' - hitTestBehavior(HitTestMode.None) 触摸穿透到下方 Web,由容器 Web.onTouch 感知手势 - aboutToAppear/aboutToDisappear 正确管理 timer 生命周期 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
0a9ba6c8be
commit
481afc803d
@@ -0,0 +1,75 @@
|
|||||||
|
/**
|
||||||
|
* 录音浮层(设计 §10,纯展示,仿微信"按住说话")。状态由容器手势驱动,自身不处理触摸
|
||||||
|
* (HitTestMode.None 让触摸穿透到下方 Web,由 Web.onTouch 观察手势)。
|
||||||
|
* - recording:跳动波形 + "手指上滑,取消发送"
|
||||||
|
* - cancel:红底 + "✕" + "松开手指,取消发送"
|
||||||
|
* - tooShort:黑底 + "!" + "说话时间太短"
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
export struct RecordingOverlay {
|
||||||
|
/** 'recording' | 'cancel' | 'tooShort' */
|
||||||
|
@Prop state: string;
|
||||||
|
@State private bars: number[] = [0.4, 0.7, 0.5, 0.9, 0.6, 0.8, 0.45];
|
||||||
|
private timer: number = -1;
|
||||||
|
|
||||||
|
aboutToAppear(): void {
|
||||||
|
this.timer = setInterval(() => {
|
||||||
|
this.bars = this.bars.map(() => 0.25 + 0.75 * Math.random());
|
||||||
|
}, 120);
|
||||||
|
}
|
||||||
|
|
||||||
|
aboutToDisappear(): void {
|
||||||
|
if (this.timer >= 0) {
|
||||||
|
clearInterval(this.timer);
|
||||||
|
this.timer = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private hintText(): string {
|
||||||
|
if (this.state === 'cancel') {
|
||||||
|
return '松开手指,取消发送';
|
||||||
|
}
|
||||||
|
if (this.state === 'tooShort') {
|
||||||
|
return '说话时间太短';
|
||||||
|
}
|
||||||
|
return '手指上滑,取消发送';
|
||||||
|
}
|
||||||
|
|
||||||
|
build() {
|
||||||
|
Column() {
|
||||||
|
Column({ space: 14 }) {
|
||||||
|
if (this.state === 'recording') {
|
||||||
|
Row({ space: 4 }) {
|
||||||
|
ForEach(this.bars, (h: number, i: number) => {
|
||||||
|
Column()
|
||||||
|
.width(5)
|
||||||
|
.height(48 * h)
|
||||||
|
.borderRadius(3)
|
||||||
|
.backgroundColor('#FFFFFF')
|
||||||
|
}, (h: number, i: number) => i.toString())
|
||||||
|
}
|
||||||
|
.height(52)
|
||||||
|
.alignItems(VerticalAlign.Center)
|
||||||
|
} else {
|
||||||
|
Text(this.state === 'cancel' ? '✕' : '!')
|
||||||
|
.fontSize(42)
|
||||||
|
.fontWeight(FontWeight.Bold)
|
||||||
|
.fontColor('#FFFFFF')
|
||||||
|
}
|
||||||
|
Text(this.hintText())
|
||||||
|
.fontSize(14)
|
||||||
|
.fontColor('#FFFFFF')
|
||||||
|
}
|
||||||
|
.width(168)
|
||||||
|
.height(168)
|
||||||
|
.justifyContent(FlexAlign.Center)
|
||||||
|
.borderRadius(18)
|
||||||
|
.backgroundColor(this.state === 'cancel' ? '#C0392B' : 'rgba(0, 0, 0, 0.78)')
|
||||||
|
}
|
||||||
|
.width('100%')
|
||||||
|
.height('100%')
|
||||||
|
.justifyContent(FlexAlign.Center)
|
||||||
|
.alignItems(HorizontalAlign.Center)
|
||||||
|
.hitTestBehavior(HitTestMode.None)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user