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

相关推荐
不坑老师11 小时前
不坑盒子的插入网页功能是完全免费的!
前端·html
Wang's Blog11 小时前
前端FAQ: 描述⼀下你最近使⽤过的前端框架,并解释为何选择它们?
前端·vue.js·faq
wgego11 小时前
做题笔记BUU (XSS-Lab)(1-14)
前端·笔记·xss
dllxhcjla11 小时前
css第二天
java·前端·css
远航_12 小时前
10 个被严重低估的 JS 特性,直接少写 500 行代码
前端·javascript
起这个名字12 小时前
感觉这篇 DeepSeek 给出的微前端实践很牛!轻喷!
前端
小高00712 小时前
当前端面临百万级 API 请求:从"修 CSS 的"到架构师的进化之路
前端·javascript·面试
P7Dreamer12 小时前
package.json:你以为只是配置文件,其实是项目的命门!
前端·node.js
BBB努力学习程序设计12 小时前
让数据阅读更轻松:隔行换色表格的妙用
前端·html
西甲甲12 小时前
Process Explorer、windows 任务管理器以及系统 API 之间的关系
前端