Compare commits

..

No commits in common. "2f4d48a8c0bcdaff2afce10daed71c1e4252f622" and "71aff5d1f8cb3dbe669ee1b4c5b9b3705265d4cd" have entirely different histories.

7 changed files with 57 additions and 286 deletions

View File

@ -1,4 +1,4 @@
import { app, shell, BrowserWindow, ipcMain, session, BrowserView } from 'electron' import { app, shell, BrowserWindow, ipcMain, session } from 'electron'
import { join } from 'path' import { join } from 'path'
import { electronApp, optimizer, is } from '@electron-toolkit/utils' import { electronApp, optimizer, is } from '@electron-toolkit/utils'
import icon from '../../resources/icon.png?asset' import icon from '../../resources/icon.png?asset'
@ -31,7 +31,6 @@ function createLoginWindow() {
nodeIntegration: true nodeIntegration: true
} }
}) })
loginWindow.type = 'login' // 唯一标识
// handleUpdate(loginWindow,ipcMain) // handleUpdate(loginWindow,ipcMain)
// const loginURL = is.dev ? `http://localhost:5173/#/login` : `file://${__dirname}/index.html/#/login` // const loginURL = is.dev ? `http://localhost:5173/#/login` : `file://${__dirname}/index.html/#/login`
// loginWindow.loadURL(loginURL) // loginWindow.loadURL(loginURL)
@ -70,13 +69,12 @@ function createMainWindow() {
// webSecurity: false // 跨域关闭 // webSecurity: false // 跨域关闭
} }
}) })
mainWindow.type = 'main' // 唯一标识
mainWindow.on('ready-to-show', () => { mainWindow.on('ready-to-show', () => {
mainWindow.show() mainWindow.show()
}) })
mainWindow.on('closed', () => { mainWindow.on('closed', () => {
mainWindow = null mainWindow = null
app.quit() // 主窗口关闭-结束所有进程
}) })
mainWindow.webContents.setWindowOpenHandler((details) => { mainWindow.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url) shell.openExternal(details.url)
@ -115,7 +113,6 @@ async function createLinkWin(data) {
contextIsolation: true contextIsolation: true
} }
}) })
linkWindow.type = 'link' // 唯一标识
let cookieDetails = { ...data.cookieData } let cookieDetails = { ...data.cookieData }
await linkWindow.webContents.session.cookies.set(cookieDetails).then(()=>{ await linkWindow.webContents.session.cookies.set(cookieDetails).then(()=>{
console.log('Cookie is successful'); console.log('Cookie is successful');

View File

@ -50,9 +50,7 @@ const renderPage = async (canvasobj) => {
const pdf = await pdfjsLib.getDocument(props.pdfObj.pdfUrl).promise const pdf = await pdfjsLib.getDocument(props.pdfObj.pdfUrl).promise
// //
const page = await pdf.getPage(canvasobj.page) const page = await pdf.getPage(canvasobj.page)
var screenWidth = window.innerWidth/2-100; const viewport = page.getViewport({ scale: 1 })
var screenHeight = window.innerHeight;
const viewport = page.getViewport({ scale:2})
const canvasElement = canvasobj.canvas const canvasElement = canvasobj.canvas
canvasElement.width = viewport.width canvasElement.width = viewport.width
@ -69,20 +67,34 @@ const renderPage = async (canvasobj) => {
img.onload = () => { img.onload = () => {
// //
// pdf fabric // pdf fabric
var screenWidth = window.innerWidth/2-10;
var screenHeight = window.innerHeight;
//
var imgWidth = img.width;
var imgHeight = img.height;
//
var widthRatio = screenWidth / imgWidth;
var heightRatio = screenHeight / imgHeight;
//
var scaleRatio = Math.min(widthRatio, heightRatio);
//
var targetWidth = imgWidth * scaleRatio;
var targetHeight = imgHeight * scaleRatio;
if (props.pdfObj.numberOfPdf == 2) { if (props.pdfObj.numberOfPdf == 2) {
if (canvasobj.index == 0) { if (canvasobj.index == 0) {
fabriccanvas.value.setWidth(screenWidth) fabriccanvas.value.setWidth(targetWidth)
fabriccanvas.value.setHeight(screenHeight) fabriccanvas.value.setHeight(targetHeight)
displayData(fabriccanvas, canvsStore, canvasobj, fabric, img) displayData(fabriccanvas, canvsStore, canvasobj, fabric, img)
} else { } else {
fabriccanvas1.value.setWidth(screenWidth) fabriccanvas1.value.setWidth(targetWidth)
fabriccanvas1.value.setHeight(screenHeight) fabriccanvas1.value.setHeight(targetHeight)
displayData(fabriccanvas1, canvsStore, canvasobj, fabric, img) displayData(fabriccanvas1, canvsStore, canvasobj, fabric, img)
} }
} else { } else {
fabriccanvas.value.setWidth(screenWidth) fabriccanvas.value.setWidth(targetWidth)
fabriccanvas.value.setHeight(screenHeight) fabriccanvas.value.setHeight(targetHeight)
displayData(fabriccanvas, canvsStore, canvasobj, fabric, img) displayData(fabriccanvas, canvsStore, canvasobj, fabric, img)
} }
// console.log(imgarr.value) // console.log(imgarr.value)
@ -161,10 +173,8 @@ const initPdfone = async () => {
setTimeout(() => { setTimeout(() => {
fabriccanvas1.value = new fabric.Canvas('pdf-fabric1') fabriccanvas1.value = new fabric.Canvas('pdf-fabric1')
fabriccanvas1.value.isDrawingMode = true fabriccanvas1.value.isDrawingMode = true
fabriccanvas1.value.freeDrawingBrush.color = '#A33AFE' fabriccanvas1.value.freeDrawingBrush.color = 'red'
fabriccanvas1.value.freeDrawingCursor = 'default'
fabriccanvas1.value.setWidth(595) fabriccanvas1.value.setWidth(595)
handleevent(fabriccanvas1.value, imgarr, 'two')
}, 0) }, 0)
initPdf('addOnePage') initPdf('addOnePage')
} }

View File

@ -1,223 +0,0 @@
/**
* @description 封装fabric js
*/
import { fabric } from 'fabric'
// 当前使用到的常量|类型(枚举)
export class TYPES {
static ActionMode = {
DRAW: 'draw', // 画笔模式
ERASE: 'erase', // 橡皮擦模式
SELECT: 'select', // 选择模式
Board: 'board' // 画板模式
}
// 画笔类型
static DrawType = {
FreeStyle: 'freeStyle',
Shape: 'shape'
}
// 画笔样式
static DrawStyle = {
Basic: 'basic',
Rainbow: 'rainbow',
Shape: 'shape',
Material: 'material',
Pixels: 'pixels',
MultiColor: 'multiColor',
Text: 'text',
MultiLine: 'multiLine',
Reticulate: 'reticulate',
MultiPoint: 'multiPoint',
Wiggle: 'wiggle',
Thorn: 'thorn'
}
// 各种形状
static DrawShape = {
Bubble: 'bubble',
Star: 'star',
Love: 'love',
Butterfly: 'butterfly',
Snow: 'snow',
Music: 'music',
Sun: 'sun',
Moon: 'moon',
Leaf: 'leaf',
Flower: 'flower'
}
// 材质类型
static MATERIAL_TYPE = {
CRAYON: 'crayon',
CARBON: 'carbon',
CLOTH: 'cloth',
OIL: 'oil',
CRAYON_DARK: 'crayonDark'
}
// 多颜色类型
static MultiColorType = {
COL: 'col',
ROW: 'row',
CIRCLE: 'circle'
}
// 画笔元素
static FREESTYLE_ELEMENT_CUSTOM_TYPE = {
IMAGE: 'image',
I_TEXT: 'itext',
RAINBOW: 'rainbow',
SHAPE: 'shape',
PIXELS: 'pixels',
DRAW_TEXT: 'drawText',
MULTI_LINE: 'multiLine',
RETICULATE: 'reticulate',
MULTI_POINT: 'multiPoint',
WIGGLE: 'wiggle',
THORN: 'thorn'
}
// 形状元素
static SHAPE_ELEMENT_CUSTOM_TYPE = {
SHAPE_LINE: 'shapeLine',
SHAPE_RECT: 'shapeRect',
SHAPE_CIRCLE: 'shapeCircle',
SHAPE_ELLIPSE: 'shapeEllipse',
SHAPE_TRIANGLE: 'shapeTriangle',
SHAPE_ARROW_LINE: 'shapeArrowLine',
SHAPE_ARROW_OUTLINE: 'shapeArrowOutline',
SHAPE_CLOUD: 'shapeCloud',
SHAPE_TOOLTIPS: 'shapeTooltips',
SHAPE_LIGHTNING: 'shapeLightning',
SHAPE_CLOSE: 'shapeClose',
SHAPE_CHECK: 'shapeCheck',
SHAPE_INFO: 'shapeInfo',
SHAPE_BACKSPACE: 'shapeBackspace',
SHAPE_BLOCK: 'shapeBlock',
SHAPE_SPEAKER: 'shapeSpeaker',
SHAPE_SEARCH: 'shapeSearch',
SHAPE_INFO_OUTLINE: 'shapeInfoOutline',
SHAPE_HEART: 'shapeHeart',
SHAPE_ALERT: 'shapeAlert'
}
// 元素类型
static ELEMENT_CUSTOM_TYPE = {
...this.FREESTYLE_ELEMENT_CUSTOM_TYPE,
...this.SHAPE_ELEMENT_CUSTOM_TYPE
}
}
// 自由绘画 FreeStyle
// export class FreeStyle {
// }
export const FreeStyle = {
renderPencilBrush: (canvas) => {
const pencilBrush = new fabric.PencilBrush(canvas)
canvas.isDrawingMode = true
canvas.freeDrawingBrush = pencilBrush
canvas.freeDrawingBrush.width = fabricUtils.getDrawWidth()
canvas.freeDrawingBrush.color = useDrawStore.drawColors[0]
canvas.freeDrawingBrush.shadow = new fabric.Shadow({
blur: fabricUtils.getShadowWidth(),
offsetX: 0,
offsetY: 0,
color: useDrawStore.shadowColor
})
}
}
// 封装fabric-canvas
export class fabricVue {
canvas = null // fabric-canvas 对象
evnet = null // 事件对象
history = null // 历史记录
textElement // 文本节点
hookFn = [] // 钩子
defConfig // 默认配置-canvas
drawConfig // 默认配置-画笔
boardConfig // 默认配置-画板
publicConfig // 公共属性-配置
/** 构造函数 */
constructor() {
// this.textElement = new TextElement() // 创建文本节点
const initLanguage = ['en', 'en-US', 'en-us'].includes(navigator.language) ? 'en' : 'zh'
this.drawConfig = { // 默认配置
drawWidth: 1,
drawColors: ['#000000'],
shadowWidth: 0,
shadowColor: '#000000',
drawTextValue: 'draw',
drawStyle: TYPES.DrawStyle.Basic,
drawShapeCount: 2,
materialType: TYPES.MATERIAL_TYPE.CRAYON,
drawShape: TYPES.DrawShape.Bubble,
eraserWidth: 20,
multiColorType: TYPES.MultiColorType.COL,
textFontFamily: 'Georgia',
openAutoDraw: false,
fontStyles: [],
}
this.boardConfig = { // 默认配置
mode: TYPES.ActionMode.DRAW,
drawType: TYPES.DrawType.FreeStyle,
language: initLanguage,
canvasWidth: 1,
canvasHeight: 1,
backgroundColor: 'rgba(255, 255, 255, 1)',
backgroundOpacity: 1,
hasBackgroundImage: false,
backgroundImageOpacity: 1,
isObjectCaching: true,
openGuideLine: false,
}
this.defConfig = { // 默认配置-canvas
// 它用于指定选中对象时显示的选择框的颜色
selectionColor: 'rgba(101, 204, 138, 0.3)',
// 用于控制在组合group对象时是否保留每个对象的堆叠顺序
preserveObjectStacking: true,
// 它用于控制是否在高分辨率显示器(例如 Retina 显示器)上启用图像的像素级缩放
enableRetinaScaling: true,
// 锁定背景,不受缩放影响 false
backgroundVpt: false,
// 默认全屏模式
width: window.innerWidth,
height: window.innerHeight,
}
this.publicConfig = { // 公共属性配置
Object: { // 默认配置-对象上
// 设置对象边框的颜色(Rect、Circle、Ellipse、Path)
borderColor: '#65CC8A',
// 设置对象角落的颜色(Rect、RoundRect、Circle 和 Ellipse 类型的对象)
cornerColor: '#65CC8A',
// 设置角落的形状(Rect、RoundRect、Circle 和 Ellipse 类型的对象)
// circle rect round [圆形,直角(默认),圆角]
cornerStyle: 'circle',
// 设置边框虚线的样式(Rect、Circle、Ellipse、Path)
borderDashArray: [3, 3],
// 矩形Rect、圆角矩形RoundRect、圆形Circle和椭圆形Ellipse对象的角落是否透明
transparentCorners: false
},
Line: {
// 设置边缘连接方式为 miter bevel round|尖角(默认) 斜角切割线 圆形连接
strokeLineJoin: 'round',
// 设置结束连接方式为 butt round square|直角(默认) 圆形 正方形
strokeLineCap: 'round'
}
}
}
/**
* 初始化canvas
* @param {*} canvasEl canvas元素
* @returns boolean 是否初始化成功
*/
initCanvas(canvasEl, option = {}) {
return new Promise(async(resolve) => {
if (!canvasEl) resolve(false)
this.canvas = new fabric.Canvas(canvasEl, {
...this.defConfig, ...option
})
// this.evnet = new CanvasEvent() // 创建相关事件
// this.history = new History() // 创建历史记录
// await this.initCanvasStorage()
resolve(true)
})
}
}
export const FabricVue = new fabricVue()
export default FabricVue

View File

@ -14,24 +14,23 @@ export function handleevent(canvas, imgarr, type = 'defalut') {
if (imgarr.value[0].index == 0) { if (imgarr.value[0].index == 0) {
imgarr.value[0].JSONdata = canvas.toJSON() imgarr.value[0].JSONdata = canvas.toJSON()
} }
if (imgarr.value[1]?.index == 0) { if (imgarr.value[1].index == 0) {
imgarr.value[1].JSONdata = canvas.toJSON() imgarr.value[1].JSONdata = canvas.toJSON()
} }
} else { } else {
if (imgarr.value[0].index == 1) { if (imgarr.value[0].index == 1) {
imgarr.value[0].JSONdata = canvas.toJSON() imgarr.value[0].JSONdata = canvas.toJSON()
} }
if (imgarr.value[1]?.index == 1) { if (imgarr.value[1].index == 1) {
imgarr.value[1].JSONdata = canvas.toJSON() imgarr.value[1].JSONdata = canvas.toJSON()
} }
} }
console.log(imgarr.value)
}) })
} }
// 保存数据 // 保存数据
export function savecanvsStore(imgarr, canvsStore) { export function savecanvsStore(imgarr, canvsStore) {
canvsStore.pageArr = mergeAndReplace(canvsStore.pageArr, imgarr.value) canvsStore.pageArr = mergeAndReplace(canvsStore.pageArr, imgarr.value)
// console.log(canvsStore.pageArr,22222222222222222222+'存入')
} }
// 重显数据 // 重显数据
export function displayData(canvas, canvsStore, canvasobj, fabric, img) { export function displayData(canvas, canvsStore, canvasobj, fabric, img) {
@ -51,21 +50,19 @@ export function displayData(canvas, canvsStore, canvasobj, fabric, img) {
canvsStore.pageArr.forEach((item) => { canvsStore.pageArr.forEach((item) => {
//初始化 //初始化
if (item.page == canvasobj.page) { if (item.page == canvasobj.page) {
// canvas.value.clear() // 清除 Canvas canvas.value.clear() // 清除 Canvas
// console.log(item.JSONdata, '找到一样的数据') // console.log(item.JSONdata, '找到一样的数据')
canvas.value.loadFromJSON(item.JSONdata, () => { canvas.value.loadFromJSON(item.JSONdata, () => {
// 在所有对象加载完成后重新渲染画布 // 在所有对象加载完成后重新渲染画布
requestAnimationFrame(() => {
// 渲染所有对象
canvas.value.renderAll.bind(canvas.value) canvas.value.renderAll.bind(canvas.value)
canvas.value.renderAll() canvas.value.renderAll()
// requestAnimationFrame(() => { })
// // 渲染所有对象
// })
}) })
} else { } else {
// 使用 requestAnimationFrame 来更新画布,确保在下一帧进行重绘 // 使用 requestAnimationFrame 来更新画布,确保在下一帧进行重绘
// // 清除 Canvas canvas.value.clear() // 清除 Canvas
canvas.value.clear()
requestAnimationFrame(function () { requestAnimationFrame(function () {
fabric.Image.fromURL(img.src, (img) => { fabric.Image.fromURL(img.src, (img) => {
img.set({ img.set({
@ -78,7 +75,6 @@ export function displayData(canvas, canvsStore, canvasobj, fabric, img) {
}) })
// 渲染所有对象 // 渲染所有对象
canvas.value.renderAll.bind(canvas.value) canvas.value.renderAll.bind(canvas.value)
canvas.value.renderAll()
}) })
} }
}) })

View File

@ -1,13 +1,12 @@
/** /**
* @description: electron 封装的工具函数 * @description: electron 封装的工具函数
*/ */
// const { ipcRenderer } = window.electron || {} const { ipcRenderer } = window.electron || {}
// import { ipcRenderer } from 'electron' // 渲染器里面可以使用ipcRenderer // import { ipcRenderer } from 'electron' // 渲染器里面可以使用ipcRenderer
const path = require('path') const path = require('path')
const Remote = require('@electron/remote') const Remote = require('@electron/remote')
const { ipcRenderer } = require('electron')
// 常用变量 // 常用变量
const BaseUrl = process.env['ELECTRON_RENDERER_URL']+'/#' const BaseUrl = process.env['ELECTRON_RENDERER_URL']+'/#'
@ -55,7 +54,6 @@ export const createWindow = async (type, data) => {
} }
data.option = {...defOption, ...option} data.option = {...defOption, ...option}
const win = await toolWindow(data) const win = await toolWindow(data)
win.type = type // 唯一标识
win.show() win.show()
win.setFullScreen(true) // 设置窗口为全屏 win.setFullScreen(true) // 设置窗口为全屏
win.setIgnoreMouseEvents(true, {forward: true}) // 忽略鼠标事件但是事件继续传递给窗口 win.setIgnoreMouseEvents(true, {forward: true}) // 忽略鼠标事件但是事件继续传递给窗口
@ -74,7 +72,7 @@ export const createWindow = async (type, data) => {
} }
data.option = {...defOption, ...option} data.option = {...defOption, ...option}
const win = await toolWindow(data) const win = await toolWindow(data)
win.type = type // 唯一标识
win.show() win.show()
win.setFullScreen(true) // 设置窗口为全屏 win.setFullScreen(true) // 设置窗口为全屏
eventHandles(type, win) // 事件监听处理 eventHandles(type, win) // 事件监听处理
@ -99,7 +97,7 @@ export function toolWindow({url, isFile, isConsole, option={}}) {
let height = option?.height || 600 let height = option?.height || 600
const mainWin = Remote.getCurrentWindow() // 获取主窗口对象 const mainWin = Remote.getCurrentWindow() // 获取主窗口对象
const devUrl = `${BaseUrl}${url}` const devUrl = `${BaseUrl}${url}`
const buildUrl = path.join(__dirname, 'index.html') const buildUrl = path.join(__dirname, 'index.html#')
const urlAll = isDev ? devUrl : buildUrl const urlAll = isDev ? devUrl : buildUrl
return new Promise((resolve) => { return new Promise((resolve) => {
const config = { const config = {
@ -115,8 +113,9 @@ export function toolWindow({url, isFile, isConsole, option={}}) {
} }
// 创建-新窗口 // 创建-新窗口
let win = new Remote.BrowserWindow(config) let win = new Remote.BrowserWindow(config)
if (!isDev) win.loadFile(urlAll,{hash: url}) // 加载文件 console.log(urlAll)
else win.loadURL(urlAll) // 加载url if (!!isFile) win.loadFile(urlAll+url) // 加载文件
else win.loadURL(urlAll,{hash: url}) // 加载url
win.once('ready-to-show', () => {resolve(win)}) win.once('ready-to-show', () => {resolve(win)})
// 主窗口关闭事件 // 主窗口关闭事件
mainWin.once('closed', () => { win.destroy()}) mainWin.once('closed', () => { win.destroy()})
@ -136,17 +135,11 @@ export function toolWindow({url, isFile, isConsole, option={}}) {
* @param {*} win 窗口对象 * @param {*} win 窗口对象
*/ */
const eventHandles = (type, win) => { const eventHandles = (type, win) => {
// const winAll = Remote.BrowserWindow.getAllWindows()
// const mainWin = winAll.find(o => o.type == 'main') // 主窗口对象
// 公共方法 // 公共方法
const publicMethods = ({onClosed}={}) => { const publicMethods = ({onClosed}) => {
// 监听主窗口-关闭事件 // 监听关闭事件
// Remote.ipcMain.on('close-window', () => {console.log('关闭窗口');win.destroy()}) Remote.ipcMain.once('close-window', () => {win.destroy()})
// mainWin.once('closed', () => {console.log('关闭窗口');win.destroy()}) win.on('closed', () => {!!onClosed && onClosed();win = null})
win.on('closed', () => {
if(onClosed) onClosed()
win = null
})
} }
switch(type) { switch(type) {
case 'tool-sphere': { // 创建-悬浮球 case 'tool-sphere': { // 创建-悬浮球
@ -156,7 +149,10 @@ const eventHandles = (type, win) => {
// 关闭窗口 // 关闭窗口
Remote.ipcMain.once('tool-sphere:close', () => { win.destroy() }) Remote.ipcMain.once('tool-sphere:close', () => { win.destroy() })
// 放大监听-测试 // 放大监听-测试
Remote.ipcMain.once('maximize-window', () => {win.destroy()}) Remote.ipcMain.once('maximize-window', () => {
win.destroy()
console.log('关闭窗口')
})
const on = { const on = {
onClosed: () => {Remote.ipcMain.off('tool-sphere:set:ignore', setIgnore)} onClosed: () => {Remote.ipcMain.off('tool-sphere:set:ignore', setIgnore)}
} }
@ -165,7 +161,6 @@ const eventHandles = (type, win) => {
case 'open-PDF': { case 'open-PDF': {
// 最小化窗口 minimize() // 最小化窗口 minimize()
Remote.ipcMain.once('open-PDF:minimize', () => {win.destroy()}) Remote.ipcMain.once('open-PDF:minimize', () => {win.destroy()})
publicMethods() // 加载公共方法
break} break}
default: default:
break break

View File

@ -36,7 +36,7 @@ import { ref, onMounted, watch, reactive } from 'vue'
import * as pdfjsLib from 'pdfjs-dist/legacy/build/pdf' import * as pdfjsLib from 'pdfjs-dist/legacy/build/pdf'
pdfjsLib.GlobalWorkerOptions.workerSrc = '/lib/build/pdf.worker.mjs' pdfjsLib.GlobalWorkerOptions.workerSrc = '/lib/build/pdf.worker.mjs'
import pdfCanvas from '@/components/pdf/index.vue' import pdfCanvas from '@/components/pdf/index.vue'
const { ipcRenderer } = window.electron || {} const { ipcRenderer } = require('electron')
// //
const pdfObj = reactive({ const pdfObj = reactive({
numberOfPdf: 2, // numberOfPdf: 2, //

View File

@ -1,26 +1,22 @@
<template> <template>
<canvas ref="canvasRef" /> <canvas class="test" ref="canvasRef" />
</template> </template>
<script setup> <script setup>
// electron // electron
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
// import { FabricVue } from '@/plugins/fabric' import { FabricVue } from '@/plugins/fabric'
// import { useBoardStore } from '@/store/modules/draw' import { useBoardStore } from '@/store/modules/draw'
import FabricVue from '@/plugins/fabric/test'
let canvasRef = ref(null) let canvasRef = ref(null)
onMounted(async() => { onMounted(async() => {
console.log(canvasRef, FabricVue) console.log(canvasRef, FabricVue)
if (canvasRef.value) { if (canvasRef.value) {
// useBoardStore().backgroundColor = 'transparent' useBoardStore().backgroundColor = 'transparent'
const option = { freeDrawingCursor: 'default' } const option = { freeDrawingCursor: 'default' }
// await FabricVue.initCanvas(canvasRef.value, option) await FabricVue.initCanvas(canvasRef.value, option)
// FabricVue.canvas.setWidth(500) // FabricVue.canvas.setWidth(500)
// FabricVue.canvas.setHeight(500) // FabricVue.canvas.setHeight(500)
await FabricVue.initCanvas(canvasRef.value, option)
// const pencilBrush = new fabric.PencilBrush(canvas)
FabricVue.canvas.isDrawingMode = true
} }
}) })