rebuilt: canvas design store to pinia

This commit is contained in:
IchliebedichZhu 2024-03-20 18:08:16 +00:00
parent 3106562174
commit 54b9338e52
20 changed files with 605 additions and 349 deletions

View File

@ -13,11 +13,15 @@
import { useStore } from 'vuex'
import html2canvas from 'html2canvas'
import Qiniu from '@/common/methods/QiNiu'
import { useSetupMapGetters } from '@/common/hooks/mapGetters'
// import { useSetupMapGetters } from '@/common/hooks/mapGetters'
import { storeToRefs } from 'pinia';
import { useCanvasStore } from '@/pinia';
const store = useStore();
const { dZoom } = useSetupMapGetters(['dZoom'])
// const { dZoom } = useSetupMapGetters(['dZoom'])
const canvasStore = useCanvasStore()
const { dZoom } = storeToRefs(canvasStore)
// props: ['modelValue'],
@ -29,7 +33,8 @@ async function createCover(cb: any) {
store.dispatch('selectWidget', {
uuid: '-1',
})
store.dispatch('updateZoom', 100)
canvasStore.updateZoom(100)
// store.dispatch('updateZoom', 100)
const opts = {
useCORS: true, //
@ -51,7 +56,8 @@ async function createCover(cb: any) {
'image/jpeg',
0.15,
)
store.dispatch('updateZoom', nowZoom)
canvasStore.updateZoom(nowZoom)
// store.dispatch('updateZoom', nowZoom)
clonePage.remove()
})
}, 10)

View File

