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

相关推荐
漫谈网络6 分钟前
TypeScript 编译 ES6+ 语法到兼容的 JavaScript介绍
javascript·typescript·es6
Uyker20 分钟前
空间利用率提升90%!小程序侧边导航设计与高级交互实现
前端·微信小程序·小程序
bin915329 分钟前
DeepSeek 助力 Vue3 开发:打造丝滑的日历(Calendar),日历_天气预报日历示例(CalendarView01_18)
前端·javascript·vue.js·ecmascript·deepseek
江城开朗的豌豆29 分钟前
JavaScript篇:反柯里化:让函数'反悔'自己的特异功能,回归普通生活!
前端·javascript·面试
江城开朗的豌豆37 分钟前
JavaScript篇:数字千分位格式化:从入门到花式炫技
前端·javascript·面试
十年砍柴---小火苗1 小时前
原生js操作元素类名(classList,classList.add...)
javascript·css·css3
henujolly2 小时前
网络资源缓存
前端
yuren_xia5 小时前
Spring Boot中保存前端上传的图片
前端·spring boot·后端
普通网友6 小时前
Web前端常用面试题,九年程序人生 工作总结,Web开发必看
前端·程序人生·职场和发展
站在风口的猪11087 小时前
《前端面试题:CSS对浏览器兼容性》
前端·css·html·css3·html5