實(shí)現(xiàn)效果圖:
image.png
1. 創(chuàng)建vue2項(xiàng)目
vue create vue2-board
2. 將項(xiàng)目進(jìn)行一個(gè)簡(jiǎn)單的配置青伤,比如全局scss樣式文件边灭,將p標(biāo)簽潦牛、ul li標(biāo)簽眶掌、h1、h2等標(biāo)簽
的默認(rèn)樣式去除巴碗,ps:推薦樣式重置文件reset.css
朴爬,樣式地址:https://meyerweb.com/eric/tools/css/reset/
2.1 在src
路徑下新建styles
文件夾,內(nèi)建index.scss
橡淆,然后在main.js
內(nèi)引入該文件召噩。import './styles/index.scss'
代碼如下:
/* http://meyerweb.com/eric/tools/css/reset/ */
/* v1.0 | 20080212 */
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, font, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td {
margin: 0;
padding: 0;
border: 0;
outline: 0;
font-size: 100%;
vertical-align: baseline;
background: transparent;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
/* remember to define focus styles! */
:focus {
outline: 0;
}
/* remember to highlight inserts somehow! */
ins {
text-decoration: none;
}
del {
text-decoration: line-through;
}
/* tables still need 'cellspacing="0"' in the markup */
table {
border-collapse: collapse;
border-spacing: 0;
}
2.2 安裝必要的插件element-ui
,fabric
cnpm i element-ui fabric --save
2.3 main.js
引入插件element-ui
逸爵,fabric
在需要的頁(yè)面在導(dǎo)入
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
2.4 清空App.vue,開(kāi)始編碼具滴,在此頁(yè)面引入 fabric
<template>
<div id="app">頁(yè)面初始化</div>
</template>
<script>
import { fabric } from "fabric";
export default {
name: "App",
};
</script>
<style lang="scss">
html,
body {
width: 100%;
height: 100%;
}
#app {
height: 100%;
background-color: #eee;
}
</style>
3. 先開(kāi)發(fā)靜態(tài)頁(yè)面,也就是右側(cè)的操作欄痊银,
//豎形div抵蚊,定位到右側(cè)
<div class="tools"></div>
//css
.tools {
width: 46px;
height: 500px;
background: rgba(51, 51, 51, 0.4);
border-radius: 4px;
position: absolute;
right: 10px;
top: 10%;
}
<template>
<div id="app">
<!-- 右側(cè)操作欄 -->
<div class="tools">
<ul>
<el-tooltip class="item" effect="dark" content="畫(huà)筆" placement="left">
<li>
<i class="el-icon-edit"></i>
</li>
</el-tooltip>
<li>
<i class="el-icon-top-left"></i>
</li>
<li>
<i class="el-icon-minus"></i>
</li>
<li>
<i class="el-icon-full-screen"></i>
</li>
<li>
<i class="el-icon-edit-outline"></i>
</li>
<li>
<el-color-picker v-model="fontColor"></el-color-picker>
</li>
<li>
<i class="el-icon-d-arrow-left"></i>
</li>
<li>
<i class="el-icon-brush"></i>
</li>
</ul>
</div>
</div>
</template>
<script>
import { fabric } from "fabric";
export default {
name: "App",
data() {
return {
fontColor: "#409EFF",
};
},
};
</script>
<style lang="scss">
html,
body {
width: 100%;
height: 100%;
}
#app {
height: 100%;
background-color: #eee;
.tools {
// width: 46px;
// height: 500px;
background: rgba(51, 51, 51, 0.4);
border-radius: 4px;
position: absolute;
right: 10px;
top: 10%;
display: flex;
justify-content: center;
align-items: center;
padding: 10px 5px;
ul {
li {
width: 38px;
height: 38px;
background: #565656;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 10px;
color: #fff;
border-radius: 4px;
font-size: 20px;
cursor: pointer;
border: 1px solid transparent;
&:hover {
background: #333;
border: 1px solid #fff;
}
}
}
}
}
</style>
4. 既然是vue,我們就要循環(huán)渲染出來(lái)右側(cè)的操作欄
<template>
<div id="app">
<!-- 右側(cè)操作欄 -->
<div class="tools">
<ul>
<el-tooltip
v-for="(item, index) in brushModelData"
:key="index"
class="item"
effect="dark"
:content="item.tips"
placement="left"
>
<li v-if="item.type != 5">
<i :class="item.icon"></i>
</li>
<li v-if="item.type == 5">
<el-color-picker v-model="fontColor" size="mini"></el-color-picker>
</li>
</el-tooltip>
</ul>
</div>
</div>
</template>
<script>
import { fabric } from "fabric";
export default {
name: "App",
data() {
return {
fontColor: "#409EFF",
brushModelData: [
{ icon: "el-icon-edit", tips: "畫(huà)筆", type: 0 },
{ icon: "el-icon-top-left", tips: "箭頭", type: 1 },
{ icon: "el-icon-minus", tips: "直線", type: 2 },
{ icon: "el-icon-full-screen", tips: "矩形", type: 3 },
{ icon: "el-icon-edit-outline", tips: "文本", type: 4 },
{ icon: "el-icon-edit", tips: "顏色", type: 5 },
{ icon: "el-icon-d-arrow-left", tips: "撤銷(xiāo)", type: 6 },
{ icon: "el-icon-brush", tips: "清空", type: 7 },
],
};
},
};
</script>
<style lang="scss">
html,
body {
width: 100%;
height: 100%;
}
#app {
height: 100%;
background-color: #eee;
.tools {
// width: 46px;
// height: 500px;
background: rgba(51, 51, 51, 0.4);
border-radius: 4px;
position: absolute;
right: 10px;
top: 10%;
display: flex;
justify-content: center;
align-items: center;
padding: 10px 5px;
ul {
li {
width: 38px;
height: 38px;
background: #565656;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 10px;
color: #fff;
border-radius: 4px;
font-size: 20px;
cursor: pointer;
border: 1px solid transparent;
&:hover {
background: #333;
border: 1px solid #fff;
}
i {
font-weight: bold;
}
&:last-child{
margin-bottom: 0;
}
}
}
}
}
</style>
5. 靜態(tài)頁(yè)面處理好了溯革,下面來(lái)初始化 fabric
//設(shè)置一個(gè)畫(huà)板區(qū)域贞绳,和當(dāng)前頁(yè)面同比大小
<!-- 畫(huà)板區(qū)域 -->
<div class="conent">
<canvas id="canvas"></canvas>
</div>
.conent{
width: 100%;
height: 100%;
background: red;
}
methods:{
/**
* 初始化畫(huà)板
*/
initBoard(){
//固定寫(xiě)法 ('canvas')括號(hào)內(nèi)的參數(shù) 是canvas的id,{ }是一些參數(shù)
new fabric.Canvas('canvas',{
isDrawingMode:true,//默認(rèn)開(kāi)啟自由繪畫(huà)模式
})
}
},
mounted(){
this.initBoard()
}
image.png
可以看到畫(huà)板太小了致稀,下面設(shè)置下畫(huà)板的大小和紅色區(qū)域一般大
//首先把我們實(shí)例化出來(lái)的fabric賦值給一個(gè)變量
data() {
return {
canvas:null
};
},
/**
* 初始化畫(huà)板
*/
initBoard(){
this.canvas = new fabric.Canvas('canvas',{
isDrawingMode:true,//默認(rèn)開(kāi)啟自由繪畫(huà)模式
})
//最好是定義一個(gè)可復(fù)用的方法
this.handleResize();
},
handleResize() {
/** 設(shè)置canvas畫(huà)布大小 */
/** 先獲取content的寬高冈闭,給content一個(gè)ref屬性 canvasWrap */
// console.log(this.$refs.canvasWrap.offsetWidth);
let w = this.$refs.canvasWrap.offsetWidth
let h = this.$refs.canvasWrap.offsetHeight
this.canvas.setWidth(w);
this.canvas.setHeight(h);
},
mounted() {
this.initBoard();
//解決頁(yè)面大小變化動(dòng)態(tài)改變canvas畫(huà)布的大小
window.addEventListener('resize',this.handleResize)
},
下面改變下畫(huà)布的背景顏色和畫(huà)筆的默認(rèn)顏色和畫(huà)筆的默認(rèn)粗細(xì)
// 改變畫(huà)布的背景顏色
this.canvas.backgroundColor = '#fff';
// 設(shè)置默認(rèn)顏色
this.canvas.freeDrawingBrush.color = this.color;
// 設(shè)置默認(rèn)畫(huà)筆粗細(xì)
this.canvas.freeDrawingBrush.width = this.width;
當(dāng)前模式是自由繪制模式,我們給左側(cè)的操作欄一個(gè)默認(rèn)選擇狀態(tài) active
抖单,并且實(shí)現(xiàn)動(dòng)態(tài)切換active
image.png
<li v-if="item.type != 5" :class="{ active: isActive == index }" @click="setBrushModel(index, item)">
<i :class="item.icon"></i>
</li>
<li v-if="item.type == 5" :class="{ active: isActive == index }" @click="setBrushModel(index, item)">
<el-color-picker v-model="fontColor" size="mini"></el-color-picker>
</li>
* 點(diǎn)擊切換畫(huà)筆功能
*/
setBrushModel(index, item) {
if (item.type !== 5 && item.type !== 6 && item.type !== 7) {
this.isActive = index;
}
},
li.active {
background-color: #333;
border-color: #fff;
i {
font-weight: bold;
}
}
下面實(shí)現(xiàn)一個(gè)最簡(jiǎn)單的萎攒,變換畫(huà)筆的顏色
<el-color-picker v-model="fontColor" size="mini" @change="changePenColor"></el-color-picker>
/** 改變畫(huà)筆顏色 */
changePenColor(){
this.canvas.freeDrawingBrush.color = this.fontColor;
}
下面實(shí)現(xiàn) 最簡(jiǎn)單的撤銷(xiāo)和清空的操作
switch (item.type) {
//撤銷(xiāo)
case 6:
if (this.canvas._objects.length > 0) {
this.canvas._objects.pop();
// 重繪
this.canvas.renderAll();
}
break;
//清空
case 7:
this.canvas.clear();
this.canvas.backgroundColor = "#fff";
break;
default:
break;
}