您的位置: 首页> Vue> Web图像编辑神器tui.image-editor从基础到进阶的实战指南

Web图像编辑神器tui.image-editor从基础到进阶的实战指南

时间:2025-09-06 11:45:01 来源:互联网

在Web应用开发中,图像编辑功能已成为诸多场景(在线设计、社交媒体、文档处理等)的核心需求。tui.image-editor作为基于HTML5 Canvas的开源JavaScript库,由NHN Entertainment Corporation开发维护,凭借轻量化、高可定制性的特点,成为开发者快速集成图像编辑功能的优选方案。本文整合基础使用与深度定制内容,去除重复信息、补充遗漏细节,从环境准备到高级API应用,提供一站式学习指南。

1. 准备工作:环境与安装

1.1. 环境要求

1.2. 安装方式

方式1:npm/yarn安装(推荐生产环境)

通过包管理器安装可便于版本控制与依赖更新,安装命令如下:

# npm安装
npm install tui-image-editor
# yarn安装
yarn add tui-image-editor

安装后需在项目中引入核心代码与样式(以ES6模块为例):

import ImageEditor from 'tui-image-editor';
// 编辑器基础样式
import 'tui-image-editor/dist/tui-image-editor.css';
// 颜色选择器样式(绘图、文本等功能依赖)
import 'tui-color-picker/dist/tui-color-picker.css';

方式2:CDN引入(适合快速测试)

无需本地安装,直接在HTML的<head>标签中引入依赖文件(注意顺序,避免依赖缺失):

<!-- 样式文件 -->
<link rel="stylesheet" href="https://uicdn.t*oa*s*t.com/tui-image-editor/latest/tui-image-editor.min.css">
<link rel="stylesheet" href="https://uicdn.*t*o*ast.com/tui-color-picker/latest/tui-color-picker.min.css">
<!-- 核心依赖 -->
<script src="https://uicdn.toa***st.com/tui-code-snippet/latest/tui-code-snippet.min.js"></script>
<script src="https://uicdn.*t*oa*st.com/jquery/latest/jquery.min.js"></script>
<script src="https://uicdn.*t**oast.com/fabric.js/latest/fabric.min.js"></script>
<!-- 编辑器核心代码 -->
<script src="https://uicdn.t**oa*st.com/tui-image-editor/latest/tui-image-editor.min.js"></script>

2. 初始化配置:从基础到界面定制

tui.image-editor的初始化需传入容器元素配置对象(options),配置对象决定编辑器的尺寸、界面、功能启用状态等核心属性。

2.1. HTML容器搭建

需为编辑器准备固定高度的容器(否则可能无法正常渲染),建议通过CSS适配页面布局:

<!-- 编辑器容器 -->
<div id="editor-container">
  <canvas></canvas> <!-- 内置Canvas元素,用于图像渲染 -->
</div>

<style>
#editor-container {
  width: 100%; /* 自适应父元素宽度 */
  max-width: 1000px; /* 限制最大宽度 */
  height: 600px; /* 固定高度(必需) */
  margin: 20px auto; /* 居中布局 */
  border: 1px solid #eee; /* 可选:添加边框便于区分 */
}
</style>

2.2. 核心配置参数详解

配置对象分为基础配置界面配置(includeUI)滤镜配置(filters) 三大类,以下为完整参数说明:

2.2.1. 基础配置(根级参数)

参数名 类型 默认值 说明
cssMaxWidth Number 1000 编辑器最大宽度(px),超出后自动缩放,避免布局溢出。
cssMaxHeight Number 800 编辑器最大高度(px),与cssMaxWidth配合控制组件尺寸。
selectionStyle Object 见下方示例 选择区域样式(如裁剪、形状绘制时的选框),可自定义颜色、虚线等。
usageStatistics Boolean true 是否允许收集使用统计数据(生产环境建议设为false,保护用户隐私)。

selectionStyle默认值示例

selectionStyle: {
  cornerSize: 20,        // 选框角落控制点大小
  cornerColor: '#fff',   // 控制点颜色
  cornerStrokeColor: '#333', // 控制点边框颜色
  borderColor: '#333',   // 选框边框颜色
  borderDash: [4, 4],    // 边框虚线样式([实线长度, 间隙长度])
  backgroundColor: 'rgba(0, 0, 0, 0.1)' // 选框内部半透明背景
}

