【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 嵌入到主文本中,实现了类似文字环绕图片的布局效果。

相关推荐
薛定猫AI4 小时前
【深度解析】Gemma Chat 本地 AI 编程 Agent:Electron + MLX + 开源模型的离线 Vibe Coding 实战
javascript·人工智能·electron
kyriewen4 小时前
Webpack vs Vite:一个是“老黄牛”,一个是“猎豹”,你选谁?
前端·webpack·vite
若兰幽竹4 小时前
【HarmonyOS 6.1 全场景实战】开篇词:打造消除“吃饭焦虑”的《灵犀厨房》
harmonyos·鸿蒙开发·华为鸿蒙系统
打小就很皮...4 小时前
html2canvas + jsPDF 生成 PDF 的踩坑与解决方案总结
前端·pdf
机构师4 小时前
<鸿蒙><APP><3D>鸿蒙3D开发,如何获取ktx格式的天空盒图?
华为·harmonyos
全栈前端老曹4 小时前
【前端地图】多地图平台适配方案——高德、百度、腾讯、Google Maps SDK 差异对比、封装统一地图接口
前端·javascript·百度·dubbo·wgs84·gcj-02·bd09
笑虾5 小时前
Win10 修改注册表 让鼠标悬停PNG上时 tip 始终显示分辨率
开发语言·javascript·ecmascript
雾岛听风6915 小时前
JavaScript基础语法速查手册
开发语言·前端·javascript
遇见~未来5 小时前
第三篇_现代布局_从弹性到网格
前端·css3
前端那点事5 小时前
Vue前端SEO优化全攻略(实操落地版,新手也能上手)
前端·vue.js