←首页

之前因为工作需要,用前端代码做了一个类似脸萌的前端页面,打包成小程序给 app 端调用。做之前顾虑这顾虑那,实现出来后,感觉还可以,写篇文章记录下~写文章的目的主要还是记录下思路和一些知识点。记忆能力差的人还是多记录点东西好一些。

faceQ

利益关系,这里这讨论技术上的东西,不放任何与公司产品相关的代码,资源。

绘制原理

效果
实现的效果是点击组件分类下的组件后,展示区域绘制相应的元素,比如头发,脸型,眉毛,鼻子,嘴巴,等等元素。其中部分元素具有颜色属性,点击颜色块可以更改已在展示区显示元素的填充颜色。

颜色替换需求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 伪代码
loadImage(src)
.then((image) => {
if (hasColor) {
replaceColor()
// 创建一个空 Canvas 元素
offscreenCanvas.drawImage(image)
return offscreenCanvas
} else {
return image
}
})
.then((image) => {
canvas.drawImage()
})

Canvas 绘制图片需要等图片加载完毕之后才能使用(通常是 new Image 对象,并监听 onload 事件),替换颜色一开始的做法是使用 Canvas getImageData 拿到所有像素值信息,遍历所有像素点,找到目标灰度值进行替换。

这种替换方式会有很大的问题,当你放大一张非矢量图到一定程度,就会发现,图片边缘并不是纯色的。通过像素值去更换出来的效果就是边缘部分会有“锯齿”。
边缘颜色点不纯
避开这个问题的解决方式可以使用 svg 格式的展示图,并通过 get 请求加载源码,替换里面所有的目标颜色值。

1
2
3
4
5
return ajax.get(svgUrl)
.then((xml) => {
var reg = new RegExp(templateColor,"ig")
return xml.replace(reg, targetColor)
})

展示区显示的内容是很多图层的叠加,提前约定好图层的大小,所有图层都一样大,保证组件会绘制在特定的位置上。所有图层都应该有一个叠加顺序,通过这个叠加顺序做替换更新,比如叠加顺序为 10 的组件与同样为 10 的组件互斥,新的图层元素替换旧的,就可以简单地实现组件更换效果。

图层

但是有些组件和图层不是一对一的关系,比如女生头发,就是多个图层的例子,写绑定关系的时候需要注意。

前端生成图片

Canvas 可以通过 toDataURL 将画布内的内容转化为 Base64 输出,但是 base64 终究还是需要转化为 File 对象进行上传保存,可以使用 toBlob 方法得到一个 Blob 对象,然后通过 FormData 进行上传。toBlob 方法兼容性较差,可以通过 base 64 转成Blob 。stackoverflow

1
2
3
let blob = b64toBlob(b64Data, contentType)
let fromData = new FormData()
formData.append('file', blob)

缓存

这个页面里面会有大量的图片,缩略图,展示图,分类图标等等,如果没有做缓存意味着,每次用户进到页面都需要去拉去好几 M 的图片,要了命!

好在使用 WebViewJavascriptBridge 之类的轮子,和客户端做数据传递非常方便。把下载图片的任务交给客户端就好了,下载完毕之后给前端返回一个相对地址。这样下次用户进来,所有资源都是在本地,离线访问也完全没压力~

图片缓存

JS 如何和 Native 交互可以看看之前写的一篇文章
How JS Bridge Works?

工作量

说到底,这个页面还是用 html, css, js 等技术实现出来的。记得有谁说过,无论你如何优化代码,只要你尝试用前端技术去实现 Native 效果,做出来的东西都是垃圾。我的理解是页面上的效果终究无法媲美 Native ,但如果使用前端技术来实现能缩短工期,并且实现效果不是难以被接受的话,有什么关系呢。页面本身难度不大,但是每个人的实现方式都有所不同,如果分开让 iOS,Android 各实现一套,debug,后期维护,等等都会浪费不少时间,线上版本出问题无法热更新,前端能做就用前端做呗,毕竟这只是次要功能。

总结

  • 图片需要等加载完毕之后才能绘制在 Canvas 上
  • 非矢量图,图片边缘会被特殊处理,呈现为“过度颜色”
  • 访问图片内容需要资源允许跨域,比如通过 Canvas 获取 ImageData,通常是在 CDN 上配置响应头,Access-Control-Allow-Origin: *
  • 由于 iOS 沙盒机制,前端页面里面只能访问到同个目录下相对路径的资源

如需转载,请注明出处: http://w3ctrain.com/2017/05/01/web-version-face-q/