#HarmonyOS NEXT体验官# 基于鸿蒙Next的App(创意工坊)解析4:插入文本 原创

蒙娜丽宁
发布于 2024-7-29 22:34
浏览
0收藏

在这篇文章中,我们会详细讲解如何实现《创意工坊》中的插入文字的功能,效果如下图所示:
#HarmonyOS NEXT体验官# 基于鸿蒙Next的App(创意工坊)解析4:插入文本-鸿蒙开发者社区

首先,插件文本需要弹出如下图的设置对话框。
#HarmonyOS NEXT体验官# 基于鸿蒙Next的App(创意工坊)解析4:插入文本-鸿蒙开发者社区

这个对话框需要使用InsertText组件完成,代码如下:

// 导入背景颜色和按钮背景颜色常量
import { PANEL_BACKGROUND_COLOR, PANEL_BUTTON_BACKGROUND_COLOR } from '../common/const'
// 导入文档大小相关的类型
import { DocumentSize, DocumentSizes } from '../data/Documents'
// 导入EntryAbility类
import EntryAbility from '../entryability/EntryAbility'
// 导入一些通用函数
import { documentExists, createDocumentDir, getInverseColor, rgbaToHex } from '../common/common'
// 导入Web颜色选择器对话框组件
import { WebColorPickerDialog } from './WebColorPickerDialog';
// 导入ObjConfig类型
import {ObjConfig} from '../common/types';

// 使用@Component装饰器标记这是一个自定义组件
@Component
// 导出InsertText结构体
export struct InsertText {
  // 定义插入文本成功的回调函数
  insertTextOK?: (textConfig: ObjConfig) => void
  // 定义编辑文本成功的回调函数
  editTextOK?: (textConfig: ObjConfig) => void
  // 定义取消插入文本的回调函数
  insertTextCancel?: () => void
  // 使用@Link装饰器定义双向绑定的配置对象
  @Link config:ObjConfig;
  // 使用@Link装饰器定义双向绑定的插入或编辑状态
  @Link insertOrEdit:boolean;

  // 创建填充颜色选择器的控制器
  fillController: CustomDialogController = new CustomDialogController({
    builder: WebColorPickerDialog({
      onClickEvent: this.onFillClickEvent.bind(this)
    }),
  })
  // 创建边框颜色选择器的控制器
  strokeController: CustomDialogController = new CustomDialogController({
    builder: WebColorPickerDialog({
      onClickEvent: this.onStrokeClickEvent.bind(this)
    }),
  })
  // 定义边框颜色点击事件处理函数
  onStrokeClickEvent(color: string) {
    // 如果选择的颜色不为空
    if (color.trim() != '') {
      // 将RGBA颜色转换为十六进制并设置到配置对象中
      this.config.stroke = rgbaToHex(color);
    }
    // 关闭边框颜色选择器
    this.strokeController.close();
  }
  // 定义填充颜色点击事件处理函数
  onFillClickEvent(color: string) {
    // 如果选择的颜色不为空
    if (color.trim() != '') {
      // 将RGBA颜色转换为十六进制并设置到配置对象中
      this.config.fill = rgbaToHex(color);
    }
    // 关闭填充颜色选择器
    this.fillController.close();
  }

