canves 绘图的运用是前端必知必会的基础,但是canves 本身并不好用,而fabric.js是基于canves的一个绘图框架库,用起来可比canves友好多了!接下来带你看看案例和初识fabric.js~
主要实现H5 移动端简单的图片的可视化编辑、预览、下载等功能。
目前仅实现以上的功能,后续在继续完善更新~~
editor.vue:
<template>
<el-container >
<el-header >
<el-menu
style="background: #6A60E3"
mode="horizontal"
>
<div style="width: 400px; float: left;height:70px;color: #fff; background: #5951B6;line-height: 70px">
<h1 style="text-align: center">{{ templatesTitle }}</h1>
</div>
<el-form inline="inline" style="float: right; width: 1000px" >
<el-button
class="el-icon-plus btn_style" style="width: 140px"
@click="addTextHandle('textbox', 'add')"
> 添加文本框
</el-button>
<el-button class="el-icon-plus btn_style" @click="imgDraw" >
<input type="file" accept="image/*"
style="display:none"
id="uploadfile"
@change="uploadFile" />
上传图片
</el-button>
<el-button class="el-icon-edit btn_style"
@click="initD"
> 绘 制 </el-button>
<el-button
class="btn-delete btn_style el-icon-delete"
@click="deleteText"
> 删除控件
</el-button>
</el-form>
</el-menu>
</el-header>
<el-container>
<el-aside :width="isCollapsed ? '30px' : '300px'">
<div class="text-edit">
<div
@click="closeSetting"
class="title-setting"
:tipTitle="isCollapsed ? '展开设置' : '收起设置'"
>
<span class="text-setting">{{ title }}</span>
<i :class="isCollapsed ? 'el-icon-s-unfold' : 'el-icon-close'"/>
</div>
<el-form ref="form" >
<el-form-item label="文本内容:">
<div id="textBox" style="width: 100% ">
<el-input type="textarea"
@input="changeText"
@focus="changeText"
id="in" ref="in"
v-model="msg" ></el-input>
</div>
</el-form-item>
<el-form-item label="字体:" label-width="82px">
<el-select v-model="fontFamilies.value"
placeholder="请选择字体"
@change="changeFontFamily">
<el-option
v-for="item in fontFamilies"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="字体颜色:" >
<el-select v-model="fontColor.value"
placeholder="请选择字体颜色"
@change="changeFontColor"
>
<el-option
v-for="item in fontColor"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="字体粗细:" >
<el-select v-model="fontWeight.value"
placeholder="请选择字体粗细"
@change="changefontWeight"
>
<el-option
v-for="item in fontWeight"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="字体风格:" >
<el-select v-model="fontStyle.value"
placeholder="请选择字体风格"
@change="changeFontStyle">
<el-option
v-for="item in fontStyle"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="字体大小:" >
<el-select v-model="fontSizes.value"
placeholder="选择字体大小"
@change="changeFontSize">
<el-option
v-for="item in fontSizes"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="对齐方式:" >
<el-select v-model="textAlign.value"
@change="changeTextAlign"
placeholder="选择对齐方式">
<el-option
v-for="item in textAlign"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="背景颜色:" >
<el-color-picker
class="color-picker"
v-model="bgcolor" circle
size="small"
show-alpha
:predefine="predefineColors"
@change="changeBgColor"
color-format="hex"
></el-color-picker>
</el-form-item>
<el-form-item>
<!-- <ul class="template-edit" id="template-edit" @click="chooseTemplate">
<li><img src="@/assets/logo.png" alt="" id="bg1" style="width: 100px;height: 50px" /></li>
<li><img src="@/assets/logo.png" alt="" id="bg2" style="width: 100px;height: 50px" /></li>
<li><img src="@/assets/logo.png" alt="" id="bg3" style="width: 100px;height: 50px" /></li>
</ul>-->
</el-form-item>
</el-form>
</div>
</el-aside>
<el-main style="height: 1040px" >
<div class="content-show">
<div class="canvas">
<canvas ref="canvas" id="editorCanvas"></canvas>
</div>
</div>
<el-form class="handleSave">
<el-button type="primary" class="btn-save" @click="downLoadImage1">预览图片</el-button>
<el-button type="primary" class="btn-download" @click="downLoad">下载图片</el-button>
<el-form-item inline="inline" class="btn-zoom" style="margin: 10px 0px">
<i class="el-icon-caret-left" @click="zoomIt(0.8)"></i>
<span> {{ zoomCounter }} % </span>
<i class="el-icon-caret-right" @click="zoomIt(1.2)"></i>
</el-form-item>
<el-button type="danger" class="btn-reset" @click="resetCanvas">重 置</el-button>
</el-form>
</el-main>
<el-aside :width="isCollapsed ? '30px' : '375px'">
<div
@click="closeSetting"
class="title-setting"
:tipTitle="isCollapsed ? '展开设置' : '收起设置'"
>
<span class="text-setting">{{ showTitle }}</span>
<i :class="isCollapsed ? 'el-icon-s-unfold' : 'el-icon-close'"/>
</div>
<img :src="imageBase64" alt="">
</el-aside>
</el-container>
</el-container>
</template>
<script>
import {
fontFamilies,
fontSizes,
fontColor,
fontStyle,
fontWeight,
backgroundColor,
textAlign,
} from "@/utils/fontData";
import { fabric } from "fabric";
let editorCanvas = "";
fabric.Object.prototype.set({
cornerStrokeColor: "#66b0ef",
cornerColor: "#60abec",
cornerStyle: "rectangele",
cornerSize: 8,
borderScaleFactor: 2,
transparentCorners: false,
borderColor: "#61abe8",
});
export default {
name: "Editor",
data() {
return {
isHide: true,
checkAll: false,
isChecked:false,
isIndeterminate: true,
fontFamilies,
fontSizes,
fontColor,
fontStyle,
fontWeight,
textAlign,
backgroundColor,
predefineColors: ["#ffffff", "#FF0000", "#000000","#FFF800","#00FF0A","#FD00FF","#0095FF"],
checked: true,
// 模板图片保存数组
templateImgs: [],
zoomCounter: 100,
tipTitle: "",
imageBase64: "",
isCollapsed: false,
backColor: "#eec5c5",
backGroundStatus: false,
bgcolor: "#ff0000",
itemWidth: "230px",
done: false,
// 文本控件属性
textStyleData: {
type: "editText",
text: "双击编辑文字",
top: 50,
left: 50,
width: 100,
opacity: 1,
stroke: "#ffffff",
strokeWidth: 0,
textAlign: "left",
lineHeight: 1,
charSpacing: 1,
fontFamily: "hyzktjjkt",
fontSize: 40,
fontWeight: "normal",
fontStyle: "normal",
fill: "#000000",
textBackgroundColor: "rgba(0,0,0,0)",
selectable: true,
},
isOpen: false,
isMove: false,
src: "",
msg: "",
canvas: null,
templatesTitle: "H5 可视化编辑",
title: "文本设置",
showTitle:"100% 预览",
templateData: {},
mouseFrom: {},
mouseTo: {},
moveCount: 1,
};
},
mounted() {
this.initeditorCanvas();
},
methods: {
downLoadImage1() {
this.done = true
let base64URl = editorCanvas.toDataURL({
formart: 'png',
multiplier: 1
})
this.imageBase64 = base64URl
this.done = false
},
saveTemplates() {
console.log("你点击了模板保存");
let base64URl = editorCanvas.toDataURL({
formart: "jpg",
multiplier: 1,
});
},
addTemplates() {
console.log("添加模板");
},
initD() {
// 监听鼠标按下
const obj = editorCanvas.getActiveObject();
editorCanvas.on("mouse:down", (options) => {
// 记录当前鼠标的起点坐标
if(!obj) {
this.mouseFrom.x = options.e.clientX - editorCanvas._offset.left;
this.mouseFrom.y = options.e.clientY - editorCanvas._offset.top;
}
});
// 监听鼠标移动
editorCanvas.on("mouse:move", (options) => {
// 记录当前鼠标移动终点坐标
if (!obj) {
this.mouseTo.x = options.e.clientX - editorCanvas._offset.left
this.mouseTo.y = options.e.clientY - editorCanvas._offset.top
this.drawRect();
}
});
editorCanvas.on("mouse:up", (options) => {
if (!obj) {
this.mouseFrom.x = options.e.clientX - editorCanvas._offset.left;
this.mouseFrom.y = options.e.clientY - editorCanvas._offset.top;
this.doDrawing = false;
this.canvasObject = null;
this.mouseFrom = {};
this.mouseTo = {}
}
});
editorCanvas.on("selection:created",(option) => {
if (option) {
this.doDrawing = false;
}
})
},
getTransformedPosX(x) {
let zoom = Number(editorCanvas.getZoom())
return (x - editorCanvas.viewportTransform[4]) / zoom;
},
getTransformedPosY(y) {
let zoom = Number(editorCanvas.getZoom())
return (y - editorCanvas.viewportTransform[5]) / zoom;
},
// 绘制矩形
drawRect() {
// 计算矩形长宽
let left = this.getTransformedPosX(this.mouseFrom.x);
let top = this.getTransformedPosY(this.mouseFrom.y);
let width = this.mouseTo.x - this.mouseFrom.x;
let height = this.mouseTo.y - this.mouseFrom.y;
const canvasObject = new fabric.Rect({
left: left,
top: top,
width: width,
height: height,
fill: "#d70202",
strokeWidth: 2,
});
editorCanvas.add(canvasObject);
},
// 初始化模板编辑画布
initeditorCanvas() {
editorCanvas = new fabric.Canvas("editorCanvas", {
devicePixelRatio: true,
width: "375",
height: "667",
originX: "center",
originY: "center",
backgroundColor: "#ffffff",
transparentCorners: false,
});
editorCanvas.preserveObjectStacking = true;
},
// 收起文本设置
closeSetting() {
this.isCollapsed = !this.isCollapsed;
},
// 载入图片
imgDraw() {
document.getElementById("uploadfile").click();
},
uploadFile(e) {
editorCanvas.isDrawingMode = false;
let file = e.target.files[0];
let reader = new FileReader();
reader.onload = (e) => {
let data = e.target.result;
fabric.Image.fromURL(data, (img) => {
editorCanvas.add(img).renderAll();
});
};
reader.readAsDataURL(file);
e.target.value = "";
},
// 下载图片
downLoad() {
this.done = true;
const dataURL = editorCanvas.toDataURL({
width: editorCanvas.width,
height: editorCanvas.height,
left: 0,
top: 0,
format: "png",
});
const link = document.createElement("a");
link.download = "图片.png";
link.href = dataURL;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
},
// 清空画布
resetCanvas() {
let children = editorCanvas.getObjects();
if (children.length > 0) {
editorCanvas.remove(...children);
}
editorCanvas.setBackgroundColor("#fff");
},
// 缩放
zoomIt(factor) {
let zoomCounter = this.zoomCounter;
let cWidth = editorCanvas.width;
let cHeight = editorCanvas.height;
/* 同步缩小 */
if (factor < 1 && zoomCounter > 0) {
this.zoomCounter -= 20;
editorCanvas.setWidth(cWidth * factor);
editorCanvas.setHeight(cHeight * factor);
const objects = editorCanvas.getObjects();
for (let i in objects) {
let scaleX = objects[i].scaleX;
let scaleY = objects[i].scaleY;
let left = objects[i].left;
let top = objects[i].top;
let tempScaleX = scaleX * factor;
let tempScaleY = scaleY * factor;
let tempLeft = left * factor;
let tempTop = top * factor;
objects[i].scaleX = tempScaleX;
objects[i].scaleY = tempScaleY;
objects[i].left = tempLeft;
objects[i].top = tempTop;
objects[i].setCoords();
let zoomPoint = new fabric.Point(
editorCanvas.width / 2,
editorCanvas.height / 2
);
editorCanvas.zoomToPoint(zoomPoint, factor);
editorCanvas.renderAll();
editorCanvas.calcOffset();
}
}
/* 同步放大 */
if (factor > 1 && zoomCounter < 100) {
this.zoomCounter += 20;
editorCanvas.setWidth(cWidth * factor);
editorCanvas.setHeight(cHeight * factor);
const objects = editorCanvas.getObjects();
for (let i in objects) {
let scaleX = objects[i].scaleX;
let scaleY = objects[i].scaleY;
let left = objects[i].left;
let top = objects[i].top;
let tempScaleX = scaleX * factor;
let tempScaleY = scaleY * factor;
let tempLeft = left * factor;
let tempTop = top * factor;
objects[i].scaleX = tempScaleX;
objects[i].scaleY = tempScaleY;
objects[i].left = tempLeft;
objects[i].top = tempTop;
objects[i].setCoords();
}
let zoomPoint = new fabric.Point(
editorCanvas.width / 2,
editorCanvas.height / 2
);
editorCanvas.zoomToPoint(zoomPoint, factor);
editorCanvas.renderAll();
editorCanvas.calcOffset();
} else {
return;
}
},
// 删除当前鼠标活动控件
deleteText() {
const obj = editorCanvas.getActiveObject();
if (obj) {
editorCanvas.remove(obj);
}
editorCanvas.renderAll();
},
// 更改字体大小
changeFontSize(value) {
let mfontSize = value;
const obj = editorCanvas.getActiveObject();
if (obj) {
obj.set({
fontSize: mfontSize,
});
editorCanvas.renderAll();
}
this.templateData.fontSize = mfontSize;
},
// 背景颜色
changeBgColor(value) {
let mbgColor = value;
const obj = editorCanvas;
if (obj) {
obj.set({
backgroundColor: mbgColor,
});
editorCanvas.renderAll();
}
this.templateData.bgColor = mbgColor;
},
// 字体颜色
changeFontColor(value) {
let mfontColor = value;
const obj = editorCanvas.getActiveObject();
if (obj) {
obj.set({
fill: mfontColor,
});
editorCanvas.renderAll();
}
this.templateData.textColor = mfontColor;
},
// 对齐方式
changeTextAlign(value) {
let mtextAlign = value;
const obj = editorCanvas.getActiveObject();
if (obj) {
obj.set("textAlign", mtextAlign);
obj.centerH();
editorCanvas.renderAll();
}
this.templateData.horizontalAlign = mtextAlign;
},
// 字体粗细
changefontWeight(value) {
let mfontWeight = value;
const obj = editorCanvas.getActiveObject();
if (obj) {
obj.set("fontWeight", mfontWeight);
editorCanvas.renderAll();
}
this.templateData.fontWeight = mfontWeight;
},
// 字体风格
changeFontStyle(value) {
let mfontStyle = value;
const obj = editorCanvas.getActiveObject();
if (obj) {
obj.set("fontStyle", mfontStyle);
editorCanvas.renderAll();
}
this.templateData.fontStyle = mfontStyle;
},
// 图片预览
downLoadImage() {
let base64URl = editorCanvas.toDataURL({
formart: "png",
multiplier: 1,
});
this.imageBase64 = base64URl;
this.done = false;
},
},
......
components: {},
created() {},
};
</script>
<style scoped>
* {
margin: 0;
padding: 0;
}
body {
font-family: 'PingFang SC', "Helvetica Neue", Helvetica, "microsoft yahei", arial, STHeiTi, sans-serif;
}
a {
text-decoration: none
}
.el-aside {
background-color: #F3F3F3;
color: #333;
text-align: center;
}
.el-main {
background-color: #EEEEEE;
color: #333;
text-align: center;
line-height: 160px;
}
.el-main .content-show {
display: flex;
flex-direction: column;
}
.text-edit h6 {
height: 40px;
color: black;
}
.text-edit .el-form {
margin: 15px;
}
.btn-delete {
min-height: 36px;
}
.template-content {
padding: 0px;
margin: 0px;
}
ton-box:hover {
color: white;
opacity: 1;
}
.templateContent {
width: 100%;
padding: 0px;
background-color: #fff;
}
.template-upload {
width: 256px;
height: 130px;
border: 2px solid #adadad;
margin: 10px auto;
line-height: 130px;
text-align: center;
cursor: pointer;
}
.template-img {
width: 300px;
height: 130px;
margin: 0px auto;
line-height: 130px;
text-align: center;
cursor: pointer;
}
#editorCanvas {
box-shadow: 0 0 25px #cac6c6;
width: 100%;
display: block;
margin: 15px auto;
height: 100%;
}
.text-setting {
text-align: center;
width: 80px;
font-size: 18px;
font-weight: 500;
margin-right:170px;
}
.text-edit h4,
.side-right h4,
.show h1 {
text-align: center;
font-size: 16px;
color: #585858;
font-weight: normal;
margin: 15px auto;
}
.show h1 {
text-align: center;
color: #585858;
padding: 15px;
font-weight: bold;
font-size: 24px;
margin: 15px auto;
}
.btn-zoom {
display: inline-block;
line-height: 1;
height: 40px;
width: 104px;
white-space: nowrap;
-webkit-appearance: none;
text-align: center;
-webkit-box-sizing: border-box;
box-sizing: border-box;
outline: 0;
margin: 0;
-webkit-transition: 0.1s;
transition: 0.1s;
font-weight: 500;
font-size: 14px;
border-radius: 4px;
color: #fff;
background-color: #6A60E3;
border-color: #6A60E3;
}
.btn-download,
.btn-save,
.btn-load,
.btn-reset {
display: inline-block;
line-height: 1;
height: 40px;
width: 104px;
white-space: nowrap;
-webkit-appearance: none;
text-align: center;
-webkit-box-sizing: border-box;
box-sizing: border-box;
outline: 0;
margin: 0;
-webkit-transition: 0.1s;
transition: 0.1s;
font-weight: 500;
padding: 12px 20px;
font-size: 14px;
border-radius: 4px;
color: #fff;
background-color: #6A60E3;
margin: 10px 0px 0px 10px;
border-color: #6A60E3;
}
.btn-reset {
height: 40px;
color: #fff;
width: 98px;
background-color: #6A60E3;
margin: 10px 0px 0px 10px;
border-color: #6A60E3;
}
.el-form-item {
margin: 15px auto;
}
.el-form {
height: 70px;
line-height: 70px;
}
.el-header {
height: 60px;
}
.el-form--inline .el-form-item__content {
margin: 0;
padding: 0;
height: 60px;
}
.btn_style {
display: inline-block;
height: 40px;
margin-left: 15px;
width: 120px;
line-height: 40px;
white-space: nowrap;
-webkit-appearance: none;
text-align: center;
font-weight: 500;
background: none;
padding: 0px 20px 20px;
font-size: 14px;
border-radius: 4px;
color: #fff;
}
</style>
文本属性数据:fontData.js
export const fontFamilies = [
{ label: '宋体', value: "'sans-serif, cursive" },
{ label: '黑体', value: "'SimHei', cursive" },
{ label: '微软雅黑', value: "'Microsoft Yahei', cursive" },
{ label: '微软正黑体', value: "'Microsoft JhengHei', cursive" },
{ label: '楷体', value: "'KaiTi', cursive" },
{ label: '仿宋', value: "'FangSong', cursive" },
{ label: '苹方', value: "'PingFang SC', cursive" },
{ label: '华文黑体', value: "'STHeiti', cursive" },
{ label: '隶书', value: "'LiSu', cursive" },
{ label: 'Yatra One', value: "'Yatra One', cursive" }
];
export const fontSizes = [
{ label: '9', value: 9 },
{ label: '10', value: 10 },
{ label: '12', value: 12 },
{ label: '14', value: 14 },
{ label: '16', value: 16 },
{ label: '18', value: 18 },
{ label: '20', value: 20 },
{ label: '24', value: 24 },
{ label: '28', value: 28 },
{ label: '32', value: 32 },
{ label: '36', value: 36 },
{ label: '40', value: 40 },
{ label: '42', value: 42 },
{ label: '50', value: 50 },
{ label: '60', value: 64 },
{ label: '72', value: 72 },
{ label: '82', value: 82 },
{ label: '90', value: 90 },
{ label: '96', value: 96 }
];
export const fontStyle = [
{ value: 'normal', label: '正常' },
{ value: 'italic', label: '斜体' }
];
export const fontColor = [
{ label: '黑色', value: 'block' },
{ label: '白色', value: 'white' },
{ label: '红色', value: 'red' },
{ label: '绿色', value: 'green' },
{ label: '蓝色', value: 'blue' },
{ label: '紫色', value: 'purple' }
];
export const fontWeight = [
{ value: 'normal', label: '正常' },
{ value: 'bold', label: '粗体' },
{ value: 'lighter', label: '细体' },
{ value: '100', label: '100' },
{ value: '200', label: '200' },
{ value: '300', label: '300' },
{ value: '400', label: '400' },
{ value: '500', label: '500' },
{ value: '600', label: '600' },
{ value: '700', label: '700' },
{ value: '800', label: '800' },
{ value: '900', label: '900' }
];
export const backgroundColor = [
{ label: '黑色', value: 'block' },
{ label: '白色', value: 'white' },
{ label: '红色', value: 'red' },
{ label: '绿色', value: 'green' },
{ label: '蓝色', value: 'blue' },
{ label: '紫色', value: 'purple' }
];
export const textAlign = [
{ label: '居中', value: 'center' },
{ label: '两端对齐', value: 'justify' }
/* { label: '左对齐', value: 'left' },
{ label: '右对齐', value: 'right' },*/
];
vue项目中main.js:
import { createApp } from 'vue'
import ElementPlus from 'element-plus';
import 'element-plus/lib/theme-chalk/index.css';
import App from './App.vue';
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')
代码有点长,没有全部放上去,需要源码的下面留言!
Fabric.js是一个可以简化Canvas程序编写的canvas绘图库。
fabric.js官网:http://fabricjs.com/
Fabric.js可以做很多事情:
vue项目里,通过npm 安装:
npm install fabric --save
html文件的可以通过通过cdn引用:
<script src="http://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.6/fabric.min.js"></script>
html 结构里,创建一个canves,并设置id
<canvas ref="canvas" id="editorCanvas"></canvas>
初始化fabric的canvas对象:
// 初始化模板编辑画布
initeditorCanvas() {
const editorCanvas = new fabric.Canvas("editorCanvas", {
devicePixelRatio: true,
width: "375",
height: "667",
originX: "center",
originY: "center",
backgroundColor: "#ffffff",
transparentCorners: false,
});
editorCanvas.preserveObjectStacking = true;
},
以上canves 画布就创建好了,可以在画布上操作添加内容了。
object:added 添加图层
object:modified 编辑图层
object:removed 移除图层
selection:created 初次选中图层
selection:updated 图层选择变化
selection:cleared 清空图层选中
// 监听鼠标按下
const obj = editorCanvas.getActiveObject();
editorCanvas.on("mouse:down", (options) => {
// 记录当前鼠标的起点坐标
if(!obj) {
this.mouseFrom.x = options.e.clientX - editorCanvas._offset.left;
this.mouseFrom.y = options.e.clientY - editorCanvas._offset.top;
}
});
// 监听鼠标移动
editorCanvas.on("mouse:move", (options) => {
// 记录当前鼠标移动终点坐标
if (!obj) {
this.mouseTo.x = options.e.clientX - editorCanvas._offset.left
this.mouseTo.y = options.e.clientY - editorCanvas._offset.top
this.drawRect();
}
});
// 鼠标抬起
editorCanvas.on("mouse:up", (options) => {
if (!obj) {
this.mouseFrom.x = options.e.clientX - editorCanvas._offset.left;
this.mouseFrom.y = options.e.clientY - editorCanvas._offset.top;
this.doDrawing = false;
this.canvasObject = null;
this.mouseFrom = {};
this.mouseTo = {}
}
});
editorCanvas.on("selection:created",(option) => {
if (option) {
this.doDrawing = false;
}
})
// 读取图片地址,设置画布背景
let imgScanle = 50;
fabric.Image.fromURL('xx/xx/bg.jpg', (img) => {
img.set({
// 通过scale来设置图片大小,这里设置和画布一样大
scaleX: 400,
scaleY: 400,
originX: 'left',
originY: 'top'
});
// 设置背景
editorCanvas.setBackgroundImage(img,editorCanvas.renderAll.bind(editorCanvas));
editorCanvas.renderAll(); //重新渲染画布
});
let img = new Image()
img.src = '../img/pic' //图片路径
img.onload = function () {
let _img = new fabric.Image(img)
canves.setBackgroundImage(_img, canves.renderAll.bind(canves), {
left: 0,
top: 0,
scaleX: imgScale,
scaleY: imgScale,
originX: 'left',
originY: 'top'
})
}
通过backgroundColor设置背景颜色:
editorCanvas.set({
backgroundColor: '#000',
});
editorCanvas.renderAll();
Fabric.js 里有 Image,Textbox,Group,Rect,Circle,Line,Ellipse,Polygon,Polyline等对象,可以实现对象内容的添加:
// 添加Textbox文本框
const textbox = new fabric.Textbox('我是TextBox', {
left: 50,
top: 50,
width: 150,
textAlign: "left", // 对齐方式
lineHeight: 1, // 字体行高
charSpacing: 1, // 字体间距
fontFamily: "hyzktjjkt", // 字体
fontSize: 40, // 字体大小
fontWeight: 'blod', // 字体粗细
fill: 'red', // 字体颜色
fontStyle: 'italic', // 斜体
fontFamily: 'Delicious', // 设置字体
stroke: 'green', // 描边颜色
strokeWidth: 3, // 描边宽度
hasControls: false,
borderColor: 'red', // 字体描边颜色
type: "editText", // 类型
});
editorCanvas.add(textbox); // 通过add方法把对象添加到画布上
// 获取当前选中的对象
this.selectedObj = editorCanves.getActiveObject();
// 获取选中的图层
editorCanves.on('selection:created', (e) => {
this.selectedObj = e.target
})
向画布添加图层,默认是依次往上叠加,但是当你选中一个图层进入active状态时,该图层会默认置于顶层:
// 上移图层
this.selectedObj.bringForward();
// 下移图层
this.selectedObj.sendBackwards();
// 通过canvas对象的moveTo方法,移至图层到指定位置
editorCanves.moveTo(object, index);
禁止选中图层时指定:
// 在画布初始化后设置
editorCanves.preserveObjectStacking = true // 禁止选中图层时自定置于顶部
// 图片预览
downLoadImage() {
let base64URl = editorCanvas.toDataURL({
formart: "png",
multiplier: 1,
});
this.imageBase64 = base64URl;
this.done = false;
},
// 下载图片
downLoad() {
this.done = true;
const dataURL = editorCanvas.toDataURL({
width: editorCanvas.width,
height: editorCanvas.height,
left: 0,
top: 0,
format: "png",
});
const link = document.createElement("a");
link.download = "图片.png";
link.href = dataURL;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
},
框架提供了如 toJSON 和 loadFromJSON 方法,作用分别为导出当前画布的json信息,加载json画布信息来还原画布状态。
// 导出当前画布信息
const currState = editorCanves.toJSON();
// 移除某个选中的对象
editorCanves.remove(this.selectedObj)
editorCanves.renderAll() // 重新渲染
// 清除画布上的所有对象内容
editorCanves.clear();
editoeCanves.renderAll();
// 清空画布
resetCanvas() {
let children = editorCanvas.getObjects();
if (children.length > 0) {
editorCanvas.remove(...children);
}
editorCanvas.setBackgroundColor("#fff");
},