-
fabric+customiseControls简介
fabric是一个canvas库,其核心就是将canvasAPI转换为对象模型,让我们更加便捷的使用。
customiseControls则是基于fabric的插件,可为新建的fabric对象设置角标,并为其提供一系列事件:drag,
scale,rotate,remove,或者自定义事件。
fabric:github.com/fabricjs/fa…
customiseControls:github.com/pixolith/fa…
-
效果说明(模糊的图片是故意的)
- 用户涂鸦环节:

涂鸦的构成 = 1.城市背景图 + 贴纸图片素材 + 文字图片素材
1点击缩略图切换,切换时2,3不变化。 2,3是可选中后可拖拽,拖拽时不影响其他图片素材,选中之后出现四个角的角标,角标的事件为放大,缩小,旋转,移除。
- 生成环节

根据用户涂鸦的结果,生成一张带有其他信息的海报(长按保存到手机相册),比如: 用户地理位置信息,当前参与人数,二维码,昵称,头像等等
-
开撸
开撸之前还是梳理一下重点和优化点:
A.涂鸦画板的交互(重点)
B.精准生成海报(重点)和海报的清晰度(优化点)
A部分: 用户选择对应背景或者涂鸦素材后有正确相应,设置边界值
1.初始化fabric插件
// 设置部分角标不可见
fabric.Object.prototype.setControlsVisibility({
ml: false,
mr: false,
mtr: false,
mt: false,
mb: false
});
// 角标事件设置
fabric.Canvas.prototype.customiseControls({
br: {
action: "rotate"
},
bl: {
action: 'scale'
},
tl: {
action: function action(e, target) {
var canvas = target.canvas;
canvas.remove(target).renderAll();
}
},
tr: {
action: 'scale'
}
});
// 禁用选框
fabric.Object.prototype.transparentCorners = false;
// 角标基础设置
fabric.Object.prototype.customiseCornerIcons({
settings: {
borderColor: '#fff',
cornerSize: 20,
cornerBackgroundColor: 'transparent',
cornerPadding: 5
},
br: {
action: "rotate",
icon: './imgs/icons/rotate.svg'
},
bl: {
action: 'scale',
icon: './imgs/icons/resize.svg'
},
tl: {
icon: './imgs/icons/remove.svg',
action: function action(e, target) {
var canvas = target.canvas;
canvas.remove(target).renderAll();
}
},
tr: {
action: 'scale',
icon: './imgs/icons/resize.svg'
}
}, function () {
board.renderAll();
});
var ww = $(window).width();
var wh = $(window).height();
var board = new fabric.Canvas('drawBoard', {
backgroundColor: 'transparent',
width: (ww * 0.92
height: (wh * 0.67
});
2.添加用户点击交互(click -> 新增对应素材至画板 或者切换城市背景)
var fabricItemList = [];
// 设置图片背景
function setImagebg(imgSrc) {
fabric.Image.fromURL(imgSrc, function (img) {
img.set({
scaleX: board.width / img.width,
scaleY: board.height / img.height
});
board.setBackgroundImage(img, board.renderAll.bind(board));
board.requestRenderAll();
}, {
crossOrigin: 'anonymous'
});
}
// 设置自定义图片
function setPicture(obj) {
obj.customiseCornerIcons(function () {
board.renderAll();
});
fabricItemList.push(obj);
board.add(obj);
board.setActiveObject(obj);
}
// 生成文字对象
function addText(ctext) {
var color = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "#fff";
var textObj = new fabric.Text(ctext, {
fontFamily: "PingFang SC, Verdana, Helvetica Neue, Microsoft Yahei, Hiragino Sans GB, Microsoft Sans Serif, WenQuanYi Micro Hei, sans-serif",
fontSize: 30,
top: 150,
left: 150,
originX: 'center',
originY: 'center',
fill: color
});
setPicture(textObj);
}
// 生成图片对象
function featImg(imgSrc, scale) {
fabric.Image.fromURL(imgSrc, function (img) {
img.set({
scaleX: scale,
scaleY: scale,
angle: 0,
left: Math.random() < 0.5 ? Math.random() * 50 + 110 : -Math.random() * 50 + 110,
top: Math.random() < 0.5 ? Math.random() * 50 + 120 : -Math.random() * 50 + 120,
hasControls: true,
borderColor: '#fff'
});
setPicture(img);
}, {
crossOrigin: 'anonymous'
});
}
3.生成画板图
let drawImg = board.toDataURL();
// 生成后复原fabric对象
fabricItemList.forEach(function (item) {
return board.remove(item);
});
fabricItemList = [];
B部分: 如何根据设计稿精准绘制海报(重点)?
1.使用现有成熟的方案: canvas2Image + html2canvas(高质量前端快照方案:来自页面的「自拍」)
详见网易云音乐团队的文章:juejin.cn/post/684490…
2.自己瞎折腾
最终选择:**自己瞎折腾 **
心路历程:
????作者是不是脑子有病(小声BB)...
为什么我要做费力不讨好的事情? 现成的库他不香吗?
原因在于ios手机在授权后的:微信浏览器的白条问题
白条会使得整个可视区的DOM被向上顶一部分距离...
整个网上的方案都在现有资源下都不能好好解决,developers.weixin.qq.com/community/d…
白条问题使得在使用前面的「自拍」方案时,生成的海报会错位...结果惨不忍睹!
html2canvas是根据遍历DOM样式信息来生成的,而且在遍历过程中,它会重新加载所有资源(在network自己看),如果遇到质量较高的图片,会出现绘制空白的可能。
自己瞎折腾的结果:
根据canvas的drawimage写了个生成图的js,写的垃圾,大佬勿喷... github.com/tsunamiGG/u…
大概思路:
canvas图层堆叠是有顺序的,所以按照图层顺序进行传参。
为保证生成资源的完整性,必须先确保onload之后进入合成过程。
以左上角为原点,然后使用性能不错的drawimage(x,y,w,h)离屏渲染生成,从而避开了生成图被白条影响。
b部分:海报的清晰度(优化点)
可以看到上面的效果图清晰度是非常不理想的,需要优化。
为什么我们的canvas绘制的东西看上去总是比用DOM合成的东西糊一些?
真实物理设备的像素和css的像素是有差距的,window.devicePixelRatio的值就反应了其比例。它告诉浏览器应使用多少屏幕实际像素来绘制单个CSS像素。
同一个东西,你使用一个css像素来表示,假设在不同手机上分别浏览器本需要4个物理像素或8个物理像素。而canvas只用一个位图像素来填充4个物理像素或8个物理像素,不糊才怪呢..
如果想深入了解,参考:juejin.cn/post/684490…
1.js设置放大devicePixelRatio倍数宽高的canvas,css设置缩小devicePixelRatio倍数的canvas。扩大实际像素数量,视觉上保持跟原有尺寸一致,在API生成图片后会清晰很多。
2.关闭canvas抗锯齿
const canvas = document.createElement("canvas");
const scale = window.devicePixelRatio;
canvas.width = width * scale;
canvas.height = height * scale;
const context = canvas.getContext("2d").scale(scale, scale)
// 关闭抗锯齿
context.mozImageSmoothingEnabled = false;
context.webkitImageSmoothingEnabled = false;
context.msImageSmoothingEnabled = false;
context.imageSmoothingEnabled = false;
优化后效果如下,手机保存图实际质量比这个高:

4.总结
对十分陌生的canvas稍微有了一些认识,用canvas做动画的库非常多,但有个致命的缺点就是没办法进行调试...
其中还有很多微信浏览器的很多玄学问题就不展开了,白条问题确实是很头疼的。fabric可以整出很多惊艳的效果,但其乱七八糟的文档和糟糕的示例会带来很多麻烦,影响发挥...