  // 定义build函数,用于构建组件的UI
  build() {
    // 创建一个垂直布局的列
    Column() {
      // 创建标题文本
      Text('编辑文本属性')
        .fontSize(16)
        .fontColor('#FFFFFF')
        .width('100')
        .height('30')
        .textAlign(TextAlign.Center)
        .margin({ bottom: 5, top: 10 })

      // 创建文本输入框
      TextInput({text:this.config.text,placeholder:'请输入要插入的文本'}).fontSize(16)
      .width(200)
      .height(40)
      .margin({top:10})
      .fontColor('#000000')
        .backgroundColor('#FFFFFF')
        .onChange((value: string) => {
            // 当输入内容改变时,更新配置对象中的文本
            this.config.text = value;
        })

      // 创建一个水平布局的行,用于填充颜色选择
      Row() {
        // 创建填充颜色按钮
        Button({ type: ButtonType.Normal }) {
          Text('填充颜色')
            .fontSize(16)
            .fontColor('#FFFFFF')
            .width('100%')
            .height('30')
            .textAlign(TextAlign.Center)
        }
        .borderRadius(5)
        .width(100)
        .height(30)
        .onClick(() => {
          // 点击按钮时打开填充颜色选择器
          this.fillController.open();
        });
        // 显示当前填充颜色
        Text(this.config.fill) {

        }
        .fontSize(16)
        .width('100')
        .height('30')
        .margin({ left: 5 })
        .foregroundColor(getInverseColor(this.config.fill))
        .backgroundColor(this.config.fill);

      }.margin({ top: 10 })

      // 创建一个水平布局的行,用于边框颜色选择
      Row() {
        // 创建边框颜色按钮
        Button({ type: ButtonType.Normal }) {
          Text('边线颜色')
            .fontSize(16)
            .fontColor('#FFFFFF')
            .width('100%')
            .height('30')
            .textAlign(TextAlign.Center)
        }
        .borderRadius(5)
        .width(100)
        .height(30)
        .onClick(() => {
          // 点击按钮时打开边框颜色选择器
          this.strokeController.open();
        });
        // 显示当前边框颜色
        Text(this.config.stroke) {
        }
        .fontSize(16)
        .width('100')
        .height('30')
        .margin({ left: 5 })
        .foregroundColor(getInverseColor(this.config.stroke))
        .backgroundColor(this.config.stroke);

      }.margin({ top: 10 })

      // 创建一个水平布局的行,用于显示边框宽度
      Row() {
        Text('边线宽度')
          .fontSize(16)
          .fontColor('#FFFFFF')
          .width('80')
          .height('30')
          .textAlign(TextAlign.Center)
        Text(`${this.config.strokeWidth}`)
          .fontSize(16)
          .fontColor('#FFFFFF')
          .width('50')
          .height('30')
          .textAlign(TextAlign.Center)

      }.margin({ top: 10 })

      // 创建边框宽度滑动条
      Slider({
        value: this.config.strokeWidth,
        min: 0,
        max: 10,
        step: 1,
        style: SliderStyle.OutSet
      })
        .margin({ left: 10, right: 10 })
        .onChange((value: number, mode: SliderChangeMode) => {
          // 当滑动条值改变时,更新配置对象中的边框宽度
          this.config.strokeWidth = Math.round(value);
        })

      // 创建一个水平布局的行,用于显示字体大小
      Row() {
        Text('字体尺寸')
          .fontSize(16)
          .fontColor('#FFFFFF')
          .width('80')
          .height('30')
          .textAlign(TextAlign.Center)
        Text(`${this.config.fontSize}`)
          .fontSize(16)
          .fontColor('#FFFFFF')
          .width('50')
          .height('30')
          .textAlign(TextAlign.Center)

      }.margin({ top: 10 })

      // 创建字体大小滑动条
      Slider({
        value: this.config.fontSize,
        min: 10,
        max: 100,
        step: 1,
        style: SliderStyle.OutSet
      })
        .margin({ left: 10, right: 10 })
        .onChange((value: number, mode: SliderChangeMode) => {
          // 当滑动条值改变时,更新配置对象中的字体大小
          this.config.fontSize = Math.round(value);
        })

      // 创建一个弹性布局,用于上划线选项
      Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
        // 创建上划线复选框
        Checkbox({ name: 'overline' })
          .selectedColor(0x39a2db)
          .select(this.config.overline)
          .shape(CheckBoxShape.ROUNDED_SQUARE)
          .onChange((value: boolean) => {
              // 当复选框状态改变时,更新配置对象中的上划线状态
              this.config.overline = value;
          })
          .mark({
            strokeColor: Color.Black,
            size: 50,
            strokeWidth: 5
          })
          .width(25)
          .height(25)
        // 创建上划线文本
        Text('上划线').fontSize(16).foregroundColor('#FFFFFF')
          .onClick(() =>{
            // 点击文本时切换上划线状态
            this.config.overline = !this.config.overline;
          })
      }
      .margin({ top: 10,left:10 })

      // 创建一个弹性布局,用于删除线选项
      Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
        // 创建删除线复选框
        Checkbox({ name: 'linethrough' })
          .selectedColor(0x39a2db)
          .select(this.config.linethrough)
          .shape(CheckBoxShape.ROUNDED_SQUARE)
          .onChange((value: boolean) => {
            // 当复选框状态改变时,更新配置对象中的删除线状态
            this.config.linethrough = value;
          })
          .mark({
            strokeColor: Color.Black,
            size: 50,
            strokeWidth: 5
          })
          .width(25)
          .height(25)
        // 创建删除线文本
        Text('删除线').fontSize(16).foregroundColor('#FFFFFF')
          .onClick(() =>{
            // 点击文本时切换删除线状态
            this.config.linethrough = !this.config.linethrough;
          })
      }
      .margin({ top: 10,left:10 })

