fabric.js简单小画板
<template>
<div class="home">
<div class="btnwrap">
<span class="brushColor" ref="brushColor">
<ColorPicker
class="colorPicker"
@on-active-change="updateValue"
v-model="colors"
/>
</span>
<el-input-number
:step="1"
size="small"
v-model="width"
:min="1"
:max="100"
label="线条粗细"
></el-input-number>
<span class="up">
<el-button
size="small"
type="primary"
icon="el-icon-wallet"
@click="toFile"
>上传图片</el-button
>
<input
type="file"
accept="image/*"
style="display: none"
id="upload"
@change="uploadFile"
/>
</span>
<el-button-group>
<el-button size="small" icon="el-icon-edit" @click.native="freeDraw"
>自由画笔</el-button
>
<el-button size="small" icon="el-icon-rank" @click="freeMove"
>移动</el-button
>
<el-button size="small" icon="el-icon-refresh-left" @click="undo"
>撤销</el-button
>
<el-button size="small" icon="el-icon-refresh-right" @click="redo"
>重做</el-button
>
<el-button
size="small"
@click="clear"
icon="el-icon-delete"
class="clearBtn"
>清除</el-button
>
<el-button size="small" @click="exportPic" icon="el-icon-download"
>导出</el-button
>
<el-button
size="small"
@click="unSelectDraw"
icon="el-icon-document-delete"
>不可选中</el-button
>
</el-button-group>
<span v-if="currentTool == 'text'">
<el-input-number
:step="1"
size="small"
v-model="fontSize"
:min="1"
:max="100"
></el-input-number>
文字大小</span
>
<div class="tools">
<el-button-group class="simpleTool">
<el-button size="small" @click="currentTool = 'pencil'">🖊</el-button>
<el-button size="small" @click="currentTool = 'line'">|</el-button>
<el-button size="small" @click="currentTool = 'rect'">口</el-button>
<el-button size="small" @click="currentTool = 'circle'">⚪</el-button>
<el-button size="small" @click="currentTool = 'triangle'"
>▲</el-button
>
<el-button size="small" @click="currentTool = 'text'">Aa</el-button>
<el-button size="small" @click="eraser">目</el-button>
</el-button-group>
</div>
</div>
<canvas id="c" width="1200px" height="700px"></canvas>
</div>
</template>
<script>
export default {
name: "Home",
components: {},
data() {
return {
// Control the state of eraser
eraserState: "0",
// Text
textObject: null,
// Text font size
fontSize: "18",
// Component Button Group
currentTool: "pencil",
// Mouse Up
mouseUp: {
x: "0",
y: "0",
},
// Mouse Down
mouseDown: {
x: "0",
y: "0",
},
// the computed state Array
stateArr: [],
// uploaded pictures list
fileList: [],
colors: "#478E7C",
width: 5,
canvas: null,
};
},
watch: {
width(val) {
this.canvas.freeDrawingBrush.width = parseInt(this.width, 5);
},
currentTool() {
this.cmposition();
switch (this.currentTool) {
case "pencil":
this.initPencil();
break;
case "line":
this.initLine();
break;
case "rect":
this.initRect();
break;
case "circle":
this.initCircle();
break;
case "triangle":
this.initTriangle();
break;
case "text":
this.initText();
break;
default:
break;
}
},
},
methods: {
initTriangle() {
let pattern = new fabric.Triangle({
left: this.mouseDown.x,
top: this.mouseDown.y,
width: this.mouseUp.x - this.mouseDown.x,
height: this.mouseUp.y - this.mouseDown.y,
stroke: this.colors,
strokeWidth: this.width,
fill: null,
});
// console.log("triangle");
this.toggleDrawingObject(pattern);
},
initText() {
this.canvas.isDrawingMode = false;
this.canvas.selection = false;
this.textObject = new fabric.Textbox("", {
left: this.mouseDown.x,
top: this.mouseDown.y,
fontSize: this.fontSize,
fill: this.colors,
hasControls: false,
});
this.canvas.add(this.textObject);
this.textObject.enterEditing();
this.textObject.hiddenTextarea.focus();
console.log("text");
},
initCircle() {
let left = this.mouseDown.x;
let top = this.mouseDown.y;
let radius =
Math.sqrt(
(this.mouseUp.x - left) * (this.mouseUp.x - left) +
(this.mouseUp.y - top) * (this.mouseUp.y - top)
) / 2;
let pattern = new fabric.Circle({
left: left,
top: top,
originX: "center",
originY: "center",
stroke: this.colors,
strokeWidth: this.width,
fill: null,
radius: radius,
});
// console.log("circle");
this.toggleDrawingObject(pattern);
},
initRect() {
let pattern = new fabric.Rect({
left: this.mouseDown.x,
top: this.mouseDown.y,
stroke: this.colors,
strokeWidth: this.width,
fill: null,
width: this.mouseUp.x - this.mouseDown.x,
height: this.mouseUp.y - this.mouseDown.y,
});
// console.log("rect");
this.toggleDrawingObject(pattern);
},
initLine() {
let pattern = new fabric.Line(
[this.mouseDown.x, this.mouseDown.y, this.mouseUp.x, this.mouseUp.y],
{
stroke: this.colors, //画笔颜色
strokeWidth: this.width, //画笔宽度
}
);
// console.log("line");
this.toggleDrawingObject(pattern);
},
//toggle the draw model
toggleDrawingObject(canvasObject) {
this.canvas.isDrawingMode = false;
if (this.drawingObject) {
this.canvas.remove(this.drawingObject);
}
// No the canvasObject, the painting will be extremely weird
// console.log('drawingObject',this.drawingObject)
this.canvas.add(canvasObject);
this.drawingObject = canvasObject;
if (this.textObject) {
this.textObject.exitEditing();
this.textObject = null;
}
this.unSelectDraw();
},
//click to start to upload file
toFile() {
document.getElementById("upload").click();
},
// Input image methods to get image file which was uploaded
// the kind of return value is FileList
uploadFile(e) {
this.canvas.isDrawingMode = false;
// event -> event.target(input Element) -> event.target.files
// event.target.files[0] is the image what uploaded
// console.log('e.target.files[0]',e.target.files[0])
var file = e.target.files[0];
var reader = new FileReader();
reader.onload = (e) => {
var data = e.target.result;
fabric.Image.fromURL(data, (img) => {
// add the control of size and rotation diretion
img.hasControls = true;
this.canvas.add(img).renderAll();
});
};
reader.readAsDataURL(file);
e.target.value = "";
},
// freeMove: change the position of the element
freeMove() {
// The effect of [mouse: up] Action always exists.
// To eliminate the effect, use the eraserState to be state flag
this.eraserState = 0;
this.canvas.isDrawingMode = false;
this.currentTool = "";
this.canvas.skipTargetFind = false;
this.canvas.selectable = true;
this.canvas.on("mouse:up", (options) => {
console.log("now tool[" + this.currentTool + "]catch me");
// this.canvas.remove(this.canvas.getActiveObject());
});
},
// redo the step
redo() {
// only stateArr is not null ,it can redo
if (this.stateArr.length > 0) {
this.isRedoing = true;
this.canvas.add(this.stateArr.pop());
this.canvas.renderAll();
// console.log("redo successfully.");
}
},
// undo the step
undo() {
if (this.canvas._objects.length > 0) {
this.stateArr.push(this.canvas._objects.pop());
this.canvas.renderAll();
// console.log("undo successfully.");
} else {
this.$message({
message: "You can not undo the blank canvas.",
center: true,
offset: 300,
duration: 1000,
});
}
},
//导出图片
exportPic() {
//canvas的toDataURL()方法返回一个包含图片展示的数据URL
const dataURL = this.canvas.toDataURL({
width: this.canvas.width,
height: this.canvas.height,
left: 0,
top: 0,
format: "png",
});
//创建一个a标签,指向图片的URL地址,
//点击即可下载名为canvas.png的图片
const link = document.createElement("a");
link.download = "canvas.png";
link.href = dataURL;
//当前文档的Body对象上挂载一个元素,此处为a标签
//模拟a标签的点击,下载后移除该元素
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
},
//初始化笔刷颜色
initBrushColor() {
this.$refs.brushColor.style.backgroundColor = this.colors;
},
//每次选色后更新笔刷颜色
updateValue(val) {
console.log(val);
this.colors = val;
// this.initBrushColor();
this.canvas.freeDrawingBrush.color = this.colors;
},
//自由绘画
freeDraw() {
if (this.canvas == null) {
this.canvas = new fabric.Canvas("c");
this.canvas.backgroundColor = "#efefef";
this.canvas.isDrawingMode = 1;
}
this.canvas.isDrawingMode = 1;
this.canvas.freeDrawingBrush.color = this.colors;
this.canvas.freeDrawingBrush.width = this.width;
this.canvas.renderAll();
},
//清除画布
clear() {
this.canvas.clear();
this.canvas.backgroundColor = "#efefef";
this.fileList = [];
//redo undo list also need to clear
this.stateArr = [];
},
//clean mouse position
cmposition() {
(this.mouseDown = {}), (this.mouseUp = {});
},
// find which painting pencil is selected and decide do what
// this mouse position will be the start point of the pattern
initEvent() {
let eventType = ["line", "circle", "rect", "triangle", "text"];
this.canvas.on("mouse:down", (options) => {
if (eventType.indexOf(this.currentTool) != -1) {
console.log("have event");
this.canvas.selection = false;
this.idDrawing = true;
this.mouseDown.x = options.e.clientX;
// -32 because the top tools bar has height
// to get the correct height, should minus its height
this.mouseDown.y = options.e.clientY - 32;
switch (this.currentTool) {
// TextArea should add on the canvas firstly.
// Otherwise it wont show the edit area
case "text":
this.initText();
break;
default:
break;
}
}
});
// Mouse movement is a constant state, need always function
this.canvas.on("mouse:move", (options) => {
if (this.idDrawing && eventType.indexOf(this.currentTool) != -1) {
this.mouseUp.x = options.e.clientX;
this.mouseUp.y = options.e.clientY - 32;
switch (this.currentTool) {
case "pencil":
this.initPencil();
break;
case "line":
this.initLine();
break;
case "rect":
this.initRect();
break;
case "circle":
this.initCircle();
break;
case "triangle":
this.initTriangle();
break;
case "text":
this.initText();
break;
// case "eraser":
// this.eraser();
// break;
default:
break;
}
}
});
this.canvas.on("mouse:up", (options) => {
if (eventType.indexOf(this.currentTool) != -1) {
this.mouseUp.x = options.e.clientX;
this.mouseUp.y = options.e.clientY - 42;
this.drawingObject = null;
this.idDrawing = false;
this.cmposition();
}
});
},
// Pencil
initPencil() {
this.canvas.freeDrawingBrush.color = this.colors; //画笔颜色
this.canvas.freeDrawingBrush.width = this.width; //画笔宽度
this.canvas.isDrawingMode = 1;
console.log("pencil");
},
// Eraser
eraser() {
// Eraser Ⅰ: use the brush which is same with the background color
// this.unSelectDraw();
// this.canvas.isDrawingMode = 1;
// this.canvas.freeDrawingBrush.color = "#efefef";
// Eraser Ⅱ : remove the chosen object
this.freeMove();
this.eraserState = 1;
// The eraserState is affected by the end code [L461]
// To avoid the suitation, we change the position of time that state flag change
this.canvas.on("mouse:up", (options) => {
if (this.eraserState == 1) {
this.canvas.remove(this.canvas.getActiveObject());
}
});
this.canvas.renderAll();
// this.eraserState = 0;
console.log("Eraser---");
},
// About the situation of the different selected object
unSelectDraw() {
this.removeTextObject();
this.canvas.skipTargetFind = true;
// console.log("This whole canvas cant be selected.");
},
// When the selected area is the textArea
removeTextObject() {
this.currentType = "";
if (this.textObject) {
console.log("remove text");
this.textObject.exitEditing();
this.textObject = null;
}
},
},
mounted() {
document.title = "小画板";
this.initBrushColor();
this.freeDraw();
this.initEvent();
//control the canvas actions
this.canvas.on({
"object:moving": (e) => {
//when move the object, its opacity will be 0.5
e.target.opacity = 0.5;
},
//after the move actions, the opacity will return 1
"object:modified": (e) => {
e.target.opacity = 1;
},
});
},
};
</script>
<style>
.up {
float: left;
}
.tools {
background-color: cadetblue;
width: 100%;
float: left;
position: absolute;
bottom: 0vw;
z-index: 10;
}
.simple el-button {
margin: 10px 0 !important;
}
#c {
position: relative;
}
</style>
人生到处知何似,应似飞鸿踏雪泥。