AI绘画/涂抹/框选

前言

上次写到图片框选的技术难点,现在项目已经发布4个月之久。总结一下AI绘画两种方式:画笔和框选分别的技术难点

参考原文:小程序实现图片框选 - 掘金 (juejin.cn)

最终效果

可以使用涂抹 或者框选的方式对图片进行AI替换

和传统的AI绘画的区别是我们是在图片的基础上进行局部替换

难点坑点

一、样式难点

1.canvas层级

小程序上的canvas的层级比普通view元素要高,即使view设置的z-index无限大。由于导航栏是自定义的,弹窗也是自定义的,遮不住canvas区域。所以只要是比canvas层级高的元素,都要使用cover-viewcover-image 进行开发,而这两个标签本身存在bug。详情官方文档 / cover-view (qq.com)

html 复制代码
<cover-view class="navigationbar-left">
    <cover-view class="navigationbar-button-single" @click="goBack">
        <cover-image class="button-group-image" mode="scaleToFill" src="/static/image/navigation/nav_back_white.png">
         </cover-image>
     </cover-view>
</cover-view>

2.图片尺寸

用户上传的图片是横图还是竖图,要分别处理对应的样式,保持永远居中。从而动态设置canvas宽高

js 复制代码
  if (isHorizontal) {
    // 横图
    this.canvasWidth = this.windowWidth
    this.canvasHeight = this.windowWidth * imageHeight / imageWidth
    this.canvasLeft = 0
    this.canvasTop = (this.canvasBgHeight - this.canvasHeight) / 2

  } else {
    // 竖图
    this.canvasHeight = this.canvasBgHeight
    this.canvasWidth = this.canvasBgHeight / imageHeight * imageWidth
    this.canvasTop = 0
    this.canvasLeft = (this.windowWidth - this.canvasWidth) / 2

  }
  
  export function isVerticalImage (url, scale = 1.5) {
    return new Promise(async (reslove, reject) => {
        const res = await wx.getImageInfo({ src: url })
        if (res) {
            const { width = 0, height = 0, path= "" } = res; // 图片宽度、高度
            const ratio = (height / width).toFixed(2); // 高宽比例
            reslove({
                isVertical: ratio >= scale,
                width,
                height,
                ratio,
                path
            })
        } else {
            reject(res)
        }
    })
}

二、涂抹难点

1.画笔粗细切换

粗细切换本身不是难点,只需设置 this.ctx.setLineWidth(this.width) 即可。

但是由于要保存之前的轨迹还能进行恢复和撤销功能。在安卓真机上诡异bug就出现了粗细切换好,之前画好的轨迹也会诡异的变粗变细。

最终的解决方式就是记录每一步的轨迹(保存成临时图片的路走不通,亲测了),撤销的时候重新生成。

js 复制代码
// 记录轨迹
this.ctx.lineTo(e.touches[0].x, e.touches[0].y);
this.ctx.setLineWidth(this.width);
this.ctx.setStrokeStyle(this.color);
this.ctx.setLineCap('round');
this.ctx.setLineJoin('round');
this.ctx.stroke();
this.ctx.draw(true);
this.ctx.moveTo(e.touches[0].x, e.touches[0].y);

this.drawData.splice(this.index, this.drawData.length - this.index, {
  type: 'move',
  x: e.touches[0].x,
  y: e.touches[0].y
})
this.index++

// 撤销/恢复时重画
while (this.index > 0 && this.drawData[this.index - 1].type !== 'start') {
  this.index--
}

2.轨迹撤销/恢复

这个实现起来不算难,但也不是无脑能写出来,思路如下

  • touchStart时,记录type为start,记录开始坐标,记录画笔粗细
  • touchMove时,记录type为move,记录开始轨迹
  • touchEnd时,记录type为end

每当撤销或者恢复时,重新生成画布的轨迹

3.黑白蒙版

由于AI需要传入一张同原图一样轨迹的黑白蒙版(AI能识别替换的原理)所以还需要一个画布。

亲测一个canvas实现不了,已经踩过坑了。。。

两个canvas同时画会非常卡顿 ,所以只有在用户确定生成的时候,再去根据轨迹生成另一个canvas

三、框选难点

1.必须清掉之前轨迹

之所以能够出现框选图形,是因为它在使用时要不断的清除之前画好的轨迹。

this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);

如果不清除,就会出现图形不规则,你们不信可以试试。。。

那么岂不是我之前的轨迹白画了?难点就出现了。解决方式是每次清除完轨迹,先把轨迹重绘出来,再去画新的轨迹。

js 复制代码
// 重绘之前的轨迹
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
for (let i = 0; i < this.index; i++) {
  let data = this.drawData[i]
  if (data.type === 'end') {
    this.ctx.rect(data.x, data.y, data.w, data.h);
    this.ctx.fill();
    this.ctx.stroke()
  }
}
// 新轨迹
this.ctx.setFillStyle(this.color)
this.ctx.setStrokeStyle(this.color);
this.ctx.setLineWidth(1);
let rectWidth = 0
let rectHeight = 0
if (this.p2.x >= this.p1.x) {
  rectWidth = this.p1.width
  if (this.p2.y >= this.p1.y) {
    rectHeight = this.p1.height
  } else {
    rectHeight = -this.p1.height
  }
} else {
  rectWidth = -this.p1.width
  if (this.p2.y >= this.p1.y) {
    rectHeight = this.p1.height
  } else {
    rectHeight = -this.p1.height
  }
}
this.p2.width = rectWidth
this.p2.height = rectHeight
this.ctx.rect(this.p1.x, this.p1.y, rectWidth, rectHeight);
this.ctx.fill()
this.ctx.stroke();
this.ctx.draw(true);

2.安卓真机不支持坐标为负值

用户框选的时候可以从右向左框选,那么坐标会为负值画不出来。

解决方法就是将 fillRect() 方法拆成rect ,再去fill

3.其他小坑点

参照我的另一篇文章 小程序实现图片框选 - 掘金 (juejin.cn)

总结

所以总结起来很简单,但是前前后后想过多种方案,踩过无数坑。用时一个月原创出来的方法和经验教训。希望能帮助到你们。

如果觉得有用,希望收藏一下~

原创不易,转发记得标明原文出处哦~

相关推荐
美美的海顿4 小时前
springboot基于Java的校园导航微信小程序的设计与实现
java·数据库·spring boot·后端·spring·微信小程序·毕业设计
罗狮粉 999 小时前
docker部署微信小程序自动构建发布和更新
docker·微信小程序·notepad++
Kika写代码21 小时前
【微信小程序】页面跳转基础 | 我的咖啡店-综合实训
服务器·微信小程序·小程序
源码哥_博纳软云1 天前
JAVA同城服务场馆门店预约系统支持H5小程序APP源码
java·开发语言·微信小程序·小程序·微信公众平台
YUJIAN。1 天前
使用uniapp开发微信小程序-框架搭建
微信小程序·小程序·uni-app
Anlici1 天前
three.js建立3D模型展示地球+高亮
前端·数据可视化·canvas
V+zmm101341 天前
基于微信小程序的乡村政务服务系统springboot+论文源码调试讲解
java·微信小程序·小程序·毕业设计·ssm
还这么多错误?!1 天前
uniapp微信小程序,使用fastadmin完成一个一键获取微信手机号的功能
微信小程序·小程序·uni-app
_院长大人_1 天前
微信小程序用户信息解密 AES/CBC/NoPadding 解密失败问题
微信小程序·小程序
407指导员1 天前
uniapp 微信小程序 页面部分截图实现
微信小程序·小程序·uni-app