      // 创建一个弹性布局,用于下划线选项
      Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
        // 创建下划线复选框
        Checkbox({ name: 'underline' })
          .selectedColor(0x39a2db)
          .select(this.config.underline)
          .shape(CheckBoxShape.ROUNDED_SQUARE)
          .onChange((value: boolean) => {
            // 当复选框状态改变时,更新配置对象中的下划线状态
            this.config.underline = value;
          })
          .mark({
            strokeColor: Color.Black,
            size: 50,
            strokeWidth: 5
          })
          .width(25)
          .height(25)
        // 创建下划线文本
        Text('下划线').fontSize(16).foregroundColor('#FFFFFF')
          .onClick(() =>{
            // 点击文本时切换下划线状态
            this.config.underline = !this.config.underline;
          })
      }
      .margin({ top: 10,left:10 })

      // 创建一个水平布局的行,用于确定和取消按钮
      Row() {
        // 创建确定按钮
        Button({ type: ButtonType.Normal }) {
          Text('确定')
            .fontSize(16)
            .fontColor('#FFFFFF')
            .width('100%')
            .height('30')
            .textAlign(TextAlign.Center)
        }
        .borderRadius(5)
        .width(100)
        .height(30)
        .margin({right:10})
        .onClick(() => {
          // 点击确定按钮时,根据插入或编辑状态调用相应的回调函数
          if(this.insertOrEdit) {
            if (this.insertTextOK) {
              this.insertTextOK(this.config);
            }
          } else {
            if (this.editTextOK) {
              this.editTextOK(this.config);
            }
          }
        });

        // 创建取消按钮
        Button({ type: ButtonType.Normal }) {
          Text('取消')
            .fontSize(16)
            .fontColor('#FFFFFF')
            .width('100%')
            .height('30')
            .textAlign(TextAlign.Center)
        }
        .borderRadius(5)
        .width(100)
        .height(30)
        .onClick(() => {
          // 点击取消按钮时,调用取消回调函数
          if(this.insertTextCancel) {
            this.insertTextCancel();
          }
        }).margin({left:10});
      }.margin({top:20,left:10,right:10})
    }.backgroundColor(PANEL_BACKGROUND_COLOR)
    .border({ width: 0, color: '#000000', radius: 10 })
    .width('100%')
    .height('100%')
  }
}