2.2.2. 界面配置(includeUI)

includeUI是最核心的配置项,控制界面元素、菜单、初始图片等,类型为Object,子参数如下:

子参数名 类型 默认值 说明
loadImage Object null 初始化加载的图片,null则显示空白画布;需传入path(图片URL/Base64)与name(图片名称)。
initMenu String 'filter' 初始激活的菜单,可选值:'crop'(裁剪)、'draw'(绘图)、'text'(文本)、'shape'(形状)、'icon'(图标)、'filter'(滤镜)。
menuBarPosition String 'bottom' 菜单栏位置,可选'top'/'bottom'/'left'/'right'
theme Object 浅色主题 自定义界面主题(颜色、图标),支持修改背景色、图标颜色等(见2.2.3)。
locale Object 英文 国际化配置(界面文字翻译),支持多语言(见2.2.4)。
uiSize String 'medium' 界面元素尺寸,可选'small'/'medium'/'large',适配不同屏幕。
menu Object 全量菜单 自定义启用/禁用菜单(如隐藏“绘图”功能),值为true启用、false禁用。

2.2.3. 主题定制(theme)

通过theme参数修改界面颜色与图标,支持以下属性(值为CSS颜色或SVG路径):

主题属性 说明 示例值
common.backgroundColor 编辑器整体背景色 '#f5f5f5'(浅灰)
header.backgroundColor 顶部工具栏(加载/下载按钮区)背景色 '#fff'(白色)
menu.normalIcon.color 菜单图标默认颜色 '#888'(深灰)
menu.activeIcon.color 菜单图标激活颜色 '#2196F3'(蓝色)
menu.disabledIcon.color 菜单图标禁用颜色 '#ccc'(浅灰)
menu.normalIcon.path 自定义默认图标SVG路径(替代内置) './icons/normal.svg'

深色主题示例

const darkTheme = {
  'common.backgroundColor': '#2d2d2d',
  'header.backgroundColor': '#3d3d3d',
  'header.borderColor': '#4d4d4d',
  'menu.normalIcon.color': '#bbb',
  'menu.activeIcon.color': '#4CAF50', // 激活图标为绿色
  'canvas.borderColor': '#555'
};

2.2.4. 国际化配置(locale)

通过locale参数翻译界面文字,需覆盖所有内置英文关键词,中文配置示例:

const localeZhCN = {
  'Crop': '裁剪',
  'Draw': '绘图',
  'Text': '文本',
  'Shape': '形状',
  'Icon': '图标',
  'Filter': '滤镜',
  'Rotate': '旋转',
  'Flip': '翻转',
  'Zoom': '缩放',
  'Undo': '撤销',
  'Redo': '重做',
  'Reset': '重置',
  'Apply': '应用',
  'Cancel': '取消',
  'Download': '下载',
  'Load Image': '加载图片',
  'Delete-all': '全部删除',
  'Flip X': '水平翻转',
  'Flip Y': '垂直翻转',
  'Rotate CW': '顺时针旋转',
  'Rotate CCW': '逆时针旋转'
};

2.2.5. 菜单启用/禁用(menu)

通过menu参数隐藏不需要的功能,简化界面,示例:

menu: {
  'crop': true,    // 启用裁剪
  'draw': false,   // 禁用绘图
  'text': true,    // 启用文本
  'shape': false,  // 禁用形状
  'icon': true,    // 启用图标
  'filter': true   // 启用滤镜
}

2.2.6. 完整初始化示例

整合上述配置,初始化一个支持中文、深色主题、仅启用核心功能的编辑器:

const editor = new tui.ImageEditor('#editor-container canvas', {
  cssMaxWidth: 1000,
  cssMaxHeight: 600,
  selectionStyle: {
    borderColor: '#2196F3',
    borderDash: [2, 2]
  },
  includeUI: {
    loadImage: {
      path: 'https://e*x*a*mple.com/initial-image.jpg', // 初始图片
      name: '示例图片'
    },
    initMenu: 'crop', // 初始进入裁剪模式
    menuBarPosition: 'bottom',
    theme: darkTheme, // 应用深色主题
    locale: localeZhCN, // 应用中文
    uiSize: 'medium',
    menu: {
      'crop': true,
      'text': true,
      'filter': true,
      'draw': false,
      'shape': false,
      'icon': false
    }
  }
});

2.2.7. 滤镜配置(filters)

