项目AI绘画开发总结

数组去重操

  • 数组去重我们使用:set对象,这是ES6中提出的新语法。
  • 基本语法: const obj = new Set([1, 2, 3, 3, 4])

Set 对象:

  1. 定义:Set 是一种存储唯一值的集合数据结构。

  2. 创建:new Set([iterable])

  3. 主要方法:

    • add(value):添加值
    • delete(value):删除值
    • has(value):检查值是否存在
    • clear():清空 Set
    • size:获取 Set 大小
  4. 特点:

    • 值唯一性
    • 可存储任何类型的值
    • 不使用索引,而是使用键来引用值
  5. 遍历:

    • forEach()
    • for…of 循环
  6. 常见用途:

    • 数组去重
    • 检查元素是否存在
    • 实现集合运算(交集、并集、差集)
  7. 示例:

    1
    2
    3
    4
    const mySet = new Set([1, 2, 3, 3, 4]);
    console.log(mySet); // Set(4) {1, 2, 3, 4}
    mySet.add(5);
    console.log(mySet.has(3)); // true
  8. 与数组转换:

    • Set 转数组:[…mySet] 或 Array.from(mySet)
    • 数组转 Set:new Set(myArray)

项目体现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//监听键盘抬起事件
function enterUp(){
// 首先检查 tipWord 是否为空或者已经存在于数组中
if (form.tipWord && !form.tipWords.includes(form.tipWord)) {
// 如果不存在,则添加到数组中
form.tipWords.push(form.tipWord);
}
// 对数组进行去重操作 - 这里使用展开运算符对去重后的数组进行展开,并赋值
form.tipWords = [...new Set(form.tipWords)];

console.log(form.tipWords);

// 清空 tipWord
form.tipWord = '';

}

查找DOM节点

  • 这里介绍一个新函数:closest()函数。

closest 函数:

  1. 定义:closest() 是一个 DOM 方法,用于获取最近的匹配特定选择器的祖先元素。

  2. 用法:element.closest(selector)

  3. 特点:

    • 从当前元素开始向上遍历 DOM 树
    • 返回第一个匹配选择器的祖先元素
    • 如果没有匹配的祖先元素,返回 null
  4. 示例:

    1
    2
    const button = document.querySelector('button');
    const closestDiv = button.closest('div');
  5. 用途:

    • 事件委托
    • 查找特定的父元素
    • 在复杂的 DOM 结构中导航
  6. 浏览器支持:现代浏览器都支持,但在旧版本可能需要 polyfill

处理特殊数据

场景:这是在进行该项目前端开发时遇到的:在进行对表单需要数据校验时,发现了一个格式比较特殊的表单项,由于直接在代码中更改我发现很不好下手。所以这里将修改数据这一步封装为一个函数

如果需要对数据进行更改显示格式,调用函数即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"init_images": [
],
"sampler_index": "DPM++ 2M",
"denoising_strength": 0.4,
"prompt": "(1girl,1.5),(solo,1.3)",
"seed": -1,
"steps": 20,
"cfg_scale": 11,
"width": 948,
"height": 533,
"negative_prompt": "(EasyNegative,1.3),(nsfw,1.2)",
"mask": null,
"mask_blur": null,
"inpainting_mask_invert": null
}

promptnegative_prompt字段,查了下文档,左边叫做提示词,右边叫权重。发现显示是(提示词:权值)这样的格式,于是我们就可以封装一个数据处理函数,来获得我们需要的这样格式的数据。就比如:

1
2
3
4
5
6
//格式化数据函数,用来生成格式为(提示词:权值) 数据
function formatPrompt(promptObj: { [key: string]: number }): string {
return Object.entries(promptObj)
.map(([key, value]) => `(${key}:${value.toFixed(1)})`)
.join(',');
}

最后也是成功获得同样格式的数据,所以记录一下这种思路。因为直接去操作原本的数据我发现十分困难。

接入翻译API

场景:由于在输入提示词的时候,需要把中文提示词转化为英文的,不然后台ai无法进行识别。所以这里就用到了翻译的API。首选推荐的API是百度的API,这是一款免费的API。下面我就以百度翻译API为例,来写一个翻译函数。

百度翻译API官方文档:https://fanyi-api.baidu.com/product/113,需要可以查看。

签名生成方法

为什么需要签名:签名是为了保证调用安全,使用 MD5 算法生成的一段字符串,生成的签名长度为 32 位,签名中的英文字符均为小写格式。

Step1. 将请求参数中的 APPID(appid), 翻译 query(q,注意为UTF-8编码),随机数(salt),以及平台分配的密钥(可在管理控制台查看) 按照 appid+q+salt+密钥的顺序拼接得到字符串 1。
Step2. 对字符串 1 做 MD5 ,得到 32 位小写的 sign。 —— const md5Sign = CryptoJS.MD5(signStr).toString();
注:

  1. 待翻译文本(q)参数需为 UTF-8 编码;
  2. 在生成签名拼接 appid+q+salt+密钥 字符串时,q 不需要做 URL encode,在生成签名之后,发送 HTTP 请求之前才需要对要发送的待翻译文本字段 q 做 URL encode

3.如遇到报 54001 签名错误,请检查您的签名生成方法是否正确,在对 sign 进行拼接和加密时,q 不需要做 URL encode,很多开发者遇到签名报错均是由于拼接 sign 前就做了 URL encode;
4.在生成签名后,发送 HTTP 请求时,如果将 query 拼接在URL上,需要对 query 做 URL encode。