下面详细解释一下这段代码
这段代码是一个名为InsertText的ArkTS自定义组件,用于实现文本插入和编辑的用户界面。我会非常详细地解释这段代码的结构和功能:
1.导入声明:
代码开始部分导入了必要的常量、类型和函数。这包括背景颜色常量、文档大小相关类型、一些通用函数(如颜色转换函数),以及一个自定义的Web颜色选择器对话框组件。
2.组件定义:
使用@Component装饰器定义了InsertText结构体,这是ArkTS中声明自定义组件的方式。
3.属性和方法:

  • 定义了三个可选的回调函数:insertTextOK、editTextOK和insertTextCancel,用于处理文本插入、编辑和取消操作。
  • 使用@Link装饰器定义了两个双向绑定属性:config(ObjConfig类型)和insertOrEdit(布尔类型)。这允许组件与父组件共享和同步数据。
  • 创建了两个CustomDialogController实例:fillController和strokeController,用于管理填充颜色和边框颜色的选择器对话框。
  • 定义了onStrokeClickEvent和onFillClickEvent方法,用于处理颜色选择后的逻辑,包括更新配置和关闭对话框。
    4.UI构建(build方法):
    build方法定义了组件的用户界面,使用ArkTS的声明式UI语法。主要包含以下元素:
    a. 文本输入:
    提供一个TextInput组件,允许用户输入或编辑文本。
    b. 颜色选择:
    为填充颜色和边框颜色各提供一个按钮和颜色显示区域。点击按钮会打开相应的颜色选择器对话框。
    c. 边框宽度和字体大小调整:
    使用Slider组件允许用户通过滑动来调整边框宽度和字体大小。当前值显示在滑动条旁边。
    d. 文本装饰选项:
    提供了三个Checkbox组件,分别用于选择上划线、删除线和下划线。每个选项旁边有一个文本标签,点击标签也可以切换选项状态。
    e. 确定和取消按钮:
    在界面底部提供了确定和取消按钮。确定按钮会根据当前是插入还是编辑模式调用相应的回调函数,而取消按钮会调用取消回调函数。
    5.样式和布局:
  • 整个界面使用Column布局,将各个元素垂直排列。
  • 使用Row和Flex布局来组织水平方向的元素,如颜色选择器和按钮。
  • 应用了一些通用样式,如字体大小、颜色、边距等,以确保界面的一致性和美观性。
  • 使用PANEL_BACKGROUND_COLOR作为整个面板的背景色,并添加了圆角边框。
    6.交互逻辑:
  • 文本输入、滑动条、复选框等UI元素都绑定了相应的事件处理函数,用于实时更新config对象中的相关属性。
  • 颜色选择使用自定义的WebColorPickerDialog组件,通过CustomDialogController来管理其显示和隐藏。
  • 确定按钮根据insertOrEdit的值来决定调用insertTextOK还是editTextOK回调函数,传递更新后的config对象。
  • 取消按钮调用insertTextCancel回调函数,允许父组件处理取消操作。
    这个组件提供了一个全面的文本样式编辑界面,包括文本内容、颜色、大小、边框和装饰等多个方面的定制选项。它的设计考虑了用户交互的便利性,并通过回调函数和数据绑定实现了与父组件的良好集成。这使得它可以轻松地嵌入到需要文本编辑功能的更大型应用中。
    当点击“确定”按钮插入文本时,会调用insertTextOK函数,代码如下:
// insertTextOK 函数用于处理文本插入操作
// 参数 textConfig 是一个 ObjConfig 类型的对象,包含了要插入文本的所有配置信息
insertTextOK(textConfig: ObjConfig) {
  // 使用 formatObjectProperties 函数将 textConfig 对象转换为格式化的字符串
  // 这可能是为了确保对象属性被正确序列化,以便在 JavaScript 中使用
  let configText = formatObjectProperties(textConfig);

  // 使用 controller1 对象运行 JavaScript 代码
  // addText 是一个在 WebView 或其他 JavaScript 环境中定义的函数
  // 我们将格式化后的配置文本作为参数传递给这个函数
  this.controller1.runJavaScript(`addText(${configText})`)
    .then(data => {
      // 当 JavaScript 代码执行完毕后,这个 Promise 会被解决
      // data 参数可能包含 addText 函数的返回值,但在这里没有被使用

      // 调用 toggleInsertTextPanel 方法
      // 这可能是用来隐藏或关闭文本插入面板的函数
      this.toggleInsertTextPanel();
    });
  // 注意:如果 JavaScript 执行过程中出现错误,这里没有进行错误处理
}

