AI绘画/涂抹/框选

前言

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

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

最终效果

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

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

难点坑点

一、样式难点

1.canvas层级

小程序上的canvas的层级比普通view元素要高,即使view设置的z-index无限大。由于导航栏是自定义的,弹窗也是自定义的,遮不住canvas区域。所以只要是比canvas层级高的元素,都要使用cover-view 和cover-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)

总结

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

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

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

相关推荐
程序员小孔.28 分钟前
基于 SpringBoot + UniApp + Vue 的家政服务系统设计与实现
前端·数据库·spring boot·python·docker·微信小程序
IT研究室1 天前
最新计算机毕业设计选题推荐-基于微信小程序的汉服交易系统-网站-文档指导-Java-springboot
java·微信小程序·课程设计
长路 ㅤ   1 天前
从中国城市散点到 3D 全球访客地球:BlogLoom 访客地图的设计与实现
开源项目·数据可视化·canvas·blogloom·访客地图
贾伟康1 天前
【化学实验室|04】HarmonyOS ArkTS 场景选择器实战:让 13 个离线实验的入口、筛选和返回路径保持一致
harmonyos·arkts·canvas·arkui·化学实验
2301_32241428041 天前
springboot学生课堂出勤统计系统15835-计算机毕设 原创(免费领源码+部署教程+开发环境)
java·mysql·随机森林·微信小程序·uni-app·微信开发者工具·推荐算法
小蒜学长2 天前
微信小程序-高校自习室管理系统(代码+数据库+LW)
java·spring boot·后端·微信小程序·自习室管理
MicrosoftReactor2 天前
技术速递|当聊天框不再是合适的交互界面
ui·ai·copilot·canvas
微擎应用市场3 天前
多功能预约公众号管理系统介绍
微信小程序·小程序
小皮虾3 天前
小程序全局状态还能这么简单?只需初始化一次,页面自动同步
前端·javascript·微信小程序
andrsted3 天前
练题簿助学教学:在线刷题、随堂考试,让一门课拥有自己的题库
学习·微信小程序·小程序·学习方法·刷题