【HarmonyOS NEXT】实现文字环绕动态文本效果

一、背景

在开发鸿蒙应用时,我们常常需要实现一些复杂的布局效果,比如文字环绕动态文本的效果。这种效果类似于文字环绕图片,但需要环绕的文本是动态生成的,并且带有边框和样式。传统的布局组件(如 Row)无法直接实现这种效果,因为 Row 的换行逻辑是基于每个子组件的起始端,无法实现第二个文本环绕第一个文本的效果。

下面介绍如何通过 组件截图ImageSpan 来实现文字环绕动态文本的效果。

二、实现思路

1. 初步尝试:使用 Row 组件

最初的想法是使用 Row 组件包裹两个 Text 组件,分别表示需要环绕的文本和主文本。然而,Row 组件的布局方式是左右排列,当文本换行时,第二个 Text 组件会基于自己的起始端换行,无法实现环绕效果。

如下效果:

2. 解决方案:组件截图 + ImageSpan

为了实现文字环绕效果,采用了以下方案:

  1. 使用 componentSnapshot 组件对需要环绕的文本进行截图,生成图片。

  2. 将截图通过 ImageSpan 嵌入到主文本中,实现文字环绕图片的效果。

三、具体实现

3.1 组件截图:componentSnapshot

componentSnapshot 是鸿蒙 ArkUI 提供的一个 API,用于获取组件的截图。它支持截取已加载组件的内容,并将结果保存为 PixelMap 对象。

3.1.1 导入模块

首先,需要导入 componentSnapshot 模块:

复制代码
import { componentSnapshot } from '@kit.ArkUI';
3.1.2 定义需要截图的组件

我们定义一个带有边框和样式的 Text 组件,作为需要环绕的文本:

javascript 复制代码
 //获取需要加载组件的文本
  @Builder
  hotTopText() {
    Text('精选')
      .borderRadius(2)
      .border({
        width: 0.5,
        color: '#0165B8',
        style: BorderStyle.Solid
      })
      .fontSize(12)
      .fontColor('#0165B8')
      .backgroundColor('#DBEFFF')
      .padding({
        left: 2,
        right: 2
      })
      .margin({ top: 5 })
      .height(15)
      .id('hotTopText') //组件标识
      .visibility(this.isShowTag ? Visibility.Visible : Visibility.None)
  }
3.1.3 获取组件截图

通过 componentSnapshot.get 方法获取组件的截图:

javascript 复制代码
getComponentSnapshot() {
    this.isShowTag = true
    // 增加延迟,确保组件渲染完成
    setTimeout(() => {
      componentSnapshot.get('hotTopText', { scale: 2, waitUntilRenderFinished: true })
        .then((pixmap: image.PixelMap) => {
          this.pixmap = pixmap;
          this.isShowTag = false
        })
        .catch(() => {
          console.log('lucy== 获取标签快照失败')
        });
    }, 100);
  }
3.2 使用 ImageSpan 实现文字环绕

将截图通过 ImageSpan 嵌入到主文本中,实现文字环绕效果:

javascript 复制代码
    Row() {
      this.hotTopText() //只有加载了才能获取截图
      Text() {
        ImageSpan(this.pixmap)
          .height(14)
          .width('auto')
          .verticalAlign(ImageSpanAlignment.CENTER)
          .margin({ right: 3 })
          .objectFit(ImageFit.Contain)
        Span('HarmonyOS NEXT 全栈自研架构,鸿蒙原生应用,带来全新体验')
          .fontSize(15)
          .fontWeight(FontWeight.Medium)
      }
      .align(Alignment.Top)
      .textAlign(TextAlign.Start)

    }
    .margin({
      top: 12,
    })
    .padding(12)
    .borderWidth(1)
    .borderColor(Color.Red)

四、完整代码

以下是完整的实现代码:

javascript 复制代码
import { image } from '@kit.ImageKit'
import { componentSnapshot } from '@kit.ArkUI'

@Entry
@Component
struct TextSpanPage {
  @State pixmap: image.PixelMap | undefined = undefined
  @State isShowTag: boolean = false

  aboutToAppear(): void {
    this.getComponentSnapshot()
  }

  getComponentSnapshot() {
    this.isShowTag = true
    // 增加延迟,确保组件渲染完成
    setTimeout(() => {
      componentSnapshot.get('hotTopText', { scale: 2, waitUntilRenderFinished: true })
        .then((pixmap: image.PixelMap) => {
          this.pixmap = pixmap;
          this.isShowTag = false
        })
        .catch(() => {
          console.log('lucy== 获取标签快照失败')
        });
    }, 100);
  }

  build() {
    Row() {
      this.hotTopText() //只有加载了才能获取截图
      Text() {
        ImageSpan(this.pixmap)
          .height(14)
          .width('auto')
          .verticalAlign(ImageSpanAlignment.CENTER)
          .margin({ right: 3 })
          .objectFit(ImageFit.Contain)
        Span('HarmonyOS NEXT 全栈自研架构,鸿蒙原生应用,带来全新体验')
          .fontSize(15)
          .fontWeight(FontWeight.Medium)
      }
      .align(Alignment.Top)
      .textAlign(TextAlign.Start)

    }
    .margin({
      top: 12,
    })
    .padding(12)
    .borderWidth(1)
    .borderColor(Color.Red)
  }

  //获取需要加载组件的文本
  @Builder
  hotTopText() {
    Text('精选')
      .borderRadius(2)
      .border({
        width: 0.5,
        color: '#0165B8',
        style: BorderStyle.Solid
      })
      .fontSize(12)
      .fontColor('#0165B8')
      .backgroundColor('#DBEFFF')
      .padding({
        left: 2,
        right: 2
      })
      .margin({ top: 5 })
      .height(15)
      .id('hotTopText') //组件标识
      .visibility(this.isShowTag ? Visibility.Visible : Visibility.None)
  }
}

五、最终效果

通过以上方法,实现了文字环绕动态文本的效果。动态文本被截图为图片,并通过 ImageSpan 嵌入到主文本中,实现了类似文字环绕图片的布局效果。

相关推荐
小杨同学yx40 分钟前
前端三剑客之Css---day3
前端·css
星月心城2 小时前
Promise之什么是promise?(01)
javascript
二川bro2 小时前
第二篇:Three.js核心三要素:场景、相机、渲染器
开发语言·javascript·数码相机
Mintopia2 小时前
🧱 用三维点亮前端宇宙:构建你自己的 Three.js 组件库
前端·javascript·three.js
故事与九3 小时前
vue3使用vue-pdf-embed实现前端PDF在线预览
前端·vue.js·pdf
小西↬3 小时前
vite+vue3+websocket处理音频流发送到后端
javascript·websocket·音视频
生涯にわたる学び3 小时前
数据库02 网页html01 day44
数据库·html
Mintopia3 小时前
🚀 顶点-面碰撞检测之诗:用牛顿法追寻命运的交点
前端·javascript·计算机图形学
wb1893 小时前
企业WEB应用服务器TOMCAT
运维·前端·笔记·tomcat·云计算
zhanshuo4 小时前
用鸿蒙做多人协作,真的可以跨屏秒同步!
harmonyos