在这段代码中,核心的函数就是用js实现的addText函数,用于添加文本,代码如下:

// addText 函数用于在 Fabric.js canvas 上添加可交互的文本对象
function addText(config) {
    // 定义默认配置对象
    var defaultConfig = {
        left: 20,                // 文本左侧距离画布左边的像素数
        top: 20,                 // 文本顶部距离画布顶部的像素数
        fontFamily: 'Arial',     // 字体族
        fill: '#333',            // 文本填充颜色
        fontSize: 20,            // 字体大小(像素)
        textAlign: 'left',       // 文本对齐方式
        lineHeight: 1.16,        // 行高
        charSpacing: 0,          // 字符间距
        fontWeight: 'normal',    // 字体粗细
        fontStyle: 'normal',     // 字体样式(如斜体)
        underline: false,        // 是否添加下划线
        overline: false,         // 是否添加上划线
        linethrough: false,      // 是否添加删除线
        editable: true,          // 是否可编辑
        stroke: null,            // 文本描边颜色
        strokeWidth: 1,          // 描边宽度
        shadow: null             // 阴影设置
    };

    // 使用 Object.assign 合并用户提供的配置和默认配置
    // 用户配置会覆盖默认配置中的同名属性
    var settings = Object.assign({}, defaultConfig, config);

    // 创建一个新的 fabric.IText 对象
    // 如果 settings.text 存在则使用它,否则使用默认文本 'New text'
    var text = new fabric.IText(settings.text || 'New text', settings);

    // 将新创建的文本对象添加到 canvas 上
    canvas.add(text);
}

addText函数的详细介绍如下:

1.函数定义:
addText 函数接受一个 config 参数,允许用户自定义文本对象的属性。
2.默认配置:
defaultConfig 对象定义了文本对象的默认属性,包括位置、字体、颜色、大小等样式设置。
3.配置合并:
使用 Object.assign 方法将用户提供的 config 与 defaultConfig 合并,允许用户覆盖默认设置。
4.创建 IText 对象:
使用合并后的配置创建一个新的 fabric.IText 对象。
5.添加到 Canvas:
将新创建的文本对象添加到 Fabric.js canvas 上。
关于 Fabric.js 中的 IText 类:
IText 是 Fabric.js 库中的一个交互式文本类,它继承自基本的 Text 类,但提供了更多的交互功能。以下是 IText 类的一些主要特点:
1.交互式编辑:
用户可以直接在画布上双击 IText 对象来编辑文本内容。
2.光标和选择:
支持文本光标和文本选择功能,就像在常规文本编辑器中一样。
3.样式编辑:
允许对文本的不同部分应用不同的样式,如颜色、字体大小等。
4.键盘事件:
响应键盘事件,支持常见的文本编辑操作,如删除、复制、粘贴等。
5.多行支持:
自动处理文本换行,支持多行文本。
6.富文本功能:
可以为文本的不同部分设置不同的样式,实现富文本效果。
7.动画支持:
可以轻松地为 IText 对象添加动画效果。
8.事件处理:
提供了多种事件钩子,允许开发者响应用户的交互行为。
IText 类使得在 Fabric.js 画布上创建和操作交互式文本变得非常简单。它特别适用于需要用户能够直接在画布上编辑文本的应用场景,如在线设计工具、图片编辑器等。通过提供的配置选项,开发者可以精细地控制文本的外观和行为,使其满足各种复杂的设计需求。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
标签
1
收藏
回复
举报
回复
    相关推荐