zdg #96
|
@ -1453,14 +1453,14 @@ export class History {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 清除记录
|
||||||
clean() {
|
clean() {
|
||||||
this.FabricVue?.canvas?.clear()
|
this.FabricVue?.canvas?.clear()
|
||||||
this.index = 0
|
this.index = 0
|
||||||
this.diffs = []
|
this.diffs = []
|
||||||
this.canvasData = {}
|
this.canvasData = {}
|
||||||
}
|
}
|
||||||
|
// 重新加载历史记录
|
||||||
initHistory() {
|
initHistory() {
|
||||||
const canvas = this.FabricVue.canvas
|
const canvas = this.FabricVue.canvas
|
||||||
if (canvas) {
|
if (canvas) {
|
||||||
|
|
|
@ -69,7 +69,7 @@ export const createWindow = async (type, data) => {
|
||||||
// parent: mainWin, // 父窗口
|
// parent: mainWin, // 父窗口
|
||||||
// autoClose: true, // 关闭窗口后自动关闭
|
// autoClose: true, // 关闭窗口后自动关闭
|
||||||
}
|
}
|
||||||
// data.isConsole = true // 是否开启控制台
|
data.isConsole = true // 是否开启控制台
|
||||||
data.option = {...defOption, ...option}
|
data.option = {...defOption, ...option}
|
||||||
const win = await toolWindow(data)
|
const win = await toolWindow(data)
|
||||||
win.type = type // 唯一标识
|
win.type = type // 唯一标识
|
||||||
|
@ -145,7 +145,7 @@ export function toolWindow({url, isConsole, option={}}) {
|
||||||
// 内部监听器-是否打印
|
// 内部监听器-是否打印
|
||||||
if (!!isConsole) {
|
if (!!isConsole) {
|
||||||
win.webContents.on('console-message', (e,leve,m,lin,s) => {
|
win.webContents.on('console-message', (e,leve,m,lin,s) => {
|
||||||
console.log('console-msg: ', m)
|
console.log(m)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,27 +4,28 @@
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
// 功能说明:画板
|
// 功能说明:画板
|
||||||
import { ref, onMounted, watchEffect } from 'vue'
|
import { ref, onMounted, watch } from 'vue'
|
||||||
import {FabricVue, TYPES} from '@/plugins/fabric'
|
import {FabricVue, TYPES} from '@/plugins/fabric'
|
||||||
const canvasRef = ref(null) // 画布
|
const canvasRef = ref(null) // 画布
|
||||||
const isMouse = ref(true) // 鼠标是否在画布上
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: String
|
modelValue: String
|
||||||
})
|
})
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
// 画板初始化配置
|
// 画板初始化配置
|
||||||
onMounted(async() => {
|
onMounted(async() => {
|
||||||
if (canvasRef.value) {
|
if (canvasRef.value) {
|
||||||
FabricVue.drawConfig.drawColors = ['red']
|
FabricVue.drawConfig.drawColors = ['red']
|
||||||
|
FabricVue.boardConfig.mode = TYPES.ActionMode.OTHER
|
||||||
FabricVue.boardConfig.backgroundColor = 'transparent'
|
FabricVue.boardConfig.backgroundColor = 'transparent'
|
||||||
const option = { freeDrawingCursor: 'default' }
|
const option = { freeDrawingCursor: 'default' }
|
||||||
await FabricVue.initCanvas(canvasRef.value, option)
|
await FabricVue.initCanvas(canvasRef.value, option)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 监听
|
// 监听
|
||||||
watchEffect(() => {
|
watch(() => props.modelValue, (newVal, oldVal) => {
|
||||||
// console.log('board 画板: ', props.modelValue)
|
// console.log(newVal, oldVal)
|
||||||
isMouse.value = false
|
switch(newVal) {
|
||||||
switch(props.modelValue) {
|
|
||||||
case 'select': // 选择模式
|
case 'select': // 选择模式
|
||||||
FabricVue.handleMode(TYPES.ActionMode.OTHER)
|
FabricVue.handleMode(TYPES.ActionMode.OTHER)
|
||||||
break
|
break
|
||||||
|
@ -35,6 +36,10 @@ watchEffect(() => {
|
||||||
case 'eraser': // 板擦模式
|
case 'eraser': // 板擦模式
|
||||||
FabricVue.handleMode(TYPES.ActionMode.ERASE)
|
FabricVue.handleMode(TYPES.ActionMode.ERASE)
|
||||||
break
|
break
|
||||||
|
case 'clear': // 清空画布
|
||||||
|
FabricVue.history?.clean()
|
||||||
|
emit('update:modelValue', oldVal)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 工具栏-拖拽 自定义指令
|
||||||
|
*/
|
||||||
|
class Drag {
|
||||||
|
isDrag = false // 是否开始拖拽
|
||||||
|
rafId // requestAnimationFrame id
|
||||||
|
x = 0 // 鼠标按下时的x坐标
|
||||||
|
y = 0 // 鼠标按下时的y坐标
|
||||||
|
data // 其他参数 元素实际坐标值
|
||||||
|
el; handle; // el, handle 挂载元素和拖拽元素
|
||||||
|
pos // 拖拽元素实际坐标值
|
||||||
|
max // 拖拽元素最大边界
|
||||||
|
// 构造器
|
||||||
|
constructor(el, binding) {
|
||||||
|
this.el = el
|
||||||
|
const { value } = binding
|
||||||
|
const handleSelector = value instanceof Object ? value.handle : value
|
||||||
|
if (!!handleSelector) {
|
||||||
|
if (handleSelector instanceof HTMLElement) this.handle = handleSelector
|
||||||
|
else {
|
||||||
|
this.handle = document.querySelector(handleSelector)
|
||||||
|
// .forEach((child) => { this.handleArray.push(child) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!this.handle) this.handle = el // 默认为当前元素
|
||||||
|
// 被拖拽元素初始坐标
|
||||||
|
const pos = this.handle?.getBoundingClientRect()
|
||||||
|
this.data = {left:this.toRound(pos.left), top:this.toRound(pos.top)}
|
||||||
|
this.pos = pos
|
||||||
|
this.max = {w: window.innerWidth, h: window.innerHeight}
|
||||||
|
}
|
||||||
|
// 移入
|
||||||
|
down(e) {
|
||||||
|
this.isDrag = true
|
||||||
|
const {cx, cy} = this.getMousePos(e)
|
||||||
|
this.x = cx
|
||||||
|
this.y = cy
|
||||||
|
// 手动-触发事件 v-drag-start
|
||||||
|
this.el.dispatchEvent(new CustomEvent('v-drag-start', {detail:{drag: this}}))
|
||||||
|
}
|
||||||
|
// 移动过程
|
||||||
|
move = (e) => {
|
||||||
|
if (!this.isDrag) return
|
||||||
|
if (this.rafId) cancelAnimationFrame(this.rafId) // 清除上一次动画
|
||||||
|
this.rafId = requestAnimationFrame(() => this.updatePosition(e))
|
||||||
|
}
|
||||||
|
// 移出
|
||||||
|
up = (e) => {
|
||||||
|
// e.preventDefault(); // 阻止默认行为
|
||||||
|
// e.stopPropagation(); // 阻止事件传播
|
||||||
|
this.isDrag = false
|
||||||
|
cancelAnimationFrame(this.rafId)
|
||||||
|
this.rafId = null
|
||||||
|
document.removeEventListener('mousemove', this.move);
|
||||||
|
document.removeEventListener('mouseup', this.up);
|
||||||
|
document.addEventListener('touchmove', this.move);
|
||||||
|
document.addEventListener('touchend', this.up);
|
||||||
|
}
|
||||||
|
// 业务逻辑
|
||||||
|
updatePosition(e) {
|
||||||
|
const {cx, cy} = this.getMousePos(e)
|
||||||
|
const {left, top} = this.data
|
||||||
|
const dx = cx - this.x
|
||||||
|
const dy = cy - this.y
|
||||||
|
this.x = cx
|
||||||
|
this.y = cy
|
||||||
|
const {x, y} = this.getPos(left + dx, top + dy) // 调用边界函数
|
||||||
|
this.data.left = x // 设置边界-x
|
||||||
|
this.data.top = y // 设置边界-y
|
||||||
|
// console.log(JSON.stringify(this))
|
||||||
|
this.handle.style.left = `${this.data?.left}px`
|
||||||
|
this.handle.style.top = `${this.data?.top}px`
|
||||||
|
this.handle.style.bottom = 'unset'
|
||||||
|
this.handle.style.transform = 'unset'
|
||||||
|
this.rafId = requestAnimationFrame(() => this.updatePosition(e))
|
||||||
|
}
|
||||||
|
// 获取鼠标位置 | Get mouse position
|
||||||
|
getMousePos(e){
|
||||||
|
let cx = e.clientX || e.touches[0].clientX
|
||||||
|
let cy = e.clientY || e.touches[0].clientY
|
||||||
|
cx = this.toRound(cx)
|
||||||
|
cy = this.toRound(cy)
|
||||||
|
return {cx, cy}
|
||||||
|
}
|
||||||
|
// 获取移动后坐标
|
||||||
|
getPos(x, y) {
|
||||||
|
const w = this.max.w - this.toRound(this.el.clientWidth)
|
||||||
|
const h = this.max.h - this.toRound(this.el.clientHeight)
|
||||||
|
x = x < 0 ? 0 : x > w ? w : x
|
||||||
|
y = y < 0 ? 0 : y > h ? h : y
|
||||||
|
return { x, y }
|
||||||
|
}
|
||||||
|
// 小数转整数
|
||||||
|
toRound = v => Math.round(v)
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
mounted(el, binding) {
|
||||||
|
// const { style } = binding.value
|
||||||
|
const drag = new Drag(el, binding)
|
||||||
|
const dragStart = (e) => {
|
||||||
|
drag.down(e)
|
||||||
|
document.addEventListener('mousemove', drag.move);
|
||||||
|
document.addEventListener('mouseup', drag.up);
|
||||||
|
document.addEventListener('touchmove', drag.move);
|
||||||
|
document.addEventListener('touchend', drag.up);
|
||||||
|
}
|
||||||
|
el.addEventListener('mousedown', dragStart)
|
||||||
|
el.addEventListener('touchstart', dragStart)
|
||||||
|
},
|
||||||
|
}
|
|
@ -1,14 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="warp-all">
|
<div class="warp-all">
|
||||||
<board-vue v-model="tabActive"></board-vue>
|
<board-vue v-model="tabActive"></board-vue>
|
||||||
<!-- 底部工具栏 -->
|
<!-- 底部工具栏 :style="dataPos.style"-->
|
||||||
<div ref="tool" class="tool-bottom-all" :style="dataPos.style"
|
<div class="tool-bottom-all"
|
||||||
@mouseenter="mouseChange(0)" @mouseleave="mouseChange(1)">
|
@mouseenter="mouseChange(0)" @mouseleave="mouseChange(1)">
|
||||||
<div @mousedown="e => dargHandle(e,'down')"
|
<div v-drag="{handle:'.tool-bottom-all', dragtime}"
|
||||||
@mousemove="e => dargHandle(e,'move')"
|
@v-drag-start="dragtime = Date.now()">
|
||||||
@mouseup="e => dargHandle(e,'up')">
|
|
||||||
<div class="c-logo" @click="logoHandle" title="拖动 | 折叠 | 展开">
|
<div class="c-logo" @click="logoHandle" title="拖动 | 折叠 | 展开">
|
||||||
<el-image :src="logo" />
|
<el-image :src="logo" draggable="false" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tool-btns" v-show="!isFold">
|
<div class="tool-btns" v-show="!isFold">
|
||||||
|
@ -16,7 +15,7 @@
|
||||||
@change="tabChange">
|
@change="tabChange">
|
||||||
<template #default="{item}">
|
<template #default="{item}">
|
||||||
<div class="c-btn flex flex-col items-center gap-2 p-2">
|
<div class="c-btn flex flex-col items-center gap-2 p-2">
|
||||||
<i class="iconfont" :class="item.icon"></i>
|
<i class="iconfont" :class="item.icon" :style="item.style"></i>
|
||||||
<span>{{item.label}}</span>
|
<span>{{item.label}}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -31,27 +30,27 @@
|
||||||
import { onMounted, ref, reactive } from 'vue'
|
import { onMounted, ref, reactive } from 'vue'
|
||||||
import logo from '@root/resources/icon.png' // logo
|
import logo from '@root/resources/icon.png' // logo
|
||||||
import boardVue from './components/board.vue' // 画板
|
import boardVue from './components/board.vue' // 画板
|
||||||
import vDrag from '@/directive/drag'
|
import vDrag from './directive/drag'
|
||||||
import { tryOnUnmounted } from '@vueuse/core'
|
|
||||||
// const Remote = require('@electron/remote') // remote对象
|
// const Remote = require('@electron/remote') // remote对象
|
||||||
const { ipcRenderer } = require('electron')
|
const { ipcRenderer } = require('electron') // app使用
|
||||||
|
// const ipcRenderer = { send: () => {} } // 网页测试使用
|
||||||
|
|
||||||
const tool = ref()
|
|
||||||
const tabActive = ref('select') // 工具栏当前选中项
|
const tabActive = ref('select') // 工具栏当前选中项
|
||||||
const isFold = ref(false) // 折叠工具栏
|
const isFold = ref(false) // 折叠工具栏
|
||||||
const isDrag = ref(false) // 开始拖拽
|
const isDrag = ref(false) // 开始拖拽
|
||||||
const dataPos = reactive({style:{}}) // 对象属性
|
const dragtime = ref(0)
|
||||||
const btnList = [ // 工具栏按钮列表
|
const btnList = [ // 工具栏按钮列表
|
||||||
{ label: '选择', value: 'select', icon: 'icon-mouse' },
|
{ label: '选择', value: 'select', icon: 'icon-mouse' },
|
||||||
{ label: '画笔', value: 'brush', icon: 'icon-huabi' },
|
{ label: '画笔', value: 'brush', icon: 'icon-huabi' },
|
||||||
{ label: '板擦', value: 'eraser', icon: 'icon-xiangpica' },
|
{ label: '板擦', value: 'eraser', icon: 'icon-xiangpica' },
|
||||||
{ label: '互动', value: 'interact', icon: 'icon-hudong' },
|
{ label: '清除', value: 'clear', icon: 'icon-xiangpica', style: 'color: #ccc' },
|
||||||
{ label: '聚焦', value: 'focus', icon: 'icon-jujiao' },
|
// { label: '互动', value: 'interact', icon: 'icon-hudong' },
|
||||||
{ label: '更多', value: 'more', icon: 'icon-xiazai9' },
|
// { label: '聚焦', value: 'focus', icon: 'icon-jujiao' },
|
||||||
|
// { label: '更多', value: 'more', icon: 'icon-xiazai9' },
|
||||||
]
|
]
|
||||||
let offsetX = 0, offsetY = 0, dragtime = 0
|
|
||||||
// ==== 方法 ===
|
// ==== 方法 ===
|
||||||
const tabChange = (val) => { // 切换tab-change
|
const tabChange = (val) => { // 切换tab-change
|
||||||
|
console.log(val)
|
||||||
switch (val) {
|
switch (val) {
|
||||||
case 'brush': // 画笔
|
case 'brush': // 画笔
|
||||||
break
|
break
|
||||||
|
@ -71,47 +70,11 @@ const tabChange = (val) => { // 切换tab-change
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const logoHandle = (e,t) => { // logo 点击-事件 折叠|展开
|
const logoHandle = (e,t) => { // logo 点击-事件 折叠|展开
|
||||||
if (Date.now() - dragtime < 200) {
|
if (Date.now() - dragtime.value < 200) {
|
||||||
isFold.value = !isFold.value
|
isFold.value = !isFold.value
|
||||||
}
|
}
|
||||||
console.log('click', isDrag.value)
|
|
||||||
}
|
|
||||||
const dargHandle = (e, type) => { // 拖拽处理
|
|
||||||
e.preventDefault(); // 阻止默认的拖拽行为
|
|
||||||
if (type == 'down') {
|
|
||||||
dragtime = Date.now()
|
|
||||||
return isDrag.value = true
|
|
||||||
} else if (type == 'up') {
|
|
||||||
return isDrag.value = false
|
|
||||||
} else {
|
|
||||||
if (!isDrag.value) return false
|
|
||||||
if (!e.clientX&&!e.clientY){ // 最后一次松开坐标为0
|
|
||||||
offsetX = 0
|
|
||||||
offsetY = 0
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (!offsetX&&!offsetY) { // 第一次, 获取元素坐标
|
|
||||||
setStyle()
|
|
||||||
} else {
|
|
||||||
const x = e.clientX - offsetX
|
|
||||||
const y = e.clientY - offsetY
|
|
||||||
setStyle(x, y)
|
|
||||||
}
|
|
||||||
offsetX = e.clientX
|
|
||||||
offsetY = e.clientY
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const setStyle = (x, y) => { // 拖拽-设置值
|
|
||||||
if (offsetX && offsetY) { // 有值
|
|
||||||
const {left, top} = dataPos.style
|
|
||||||
const ox = parseInt(left.replace('px',''))
|
|
||||||
const oy = parseInt(top.replace('px',''))
|
|
||||||
dataPos.style = {...dataPos.style, left: ox + x + 'px', top: oy + y + 'px'}
|
|
||||||
} else { // 初始值
|
|
||||||
const {left, top} = tool.value.getBoundingClientRect() // 获取元素位置
|
|
||||||
dataPos.style = {bottom: 'unset', transform:'unset', left: left+'px', top: top+'px'}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mouseChange = (bool) => { // 鼠标移入工具栏 是否穿透
|
const mouseChange = (bool) => { // 鼠标移入工具栏 是否穿透
|
||||||
let resBool = false
|
let resBool = false
|
||||||
if (tabActive.value == 'select') resBool = !!bool
|
if (tabActive.value == 'select') resBool = !!bool
|
||||||
|
@ -153,8 +116,7 @@ const mouseChange = (bool) => { // 鼠标移入工具栏 是否穿透
|
||||||
:deep(.el-segmented__item){
|
:deep(.el-segmented__item){
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: 0 0.5rem;
|
margin: 0 0.5rem;
|
||||||
&:nth-last-child(1):before,
|
&:not(:nth-of-type(1)):before{
|
||||||
&:nth-last-child(2):before{
|
|
||||||
content: "";
|
content: "";
|
||||||
width: 2px;
|
width: 2px;
|
||||||
height: calc(100% - 20px);
|
height: calc(100% - 20px);
|
||||||
|
|
Loading…
Reference in New Issue