用于修改内置滤镜参数或添加自定义滤镜,内置滤镜包括亮度、对比度、灰度等,示例:

filters: {
  // 修改内置滤镜:初始亮度设为20,模糊范围扩展到0-20
  'brightness': { value: 20, range: [-100, 100] },
  'blur': { value: 2, range: [0, 20] },
  // 添加自定义暗角滤镜
  'customVignette': {
    name: '自定义暗角', // 滤镜显示名称
    params: { value: 50, range: [0, 100] },
    // 滤镜逻辑(基于fabric.js)
    apply: (canvas, value) => {
      const vignette = new fabric.Image.filters.Vignette({
        radius: value / 100 * 0.5, // 暗角半径随参数变化
        brightness: 0.5
      });
      canvas.getActiveObject().filters.push(vignette);
      canvas.renderAll();
    }
  }
}

3. 核心API:控制编辑流程与状态

tui.image-editor实例提供丰富API,覆盖图像加载/导出、编辑操作、状态控制等场景,按功能分类如下:

3.1. 实例管理:初始化与销毁

3.2. 图像加载与导出

3.2.1. 加载图像(替换当前画布)

支持URL或Base64格式,返回Promise便于处理成功/失败逻辑:

// 语法:loadImage(imageSrc, imageName)
editor.loadImage('https://ex**ample.*com/new-image.jpg', '新图片')
  .then(() => {
    console.log('图片加载成功');
  })
  .catch(err => {
    console.error('加载失败:', err);
  });

3.2.2. 导出图像(下载/获取Base64)

API方法 说明 示例
downloadImage(format) 下载图像到本地,format可选png(默认)/jpg/webp editor.downloadImage('jpg')
toDataURL(format, quality) 获取Base64编码(用于上传),quality仅jpg/webp支持(0-1,1为最高质量) const base64 = editor.toDataURL('jpg', 0.8)

上传示例:将Base64编码的图像上传到服务器(以axios为例):

const imageBase64 = editor.toDataURL('jpg', 0.8);
axios.post('/api/upload-image', {
  image: imageBase64,
  fileName: 'edited-image.jpg'
}).then(res => {
  console.log('上传成功:', res.data);
});

3.3. 编辑操作:裁剪、绘图与标注

3.3.1. 基础编辑(裁剪、旋转、翻转)

API方法 说明 示例
startCrop() 进入裁剪模式 editor.startCrop()
applyCrop() 确认裁剪(应用操作) editor.applyCrop()
cancelCrop() 取消裁剪(退出模式) editor.cancelCrop()
rotate(degree) 顺时针旋转(degree为90/180/270) editor.rotate(90)
flip(direction) 翻转图像,direction为x(水平)/y(垂直) editor.flip('x')
zoom(ratio) 缩放图像(ratio>1放大,<1缩小) editor.zoom(1.5)(放大1.5倍)

3.3.2. 绘图与标注(文本、形状)

