前端拼图分割实现

第一步

找到一张模板图,如下:

这是一张通过RGBA的B通道区分的模板图,每个碎片的B通道,对应序号分别是0,10,20,30,40,......

即序号的10倍等于B通道的值。

第二步

找到一张需要切分的图,如下:

确保两张图的尺寸一致,不一致会导致压缩变形既。

第三步

上代码,微信小程序: ts代码:

typescript 复制代码
// components/cut-img/index.ts
Component({

  /**
   * 组件的属性列表
   */
  properties: {
    temp: {
      type: String,
      value: getApp().imgPrefix('/model/test/temp5-3.png')
    },
    img: {
      type: String,
      value: getApp().imgPrefix('/model/test/test.jpg')
    },
    size: {
      type: Number,
      value: 15
    },
  },

  /**
   * 组件的初始数据
   */
  data: {

  },

  lifetimes: {
    ready() {
      // 通过 SelectorQuery 获取 Canvas 节点
      setTimeout(() => {
        const query = wx.createSelectorQuery().in(this)
        query.select('#canvas')
          .fields({
            node: true,
            size: true,
          })
          .exec(this.init.bind(this))
      }, 1000);
    },
  },

  /**
   * 组件的方法列表
   */
  methods: {
    /**
     * 初始化
     */
    init(res: any) {
      const canvas = this.canvas = res[0]?.node
      if (!canvas) {
        return false
      }
      this.ctx = canvas.getContext('2d')

      this.setTemp()

      return false
    },
    /**
     * 设置temp 图片上图
     */
    setTemp() {
      const ctx = this.ctx
      const canvas = this.canvas

      const img = canvas.createImage()
      ctx.clearRect(0, 0, canvas.width, canvas.height)
      img.onload = () => {
        this.width = canvas.width = img.width
        this.height = canvas.height = img.height
        ctx.drawImage(img, 0, 0, canvas.width, canvas.height)

        this.tempData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        this.setImg()
      }
      img.src = this.data.temp
    },
    /**
     * 设置img 图片上图
     */
    setImg() {
      const ctx = this.ctx
      const canvas = this.canvas
      const img = canvas.createImage()
      ctx.clearRect(0, 0, canvas.width, canvas.height)
      img.onload = () => {
        ctx.drawImage(img, 0, 0, canvas.width, canvas.height)

        this.imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  
        const size = this.data.size
        const list: any[] = []
        for(let i = 0; i < size; i++) {
          this.cut(i, list)
        }
        console.log('>>> list', list)
        // this.triggerEvent('success', list)
      }
      img.src = this.data.img
    },
    /**
     * 裁剪
     * @param index 序号
     * @param list 存储列表
     */
    cut(index: number, list: any[]) {
      const ctx = this.ctx
      const canvas = this.canvas
      const newData = ctx.createImageData(this.width, this.height);
      for (let i = 0; i < newData.data.length; i += 4) {
        const temp = this.tempData.data.slice(i, i + 4)
        if (Math.abs(temp[2] - index * 10) < 2 && temp[3] > 10) {
          newData.data[i + 0] = this.imgData.data[i + 0]
          newData.data[i + 1] = this.imgData.data[i + 1]
          newData.data[i + 2] = this.imgData.data[i + 2]
          newData.data[i + 3] = this.imgData.data[i + 3]
        } else {
          newData.data[i + 0] = 0
          newData.data[i + 1] = 0
          newData.data[i + 2] = 0
          newData.data[i + 3] = 0
        }
      }
      // ctx.putImageData(newData, 0, 0)

      let minX = newData.width;
      let minY = newData.height;
      let maxX = -1;
      let maxY = -1;

      // 遍历所有像素,查找非透明像素的位置
      for (let y = 0; y < newData.height; y++) {
        for (let x = 0; x < newData.width; x++) {
          const pixelIndex = (y * newData.width + x) * 4;
          if (newData.data[pixelIndex + 3] > 0) { // Alpha通道大于0表示非透明
            minX = Math.min(minX, x);
            maxX = Math.max(maxX, x);
            minY = Math.min(minY, y);
            maxY = Math.max(maxY, y);
          }
        }
      }
      const width = maxX - minX + 1
      const height = maxY - minY + 1
      canvas.width = width
      canvas.height = height
      // 创建新的图像数据对象用于裁剪后的图像
      const croppedImage = ctx.createImageData(width, height);

      // 复制非透明像素到新的图像数据中
      for (let y = minY; y <= maxY; y++) {
        for (let x = minX; x <= maxX; x++) {
          const srcIndex = ((y * newData.width) + x) * 4;
          const destIndex = ((y - minY) * (maxX - minX + 1) + (x - minX)) * 4;
          croppedImage.data.set(newData.data.subarray(srcIndex, srcIndex + 4), destIndex);
        }
      }

      // 清除画布并绘制裁剪后的图像
      ctx.clearRect(0, 0, width, height);
      ctx.putImageData(croppedImage, 0, 0);
      const dataUrl = canvas.toDataURL('image/png');
      list.push({
        x: minX,
        y: minY,
        width,
        height,
        dataUrl
      })
    },
  }
})

wxml代码:

wxml 复制代码
<!--components/cut-img/index.wxml-->
<view style="display: none;">
  <canvas type="2d" id="canvas"></canvas>
</view>

步骤总结

1、获取canvas

2、先渲染模板,拿到模板图的imgdata

3、再渲染需要切换的目标图,拿到目标图的imgdata

4、根据切分的数量,循环切分

4.1、创建一个空的imgdata,根据序号对应的B通道,把对应的切片数据存入imgdata

4.2、计算非空白区域的位置和尺寸,裁剪图片数据

4.3、裁剪后的数据放回canvas,导出dataURL

4.4、保存对应的x、y、width、height、dataURL

5、返回list

相关推荐
Ares码农人生12 分钟前
React 前端框架简介
前端·react.js·前端框架
小汤猿人类13 分钟前
nacos-gateway动态路由
java·前端·gateway
GISer_Jing14 分钟前
前端经典面试合集(二)——Vue/React/Node/工程化工具/计算机网络
前端·vue.js·react.js·node.js
GesLuck1 小时前
C#控件开发4—仪表盘
前端·经验分享·c#
小林爱1 小时前
【Compose multiplatform教程14】【组件】LazyColumn组件
android·前端·kotlin·android studio·框架·多平台
过往记忆7 小时前
告别 Shuffle!深入探索 Spark 的 SPJ 技术
大数据·前端·分布式·ajax·spark
高兴蛋炒饭8 小时前
RouYi-Vue框架,环境搭建以及使用
前端·javascript·vue.js
m0_748240449 小时前
《通义千问AI落地—中》:前端实现
前端·人工智能·状态模式
ᥬ 小月亮9 小时前
Vue中接入萤石等直播视频(更新中ing)
前端·javascript·vue.js
神雕杨10 小时前
node js 过滤空白行
开发语言·前端·javascript