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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

View File

@ -74,7 +74,7 @@ import { useSetupMapGetters } from '@/common/hooks/mapGetters'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { wGroupSetting } from '@/components/modules/widgets/wGroup/groupSetting' import { wGroupSetting } from '@/components/modules/widgets/wGroup/groupSetting'
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
import { usePageStore } from '@/pinia' import { useCanvasStore, usePageStore } from '@/pinia'
type TState = { type TState = {
style: CSSProperties style: CSSProperties
@ -96,9 +96,10 @@ const beforeUnload = function (e: Event): string {
!_config.isDev && window.addEventListener('beforeunload', beforeUnload) !_config.isDev && window.addEventListener('beforeunload', beforeUnload)
const { const {
dActiveElement, dHistoryParams, dCopyElement, dZoom dActiveElement, dHistoryParams, dCopyElement
} = useSetupMapGetters(['dActiveElement', 'dHistoryParams', 'dCopyElement', 'dZoom']) } = useSetupMapGetters(['dActiveElement', 'dHistoryParams', 'dCopyElement'])
const { dPage } = storeToRefs(usePageStore()) const { dPage } = storeToRefs(usePageStore())
const { dZoom } = storeToRefs(useCanvasStore())
const state = reactive<TState>({ 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 Moveable from '@/components/business/moveable/Moveable.vue'
import shortcuts from '@/mixins/shortcuts' import shortcuts from '@/mixins/shortcuts'
// import wText from '@/components/modules/widgets/wText/wText.vue' // 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 useLoading from '@/common/methods/loading'
import uploader from '@/components/common/Uploader/index.vue' import uploader from '@/components/common/Uploader/index.vue'
import designBoard from '@/components/modules/layout/designBoard/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 { processPSD2Page } from '@/utils/plugins/psd'
import { useSetupMapGetters } from '@/common/hooks/mapGetters' import { useSetupMapGetters } from '@/common/hooks/mapGetters'
import { wTextSetting } from '@/components/modules/widgets/wText/wTextSetting' import { wTextSetting } from '@/components/modules/widgets/wText/wTextSetting'
import { usePageStore } from '@/pinia' import { useCanvasStore, usePageStore } from '@/pinia'
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
type TState = { type TState = {
@ -94,9 +95,10 @@ const state = reactive<TState>({
const store = useStore() const store = useStore()
const route = useRoute() const route = useRoute()
const { dZoom } = useSetupMapGetters(['dZoom']) // const { dZoom } = useSetupMapGetters(['dZoom'])
const pageStore = usePageStore() const pageStore = usePageStore()
const { dPage } = storeToRefs(pageStore) const { dPage } = storeToRefs(pageStore)
const { dZoom } = storeToRefs(useCanvasStore())
const zoomControlRef = ref<typeof zoomControl | null>() const zoomControlRef = ref<typeof zoomControl | null>()
@ -131,7 +133,7 @@ async function loadPSD(file: File) {
setTimeout(async () => { setTimeout(async () => {
const types: any = { const types: any = {
text: wTextSetting, text: wTextSetting,
image: wImage.setting, image: wImageSetting,
} }
for (let i = 0; i < data.clouds.length; i++) { for (let i = 0; i < data.clouds.length; i++) {
const x: any = data.clouds[i] const x: any = data.clouds[i]