JS原生使用Fabric设计简单的图形编辑器
壹、在fabric和konva选型方面
作为canvas对象模型的两款框架来说都很实用,前者比较老牌,而后者符合新时代语法。fabric本身经过长时间的锤炼API很丰富但是其细节功能还是比较简单,反而konva能很好的契合TS代码简介且直观。所以我选择frabic。(笑)
贰、页面容器配置和画布创建
在你的页面直接引入不用导包
<script src="https://cdn.jsdelivr.net/npm/fabric"></script>
另起一个JS文件创建一个GraphicEditor类,哪里需要编辑器直接实例化就行:
class GraphicEditor{
static type = 'GraphicEditor'
constructor(param){
super(param)
}
//初始化
createDom(){
//创建一个div容器用来存放你的canvas,这里我是直接用的封装好的div模块就不赘述了,跳过。
......
//其次创建你的canvas来存放fabric画布
const canv = document.createElement('canvas')
//给canvas一个ID
canv.setAttribute('id','canvas')
....针对canvas设置样式
//将创建好的canvas添加到容器中
容器的DOM.appendChild(canv)
//把容器放到你页面中,可以是任意位置,我这里直接放在body下面
document.body.appendChild('容器DOM')
//接下来就是创建fabric画布
const canvas = new fabric.Canvas('canvas')
//设置画布的大小
canvas.setWidth(900)
canvas.setHeight(600)
....到此画布就创建完成了
}
}
叁、图片导入(支持本地上传,电脑桌面手动复制和直接把图片拖入编辑器)
通过上面的配置,我们基本已经创建好了画布,但是一片空白啥都没有。
接下来我们导入几张图片(在类里面写个导入方法):
// 导入图片
//这个方法没有写上传图片的功能,只是导入到画布,你自己要得到图片路径和名称传进去
exportToCanvas(imgSrc, imgName) {
let imgNames = 'myImgName'
let obj = window.grapDom.canvas.getActiveObject()
//判断画布上是否存在图片
if (obj) {
//选中的照片给清除
window.grapDom.canvas.remove(window.grapDom.canvas.getActiveObject()) } else {
window.grapDom.canvas.clear(); // 清空画布
}
fabric.Image.fromURL(imgSrc, function (oImg) {
oImg[imgNames] = imgName //给图片对象新增一个自定义属性为图片名称
window.grapDom.canvas.add(oImg) //讲图片给添加到画布中
window.grapDom.canvas.centerObject(oImg); //设置图片始终处于画板中心
window.grapDom.canvas.renderAll(); //刷新画板
}, { crossOrigin: 'anonymous' }); //有可能报跨域的错误得添加这句话
}
通过上面的方法就可以在画布上看见你自己上传的图片了。接下来记录下有用的两种导入图片的方法:
1.复制和粘贴图片从其他地方到画布
//首先解释下复制粘贴的实现,现代浏览器内置许多监听事件,比如我们常用的鼠标事件、键盘事件,其中当然也有我们要用到的粘贴事件paste,我们就是靠它实现。
//对你要粘贴的地方进行事件的监听,一般都是画布区域,我这里是画布是整个body.
document.body.addEventListener('paste',(event)=>{
//获取剪切板的内容
let cbData = event.clipboarData
//items存放的就是剪切板的数据做循环找出图片
for(let i=0;i<cbData.items.length;i++){
let cbDataItem = cbData.items[i]
let type = cbDataItem.type //判断文件类型
if(type.indexOf("image") != -1){
//对找到的文件做处理,转换为file对象
let imageData = cbDataItem.getAsFile()
//也可以在这里将文件处理为base64的文件上传到服务器后拿到图片地址,我这里通过URL方法直接转换为预览地址
let imageURL = window.webkitURL.createObjectURL(imageData)
//拿到这个地址后就和上面的导入方法进行联动了
this.exportToCanvas(imageURL,'随便起个名字')
}
}
})
是不是很简单,这样就可以随意粘贴复制图片到画布了。接下来的拖拽图片到画布也是同样的原理。
2.直接从桌面拖入到画布中
拖拽的事件为dragover+drop实现,我们都知道托一张图片到网页会打开另一个网页进行预览效果,而我们则需要阻止预览打开这一行为.再次,具体流程通俗易懂来说分为:
创建一个你要放置图片的放置区域---->拖入到浏览器时触发事件这两步
//首要要有一个放置区域,反正就是我们画布的位置。我这里画布是整个body
// 初始化时将整个页面定义为放置区域
// 实时更新浏览器界面
document.body.addEventListener('dragover', (event) => {
event.stopPropagation(); //阻止浏览器预览图片事件
event.preventDefault();
// 将拖放作为"复制文件"的操作。
event.dataTransfer.dropEffect = 'copy';
})
// 浏览器接触文件时触发
document.body.addEventListener('drop', (event) => {
event.stopPropagation();
event.preventDefault();
const fileList = event.dataTransfer.files; //访问文件列表
//文件拖进来时是一个file的列表,其次就是我们的老套路了,讲file对象进行路径的转换,可以上传到服务器也可以是我这种预览文件
let imageURL = window.webkitURL.createObjectURL(fileList[0])
//调用图片导入方法
this.exportToCanvas(imageURL,'随便起个名字')
})
这样几个图片导入到画布的方法就写完了,我个人觉得复制粘贴方法挺实用的,但还有很多东西没有考虑到,请大家明鉴。
肆、图片滤镜、添加文字、添加各种各样的图形
这些功能都在官网上面有对应的例子,但是官网文档读起来比较那个(你懂得),在此记录一下。
1.一些内置滤镜
- BaseFilter 基本过滤器
- Blur 模糊
- Brightness 亮度
- ColorMatrix 颜色矩阵
- Contrast 对比
- Convolute 卷积
- Gamma 伽玛
- Grayscale 灰度
- HueRotation 色调旋转
- Invert 倒置
- Noise 噪音
- Pixelate 像素化
- RemoveColor 移除颜色
- Resize 调整大小
- Saturation 饱和
- Sepia 色偏
怎么用?
// 模糊
setVague() {
//选中你的图片
let obj = window.grapDom.canvas.getActiveObject()
if (!obj) return //没有就返回
//设置图片的模糊度
let filter = new fabric.Image.filters.Blur({
blur: 0.5
})
//将滤镜添加到图片对象中
obj.filters.push(filter)
obj.applyFilters() //应用滤镜
//刷新下画布
canvas.renderAll()
}
想要清除滤镜效果的可以直接将图片对象中的filters设置为[]
2.添加文字
创建文本框对象
let text = new fabric.Textbox('双击编辑文本', {
width: 100
})
//添加到画布中
canvas.add(text)
3.画各种图形
// 画各种各样的图形
/**
*
* @param {String} type 什么类型的图形
* @param {Object} canvas 当前图形所在的画布DOM
*/
drawSquare(type, canvas) {
// canvas.style.cursor = 'crosshair'
// 清空图形之前的事件
canvas.__eventListeners["mouse:down"] ? canvas.__eventListeners["mouse:down"].splice(0) : null
canvas.__eventListeners["mouse:move"].splice(0)
canvas.__eventListeners["mouse:up"].splice(1)
// 画图形时禁用画布选中和移动
canvas.selectionColor = "transparent"
canvas.selectionBorderColor = "transparent"
canvas.skipTargetFind = true
// 设置图形的起点和终点
let downPoint = null
let upPoint = null
let currentSquare = null
// 监听当前画布事件对象
canvas.on('mouse:down', function (e) {
// 将鼠标按下的坐标点给起点
downPoint = e.absolutePointer
// 检测输入的图形类型
switch (type) {
case 'line':
currentSquare = new fabric.Line([
downPoint.x, downPoint.y,
downPoint.x, downPoint.y
], { stroke: 'rgba(0, 0, 0, 0.2)' })
break;
case 'circle':
currentSquare = new fabric.Circle({
top: downPoint.y,
left: downPoint.x,
radius: 0,
fill: 'transparent',
stroke: 'rgba(0, 0, 0, 0.2)'
})
break;
default:
break;
}
// 将临时轨迹添加到画布
canvas.add(currentSquare)
})
canvas.on('mouse:move', function (e) {
if (currentSquare) {
const currentPoint = e.absolutePointer
switch (type) {
case 'line':
currentSquare.set('x2', currentPoint.x)
currentSquare.set('y2', currentPoint.y)
break;
case 'circle':
let radius = Math.min(Math.abs(downPoint.x - currentPoint.x), Math.abs(downPoint.y - currentPoint.y)) / 2
let top = currentPoint.y > downPoint.y ? downPoint.y : downPoint.y - radius * 2
let left = currentPoint.x > downPoint.x ? downPoint.x : downPoint.x - radius * 2
currentSquare.set('radius', radius)
currentSquare.set('top', top)
currentSquare.set('left', left)
break;
default:
break;
}
canvas.requestRenderAll()
}
})
canvas.on('mouse:up', function (e) {
upPoint = e.absolutePointer
if (JSON.stringify(downPoint) === JSON.stringify(upPoint)) {
window.grapDom.canvas.remove(currentSquare)
} else if (currentSquare) {
currentSquare.set('stroke', '#000')
}
currentSquare = null
})
}
其他的什么裁剪图片、旋转放大缩小都比较简单就不详细说了,官网都有实际例子.
到此这个简单的图形编辑器就完成了,正好项目中要用就在此记录了一下。