输入参数

请求方式: 可使用 GET 或 POST 方式,如使用 POST 方式,Content-Type 请指定为:application/x-www-form-urlencoded
字符编码:统一采用 UTF-8 编码格式
query 长度:为保证翻译质量,请将单次请求长度控制在 6000 bytes以内(汉字约为输入参数 2000 个)

字段名 类型 是否必填 描述 备注
q string 请求翻译query UTF-8编码
from string 翻译源语言 可设置为auto
to string 翻译目标语言 不可设置为auto
appid string APPID 可在管理控制台查看
salt string 随机数 可为字母或数字的字符串
sign string 签名 appid+q+salt+密钥的MD5值
以下字段仅开通了”我的术语库“用户需要填写
needIntervene integer 判断是否需要使用自定义术语干预API 1-是,0-否
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//接入百度翻译api
async function baiduTranslate(word: string) {
//定义翻译后的结果
let t_Result = ref('');
//生成salt
const salt = new Date().getTime();
//生成签名
const sign = appid + word + salt + sKey;
const signStr = sign.replace(/-/g, ''); // 移除签名中的'-'
const md5Sign = CryptoJS.MD5(signStr).toString(); // MD5加密签名
try {
//利用axios向翻译发送请求
const result = await axios({
url: "/fanyi",
method: 'POST',
headers: { "Content-Type": "application/x-www-form-urlencoded" },
data: {
q: word,
from: 'zh',
to: 'en',
appid: appid,
salt: salt,
sign: md5Sign
}
})
//成功后打印输出结果
console.log(result);
const {data} = result
const {trans_result} = data;
//返回翻译的结果
return t_Result = trans_result[0].dst
} catch (error:any) {
//抛出一个错误提示
throw new Error(error.message)
}
}

在想要翻译的地方调用该函数即可完成对汉字的翻译。详细更多语种请查看百度翻译API官网。

拉取设备摄像头

场景:因为需要把照片作为输入,传给AI进行识别,所以这里就需要采集照片,该项目使用的是利用相机来采集拍摄的照片。

调取摄像头

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 调起摄像头
const handleOpen = async () => {
dialogVisible.value = true
try {
// 调起摄像头 - 重要的一步
const stream = await navigator.mediaDevices.getUserMedia({ video: true })
// 也可以调摄像头的比例
// const stream = await navigator.mediaDevices.getUserMedia({ video: { width: 300,height: 400 }})
const videoElement = video.value
// 将摄像头画面绑定到 video 元素上
videoElement.srcObject = stream
videoElement.play()
} catch (error) {
console.error('无法访问摄像头:', error)
}
}

点击拍摄

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 点击确认
let photo = ref() // 获取img的DOM
const submitImg = () => {
dialogVisible.value = false
// 创建一个canvas - 这里是一个画布。
const canvas = document.createElement('canvas')
canvas.width = video.value.videoWidth
canvas.height = video.value.videoHeight
// 将摄像头捕捉到的图像绘制到canvas上
canvas.getContext('2d')!.drawImage(video.value, 0, 0, canvas.width, canvas.height)
// 将canvas转换为图片,这时候的图片格式是base64,如果需要什么格式,需要另外进行转换 - canvas.toDataURL('image/png')
//同时也赋值给表单的init_images属性
form.init_images.push(canvas.toDataURL('image/png'))
//从表单当中拿去照片的base64值
photo.value.src = form.init_images[0];
console.log("1111", form);

// 如果使用formData的话
// let formData = new FormData()
// formData.append('picture', photo.value.src)
// 如果使用formdata,那么接口需要添加 headers: {'Content-Type': 'multipart/form-data'}
// 显示图片
photo.value.style.display = 'block'
clearVideo()
}

关闭摄像头

1
2
3
4
5
6
7
8
9
const clearVideo = () => {
// 关闭摄像头
const stream = video.value.srcObject
const tracks = stream.getTracks()
tracks.forEach((track: any) => {
track.stop()
})
video.value.srcObject = null
}

页面中文但表单中的数据是英文

今天遇到了这么一个业务需求:前端页面展示用户的输入是中文,但携带到后端提交的数据的时候必须是英文。经过思考后,决定使用Map来以K,V键值对的形式存储内容

逻辑:因为要做到一一对应,所以想法就是一个英文单词作为key,英文单词对应的翻译作为value,这样的话就可以做到前端我们通过key去获取中文,而表单数据我们继续用英文。

  • 创建Map
1
2
//为了实现前端显示中文,表单数据携带应为提示词,这里需要做一个翻译映射
const translationMap = new Map<string,string>();
  • 向Map中存入数据 - 对象.set(key,value)
1
2
3
4
5
6
//将翻译的结果添加进映射里面
const englishWord = t_Result.value
console.log("2222",englishWord);
console.log(word);

translationMap.set(englishWord,word);
  • 从Map拿数据 - 对象.get(key)
1
2
3
4
5
6
7
8
9
10
//添加一个新的函数,来map映射中获取中文部分
function getChineseWord(englishWord:string):string {
//利用get方法,传入键来获取对应的值
const result = translationMap.get(englishWord)
if(result) {
return result;
}else {
return englishWord;
}
}