mirror of
https://github.com/palxiao/poster-design.git
synced 2025-07-15 16:02:19 +08:00
feat: 添加裁剪功能
This commit is contained in:
parent
3419879ebf
commit
6aa77b1a3d
@ -2,7 +2,7 @@
|
|||||||
* @Description: widget move
|
* @Description: widget move
|
||||||
* @Author: xi_zi
|
* @Author: xi_zi
|
||||||
* @Date: 2024-04-03 10:25:49
|
* @Date: 2024-04-03 10:25:49
|
||||||
* @LastEditTime: 2024-04-04 00:28:22
|
* @LastEditTime: 2024-04-04 11:52:22
|
||||||
* @LastEditors: xi_zi
|
* @LastEditors: xi_zi
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
@ -73,6 +73,9 @@ import type {
|
|||||||
OnResizeGroupEnd,
|
OnResizeGroupEnd,
|
||||||
OnRotateGroup,
|
OnRotateGroup,
|
||||||
OnRotateGroupEnd,
|
OnRotateGroupEnd,
|
||||||
|
ClippableProps,
|
||||||
|
OnClip,
|
||||||
|
OnClipEnd,
|
||||||
} from 'vue3-moveable'
|
} from 'vue3-moveable'
|
||||||
import { TUpdateWidgetMultiplePayload } from '@/store/design/widget/actions/widget'
|
import { TUpdateWidgetMultiplePayload } from '@/store/design/widget/actions/widget'
|
||||||
|
|
||||||
@ -81,9 +84,8 @@ type TModifierStyle = { transform?: string; transformOrigin?: string; width?: nu
|
|||||||
const widgetStore = useWidgetStore()
|
const widgetStore = useWidgetStore()
|
||||||
const controlStore = useControlStore()
|
const controlStore = useControlStore()
|
||||||
const forceStore = useForceStore()
|
const forceStore = useForceStore()
|
||||||
const historyStore = useHistoryStore()
|
|
||||||
const { guidelines } = storeToRefs(useCanvasStore())
|
const { guidelines } = storeToRefs(useCanvasStore())
|
||||||
const { showMoveable, showRotatable, dAltDown, resizable, scalable, warpable, dSpaceDown } = storeToRefs(controlStore)
|
const { showMoveable, showRotatable, dAltDown, resizable, scalable, warpable, clippable, dSpaceDown } = storeToRefs(controlStore)
|
||||||
const { dSelectWidgets, dActiveElement, activeMouseEvent, dWidgets } = storeToRefs(widgetStore)
|
const { dSelectWidgets, dActiveElement, activeMouseEvent, dWidgets } = storeToRefs(widgetStore)
|
||||||
const { updateRect, updateSelect } = storeToRefs(forceStore)
|
const { updateRect, updateSelect } = storeToRefs(forceStore)
|
||||||
|
|
||||||
@ -112,6 +114,7 @@ const updateStoreModifierStyle = (target: HTMLElement | SVGElement, { transform,
|
|||||||
widgetStore.updateWidgetMultiple({
|
widgetStore.updateWidgetMultiple({
|
||||||
uuid,
|
uuid,
|
||||||
data: updateData,
|
data: updateData,
|
||||||
|
pushHistory: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,12 +254,33 @@ const rotateOptions = reactive<RotatableProps>({
|
|||||||
onRotateEnd,
|
onRotateEnd,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const onClip = (e: OnClip) => {
|
||||||
|
e.target.style.clipPath = e.clipStyle
|
||||||
|
}
|
||||||
|
const onClipEnd = (e: OnClipEnd) => {
|
||||||
|
const uuid = dActiveElement.value?.uuid || ''
|
||||||
|
widgetStore.updateWidgetData({
|
||||||
|
uuid,
|
||||||
|
key: 'clipPath',
|
||||||
|
value: e.target.style.clipPath,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const clipOptions = reactive<ClippableProps>({
|
||||||
|
clippable: true,
|
||||||
|
clipRelative: false,
|
||||||
|
clipArea: false,
|
||||||
|
clipTargetBounds: false,
|
||||||
|
onClip,
|
||||||
|
onClipEnd,
|
||||||
|
})
|
||||||
|
|
||||||
const options = computed(() => {
|
const options = computed(() => {
|
||||||
const opt = { ...dragOptions }
|
const opt = { ...dragOptions }
|
||||||
if (showRotatable.value) Object.assign(opt, rotateOptions)
|
if (showRotatable.value) Object.assign(opt, rotateOptions)
|
||||||
if (warpable.value) Object.assign(opt, warpOptions)
|
if (warpable.value) Object.assign(opt, warpOptions)
|
||||||
if (scalable.value) Object.assign(opt, scaleOptions)
|
if (scalable.value) Object.assign(opt, scaleOptions)
|
||||||
if (resizable.value) Object.assign(opt, resizeOptions)
|
if (resizable.value) Object.assign(opt, resizeOptions)
|
||||||
|
if (clippable.value) Object.assign(opt, clipOptions)
|
||||||
return opt
|
return opt
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -412,8 +412,8 @@ function getChilds(uuid: string) {
|
|||||||
// },
|
// },
|
||||||
|
|
||||||
const widgetStyle = (widgetData: TdWidgetData): CSSProperties => {
|
const widgetStyle = (widgetData: TdWidgetData): CSSProperties => {
|
||||||
const { transform, transformOrigin, width, height } = widgetData
|
const { transform, transformOrigin, width, height, opacity, clipPath } = widgetData
|
||||||
const style: CSSProperties = { position: 'absolute' }
|
const style: CSSProperties = { position: 'absolute', opacity, clipPath }
|
||||||
if (transform) style.transform = transform
|
if (transform) style.transform = transform
|
||||||
if (transformOrigin) style.transformOrigin = transformOrigin
|
if (transformOrigin) style.transformOrigin = transformOrigin
|
||||||
if (width !== undefined) style.width = `${width}px`
|
if (width !== undefined) style.width = `${width}px`
|
||||||
@ -422,11 +422,11 @@ const widgetStyle = (widgetData: TdWidgetData): CSSProperties => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const groupWidgetChildrenStyle = (groupWidgetData: TdWidgetData, widgetData: TdWidgetData): CSSProperties => {
|
const groupWidgetChildrenStyle = (groupWidgetData: TdWidgetData, widgetData: TdWidgetData): CSSProperties => {
|
||||||
const { transform, transformOrigin, width, height } = widgetData
|
const { transform, transformOrigin, width, height, opacity, clipPath } = widgetData
|
||||||
const { transform: gTransform } = groupWidgetData
|
const { transform: gTransform } = groupWidgetData
|
||||||
const { x: gX, y: gY } = getOffsetFromTransform(gTransform || '')
|
const { x: gX, y: gY } = getOffsetFromTransform(gTransform || '')
|
||||||
const { x, y } = getOffsetFromTransform(transform || '')
|
const { x, y } = getOffsetFromTransform(transform || '')
|
||||||
const style: CSSProperties = { position: 'absolute' }
|
const style: CSSProperties = { position: 'absolute', opacity, clipPath }
|
||||||
if (transform) style.transform = removeTranslate(transform)
|
if (transform) style.transform = removeTranslate(transform)
|
||||||
if (transformOrigin) style.transformOrigin = transformOrigin
|
if (transformOrigin) style.transformOrigin = transformOrigin
|
||||||
if (width !== undefined) style.width = `${width}px`
|
if (width !== undefined) style.width = `${width}px`
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* @Date: 2021-08-09 11:41:53
|
* @Date: 2021-08-09 11:41:53
|
||||||
* @Description:
|
* @Description:
|
||||||
* @LastEditors: xi_zi
|
* @LastEditors: xi_zi
|
||||||
* @LastEditTime: 2024-04-04 11:13:38
|
* @LastEditTime: 2024-04-04 11:47:25
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<div id="w-image-style">
|
<div id="w-image-style">
|
||||||
@ -21,8 +21,10 @@
|
|||||||
<!-- <el-button size="mini" style="width: 100%; margin-top: 0.5rem" plain @click="openCropper">替换图片</el-button> -->
|
<!-- <el-button size="mini" style="width: 100%; margin-top: 0.5rem" plain @click="openCropper">替换图片</el-button> -->
|
||||||
<el-button style="width: 100%; margin-bottom: 12px" plain @click="openPicBox">替换图片</el-button>
|
<el-button style="width: 100%; margin-bottom: 12px" plain @click="openPicBox">替换图片</el-button>
|
||||||
<div class="options">
|
<div class="options">
|
||||||
<el-button v-if="state.innerElement.cropEdit" plain type="primary" @click="imgCrop(false)">完成</el-button>
|
<el-button v-if="clippable" plain type="primary" @click="imgCrop(false)">完成</el-button>
|
||||||
<el-button v-else plain type="primary" @click="imgCrop(true)"><i class="icon sd-caijian" /> 裁剪</el-button>
|
<el-button v-else plain type="primary" @click="imgCrop(true)"><i class="icon sd-caijian" /> 裁剪</el-button>
|
||||||
|
<!-- <el-button v-if="state.innerElement.cropEdit" plain type="primary" @click="imgCrop(false)">完成</el-button>
|
||||||
|
<el-button v-else plain type="primary" @click="imgCrop(true)"><i class="icon sd-caijian" /> 裁剪</el-button> -->
|
||||||
<el-button plain @click="openImageCutout"><i class="icon sd-AIkoutu" /> 抠图</el-button>
|
<el-button plain @click="openImageCutout"><i class="icon sd-AIkoutu" /> 抠图</el-button>
|
||||||
<!-- <uploader class="options__upload" @done="uploadImgDone">
|
<!-- <uploader class="options__upload" @done="uploadImgDone">
|
||||||
<el-button size="small" plain>替换图片</el-button>
|
<el-button size="small" plain>替换图片</el-button>
|
||||||
@ -137,7 +139,7 @@ const canvasStore = useCanvasStore()
|
|||||||
// dActiveElement, dWidgets
|
// dActiveElement, dWidgets
|
||||||
// } = useSetupMapGetters(['dActiveElement', 'dWidgets'])
|
// } = useSetupMapGetters(['dActiveElement', 'dWidgets'])
|
||||||
const controlStore = useControlStore()
|
const controlStore = useControlStore()
|
||||||
const { dMoving } = storeToRefs(controlStore)
|
const { dMoving, clippable } = storeToRefs(controlStore)
|
||||||
const { dActiveElement, dWidgets } = storeToRefs(widgetStore)
|
const { dActiveElement, dWidgets } = storeToRefs(widgetStore)
|
||||||
|
|
||||||
let lastUuid: string | undefined = undefined
|
let lastUuid: string | undefined = undefined
|
||||||
@ -317,14 +319,15 @@ async function selectDone(img: TGetImageListResult) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function imgCrop(val: boolean) {
|
async function imgCrop(val: boolean) {
|
||||||
// TODO: 画布内图像裁剪
|
/* // TODO: 画布内图像裁剪
|
||||||
const el = document.getElementById(state.innerElement.uuid || '')
|
const el = document.getElementById(state.innerElement.uuid || '')
|
||||||
if (!el) return
|
if (!el) return
|
||||||
const { left, top } = el.getBoundingClientRect()
|
const { left, top } = el.getBoundingClientRect()
|
||||||
toolBarStyle = { left: left + 'px', top: top + 'px' }
|
toolBarStyle = { left: left + 'px', top: top + 'px' }
|
||||||
state.innerElement.cropEdit = val
|
state.innerElement.cropEdit = val
|
||||||
await nextTick()
|
await nextTick()
|
||||||
controlStore.setShowRotatable(!val)
|
controlStore.setShowRotatable(!val) */
|
||||||
|
controlStore.setClippable(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
function cropHandle() {
|
function cropHandle() {
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* @Author: Jeremy Yu
|
* @Author: Jeremy Yu
|
||||||
* @Date: 2024-03-18 21:00:00
|
* @Date: 2024-03-18 21:00:00
|
||||||
* @Description:
|
* @Description:
|
||||||
* @LastEditors: xi_zi
|
* @LastEditors: xi_zi
|
||||||
* @LastEditTime: 2024-04-03 10:26:38
|
* @LastEditTime: 2024-04-04 11:50:51
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useHistoryStore } from "@/store";
|
import { useHistoryStore } from '@/store'
|
||||||
import { Store, defineStore } from "pinia";
|
import { Store, defineStore } from 'pinia'
|
||||||
|
|
||||||
type TControlState = {
|
type TControlState = {
|
||||||
/** 是否正在移动组件 */
|
/** 是否正在移动组件 */
|
||||||
@ -29,9 +28,10 @@ type TControlState = {
|
|||||||
dSpaceDown: boolean
|
dSpaceDown: boolean
|
||||||
/** 正在编辑or裁剪的组件id **/
|
/** 正在编辑or裁剪的组件id **/
|
||||||
dCropUuid: string
|
dCropUuid: string
|
||||||
warpable: boolean, // 是否开启斜切
|
warpable: boolean // 是否开启斜切
|
||||||
scalable: boolean, // 是否开启缩放
|
scalable: boolean // 是否开启缩放
|
||||||
resizable: boolean, // 是否开启拖拽
|
resizable: boolean // 是否开启可调整大小
|
||||||
|
clippable: boolean // 是否裁剪
|
||||||
}
|
}
|
||||||
|
|
||||||
type TControlAction = {
|
type TControlAction = {
|
||||||
@ -66,11 +66,22 @@ type TControlAction = {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
setScalable: (able: boolean) => void
|
setScalable: (able: boolean) => void
|
||||||
|
/**
|
||||||
|
* 设置裁剪
|
||||||
|
* @param able
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
setClippable: (able: boolean) => void
|
||||||
|
/**
|
||||||
|
* 初始化moveable控制
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
initMoveableControl: () => void
|
||||||
setSpaceDown: (uuid: boolean) => void // 设置是否按下空格键
|
setSpaceDown: (uuid: boolean) => void // 设置是否按下空格键
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 全局控制配置 */
|
/** 全局控制配置 */
|
||||||
const ControlStore = defineStore<"controlStore", TControlState, {}, TControlAction>("controlStore", {
|
const ControlStore = defineStore<'controlStore', TControlState, {}, TControlAction>('controlStore', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
dMoving: false, // 是否正在移动组件
|
dMoving: false, // 是否正在移动组件
|
||||||
dDraging: false, // 是否正在抓取组件
|
dDraging: false, // 是否正在抓取组件
|
||||||
@ -82,7 +93,8 @@ const ControlStore = defineStore<"controlStore", TControlState, {}, TControlActi
|
|||||||
dCropUuid: '-1', // 正在编辑or裁剪的组件id
|
dCropUuid: '-1', // 正在编辑or裁剪的组件id
|
||||||
warpable: false, // 是否开启斜切
|
warpable: false, // 是否开启斜切
|
||||||
scalable: false, // 是否开启缩放
|
scalable: false, // 是否开启缩放
|
||||||
resizable: true, // 是否开启拖拽
|
resizable: true, // 是否开启可调整大小
|
||||||
|
clippable: false, // 是否开启裁剪
|
||||||
dSpaceDown: false, // 记录是否按下空格键
|
dSpaceDown: false, // 记录是否按下空格键
|
||||||
}),
|
}),
|
||||||
getters: {},
|
getters: {},
|
||||||
@ -127,7 +139,7 @@ const ControlStore = defineStore<"controlStore", TControlState, {}, TControlActi
|
|||||||
stopDMove() {
|
stopDMove() {
|
||||||
if (this.dMoving) {
|
if (this.dMoving) {
|
||||||
const historyStore = useHistoryStore()
|
const historyStore = useHistoryStore()
|
||||||
historyStore.pushHistory("stopDMove")
|
historyStore.pushHistory('stopDMove')
|
||||||
// store.dispatch('pushHistory', 'stopDMove')
|
// store.dispatch('pushHistory', 'stopDMove')
|
||||||
}
|
}
|
||||||
this.dMoving = false
|
this.dMoving = false
|
||||||
@ -161,12 +173,22 @@ const ControlStore = defineStore<"controlStore", TControlState, {}, TControlActi
|
|||||||
this.resizable = true
|
this.resizable = true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
setClippable(able: boolean) {
|
||||||
|
this.clippable = able
|
||||||
|
},
|
||||||
setSpaceDown(val: boolean) {
|
setSpaceDown(val: boolean) {
|
||||||
this.dSpaceDown = val
|
this.dSpaceDown = val
|
||||||
}
|
},
|
||||||
}
|
initMoveableControl() {
|
||||||
|
this.showRotatable = true
|
||||||
|
this.resizable = true
|
||||||
|
this.warpable = false
|
||||||
|
this.scalable = false
|
||||||
|
this.clippable = false
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
export type TControlStore = Store<"controlStore", TControlState, {}, TControlAction>
|
export type TControlStore = Store<'controlStore', TControlState, {}, TControlAction>
|
||||||
|
|
||||||
export default ControlStore
|
export default ControlStore
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* @Date: 2024-03-18 21:00:00
|
* @Date: 2024-03-18 21:00:00
|
||||||
* @Description: Store方法export
|
* @Description: Store方法export
|
||||||
* @LastEditors: xi_zi
|
* @LastEditors: xi_zi
|
||||||
* @LastEditTime: 2024-04-03 15:26:46
|
* @LastEditTime: 2024-04-04 11:32:47
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Store, defineStore } from "pinia";
|
import { Store, defineStore } from "pinia";
|
||||||
@ -29,6 +29,7 @@ export type TdWidgetData = TPageState & Partial<TCommonItemData> & {
|
|||||||
rotate?: string
|
rotate?: string
|
||||||
transform?: string
|
transform?: string
|
||||||
transformOrigin?: string
|
transformOrigin?: string
|
||||||
|
clipPath?: string
|
||||||
sliceData?: Record<string, any>
|
sliceData?: Record<string, any>
|
||||||
flip?: boolean
|
flip?: boolean
|
||||||
cropEdit?: boolean
|
cropEdit?: boolean
|
||||||
|
Loading…
x
Reference in New Issue
Block a user