写点什么

豆包 MarsCode 助力:Canvas 上的素描变色魔法✨

作者:豆包MarsCode
  • 2024-11-06
    北京
  • 本文字数:2523 字

    阅读完需:约 8 分钟

豆包MarsCode 助力:Canvas 上的素描变色魔法✨

作者:BLACK595


😎嘿!首先,先来看超酷的效果哦😃!🎨素描图在用户鼠标按下后,就像被施了魔法一样🧙‍♂️,将鼠标范围内的素描像素神奇地转换成有色像素🌈。


动图无法展示,感兴趣的朋友可以移步: https://juejin.cn/post/7410410146483781673#heading-3

原理

  1. 设置原图 A 在底层

  2. 将模糊过或者另一张图片 B 覆盖遮挡原图 A

  3. 监听鼠标按下移动事件,抹除 B 相应部分,露出原图 A。(也就是橡皮擦效果,即鼠标点下去移动所经过位置擦除,鼠标松开不清除。)


关于绘制图形,图形操作,前端一般都是用 Canvas 进行处理。最近抖音豆包 AI 的插件豆包MarsCode 也进入了插件市场,咱们拿来用用,提升下速度,自己也可以少敲点代码。

实现

滑动画线

首先,先来实现鼠标按下滑动后画出线条,先来让 AI 助手来生成下代码。



这里我们必须要把需求描述清楚,要说使用 Canvas 来实现,不然 AI 理解不了你要干嘛。先把代码粘贴进来试下。结果报错了。


onMouseMove方法重复了,这里它给的代码有点小 bug,在引入时引入了onMouseMoveonMouseUp,但 vue 中并没有这两个方法,我们把这两个引入删除。除了这个 bug,效果还是可以的。



擦除

现在完成了滑动时画出内容,但是我们需要的是滑动时清除内容,这里就要用到 cavas 的一个神奇的属性-globalCompositeOperation,该属性定义了我们在绘制图形时如何将新内容合成现有图形,合成为新图形。常用的混合结果如下:

  • source-over:默认设置,在现有画布上下文之上绘制新图形。



  • source-in:新图形只在新图形和目标画布重叠的地方绘制。其他的都是透明的。



  • source-out:在不与现有画布内容重叠的地方绘制新图形。


  • destination-over:在现有的画布内容后面绘制新的图形。



  • destination-in:现有的画布内容保持在新图形和现有画布内容重叠的位置。其他的都是透明的。


  • destination-out:现有内容保持在新图形不重叠的地方。


可以看到,我们需要的效果正是 destination-out,画笔划过的地方将原有内容擦除。ctx.globalCompositeOperation = 'destination-out'

填充原图背景

接着我们将原图的背景填充上,先来个灰色背景试试。问问 AI,代码少写点


根据它的代码,我们再加上destination-out的设置,写一个初始化Canvas 的方法 init()。

function init() {  const canvas = myCanvas.value;  const ctx =canvas.getContext('2d');  //填充整个画布为灰色  ctx.fillStyle ='gray';  ctx.fillRect(0, 0, canvas.width, canvas.height);  ctx.globalCompositeOperation = 'destination-out'}
复制代码



有效像素计算

擦除效果有了后,我们还需要考虑在擦除的时候我们并不需要将全部的的遮挡都擦除,因此需要设置当擦除完大部分的灰色前景后就自动将全部的灰色抹除,也就需要判断当前擦除面积是否达到一定百分比

Canvas 中擦除实际是改变已有图像的透明度,Canvas 给我们提供了getImageData()查看当前图像的像素信息,通过在onMouseMove中统计图像当前有效像素比,看是否需要直接清空前景。有了思路后,让 AI 给我们生成代码,我们再来看合不合适。

给我们生成了checkAndClearForeground方法,这里这个方法我们不太清楚,让豆包 MarsCode 给我们注释下。

/** * 检查并清空前景 * @param {number} x - 鼠标在画布上的 x 坐标 * @param {number} y - 鼠标在画布上的 y 坐标 */function checkAndClearForeground(x, y) {  // 获取对当前画布元素的引用  const canvas = myCanvas.value;  // 从画布上获取图像数据  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);  // 初始化有效像素计数为 0  let validPixels = 0;
// 遍历图像数据中的每个像素点 // rgba 显示的模式,所以一个像素表示有 4 个分量,透明度是最后一个分量 for (let i = 0; i < imageData.data.length; i += 4) { // 提取当前像素点的 alpha 通道值 const alpha = imageData.data[i + 3]; // 判断 alpha 通道值是否大于 0,若大于 0 则表示当前像素点有效 if (alpha > 0) { validPixels++; } }
// 计算有效像素比例(有效像素数除以总像素数) const validPixelRatio = validPixels / (imageData.data.length / 4);
// 判断有效像素比例是否小于 50% if (validPixelRatio < 0.5) { // 将填充样式设置为灰色 ctx.fillStyle = 'gray'; // 用灰色填充整个画布 ctx.fillRect(0, 0, canvas.width, canvas.height); }}
复制代码

计算的关键原来在于,在rgba模式下,一个像素有 4 个分量,最后一个分量表示透明度,当透明度的分量大于 0 时,表示这个像素点就是有效的,通过计算有效的像素点就能知道百分比了。

设置背景

有了基本的功能,我们再让 UI 小姐姐将我们的原图转为素描图,来替换之前的灰色前景。

function init() {  const canvas = myCanvas.value;  const ctx =canvas.getContext('2d');  const image = new Image();  image.src = imagePath;  // 图片加载完成后执行  image.onload = function() {    //填充画布为图像    ctx.drawImage(image, 0, 0, canvas.width, canvas.height);    ctx.globalCompositeOperation = 'destination-out'  };}
复制代码



因为 gif 帧率的问题,擦除完毕后,清空页面后感觉像变成了白色的,但其实清空后屏幕就透明了。接着,我们把有色原图设置到 Canvas 的背景上,这样擦除后,显示的就是有色背景了。


canvas {  background-image: url('@/assets/erase/back.jpg');  background-size: cover;  background-position: center;}
复制代码


最终效果:


扩展

基于此原理,我们还可以做擦除原本模糊的图像,露出清晰的原图。或者给前景写上文字后,擦除后露出内容的刮刮卡效果。

在线体验:bulibuli.top:11083/源码地址:(gitee.com)

结尾

有了 MarsCode 帮助我们生成代码后,确实可以减少我们开发的难度和成本,但目前 AI 给我们的代码也不一定是完全正确的😏,对于部分 bug 还是需要我们手动去处理。不过当我们有了思路与具体的实现方法后,让 AI 给我们生成代码后我们就只需要大体看下代码,再改一些参数配置后就能很方便的和我们项目适配,目前 AI 时代已经来临,我们程序员应该尽快找到一个自己喜欢顺手的 AI,辅助开发,加快我们开发效率💪,目前,抖音的豆包MarsCode 也已推出,大家也可以去试试体验一番🎈。


用户头像

还未添加个人签名 2024-08-27 加入

用 AI 激发创造

评论

发布
暂无评论
豆包MarsCode 助力:Canvas 上的素描变色魔法✨_程序员_豆包MarsCode_InfoQ写作社区