API方法 说明 示例
startDrawMode() 进入绘图模式 editor.startDrawMode()
setDrawConfig(config) 设置绘图样式(颜色、粗细) editor.setDrawConfig({ color: '#ff0000', width: 5 })
endDrawMode() 退出绘图模式 editor.endDrawMode()
addText(text, config) 添加文本,config含fontSize/color editor.addText('Hello', { fontSize: 24, color: '#333' })
addShape(type, config) 添加形状(type:rect/circle/triangle/arrow editor.addShape('rect', { fill: 'rgba(255,0,0,0.3)', stroke: '#ff0000' })

3.4. 状态控制:撤销、重做与重置

3.5. 事件监听:响应编辑行为

通过on()方法监听编辑过程中的关键事件,实现自定义业务逻辑:

事件名称 触发时机 回调参数示例
loadImage 图像加载完成时 (imageName) => { console.log('加载完成:', imageName) }
applyCrop 裁剪操作应用时 (cropSize) => { console.log('裁剪尺寸:', cropSize) }
rotate 旋转操作完成时 (degree) => { console.log('旋转角度:', degree) }
filterApply 滤镜应用或参数调整时 (filterName, filterValue) => { console.log('应用滤镜:', filterName, filterValue) }
editComplete 任意编辑操作(裁剪、绘图、文本添加等)完成时 () => { console.log('编辑操作完成') }
destroy 实例销毁时 () => { console.log('编辑器已销毁') }

事件监听示例:监听滤镜应用事件,记录用户调整的滤镜参数:

editor.on('filterApply', (filterName, filterValue) => {
  // 记录滤镜操作日志(可用于用户行为分析或历史记录回溯)
  console.log(`用户调整滤镜:${filterName},参数值:${filterValue}`);
  // 可选:将操作记录存储到本地存储,支持后续恢复
  const editHistory = JSON.parse(localStorage.getItem('editHistory')) || [];
  editHistory.push({
    time: new Date().toISOString(),
    action: 'filterApply',
    filterName,
    filterValue
  });
  localStorage.setItem('editHistory', JSON.stringify(editHistory));
});

4. 功能解析:从基础操作到创意定制

tui.image-editor提供的功能可满足从简单调整到创意设计的需求,以下按使用场景拆解核心能力:

4.1. 基础图像调整(裁剪、旋转、翻转)

4.2. 绘图与标注(画笔、文本、形状、图标)

4.3. 滤镜与特效(内置滤镜+自定义滤镜)

5. 项目集成:适配Vue与React框架

tui.image-editor可无缝集成到主流前端框架中,以下提供Vue 3和React 18的完整集成示例,包含实例管理、生命周期适配与基础功能调用。

5.1. 在Vue项目中集成

5.1.1. 组件封装(单文件组件)

<template>
  <div class="editor-wrapper">
    <!-- 编辑器容器 -->
    <div id="vue-editor-container">
      <canvas></canvas>
    </div>
    <!-- 自定义操作按钮 -->
    <div class="editor-buttons">
      <button @click="loadNewImage">加载新图片</button>
      <button @click="downloadImage">下载图片</button>
      <button @click="resetEditor">重置编辑</button>
    </div>
  </div>
</template>

<script setup>
import { onMounted, onUnmounted, ref } from 'vue';
import ImageEditor from 'tui-image-editor';
import 'tui-image-editor/dist/tui-image-editor.css';
import 'tui-color-picker/dist/tui-color-picker.css';

// 存储编辑器实例
const editorInstance = ref(null);

// 组件挂载时初始化编辑器
onMounted(() => {
  const darkTheme = {
    'common.backgroundColor': '#2d2d2d',
    'header.backgroundColor': '#3d3d3d',
    'menu.activeIcon.color': '#4CAF50'
  };

  const localeZhCN = {
    'Crop': '裁剪',
    'Filter': '滤镜',
    'Download': '下载',
    'Reset': '重置'
  };

  // 初始化实例
  editorInstance.value = new ImageEditor('#vue-editor-container canvas', {
    cssMaxWidth: 1000,
    cssMaxHeight: 600,
    includeUI: {
      loadImage: {
        path: 'https://pi*csu*m.p*hotos/800/600', // 初始加载示例图片
        name: '初始示例图'
      },
      initMenu: 'crop',
      theme: darkTheme,
      locale: localeZhCN,
      menu: {
        'crop': true,
        'filter': true,
        'text': true,
        'draw': false
      }
    }
  });

  // 监听滤镜应用事件
  editorInstance.value.on('filterApply', (name, value) => {
    console.log(`滤镜调整:${name} = ${value}`);
  });
});

// 组件卸载时销毁实例(避免内存泄漏)
onUnmounted(() => {
  if (editorInstance.value) {
    editorInstance.value.destroy();
    editorInstance.value = null;
  }
});

// 自定义方法:加载新图片
const loadNewImage = () => {
  const newImageUrl = prompt('请输入图片URL:', 'https://pic**sum.ph*otos/800/600?random=1');
  if (newImageUrl && editorInstance.value) {
    editorInstance.value.loadImage(newImageUrl, '新加载图片')
      .catch(err => alert(`加载失败:${err.message}`));
  }
};

// 自定义方法:下载图片
const downloadImage = () => {
  if (editorInstance.value) {
    const format = prompt('请选择下载格式(png/jpg/webp):', 'png');
    if (['png', 'jpg', 'webp'].includes(format)) {
      editorInstance.value.downloadImage(format);
    } else {
      alert('请输入正确的格式!');
    }
  }
};

// 自定义方法:重置编辑状态
const resetEditor = () => {
  if (editorInstance.value && confirm('确定要重置所有编辑操作吗?')) {
    editorInstance.value.reset();
  }
};
</script>

<style scoped>
.editor-wrapper {
  width: 100%;
  max-width: 1200px;
  margin: 20px auto;
}

#vue-editor-container {
  width: 100%;
  height: 600px;
  border: 1px solid #444;
  border-radius: 4px;
}

