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

相关推荐
油丶酸萝卜别吃5 小时前
前端转全栈学习路线
前端·学习
浮生望5 小时前
前端路由进阶:History API原理与手写HistoryRouter
前端
陆枫Larry5 小时前
JavaScript 中的竞态是什么,为啥会有竟态?
前端
上海安当技术7 小时前
半天接入:USBKey RESTful API + C 动态库,Web 和 C/S 两套集成路径实战
前端·后端·restful·集成·usbkey
DevUI团队8 小时前
从“即兴创作”到“规格先行”,华为云码道(CodeArts)代码智能体持续深耕企业级规范驱动开发能力
前端·人工智能·后端
weixin_431600449 小时前
NestJS 入门(3):Guard 如何挡住未登录请求?
前端·后端·学习·nest.js
avi91119 小时前
[AI教做人]AI平台做2项目;一个3D模型展示,另一个框架多人
javascript·人工智能·ai·3d模型·3d引擎·顶点和法线
kyriewen9 小时前
我用Claude Code两天干完了团队两周的排期——周报发出去那一刻我就后悔了
前端·javascript·ai编程
IT_陈寒10 小时前
JavaScript类型转换把我坑惨了,这破玩意真该早点搞明白
前端·人工智能·后端
用户9385156350710 小时前
Type vs Interface:读完这篇就没有面试官能难倒你了
前端·面试·typescript