基本需求
在一张图片上识别出特定的物品或者人并且标注出来,识别的坐标是由服务端接口返回的,主要的难点就是在前端图片上绘制出相应的标注框,并且在底部展示一个列表,可以在图片框选或者底部轮播点击实现勾选/取消勾选的切换,之后进入下一个页面进行详情的展示。
技术
vue + fabric.js
最终效果
开发过程遇到的问题
1、图片屏幕适配问题
由于图片是由用户拍照或者选择本地图片上传,就存在一个问题,长宽比不一致,且不同的手机分辨率不同,就不能简单的按照宽或者高来显示,需要比对图片的分辨率及canvas实际展示区域的分辨率,尽量的保持原图的比例并且在手机端能完整展示。
2、在图片上画出标识框
识别的数据是由服务端接口返回的,这边需要注意的是,需要结合手机端的分辨率及canvas的实际宽高计算后,再画出来的位置才比较准确。
3、生成底部小图
在原图上裁剪是比较准确的,不要在自己渲染的canvas上。由于这个页面是需要嵌入原生app的web页面,还遇到一个比较坑的问题,就是安卓手机会出现 toDataURL 方法不兼容的问题,后面是通过引入一个todataurl-png-js 解决了。
cutImage(img, marks) {
let h = img.height,
w = img.width;
//创建Canvas
let oCanvas = this.createCanvas(w, h);
//拿到canvas的ctx
let ctx = oCanvas.getContext('2d');
//先drawImage,然后getImageData
ctx.drawImage(img, 0, 0, w, h);
let imageData = ctx.getImageData(0, 0, w, h);
let arr = []
marks.forEach(item => {
let rect = item.rect
let x = rect.x * w
let y = rect.y * h
let height = rect.height * h
let width = rect.width * w
arr.push(
this.resolveCut(imageData, x, y, width, height)
)
})
return arr
},
resolveCut(imgData, x, y, w, h) {
var cvs = this.createCanvas(w, h);
var ctx = cvs.getContext("2d");
ctx.putImageData(imgData, -x, -y);
let imgDom = document.createElement('img');
imgDom.setAttribute('crossOrigin', 'anonymous') // 允许跨域访问
imgDom.setAttribute('src', cvs.toDataURL('image/png')); // base 64
return cvs.toDataURL('image/png')
},
4、切换选中状态
这个比较简单,fabricjs有提供对应api。修改完属性后,需要调用 this.fabricObj.renderAll() 方法重新渲染。
if (status) {
objects[i].set('strokeDashArray', [10, 0])
} else {
objects[i].set('strokeDashArray', [10, 10])
}