.editor-buttons {
  margin-top: 15px;
  display: flex;
  gap: 10px;
}

.editor-buttons button {
  padding: 8px 16px;
  background: #4CAF50;
  color: #fff;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.editor-buttons button:hover {
  background: #45a049;
}
</style>

5.1.2. 集成说明

5.2. 在React项目中集成

5.2.1. 组件封装(函数式组件)

import React, { useRef, useEffect } from 'react';
import ImageEditor from 'tui-image-editor';
import 'tui-image-editor/dist/tui-image-editor.css';
import 'tui-color-picker/dist/tui-color-picker.css';

const TuiImageEditor = () => {
  // 用于绑定编辑器DOM容器
  const editorContainerRef = useRef(null);
  // 存储编辑器实例(避免每次渲染重新创建)
  const editorInstanceRef = useRef(null);

  // 初始化编辑器(仅在组件挂载时执行一次)
  useEffect(() => {
    if (!editorContainerRef.current) return;

    // 1. 配置主题(浅色主题)
    const lightTheme = {
      'common.backgroundColor': '#f9f9f9',
      'header.backgroundColor': '#e0e0e0',
      'menu.normalIcon.color': '#888',
      'menu.activeIcon.color': '#2196F3'
    };

    // 2. 配置国际化(中文)
    const localeZhCN = {
      'Crop': '裁剪',
      'Draw': '绘图',
      'Text': '文本',
      'Filter': '滤镜',
      'Undo': '撤销',
      'Redo': '重做',
      'Download': '下载',
      'Load Image': '加载图片'
    };

    // 3. 创建编辑器实例
    editorInstanceRef.current = new ImageEditor(
      editorContainerRef.current.querySelector('canvas'),
      {
        cssMaxWidth: 1000,
        cssMaxHeight: 600,
        selectionStyle: {
          borderColor: '#2196F3',
          backgroundColor: 'rgba(33, 150, 243, 0.1)'
        },
        includeUI: {
          loadImage: {
            path: 'https://pi*csum*.*photos/800/600?random=2',
            name: 'React示例图'
          },
          initMenu: 'filter',
          menuBarPosition: 'bottom',
          theme: lightTheme,
          locale: localeZhCN,
          uiSize: 'medium',
          menu: {
            'crop': true,
            'draw': true,
            'text': true,
            'filter': true,
            'icon': false
          }
        },
        // 自定义滤镜:添加马赛克效果(简化版)
        filters: {
          'customMosaic': {
            name: '自定义马赛克',
            params: { value: 0, range: [0, 50] },
            apply: (canvas, value) => {
              if (value === 0) return; // 数值为0时取消效果
              const mosaic = new fabric.Image.filters.Mosaic({
                blocksize: value // 马赛克块大小,随参数变化
              });
              const activeObj = canvas.getActiveObject();
              if (activeObj) {
                activeObj.filters = activeObj.filters.filter(f => f.type !== 'Mosaic');
                activeObj.filters.push(mosaic);
                activeObj.applyFilters();
                canvas.renderAll();
              }
            }
          }
        }
      }
    );

    // 4. 监听编辑完成事件
    const editor = editorInstanceRef.current;
    editor.on('editComplete', () => {
      console.log('编辑操作完成,可执行保存逻辑');
    });

    // 组件卸载时销毁实例
    return () => {
      if (editor) {
        editor.destroy();
        editorInstanceRef.current = null;
      }
    };
  }, []);

  // 自定义方法:下载图片(指定JPG格式,质量0.9)
  const handleDownload = () => {
    const editor = editorInstanceRef.current;
    if (editor) {
      editor.downloadImage('jpg', 0.9);
    }
  };

  // 自定义方法:添加文本(固定内容与样式,可扩展为动态输入)
  const handleAddText = () => {
    const editor = editorInstanceRef.current;
    if (editor) {
      editor.addText('React集成示例', {
        fontSize: 32,
        color: '#2196F3',
        fontWeight: 'bold',
        left: 50, // 文本初始X坐标
        top: 50  // 文本初始Y坐标
      });
    }
  };

  return (
    <div style={{ maxWidth: '1200px', margin: '20px auto' }}>
      {/* 编辑器容器 */}
      <div 
        ref={editorContainerRef} 
        style={{ width: '100%', height: '600px', border: '1px solid #eee', borderRadius: '4px' }}
      >
        <canvas></canvas>
      </div>
      {/* 操作按钮区 */}
      <div style={{ marginTop: '15px', display: 'flex', gap: '10px' }}>
        <button 
          onClick={handleAddText}
          style={{ padding: '8px 16px', border: 'none', backgroundColor: '#2196F3', color: 'white', borderRadius: '4px', cursor: 'pointer' }}
        >
          添加示例文本
        </button>
        <button 
          onClick={handleDownload}
          style={{ padding: '8px 16px', border: 'none', backgroundColor: '#4CAF50', color: 'white', borderRadius: '4px', cursor: 'pointer' }}
        >
          下载JPG图片(高质量)
        </button>
      </div>
    </div>
  );
};

