Merge pull request 'zdg' (#96) from zdg into main

Reviewed-on: #96
This commit is contained in:
zhengdegang 2024-08-02 16:09:31 +08:00
commit fa5e132c2d
5 changed files with 144 additions and 66 deletions

View File

@ -1453,14 +1453,14 @@ export class History {
})
}
}
// 清除记录
clean() {
this.FabricVue?.canvas?.clear()
this.index = 0
this.diffs = []
this.canvasData = {}
}
// 重新加载历史记录
initHistory() {
const canvas = this.FabricVue.canvas
if (canvas) {

View File

@ -69,7 +69,7 @@ export const createWindow = async (type, data) => {
// parent: mainWin, // 父窗口
// autoClose: true, // 关闭窗口后自动关闭
}
// data.isConsole = true // 是否开启控制台
data.isConsole = true // 是否开启控制台
data.option = {...defOption, ...option}
const win = await toolWindow(data)
win.type = type // 唯一标识
@ -145,7 +145,7 @@ export function toolWindow({url, isConsole, option={}}) {
// 内部监听器-是否打印
if (!!isConsole) {
win.webContents.on('console-message', (e,leve,m,lin,s) => {
console.log('console-msg: ', m)
console.log(m)
})
}
})

View File

@ -4,27 +4,28 @@
</template>
<script setup>
//
import { ref, onMounted, watchEffect } from 'vue'
import { ref, onMounted, watch } from 'vue'
import {FabricVue, TYPES} from '@/plugins/fabric'
const canvasRef = ref(null) //
const isMouse = ref(true) //
const props = defineProps({
modelValue: String
})
const emit = defineEmits(['update:modelValue'])
//
onMounted(async() => {
if (canvasRef.value) {
FabricVue.drawConfig.drawColors = ['red']
FabricVue.boardConfig.mode = TYPES.ActionMode.OTHER
FabricVue.boardConfig.backgroundColor = 'transparent'
const option = { freeDrawingCursor: 'default' }
await FabricVue.initCanvas(canvasRef.value, option)
}
})
//
watchEffect(() => {
// console.log('board : ', props.modelValue)
isMouse.value = false
switch(props.modelValue) {
watch(() => props.modelValue, (newVal, oldVal) => {
// console.log(newVal, oldVal)
switch(newVal) {
case 'select': //
FabricVue.handleMode(TYPES.ActionMode.OTHER)
break
@ -35,6 +36,10 @@ watchEffect(() => {
case 'eraser': //
FabricVue.handleMode(TYPES.ActionMode.ERASE)
break
case 'clear': //
FabricVue.history?.clean()
emit('update:modelValue', oldVal)
break
}
})
</script>

View File

@ -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)
},
}

View File

@ -1,14 +1,13 @@
<template>
<div class="warp-all">
<board-vue v-model="tabActive"></board-vue>
<!-- 底部工具栏 -->
<div ref="tool" class="tool-bottom-all" :style="dataPos.style"
<!-- 底部工具栏 :style="dataPos.style"-->
<div class="tool-bottom-all"
@mouseenter="mouseChange(0)" @mouseleave="mouseChange(1)">
<div @mousedown="e => dargHandle(e,'down')"
@mousemove="e => dargHandle(e,'move')"
@mouseup="e => dargHandle(e,'up')">
<div v-drag="{handle:'.tool-bottom-all', dragtime}"
@v-drag-start="dragtime = Date.now()">
<div class="c-logo" @click="logoHandle" title="拖动 | 折叠 | 展开">
<el-image :src="logo" />
<el-image :src="logo" draggable="false" />
</div>
</div>
<div class="tool-btns" v-show="!isFold">
@ -16,7 +15,7 @@
@change="tabChange">
<template #default="{item}">
<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>
</div>
</template>
@ -31,27 +30,27 @@
import { onMounted, ref, reactive } from 'vue'
import logo from '@root/resources/icon.png' // logo
import boardVue from './components/board.vue' //
import vDrag from '@/directive/drag'
import { tryOnUnmounted } from '@vueuse/core'
import vDrag from './directive/drag'
// 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 isFold = ref(false) //
const isDrag = ref(false) //
const dataPos = reactive({style:{}}) //
const dragtime = ref(0)
const btnList = [ //
{ label: '选择', value: 'select', icon: 'icon-mouse' },
{ label: '画笔', value: 'brush', icon: 'icon-huabi' },
{ label: '板擦', value: 'eraser', icon: 'icon-xiangpica' },
{ label: '互动', value: 'interact', icon: 'icon-hudong' },
{ label: '聚焦', value: 'focus', icon: 'icon-jujiao' },
{ label: '更多', value: 'more', icon: 'icon-xiazai9' },
{ label: '清除', value: 'clear', icon: 'icon-xiangpica', style: 'color: #ccc' },
// { label: '', value: 'interact', icon: 'icon-hudong' },
// { label: '', value: 'focus', icon: 'icon-jujiao' },
// { label: '', value: 'more', icon: 'icon-xiazai9' },
]
let offsetX = 0, offsetY = 0, dragtime = 0
// ==== ===
const tabChange = (val) => { // tab-change
console.log(val)
switch (val) {
case 'brush': //
break
@ -71,47 +70,11 @@ const tabChange = (val) => { // 切换tab-change
}
}
const logoHandle = (e,t) => { // logo - |
if (Date.now() - dragtime < 200) {
if (Date.now() - dragtime.value < 200) {
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) => { // 穿
let resBool = false
if (tabActive.value == 'select') resBool = !!bool
@ -153,8 +116,7 @@ const mouseChange = (bool) => { // 鼠标移入工具栏 是否穿透
:deep(.el-segmented__item){
position: relative;
margin: 0 0.5rem;
&:nth-last-child(1):before,
&:nth-last-child(2):before{
&:not(:nth-of-type(1)):before{
content: "";
width: 2px;
height: calc(100% - 20px);