第一步
找到一张模板图,如下:
这是一张通过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