<template>
|
<div class="page-con">
|
<div class="canvas-box">
|
<div class="canvas-header">
|
<el-icon @click="closeDialog"><CloseBold /></el-icon>
|
</div>
|
<div class="canvas-body">
|
<canvas id="canvasBoard" width="1000" height="750"></canvas>
|
</div>
|
</div>
|
<div class="canvas-tool">
|
<div class="brushBox">
|
<div class="toolList">
|
<el-popover placement="top" width="250" trigger="click">
|
<div class="popinnerBox">
|
<div class="brush">
|
<div class="thickness">
|
<div
|
:class="toolData.thicknessActive == '1' ? 'small active hover' : 'small hover'"
|
@click="selectThickness('1')"
|
></div>
|
<div
|
:class="toolData.thicknessActive == '3' ? 'middle active hover' : 'middle hover'"
|
@click="selectThickness('3')"
|
></div>
|
<div
|
:class="toolData.thicknessActive == '5' ? 'large active hover' : 'large hover'"
|
@click="selectThickness('5')"
|
></div>
|
</div>
|
<div class="lineStyle">
|
<div class="lineTypeBox">
|
<div
|
:class="
|
toolData.lineTypeActive == 'solid' ? 'typeItem lineTypeActive hover' : 'typeItem hover'
|
"
|
@click="selectLineType('solid')"
|
>
|
<div class="solid"></div>
|
<div class="activeIcon" v-if="toolData.lineTypeActive == 'solid'">
|
<img :src="xuanzhong" />
|
</div>
|
</div>
|
<div
|
:class="
|
toolData.lineTypeActive == 'dashed' ? 'typeItem lineTypeActive hover' : 'typeItem hover'
|
"
|
@click="selectLineType('dashed')"
|
>
|
<div class="dashed"></div>
|
<div class="activeIcon" v-if="toolData.lineTypeActive == 'dashed'">
|
<img :src="xuanzhong" />
|
</div>
|
</div>
|
</div>
|
<div class="colorSelectBox">
|
<div
|
v-for="item in colorList"
|
:key="item.key"
|
class="flex1 hover"
|
@click="lineColorSelect(item)"
|
>
|
<div :style="{ background: item.key }" class="scribeItem">
|
<img :src="xuanzhong" v-if="item.key == toolData.lineColorActive" />
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
<template #reference>
|
<div class="floatToolItem hover" @click="toolSelectHandle('huabi')">
|
<el-tooltip class="box-item" effect="dark" content="画笔" placement="bottom">
|
<img :src="huabi2" alt="" class="imgBox" />
|
</el-tooltip>
|
</div>
|
</template>
|
</el-popover>
|
<el-popover placement="top" width="250" trigger="click">
|
<div class="popinnerBox">
|
<div class="lineStyle">
|
<div class="lineTypeBox">
|
<div
|
:class="
|
toolData.fontSizeActive == '12' ? 'typeItem lineTypeActive hover' : 'typeItem hover'
|
"
|
@click="selectfontSize('12')"
|
>
|
小
|
<div class="activeIcon" v-if="toolData.fontSizeActive == '12'">
|
<img :src="xuanzhong" />
|
</div>
|
</div>
|
<div
|
:class="
|
toolData.fontSizeActive == '16' ? 'typeItem lineTypeActive hover' : 'typeItem hover'
|
"
|
@click="selectfontSize('16')"
|
>
|
中
|
<div class="activeIcon" v-if="toolData.fontSizeActive == '16'">
|
<img :src="xuanzhong" />
|
</div>
|
</div>
|
<div
|
:class="
|
toolData.fontSizeActive == '20' ? 'typeItem lineTypeActive hover' : 'typeItem hover'
|
"
|
@click="selectfontSize('20')"
|
>
|
大
|
<div class="activeIcon" v-if="toolData.fontSizeActive == '20'">
|
<img :src="xuanzhong" />
|
</div>
|
</div>
|
</div>
|
<div class="lineStyle">
|
<div class="colorSelectBox">
|
<div
|
v-for="item in colorList"
|
:key="item.key"
|
class="flex1 hover"
|
@click="lineColorSelect(item)"
|
>
|
<div :style="{ background: item.key }" class="scribeItem">
|
<img :src="xuanzhong" v-if="item.key == toolData.lineColorActive" />
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
<template #reference>
|
<div class="floatToolItem hover" @click="toolSelectHandle('wenzi')">
|
<el-tooltip class="box-item" effect="dark" content="文字" placement="bottom">
|
<img :src="wenzi2" alt="" class="imgBox" />
|
</el-tooltip>
|
</div>
|
</template>
|
</el-popover>
|
<el-popover placement="top" width="250" trigger="click">
|
<div class="popinnerBox">
|
<div class="graphBox">
|
<div class="graphSelect">
|
<div class="flex1">
|
<div :class="toolData.graphType == 'square' ? 'acitveGraphType' : ''">
|
<div class="square hover" @click="graphSelect('square')"></div>
|
</div>
|
</div>
|
<div class="flex1">
|
<div :class="toolData.graphType == 'rotundity' ? 'acitveGraphType' : ''">
|
<div class="rotundity hover" @click="graphSelect('rotundity')"></div>
|
</div>
|
</div>
|
<div class="flex1">
|
<div :class="toolData.graphType == 'triangle' ? 'acitveGraphType' : ''">
|
<div class="triangle hover" @click="graphSelect('triangle')"></div>
|
</div>
|
</div>
|
<div class="flex1">
|
<div :class="toolData.graphType == 'lineSegment' ? 'acitveGraphType' : ''">
|
<div class="lineSegment hover" @click="graphSelect('lineSegment')">/</div>
|
</div>
|
</div>
|
</div>
|
<div class="lineStyle">
|
<div class="colorSelectBox">
|
<div
|
v-for="item in colorList"
|
:key="item.key"
|
class="flex1 hover"
|
@click="lineColorSelect(item)"
|
>
|
<div :style="{ background: item.key }" class="scribeItem">
|
<img :src="xuanzhong" v-if="item.key == toolData.lineColorActive" />
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
<template #reference>
|
<div class="floatToolItem hover" @click="toolSelectHandle('tuxing')">
|
<el-tooltip class="box-item" effect="dark" content="图形" placement="bottom">
|
<img :src="tuxing" alt="" class="imgBox" />
|
</el-tooltip>
|
</div>
|
</template>
|
</el-popover>
|
<div class="floatToolItem hover" @click="toolSelectHandle('chexiao')">
|
<el-tooltip class="box-item" effect="dark" content="撤销" placement="bottom">
|
<img :src="clearPrevious" alt="" class="imgBox" />
|
</el-tooltip>
|
</div>
|
<!-- <div class="floatToolItem" @click="toolSelectHandle('xiangpi')">
|
<div class="imgBox">
|
<img :src="clear" alt="" />
|
</div>
|
</div> -->
|
<div class="floatToolItem hover" @click="toolSelectHandle('qingchu')">
|
<el-tooltip class="box-item" effect="dark" content="清除" placement="bottom">
|
<img :src="qingchu2" alt="" class="imgBox" />
|
</el-tooltip>
|
</div>
|
<div class="layOutTool"></div>
|
<div class="floatToolItem hover" @click="toolSelectHandle('tuichu')">
|
<el-tooltip class="box-item" effect="dark" content="退出" placement="bottom">
|
<img :src="tuichu" alt="" class="imgBox" />
|
</el-tooltip>
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
</template>
|
<script setup lang="ts">
|
import { requestCtx } from '../../assets/js/config'
|
import { defineProps, inject, defineEmits, onMounted, reactive, ref, watch } from 'vue'
|
import clearPrevious from '@/assets/images/operation/clearPrevious.png'
|
import clear from '@/assets/images/operation/clear.png'
|
import tuichu from '@/assets/images/operation/tuichu.png'
|
import qingchu2 from '@/assets/images/operation/qingchu.png'
|
import tuxing from '@/assets/images/operation/tuxing.png'
|
import xuanzhong from '@/assets/images/operation/xuanzhong.png'
|
import huabi2 from '@/assets/images/operation/huabi.svg'
|
import wenzi2 from '@/assets/images/operation/wenzi.png'
|
|
let canvas: any = null
|
let text: any = null
|
let activeEl: any = null
|
function canvasOnMouseDownDel(opt) {
|
// 右键,且在元素上右键
|
// button: 1-左键;2-中键;3-右键
|
// 在画布上右键,target 为 null
|
if (opt.button === 1 && opt.target) {
|
// 获取当前元素
|
activeEl = opt.target
|
}
|
}
|
onMounted(() => {
|
canvas = new fabric.Canvas('canvasBoard')
|
// 按下鼠标
|
canvas.on('mouse:down', canvasOnMouseDownDel)
|
canvas.setZoom(1); // 设置画布缩放比例为1
|
canvas.absolutePan({ x: 0, y: 0 });
|
})
|
const emit = defineEmits(['whiteBoardClose'])
|
const closeDialog = () => {
|
emit('whiteBoardClose', false)
|
}
|
|
//画笔操作栏事件
|
const toolData = reactive({
|
activeTool: '',
|
fontSizeActive: '16', // 文字操作文字大小
|
thicknessActive: '3', //画笔选中粗细
|
lineTypeActive: 'solid', //画笔选中线类型
|
lineColorActive: '#333', //画笔选中线颜色
|
graphType: '' //图形类型,方形,圆形...
|
})
|
const colorList = reactive([
|
{
|
label: '黄色',
|
key: '#F5E12A'
|
},
|
{
|
label: '绿色',
|
key: '#76F0AE'
|
},
|
{
|
label: '蓝色',
|
key: '#59CFF5'
|
},
|
{
|
label: '紫色',
|
key: '#CAA5FC'
|
},
|
{
|
label: '粉色',
|
key: '#F5A0B9'
|
}
|
])
|
const toolSelectHandle = (title) => {
|
toolData.activeTool = title
|
toolData.lineColorActive = ''
|
canvas.isDrawingMode = false
|
switch (title) {
|
case 'huabi':
|
canvas.skipTargetFind = false
|
canvas.isDrawingMode = true
|
toolData.graphType = ''
|
canvas.freeDrawingBrush.color = '#333'
|
canvas.freeDrawingBrush.width = '3'
|
canvas.on('mouse:down', '') // 鼠标在画布上按下
|
canvas.on('mouse:up', '')
|
canvas.on('mouse:move', '')
|
break
|
case 'wenzi':
|
canvas.skipTargetFind = false
|
canvas.isDrawingMode = false
|
toolData.graphType = ''
|
text = new fabric.IText('请输入文本', {
|
left: 50,
|
top: 50,
|
fontSize: toolData.fontSizeActive
|
})
|
canvas.add(text)
|
canvas.on('mouse:down', '') // 鼠标在画布上按下
|
canvas.on('mouse:up', '')
|
canvas.on('mouse:move', '')
|
break
|
case 'tuxing':
|
canvas.isDrawingMode = false
|
canvas.selectionColor = 'transparent'
|
canvas.skipTargetFind = true
|
toolData.lineColorActive = '#000'
|
break
|
case 'chexiao':
|
canvas.skipTargetFind = false
|
canvas.isDrawingMode = false
|
canvas.remove(activeEl)
|
canvas.requestRenderAll()
|
activeEl = null
|
break
|
case 'tuichu':
|
canvas.skipTargetFind = false
|
canvas.isDrawingMode = false
|
emit('whiteBoardClose', false)
|
break
|
case 'qingchu':
|
canvas.clear()
|
break
|
}
|
}
|
|
//选中画笔粗细
|
const selectThickness = (str) => {
|
toolData.thicknessActive = str
|
canvas.freeDrawingBrush.width = toolData.thicknessActive
|
}
|
//画笔线类型
|
const selectLineType = (str) => {
|
toolData.lineTypeActive = str
|
if (toolData.lineTypeActive == 'dashed') {
|
canvas.freeDrawingBrush.strokeDashArray = [10]
|
} else {
|
canvas.freeDrawingBrush.strokeDashArray = null
|
}
|
}
|
|
//画笔/文字/图形颜色选择
|
const lineColorSelect = (item) => {
|
toolData.lineColorActive = item.key
|
if ((toolData.activeTool == 'huabi')) {
|
canvas.freeDrawingBrush.color = item.key
|
}
|
if ((toolData.activeTool == 'wenzi')) {
|
text.fill = toolData.lineColorActive
|
}
|
}
|
|
//文字大小
|
const selectfontSize = (str) => {
|
toolData.fontSizeActive = str
|
text.fontSize = toolData.fontSizeActive
|
}
|
|
//选择图形
|
const graphSelect = (type) => {
|
toolData.graphType = type
|
switch (type) {
|
case 'square':
|
canvas.on('mouse:down', canvasMouseDown) // 鼠标在画布上按下
|
canvas.on('mouse:up', canvasMouseUp)
|
canvas.selectionBorderColor = toolData.lineColorActive
|
break
|
case 'rotundity':
|
canvas.on('mouse:down', canvasMouseDown) // 鼠标在画布上按下
|
canvas.on('mouse:up', canvasMouseUp)
|
canvas.on('mouse:move', canvasMouseMove)
|
break
|
case 'triangle':
|
canvas.on('mouse:down', canvasMouseDown) // 鼠标在画布上按下
|
canvas.on('mouse:up', canvasMouseUp)
|
canvas.on('mouse:move', canvasMouseMove)
|
break
|
case 'lineSegment':
|
canvas.on('mouse:down', canvasMouseDown) // 鼠标在画布上按下
|
canvas.on('mouse:move', canvasMouseMove) // 鼠标在画布上移动
|
canvas.on('mouse:up', canvasMouseUp)
|
break
|
}
|
}
|
|
let downPoint = null // 鼠标按下的坐标
|
let upPoint = null
|
let currentCircle = null // 临时圆,创建圆的时候使用
|
let currentTriangle = null // 临时三角形
|
let currentLine = null // 临时线段
|
// 鼠标在画布上按下
|
function canvasMouseDown(e) {
|
downPoint = e.absolutePointer
|
if (toolData.graphType === 'rotundity') {
|
currentCircle = new fabric.Circle({
|
top: downPoint.y,
|
left: downPoint.x,
|
radius: 0,
|
fill: 'transparent',
|
stroke: toolData.lineColorActive
|
})
|
|
canvas.add(currentCircle)
|
}
|
if (toolData.graphType === 'triangle') {
|
currentTriangle = new fabric.Triangle({
|
top: downPoint.y,
|
left: downPoint.x,
|
width: 0,
|
height: 0,
|
fill: 'transparent',
|
stroke: toolData.lineColorActive
|
})
|
canvas.add(currentTriangle)
|
}
|
if (toolData.graphType === 'lineSegment') {
|
currentLine = new fabric.Line(
|
[
|
downPoint.x,
|
downPoint.y, // 起始点坐标
|
downPoint.x,
|
downPoint.y // 结束点坐标
|
],
|
{
|
stroke: toolData.lineColorActive // 笔触颜色
|
}
|
)
|
canvas.add(currentLine)
|
}
|
}
|
// 鼠标在画布上松开
|
function canvasMouseUp(e) {
|
if (toolData.graphType === 'square') {
|
// 创建矩形
|
createRect(e.absolutePointer)
|
}
|
|
if (toolData.graphType === 'rotundity') {
|
if (JSON.stringify(downPoint) === JSON.stringify(upPoint)) {
|
canvas.remove(currentCircle)
|
} else {
|
if (currentCircle) {
|
currentCircle.set('stroke', toolData.lineColorActive)
|
}
|
}
|
currentCircle = null
|
}
|
|
if (toolData.graphType === 'triangle') {
|
if (JSON.stringify(downPoint) === JSON.stringify(upPoint)) {
|
canvas.remove(currentTriangle)
|
} else {
|
currentTriangle.set('stroke', toolData.lineColorActive)
|
}
|
currentTriangle = null
|
}
|
|
if (toolData.graphType === 'lineSegment') {
|
if (JSON.stringify(downPoint) === JSON.stringify(upPoint)) {
|
canvas.remove(currentLine)
|
} else {
|
currentLine.set('stroke', toolData.lineColorActive)
|
}
|
currentLine = null
|
}
|
}
|
// 创建矩形
|
function createRect(pointer) {
|
// 点击事件,不生成矩形
|
if (JSON.stringify(downPoint) === JSON.stringify(pointer)) {
|
return
|
}
|
|
// 创建矩形
|
// 矩形参数计算
|
let top = Math.min(downPoint.y, pointer.y)
|
let left = Math.min(downPoint.x, pointer.x)
|
let width = Math.abs(downPoint.x - pointer.x)
|
let height = Math.abs(downPoint.y - pointer.y)
|
|
// 矩形对象
|
const rect = new fabric.Rect({
|
top,
|
left,
|
width,
|
height,
|
fill: 'transparent',
|
stroke: toolData.lineColorActive
|
})
|
// 将矩形添加到画布上
|
canvas.add(rect)
|
downPoint = null
|
}
|
// 鼠标在画布上移动
|
function canvasMouseMove(e) {
|
if (toolData.graphType === 'rotundity' && currentCircle) {
|
const currentPoint = e.absolutePointer
|
|
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
|
|
currentCircle.set('radius', radius)
|
currentCircle.set('top', top)
|
currentCircle.set('left', left)
|
|
canvas.requestRenderAll()
|
}
|
if (toolData.graphType === 'triangle' && currentTriangle) {
|
const currentPoint = e.absolutePointer
|
|
let width = Math.abs(downPoint.x - currentPoint.x)
|
let height = Math.abs(downPoint.y - currentPoint.y)
|
|
let top = currentPoint.y > downPoint.y ? downPoint.y : currentPoint.y
|
let left = currentPoint.x > downPoint.x ? downPoint.x : currentPoint.x
|
|
currentTriangle.set('width', width)
|
currentTriangle.set('height', height)
|
currentTriangle.set('top', top)
|
currentTriangle.set('left', left)
|
canvas.requestRenderAll()
|
}
|
if (toolData.graphType === 'lineSegment' && currentLine) {
|
const currentPoint = e.absolutePointer
|
|
currentLine.set('x2', currentPoint.x)
|
currentLine.set('y2', currentPoint.y)
|
|
canvas.requestRenderAll()
|
}
|
}
|
|
|
</script>
|
<style scoped lang="less">
|
.page-con {
|
height: 100%;
|
width: 100%;
|
|
// position: relative;
|
.canvas-header {
|
text-align: right;
|
padding: 10px;
|
background-color: #f1f1f1;
|
border-radius: 5px 5px 0 0;
|
}
|
.canvas-box {
|
width: 1000px;
|
margin: 20px auto;
|
background-color: #fff;
|
border-radius: 5px;
|
}
|
.brushBox {
|
width: 290px;
|
margin: 0 auto;
|
border-radius: 5px;
|
background: #fff;
|
overflow: hidden;
|
box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.3);
|
|
.toolList {
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
|
.floatToolItem {
|
margin: 6px 8px;
|
padding: 5px;
|
width: 30px;
|
border-radius: 5px;
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
// flex: 1;
|
font-size: 14px;
|
|
.imgBox {
|
height: 18px;
|
width: 18px;
|
text-align: center;
|
margin: 0 auto;
|
}
|
}
|
|
.floatToolItem:hover {
|
background-color: rgba(44, 44, 44, 0.2);
|
}
|
|
.layOutTool {
|
height: 20px;
|
margin-right: 5px;
|
border-left: 1px solid #e0e0e0;
|
}
|
}
|
}
|
}
|
</style>
|