export default TuiImageEditor;

5.2.2. 集成说明

Mosaic滤镜实现,支持通过参数调整马赛克块大小,满足隐藏敏感信息的需求。

6. 常见问题与优化建议

在实际项目集成与使用过程中,开发者可能会遇到兼容性、性能或功能定制相关问题,以下提供针对性解决方案与优化方向。

6.1. 常见问题解决

6.1.1. 问题1:编辑器初始化后显示空白,无画布或菜单

可能原因

  1. 容器未设置固定高度(tui.image-editor依赖明确的高度渲染Canvas);
  2. 依赖文件加载顺序错误(如fabric.js未在tui-image-editor.js之前引入);
  3. 图片加载路径错误(loadImage.path为无效URL或跨域资源)。

解决方案

6.1.2. 问题2:移动端适配错乱,菜单按钮无法点击

可能原因

  1. 未配置uiSize参数,默认medium尺寸在小屏幕上过大;
  2. 触摸事件未正确绑定(部分移动端浏览器对Canvas触摸支持存在差异);
  3. 容器宽度未设置自适应(固定宽度超出屏幕范围)。

解决方案

6.1.3. 问题3:调用toDataURL获取Base64时出现跨域错误

可能原因: 加载的图片存在跨域限制,Canvas绘制跨域图片后会被标记为“污染”,无法通过toDataURL导出数据。

解决方案

  1. 确保图片服务器配置跨域允许(如Nginx添加add_header Access-Control-Allow-Origin *;);
  2. 若无法修改服务器配置,可通过后端接口转发图片(前端请求后端,后端获取图片后返回给前端,避免直接跨域);
  3. 本地开发时,使用webpack-dev-server配置代理,将图片请求转发到目标服务器,规避跨域限制。

6.2. 性能优化建议

6.2.1. 减少不必要的渲染与事件监听

6.2.2. 优化大图片加载与处理

6.2.3. 按需加载依赖与功能

6.3. 功能扩展建议

6.3.1. 自定义工具按钮

在编辑器界面外添加自定义工具(如“一键添加水印”“批量调整尺寸”),通过API实现特定功能:

// 示例:一键添加水印(固定位置与样式)
const addWatermark = () => {
  const editor = editorInstanceRef.current;
  if (editor) {
    // 添加半透明文本水印
    editor.addText('© 2024 MyApp', {
      fontSize: 16,
      color: 'rgba(0, 0, 0, 0.3)',
      fontWeight: 'normal',
      left: 20,
      top: editor.getImageSize().height - 40, // 固定在图片底部
      selectable: false // 禁止用户选中修改
    });
  }
};

6.3.2. 集成图片上传与版本管理

7. 总结

tui.image-editor作为一款轻量、高可定制的Web图像编辑库,凭借HTML5 Canvas与fabric.js的底层支持,提供了裁剪、绘图、滤镜、文本标注等丰富功能,同时通过灵活的配置参数与API,满足从基础集成到深度定制的需求。

如需进一步探索,可参考官方文档,获取最新版本更新、API细节与社区示例,持续优化项目中的图像编辑体验。


本次分享就到这儿啦,我是鹏多多,如果看了觉得有帮助的,欢迎 点赞 关注 评论,在此谢过道友;

往期文章

上一篇:Vue 表格悬停复制指令:优雅地一键复制单元格内容 下一篇:Vue 3 复杂表单父子组件双向绑定的最佳实践

相关文章

相关应用

最近更新