@ -70,7 +70,7 @@ import getComponentsData from '@/common/methods/DesignFeatures/setComponents'
import { debounce } from 'throttle-debounce'
import { move, moveInit } from '@/mixins/move'
import { useSetupMapGetters } from '@/common/hooks/mapGetters'
import { usePageStore } from '@/pinia'
import { useCanvasStore, usePageStore } from '@/pinia'
import { storeToRefs } from 'pinia'
//
type TProps = {
@ -92,11 +92,12 @@ type TSetting = {
const store = useStore()
const { pageDesignCanvasId } = defineProps<TProps>()
const {
dPaddingTop, dZoom, dScreen, dWidgets,
dWidgets,
dActiveElement, dSelectWidgets, dAltDown, dDraging,
dHoverUuid, showRotatable
} = useSetupMapGetters(['dPaddingTop', 'dZoom', 'dScreen', 'dWidgets', 'dActiveElement', 'dHoverUuid', 'dSelectWidgets', 'dAltDown', 'dDraging', 'showRotatable'])
} = useSetupMapGetters(['dWidgets', 'dActiveElement', 'dHoverUuid', 'dSelectWidgets', 'dAltDown', 'dDraging', 'showRotatable'])
const { dPage } = storeToRefs(usePageStore())
const { dZoom, dPaddingTop, dScreen } = storeToRefs(useCanvasStore())
let _dropIn: string | null = ''
@ -188,24 +189,24 @@ async function drop(e: MouseEvent) {
}
})
const half = {
x: parent.width ? (parent.width * store.getters.dZoom) / 100 / 2 : 0,
y: parent.height ? (parent.height * store.getters.dZoom) / 100 / 2 : 0
x: parent.width ? (parent.width * dZoom.value) / 100 / 2 : 0,
y: parent.height ? (parent.height * dZoom.value) / 100 / 2 : 0
}
componentItem.forEach((element) => {
element.left += (lost ? lostX - half.x : e.layerX - half.x) * (100 / store.getters.dZoom)
element.top += (lost ? lostY - half.y : e.layerY - half.y) * (100 / store.getters.dZoom)
element.left += (lost ? lostX - half.x : e.layerX - half.x) * (100 / dZoom.value)
element.top += (lost ? lostY - half.y : e.layerY - half.y) * (100 / dZoom.value)
})
store.dispatch('addGroup', componentItem)
// addGroup(item)
}
//
const half = {
x: setting.width ? (setting.width * store.getters.dZoom) / 100 / 2 : 0,
y: setting.height ? (setting.height * store.getters.dZoom) / 100 / 2 : 0
x: setting.width ? (setting.width * dZoom.value) / 100 / 2 : 0,
y: setting.height ? (setting.height * dZoom.value) / 100 / 2 : 0
}
// const half = { x: (this.dDragInitData.offsetX * this.dZoom) / 100, y: (this.dDragInitData.offsetY * this.dZoom) / 100 }
setting.left = (lost ? lostX - half.x : e.layerX - half.x) * (100 / store.getters.dZoom)
setting.top = (lost ? lostY - half.y : e.layerY - half.y) * (100 / store.getters.dZoom)
setting.left = (lost ? lostX - half.x : e.layerX - half.x) * (100 / dZoom.value)
setting.top = (lost ? lostY - half.y : e.layerY - half.y) * (100 / dZoom.value)
if (lost && type === 'image') {
// svg
const target = await getTarget(eventTarget)

View File

@ -13,6 +13,7 @@
import { watch } from 'vue'
import { useStore } from 'vuex'
import Guides, { GuideOptions } from '@scena/guides'
import { useCanvasStore } from '@/pinia';
type TProps = {
show: boolean
@ -35,6 +36,7 @@ const props = withDefaults(defineProps<TProps>(), {
})
const store = useStore()
const canvasStore = useCanvasStore()
const container = 'page-design' // page-design out-page
let guidesTop: TGuidesData | null = null
let guidesLeft: TGuidesData | null = null
@ -47,7 +49,7 @@ watch(
)
watch(
() => store.getters.dZoom,
() => canvasStore.dZoom,
() => {
changeScroll()
},
@ -113,7 +115,7 @@ function render() {
function changeScroll() {
if (guidesTop && guidesLeft) {
const zoom = store.getters.dZoom / 100
const zoom = canvasStore.dZoom / 100
guidesTop.zoom = zoom
guidesLeft.zoom = zoom
if (zoom < 0.9) {

View File

@ -27,7 +27,7 @@ import { OtherList, TZoomData, ZoomList } from './data';
import { useSetupMapGetters } from '@/common/hooks/mapGetters';
import { useRoute } from 'vue-router';
import { storeToRefs } from 'pinia';
import { usePageStore } from '@/pinia';
import { useCanvasStore, usePageStore } from '@/pinia';
const route = useRoute()
const store = useStore()
@ -48,8 +48,10 @@ const otherIndex = ref(-1)
const bestZoom = ref(0)
const curAction = ref('')
const { dScreen, zoomScreenChange, dZoom } = useSetupMapGetters(['dScreen', 'zoomScreenChange', 'dZoom'])
const { zoomScreenChange } = useSetupMapGetters(['zoomScreenChange'])
const canvasStore = useCanvasStore()
const { dPage } = storeToRefs(usePageStore())
const { dZoom, dScreen } = storeToRefs(canvasStore)
watch(
@ -79,7 +81,8 @@ watch(
if (realValue === -1) {
realValue = calcZoom()
}
store.dispatch('updateZoom', realValue)
canvasStore.updateZoom(realValue)
// store.dispatch('updateZoom', realValue)
// updateZoom(realValue)
autoFixTop()
}
@ -152,7 +155,8 @@ function changeScreen() {
function screenChange() {
//
if (activezoomIndex.value === zoomList.value.length - 1) {
store.dispatch('updateZoom', calcZoom())
canvasStore.updateZoom(calcZoom())
// store.dispatch('updateZoom', calcZoom())
// this.updateZoom(this.calcZoom())
autoFixTop()
}
@ -221,7 +225,8 @@ function sub() {
function mousewheelZoom(down: boolean) {
const value = Number(dZoom.value.toFixed(0))
if (down && value <= 1) return
store.dispatch('updateZoom', down ? value - 1 : value + 1)
canvasStore.updateZoom(down ? value - 1 : value + 1)
// store.dispatch('updateZoom', down ? value - 1 : value + 1)
// updateZoom(down ? value - 1 : value + 1)
zoom.value.text = (value + '%') as any
autoFixTop()

View File

@ -41,7 +41,8 @@
<script lang="ts" setup>
import { reactive, toRefs, onMounted } from 'vue'
import api from '@/api'
import wImage from '../../widgets/wImage/wImage.vue'
// import wImage from '../../widgets/wImage/wImage.vue'
import wImageSetting from '../../widgets/wImage/wImageSetting'
import wSvg from '../../widgets/wSvg/wSvg.vue'
import { useStore } from 'vuex'
import setImageData from '@/common/methods/DesignFeatures/setImage'
@ -187,7 +188,7 @@ async function selectItem(item: TGetListData) {
}
store.commit('setShowMoveable', false) //
// this.$store.commit('setShowMoveable', false)
let setting = item.type === 'svg' ? JSON.parse(JSON.stringify(wSvg.setting)) : JSON.parse(JSON.stringify(wImage.setting))
let setting = item.type === 'svg' ? JSON.parse(JSON.stringify(wSvg.setting)) : JSON.parse(JSON.stringify(wImageSetting))
const img = await setImageData(item)
setting.width = img.width

View File

@ -28,7 +28,8 @@
//
// const NAME = 'img-list-wrap'
import { toRefs, reactive, computed, onMounted } from 'vue'
import wImage from '../../widgets/wImage/wImage.vue'
// import wImage from '../../widgets/wImage/wImage.vue'
import wImageSetting from '../../widgets/wImage/wImageSetting'
import api from '@/api'
import { useStore } from 'vuex'
import setImageData from '@/common/methods/DesignFeatures/setImage'
@ -81,7 +82,7 @@ onMounted(async () => {
const selectImg = async (index: number, list: TGetImageListResult[]) => {
const item = list ? list[index] : state.recommendImgList[index]
store.commit('setShowMoveable', false) //
let setting = JSON.parse(JSON.stringify(wImage.setting))
let setting = JSON.parse(JSON.stringify(wImageSetting))
const img = await setImageData(item) // await getImage(item.url)
setting.width = img.width
setting.height = img.height // parseInt(100 / item.value.ratio, 10)

View File

@ -42,7 +42,8 @@ import { useRouter } from 'vue-router'
import { useStore } from 'vuex'
import uploader from '@/components/common/Uploader'
import api from '@/api'
import wImage from '../../widgets/wImage/wImage.vue'
// import wImage from '../../widgets/wImage/wImage.vue'
import wImageSetting from '../../widgets/wImage/wImageSetting'
import setImageData, { TItem2DataParam } from '@/common/methods/DesignFeatures/setImage'
import useConfirm from '@/common/methods/confirm'
import { TGetImageListResult, TMyPhotoResult } from '@/api/material'
@ -162,7 +163,7 @@ onMounted(() => {
const selectImg = async (index: number) => {
const item = state.imgList[index]
store.commit('setShowMoveable', false) //
let setting = JSON.parse(JSON.stringify(wImage.setting))
let setting = JSON.parse(JSON.stringify(wImageSetting))
const img = await setImageData(item)
setting.width = img.width
setting.height = img.height // parseInt(100 / item.value.ratio, 10)

View File

@ -1,10 +1,10 @@
<template>
<div
:id="params.uuid"
ref="widget"
ref="widgetRef"
:class="['w-image', { 'layer-lock': params.lock }]"
:style="{
position,
position: state.position,
left: params.left - parent.left + 'px',
top: params.top - parent.top + 'px',
width: params.width + 'px',
@ -12,12 +12,18 @@
opacity: params.opacity,
}"
>
<div v-if="cropEdit" :ref="params.uuid + '_ebox'" :style="editBoxStyle" class="svg__edit__wrap">
<div
v-if="cropEdit"
:id="params.uuid + '_ebox'"
:ref="params.uuid + '_ebox'"
:style="state.editBoxStyle"
class="svg__edit__wrap"
>
<img class="edit__model" :src="params.imgUrl" />
</div>
<div :style="{ transform: params.flip ? `rotate${params.flip}(180deg)` : undefined, borderRadius: params.radius + 'px', '-webkit-mask-image': `${params.mask ? `url('${params.mask}')` : undefined}` }" :class="['img__box', { mask: params.mask }]">
<div v-if="params.isNinePatch" ref="target" class="target" :style="{ border: `${(params.height * params.sliceData.ratio) / 2}px solid transparent`, borderImage: `url('${params.imgUrl}') ${params.sliceData.left} round` }"></div>
<img v-else ref="target" class="target" style="transform-origin: center" :src="params.imgUrl" />
<div v-if="params.isNinePatch" ref="targetRef" class="target" :style="{ border: `${(params.height * params.sliceData.ratio) / 2}px solid transparent`, borderImage: `url('${params.imgUrl}') ${params.sliceData.left} round` }"></div>
<img v-else ref="targetRef" class="target" style="transform-origin: center" :src="params.imgUrl" />
</div>
<div v-if="isMask" class="drop__mask">
<div putIn="true" :style="{ fontSize: params.width / 12 + 'px' }" class="drop__btn">拖入</div>
@ -25,254 +31,304 @@
</div>
</template>
<script>
<script lang="ts" setup>
//
const NAME = 'w-image'
import { mapGetters, mapActions } from 'vuex'
// const NAME = 'w-image'
import { CSSProperties, StyleValue, computed, nextTick, onBeforeUnmount, onMounted, onUpdated, reactive, ref, watch } from 'vue'
import { mapGetters, mapActions, useStore } from 'vuex'
import { getMatrix } from '@/common/methods/handleTransform'
import setting from "./wImageSetting"
import PointImg from '@/utils/plugins/pointImg'
import { useSetupMapGetters } from '@/common/hooks/mapGetters'
import { useRoute } from 'vue-router'
import { storeToRefs } from 'pinia'
import { useCanvasStore } from '@/pinia'
export default {
name: NAME,
setting: {
name: '图片',
type: NAME,
uuid: -1,
width: 300,
height: 300,
type TProps = {
params: typeof setting
parent: {
left: number
top: number
}
}
type TState = {
position: 'absolute' | 'relative', // 'absolute'relative
editBoxStyle: CSSProperties,
cropWidgetXY: {
x: number
y: number
}
holdPosition: {
left: number
top: number
}
}
const props = defineProps<TProps>()
const state = reactive<TState>({
position: 'absolute', // 'absolute'relative
editBoxStyle: {
transformOrigin: 'center',
transform: '',
},
cropWidgetXY: {
x: 0,
y: 0,
},
holdPosition: {
left: 0,
top: 0,
zoom: 1,
transform: '',
radius: 0,
opacity: 1,
parent: '-1',
imgUrl: '',
mask: '',
setting: [],
record: {
width: 0,
height: 0,
minWidth: 10,
minHeight: 10,
dir: 'all',
},
},
props: ['params', 'parent'],
data() {
return {
position: 'absolute', // 'absolute'relative
editBoxStyle: {
transformOrigin: 'center',
transform: '',
},
}
},
computed: {
...mapGetters(['dActiveElement', 'dWidgets', 'dZoom', 'dMouseXY', 'dDropOverUuid', 'dCropUuid']),
cropEdit() {
return this.params.uuid === this.dCropUuid
},
tZoom() {
return this.params.zoom
},
isMask() {
return this.params.mask && this.dDropOverUuid === this.params.uuid
},
isDraw() {
return this.$route.name === 'Draw'
},
},
watch: {
cropEdit(val) {
// TODO
if (val) {
document.getElementById(this.params.uuid).addEventListener('mousedown', this.touchstart, false)
} else {
document.getElementById(this.params.uuid).removeEventListener('mousedown', this.touchstart, false)
}
this.fixRotate()
this.lockOthers()
},
async tZoom() {
await this.$nextTick()
this.updateRecord()
},
},
updated() {
this.updateRecord()
this.$store.commit('updateRect')
},
}
})
const route = useRoute()
const store = useStore()
const widgetRef = ref<HTMLElement | null>(null)
const targetRef = ref<HTMLImageElement | null>(null)
async mounted() {
this.updateRecord()
await this.$nextTick()
document.addEventListener('mouseup', this.touchend, false)
// if (this.params.transformData) {
// // this.$refs.widget.style.transform += `scale(${this.params.transformData.a}, ${this.params.transformData.d})`
// this.$refs.widget.style.transform += `matrix(${String(getMatrix(this.params.transformData))})`
// } else {
// // this.$refs.widget.style.transform += `scale(${this.params.transformData.a}, ${this.params.transformData.d})`
// this.params.rotate && (this.$refs.widget.style.transform += `rotate(${this.params.rotate})`)
// }
// console.log(this.params, this.params.filter)
// this.$refs.widget.style.filter = 'hue-rotate(100deg) blur(50px)'
this.$refs.widget.style.transform += this.params.rotate && (this.$refs.widget.style.transform += `rotate(${this.params.rotate})`)
},
beforeUnmount() {
document.removeEventListener('mouseup', this.touchend, false)
},
methods: {
...mapActions(['updateWidgetData']),
touchstart(e) {
const editBox = this.$refs[this.params.uuid + '_ebox']
this.cropWidgetXY = {
x: Number(editBox.style.left.replace('px', '')) || 0,
y: Number(editBox.style.top.replace('px', '')) || 0,
}
//
document.addEventListener('mousemove', this.handlemousemove, true)
},
touchend() {
//
document.removeEventListener('mousemove', this.handlemousemove, true)
// const left = Number(this.editBoxStyle.left.replace('px', ''))
// const flow = (this.params.width * (1 - this.tZoom)) / 2
// if (left + flow < 0) {
// }
},
handlemousemove(e) {
if (!this.move(e)) {
return
}
e && e.stopPropagation()
e && e.preventDefault()
const { left, top } = this.move(e)
// TODO
this.holdPosition = { left, top }
this.editBoxStyle.left = left + 'px'
this.editBoxStyle.top = top + 'px'
this.changeFinish(left / this.params.zoom, top / this.params.zoom)
},
changeFinish(x, y) {
this.setTransform('translate', `${x}px, ${y}px`)
},
move(payload) {
if (payload) {
const widgetXY = { x: this.cropWidgetXY.x, y: this.cropWidgetXY.y }
const dx = Number(payload.pageX) - this.dMouseXY.x
const dy = Number(payload.pageY) - this.dMouseXY.y
let left = Number(widgetXY.x) + Math.floor((dx * 100) / this.dZoom)
let top = Number(widgetXY.y) + Math.floor((dy * 100) / this.dZoom)
// TODO:
// const rotate = Number(this.params.rotate.replace('deg', ''))
// console.log(Math.sin(rotate), Math.cos(rotate))
return { left, top }
} else {
return this.holdPosition
}
},
updateRecord() {
if (this.dActiveElement.uuid === this.params.uuid) {
let record = this.dActiveElement.record
record.width = this.$refs.widget.offsetWidth
record.height = this.$refs.widget.offsetHeight
}
this.updateZoom()
//
if (!this.isDraw) {
const read = new PointImg(this.$refs.target)
}
},
setTransform(attrName, value) {
const iof = this.params.transform.indexOf(attrName)
let setValue = ''
if (iof != -1) {
const index = iof + attrName.length
const tf = this.params.transform
const FRONT = tf.slice(0, index + 1)
const half = tf.substring(index + 1)
const END = half.substring(half.indexOf(')'))
setValue = FRONT + value + END
} else {
setValue = this.params.transform + ` ${attrName}(${value})`
}
this.updateWidgetData({
uuid: this.params.uuid,
key: 'transform',
value: setValue,
pushHistory: false,
})
this.params.transform && (this.$refs.target.style.transform = this.params.transform)
},
setEditBox(attrName, value) {
const iof = this.editBoxStyle.transform.indexOf(attrName)
let setValue = ''
if (iof != -1) {
const index = iof + attrName.length
const tf = this.editBoxStyle.transform
const FRONT = tf.slice(0, index + 1)
const half = tf.substring(index + 1)
const END = half.substring(half.indexOf(')'))
setValue = FRONT + value + END
} else {
setValue = this.editBoxStyle.transform + ` ${attrName}(${value})`
}
this.editBoxStyle.transform = setValue
},
updateZoom() {
this.setEditBox('scale', this.params.zoom)
this.setTransform('scale', this.params.zoom)
// this.$refs.target.style.transform = this.params.transform
this.handlemousemove()
},
fixRotate() {
//
if (this.rotateTemp) {
this.$refs.widget.style.transform = `rotate(${this.rotateTemp})`
this.params.flip = this.flipTemp
this.rotateTemp = null
} else {
this.rotateTemp = this.params.rotate
this.$refs.widget.style.transform = `rotate(0deg)`
this.flipTemp = this.params.flip
this.params.flip = null
}
this.$store.commit('setShowMoveable', false)
setTimeout(() => {
this.$store.commit('setShowMoveable', true)
}, 100)
},
lockOthers() {
//
if (this.locksTemp && this.locksTemp.length > 0) {
for (let i = 0; i < this.locksTemp.length; i++) {
this.dWidgets[i].lock = this.locksTemp[i]
}
this.locksTemp = []
} else {
this.locksTemp = []
for (const widget of this.dWidgets) {
this.locksTemp.push(widget.lock)
}
this.dWidgets.forEach((widget) => {
widget.uuid != this.params.uuid && (widget.lock = true)
})
}
},
// cropDone(e) {
// e.stopPropagation()
// e.preventDefault()
// this.updateWidgetData({
// uuid: this.params.uuid,
// key: 'cropEdit',
// value: false,
// pushHistory: false,
// })
// },
},
let rotateTemp: number | null = null
let flipTemp: string | null = null
let locksTemp: string[] | null = null
const {
dActiveElement, dWidgets, dMouseXY, dDropOverUuid, dCropUuid
} = useSetupMapGetters(['dActiveElement', 'dWidgets', 'dMouseXY', 'dDropOverUuid', 'dCropUuid'])
const { dZoom } = storeToRefs(useCanvasStore())
// ...mapGetters(['dActiveElement', 'dWidgets', 'dZoom', 'dMouseXY', 'dDropOverUuid', 'dCropUuid']),
const cropEdit = computed(() => {
return props.params.uuid === dCropUuid.value
})
const tZoom = computed(() => {
return props.params.zoom
})
const isMask = computed(() => {
return props.params.mask && dDropOverUuid.value === props.params.uuid
})
const isDraw = computed(() => {
return route.name === 'Draw'
})
watch(
() => cropEdit.value,
(val) => {
// TODO
const el = document.getElementById(`${props.params.uuid}`)
if (val) {
el?.addEventListener('mousedown', touchstart, false)
} else {
el?.removeEventListener('mousedown', touchstart, false)
}
fixRotate()
lockOthers()
}
)
watch(
() => tZoom.value,
async () => {
await nextTick()
updateRecord()
}
)
onUpdated(() => {
updateRecord()
store.commit('updateRect')
})
onMounted(async () => {
updateRecord()
await nextTick()
document.addEventListener('mouseup', touchend, false)
// if (this.params.transformData) {
// // this.$refs.widget.style.transform += `scale(${this.params.transformData.a}, ${this.params.transformData.d})`
// this.$refs.widget.style.transform += `matrix(${String(getMatrix(this.params.transformData))})`
// } else {
// // this.$refs.widget.style.transform += `scale(${this.params.transformData.a}, ${this.params.transformData.d})`
// this.params.rotate && (this.$refs.widget.style.transform += `rotate(${this.params.rotate})`)
// }
// console.log(this.params, this.params.filter)
// this.$refs.widget.style.filter = 'hue-rotate(100deg) blur(50px)'
if (widgetRef.value) {
widgetRef.value.style.transform += props.params.rotate && (widgetRef.value.style.transform += `rotate(${props.params.rotate})`)
}
})
onBeforeUnmount(() => {
document.removeEventListener('mouseup', touchend, false)
})
// ...mapActions(['updateWidgetData']),
function touchstart(e: MouseEvent) {
// const editBox = this.$refs[props.params.uuid + '_ebox']
const editBox = document.getElementById(props.params.uuid + '_ebox')
state.cropWidgetXY = {
x: Number(editBox?.style.left.replace('px', '')) || 0,
y: Number(editBox?.style.top.replace('px', '')) || 0,
}
//
document.addEventListener('mousemove', handlemousemove, true)
}
/** 取消鼠标移动事件 */
function touchend() {
document.removeEventListener('mousemove', handlemousemove, true)
// const left = Number(this.editBoxStyle.left.replace('px', ''))
// const flow = (this.params.width * (1 - this.tZoom)) / 2
// if (left + flow < 0) {
// }
}
function handlemousemove(e?: MouseEvent) {
if (!move(e)) {
return
}
e && e.stopPropagation()
e && e.preventDefault()
const { left, top } = move(e)
// TODO
state.holdPosition = { left, top }
state.editBoxStyle.left = left + 'px'
state.editBoxStyle.top = top + 'px'
changeFinish(left / props.params.zoom, top / props.params.zoom)
}
function changeFinish(x: number, y: number) {
setTransform('translate', `${x}px, ${y}px`)
}
function move(payload?: MouseEvent) {
if (payload) {
const widgetXY = { x: state.cropWidgetXY.x, y: state.cropWidgetXY.y }
const dx = Number(payload.pageX) - dMouseXY.value.x
const dy = Number(payload.pageY) - dMouseXY.value.y
let left = Number(widgetXY.x) + Math.floor((dx * 100) / dZoom.value)
let top = Number(widgetXY.y) + Math.floor((dy * 100) / dZoom.value)
// TODO:
// const rotate = Number(this.params.rotate.replace('deg', ''))
// console.log(Math.sin(rotate), Math.cos(rotate))
return { left, top }
} else {
return state.holdPosition
}
}
function updateRecord() {
if (dActiveElement.value.uuid === props.params.uuid) {
let record = dActiveElement.value.record
if (widgetRef.value) {
record.width = widgetRef.value.offsetWidth
record.height = widgetRef.value.offsetHeight
}
}
updateZoom()
//
if (!isDraw.value) {
if (targetRef.value) {
const read = new PointImg(targetRef.value)
}
}
}
function setTransform(attrName: string, value: string | number) {
const iof = props.params.transform.indexOf(attrName)
let setValue = ''
if (iof != -1) {
const index = iof + attrName.length
const tf = props.params.transform
const FRONT = tf.slice(0, index + 1)
const half = tf.substring(index + 1)
const END = half.substring(half.indexOf(')'))
setValue = FRONT + value + END
} else {
setValue = props.params.transform + ` ${attrName}(${value})`
}
store.dispatch("updateWidgetData", {
uuid: props.params.uuid,
key: 'transform',
value: setValue,
pushHistory: false,
})
// updateWidgetData({
// uuid: this.params.uuid,
// key: 'transform',
// value: setValue,
// pushHistory: false,
// })
if (props.params.transform && targetRef.value) {
targetRef.value.style.transform = props.params.transform
}
}
function setEditBox(attrName: string, value: string | number) {
const iof = state.editBoxStyle.transform?.indexOf(attrName)
let setValue = ''
if (iof != -1 && iof != undefined) {
const index = iof + attrName.length
const tf = state.editBoxStyle.transform ?? ''
const FRONT = tf.slice(0, index + 1)
const half = tf.substring(index + 1)
const END = half.substring(half.indexOf(')'))
setValue = FRONT + value + END
} else {
setValue = state.editBoxStyle.transform + ` ${attrName}(${value})`
}
state.editBoxStyle.transform = setValue
}
function updateZoom() {
setEditBox('scale', props.params.zoom)
setTransform('scale', props.params.zoom)
// this.$refs.target.style.transform = this.params.transform
handlemousemove()
}
function fixRotate() {
//
if (rotateTemp) {
widgetRef.value && (widgetRef.value.style.transform = `rotate(${rotateTemp})`)
props.params.flip = flipTemp
rotateTemp = null
} else {
rotateTemp = props.params.rotate
widgetRef.value && (widgetRef.value.style.transform = `rotate(0deg)`)
flipTemp = props.params.flip
props.params.flip = null
}
store.commit('setShowMoveable', false)
setTimeout(() => {
store.commit('setShowMoveable', true)
}, 100)
}
function lockOthers() {
//
if (locksTemp && locksTemp.length > 0) {
for (let i = 0; i < locksTemp.length; i++) {
dWidgets.value[i].lock = locksTemp[i]
}
locksTemp = []
} else {
locksTemp = []
for (const widget of dWidgets.value) {
locksTemp.push(widget.lock)
}
dWidgets.value.forEach((widget: any) => {
widget.uuid != props.params.uuid && (widget.lock = true)
})
}
}
// cropDone(e) {
// e.stopPropagation()
// e.preventDefault()
// this.updateWidgetData({
// uuid: this.params.uuid,
// key: 'cropEdit',
// value: false,
// pushHistory: false,
// })
// },
</script>
<style lang="less" scoped>

View File

@ -0,0 +1,67 @@
export type TImageSetting = {
name: string
type: string
uuid: string
width: number
height: number
left: number
top: number
zoom: number
transform: string
radius: number
opacity: number
parent: string
imgUrl: string
mask: string
setting: [],
rotate: number
record: {
width: number
height: number
minWidth: number
minHeight: number
dir: string
},
lock: false,
isNinePatch: false,
flip: string | null
sliceData: {
ratio: number
left: number
}
}
const setting: TImageSetting = {
name: '图片',
type: 'w-image',
uuid: '-1',
width: 300,
height: 300,
left: 0,
top: 0,
zoom: 1,
transform: '',
radius: 0,
opacity: 1,
parent: '-1',
imgUrl: '',
mask: '',
setting: [],
rotate: 0,
record: {
width: 0,
height: 0,
minWidth: 10,
minHeight: 10,
dir: 'all',
},
lock: false,
isNinePatch: false,
flip: '',
sliceData: {
ratio: 0,
left: 0,
}
}
export default setting

View File

@ -62,7 +62,7 @@ const props = defineProps<TProps>()
const state = reactive<TState>({
qrCodeOptions: {}
})
const { dActiveElement, dZoom } = useSetupMapGetters(['dActiveElement', 'dZoom'])
const { dActiveElement } = useSetupMapGetters(['dActiveElement'])
const width = computed(() => Number(props.params.width))
const widgetRef = ref<HTMLElement | null>(null)

View File

@ -16,7 +16,8 @@ import api from '@/api'
import Qiniu from '@/common/methods/QiNiu'
import _config from '@/config'
import { getImage } from '@/common/methods/getImgDetail'
import wImage from '@/components/modules/widgets/wImage/wImage.vue'
// import wImage from '@/components/modules/widgets/wImage/wImage.vue'
import wImageSetting from '@/components/modules/widgets/wImage/wImageSetting'
import { wTextSetting } from '@/components/modules/widgets/wText/wTextSetting'
import eventBus from '@/utils/plugins/eventBus'
import { usePageStore } from '@/pinia'
@ -45,7 +46,7 @@ export default () => {
eventBus.emit('refreshUserImages')
// 添加图片到画布中
store.commit('setShowMoveable', false) // 清理掉上一次的选择
const setting = JSON.parse(JSON.stringify(wImage.setting))
const setting = JSON.parse(JSON.stringify(wImageSetting))
setting.width = width
setting.height = height
setting.imgUrl = url

View File

@ -0,0 +1,96 @@
/*
* @Author: Jeremy Yu
* @Date: 2024-03-18 21:00:00
* @Description:
* @LastEditors: Jeremy Yu <https://github.com/JeremyYu-cn>
* @LastEditTime: 2024-03-18 21:00:00
*/
import { defineStore } from 'pinia'
type TScreeData = {
/** 记录编辑界面的宽度 */
width: number
/** 记录编辑界面的高度 */
height: number
}
type TGuidelinesData = {
verticalGuidelines: number[]
horizontalGuidelines: number[]
}
type TCanvasStore = {
/** 画布缩放百分比 */
dZoom: number
/** 画布垂直居中修正值 */
dPaddingTop: number
/** 编辑界面 */
dScreen: TScreeData
/** 标尺辅助线 */
guidelines: TGuidelinesData
}
type TStoreGetter = {
dZoom: (state: TCanvasStore) => number
dPaddingTop: (state: TCanvasStore) => number
dScreen: (state: TCanvasStore) => TScreeData
guidelines: (state: TCanvasStore) => TGuidelinesData
}
type TStoreAction = {
/** 更新画布缩放百分比 */
updateZoom: (zoom: number) => void
/** 更新画布垂直居中修正值 */
updatePaddingTop: (num: number) => void
/** 更新编辑界面的宽高 */
updateScreen: (data: TScreeData) => void
/** 修改标尺线 */
updateGuidelines: (lines: TGuidelinesData) => void
}
export default defineStore<"canvasStore", TCanvasStore, TStoreGetter, TStoreAction>("canvasStore", {
state: () => ({
dZoom: 0, // 画布缩放百分比
dPaddingTop: 0, // 画布垂直居中修正值
dScreen: {
width: 0, // 记录编辑界面的宽度
height: 0, // 记录编辑界面的高度
},
// gridSize: {
// width: 0, // 网格小格子的宽度
// height: 0, // 网格小格子的高度
// },
guidelines: {
// moveable 标尺辅助线
verticalGuidelines: [],
horizontalGuidelines: [],
},
}),
getters: {
dZoom: state => state.dZoom,
dPaddingTop: state => state.dPaddingTop,
dScreen: state => state.dScreen,
guidelines: state => state.guidelines
},
actions: {
/** 更新画布缩放百分比 */
updateZoom(zoom: number) {
this.dZoom = zoom
},
/** 更新画布垂直居中修正值 */
updatePaddingTop(num: number) {
this.dPaddingTop = num
},
/** 更新编辑界面的宽高 */
updateScreen({ width, height }: TScreeData) {
this.dScreen.width = width
this.dScreen.height = height
},
/** 修改标尺线 */
updateGuidelines(lines: TGuidelinesData) {
this.guidelines = { ...this.guidelines, ...lines }
}
}
})

View File

@ -1,3 +1,11 @@
/*
* @Author: Jeremy Yu
* @Date: 2024-03-18 21:00:00
* @Description: Page全局配置
* @LastEditors: Jeremy Yu <https://github.com/JeremyYu-cn>
* @LastEditTime: 2024-03-18 21:00:00
*/
import { defineStore } from 'pinia'
export type TPageStore = {

View File

@ -8,10 +8,12 @@
import useBaseStore from "./base";
import useUserStore from "./base/user";
import usePageStore from "./design/page/index"
import usePageStore from "./design/page"
import useCanvasStore from "./design/canvas"
export {
useBaseStore,
useUserStore,
usePageStore,
useCanvasStore,
}

View File

@ -23,25 +23,29 @@ export default {
// 激活组件默认为page
store.state.dActiveElement = store.state.dPage
},
updateZoom(store, zoom) {
store.state.dZoom = zoom
},
updateScreen(store, { width, height }) {
store.state.dScreen.width = width
store.state.dScreen.height = height
},
// updateZoom(store, zoom) {
// store.state.dZoom = zoom
// },
// updateScreen(store, { width, height }) {
// store.state.dScreen.width = width
// store.state.dScreen.height = height
// },
// updateGridSize(store, { width, height }) {
// store.state.gridSize.width = width
// store.state.gridSize.height = height
// },
updatePageData(store, { key, value, pushHistory }) {
const page = store.state.dPage
if (page[key] !== value || pushHistory) {
page[key] = value
// 画布修改先不压入历史栈,因为替换模板后会重复压栈
// store.dispatch('pushHistory', 'updatePageData')
}
},
// updatePageData(store, { key, value, pushHistory }) {
// const page = store.state.dPage
// if (page[key] !== value || pushHistory) {
// page[key] = value
// // 画布修改先不压入历史栈,因为替换模板后会重复压栈
// // store.dispatch('pushHistory', 'updatePageData')
// }
// },
updateWidgetData(store, { uuid, key, value, pushHistory }: any) {
const widget = store.state.dWidgets.find((item) => item.uuid === uuid)
if (widget && (widget[key] !== value || pushHistory)) {

View File

@ -2,12 +2,12 @@ import mutations from './mutations'
import actions from './actions'
const all = {
state: {
dZoom: 0, // 画布缩放百分比
dPaddingTop: 0, // 画布垂直居中修正值
dScreen: {
width: 0, // 记录编辑界面的宽度
height: 0, // 记录编辑界面的高度
},
// dZoom: 0, // 画布缩放百分比
// dPaddingTop: 0, // 画布垂直居中修正值
// dScreen: {
// width: 0, // 记录编辑界面的宽度
// height: 0, // 记录编辑界面的高度
// },
// gridSize: {
// width: 0, // 网格小格子的宽度
// height: 0, // 网格小格子的高度
@ -38,28 +38,28 @@ const all = {
dCopyElement: [], // 复制的组件(可能是单个也可能是数组)
dHoverUuid: '-1', // 鼠标在这个图层上
dDropOverUuid: '', // 拖动时放在哪个图层上
dPage: {
name: '背景页面',
type: 'page',
uuid: '-1',
left: 0,
top: 0,
width: 1920, // 画布宽度
height: 1080, // 画布高度
backgroundColor: '#ffffff', // 画布背景颜色
backgroundImage: '', // 画布背景图片
backgroundTransform: {},
opacity: 1, // 透明度
tag: 0, // 强制刷新用
setting: [
{
label: '背景颜色',
parentKey: 'backgroundColor',
value: false,
},
],
record: {},
},
// dPage: {
// name: '背景页面',
// type: 'page',
// uuid: '-1',
// left: 0,
// top: 0,
// width: 1920, // 画布宽度
// height: 1080, // 画布高度
// backgroundColor: '#ffffff', // 画布背景颜色
// backgroundImage: '', // 画布背景图片
// backgroundTransform: {},
// opacity: 1, // 透明度
// tag: 0, // 强制刷新用
// setting: [
// {
// label: '背景颜色',
// parentKey: 'backgroundColor',
// value: false,
// },
// ],
// record: {},
// },
dWidgets: [], // 已使用的组件
dHistory: [], // 记录历史操作保存整个画布的json数据
dActiveUuidHistory: [], // 记录历史操作对应的激活的组件的uuid
@ -87,15 +87,15 @@ const all = {
selectItem(state: any) {
return state.selectItem
},
dZoom(state) {
return state.dZoom
},
dPaddingTop(state: any) {
return state.dPaddingTop
},
dScreen(state) {
return state.dScreen
},
// dZoom(state) {
// return state.dZoom
// },
// dPaddingTop(state: any) {
// return state.dPaddingTop
// },
// dScreen(state) {
// return state.dScreen
// },
// gridSize(state) {
// return state.gridSize
// },
@ -114,9 +114,9 @@ const all = {
dActiveElement(state) {
return state.dActiveElement
},
dPage(state) {
return state.dPage
},
// dPage(state) {
// return state.dPage
// },
dWidgets(state) {
return state.dWidgets
},

View File

@ -13,9 +13,9 @@ interface Iprops {
state: {}
}
export default {
updatePaddingTop(state: Type.Object, num: number) {
state.dPaddingTop = num
},
// updatePaddingTop(state: Type.Object, num: number) {
// state.dPaddingTop = num
// },
selectItem(state: Type.Object, { data, type }: any) {
state.selectItem.data = data
state.selectItem.type = type
@ -36,9 +36,11 @@ export default {
setDWidgets(state: Type.Object, e: any) {
state.dWidgets = e
},
setDPage(state: Type.Object, e: any) {
state.dPage = e
},
// setDPage(state: Type.Object, e: any) {
// state.dPage = e
// },
setShowMoveable(state: Type.Object, show: any) {
state.showMoveable = show
// if (!show) {
@ -61,10 +63,10 @@ export default {
// 强制触发元素选择
state.updateSelect = Math.random()
},
updateGuidelines(state: Type.Object, lines: any) {
// 修改标尺线
state.guidelines = { ...state.guidelines, ...lines }
},
// updateGuidelines(state: Type.Object, lines: any) {
// // 修改标尺线
// state.guidelines = { ...state.guidelines, ...lines }
// },
setCropUuid(state: Type.Object, uuid: any) {
// 设置正在裁剪or编辑的组件
state.dCropUuid = uuid

View File

View File

@ -74,7 +74,7 @@ import { useSetupMapGetters } from '@/common/hooks/mapGetters'
import { useRoute } from 'vue-router'
import { wGroupSetting } from '@/components/modules/widgets/wGroup/groupSetting'
import { storeToRefs } from 'pinia'
import { usePageStore } from '@/pinia'
import { useCanvasStore, usePageStore } from '@/pinia'
type TState = {
style: CSSProperties
@ -96,9 +96,10 @@ const beforeUnload = function (e: Event): string {
!_config.isDev && window.addEventListener('beforeunload', beforeUnload)
const {
dActiveElement, dHistoryParams, dCopyElement, dZoom
} = useSetupMapGetters(['dActiveElement', 'dHistoryParams', 'dCopyElement', 'dZoom'])
dActiveElement, dHistoryParams, dCopyElement
} = useSetupMapGetters(['dActiveElement', 'dHistoryParams', 'dCopyElement'])
const { dPage } = storeToRefs(usePageStore())
const { dZoom } = storeToRefs(useCanvasStore())
const state = reactive<TState>({

View File

@ -61,7 +61,8 @@ import RightClickMenu from '@/components/business/right-click-menu/RcMenu.vue'
import Moveable from '@/components/business/moveable/Moveable.vue'
import shortcuts from '@/mixins/shortcuts'
// import wText from '@/components/modules/widgets/wText/wText.vue'
import wImage from '@/components/modules/widgets/wImage/wImage.vue'
// import wImage from '@/components/modules/widgets/wImage/wImage.vue'
import wImageSetting from '@/components/modules/widgets/wImage/wImageSetting'
import useLoading from '@/common/methods/loading'
import uploader from '@/components/common/Uploader/index.vue'
import designBoard from '@/components/modules/layout/designBoard/index.vue'
@ -72,7 +73,7 @@ import ProgressLoading from '@/components/common/ProgressLoading/index.vue'
import { processPSD2Page } from '@/utils/plugins/psd'
import { useSetupMapGetters } from '@/common/hooks/mapGetters'
import { wTextSetting } from '@/components/modules/widgets/wText/wTextSetting'
import { usePageStore } from '@/pinia'
import { useCanvasStore, usePageStore } from '@/pinia'
import { storeToRefs } from 'pinia'
type TState = {
@ -94,9 +95,10 @@ const state = reactive<TState>({
const store = useStore()
const route = useRoute()
const { dZoom } = useSetupMapGetters(['dZoom'])
// const { dZoom } = useSetupMapGetters(['dZoom'])
const pageStore = usePageStore()
const { dPage } = storeToRefs(pageStore)
const { dZoom } = storeToRefs(useCanvasStore())
const zoomControlRef = ref<typeof zoomControl | null>()
@ -131,7 +133,7 @@ async function loadPSD(file: File) {
setTimeout(async () => {
const types: any = {
text: wTextSetting,
image: wImage.setting,
image: wImageSetting,
}
for (let i = 0; i < data.clouds.length; i++) {
const x: any = data.clouds[i]