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
|
||||
* @Author: xi_zi
|
||||
* @Date: 2024-04-03 10:25:49
|
||||
* @LastEditTime: 2024-04-04 00:28:22
|
||||
* @LastEditTime: 2024-04-04 11:52:22
|
||||
* @LastEditors: xi_zi
|
||||
-->
|
||||
<template>
|
||||
@ -73,6 +73,9 @@ import type {
|
||||
OnResizeGroupEnd,
|
||||
OnRotateGroup,
|
||||
OnRotateGroupEnd,
|
||||
ClippableProps,
|
||||
OnClip,
|
||||
OnClipEnd,
|
||||
} from 'vue3-moveable'
|
||||
import { TUpdateWidgetMultiplePayload } from '@/store/design/widget/actions/widget'
|
||||
|
||||
@ -81,9 +84,8 @@ type TModifierStyle = { transform?: string; transformOrigin?: string; width?: nu
|
||||
const widgetStore = useWidgetStore()
|
||||
const controlStore = useControlStore()
|
||||
const forceStore = useForceStore()
|
||||
const historyStore = useHistoryStore()
|
||||
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 { updateRect, updateSelect } = storeToRefs(forceStore)
|
||||
|
||||
@ -103,7 +105,7 @@ const modifierStyle = (target: HTMLElement | SVGElement, { transform, transformO
|
||||
|
||||
const updateStoreModifierStyle = (target: HTMLElement | SVGElement, { transform, transformOrigin, width, height }: TModifierStyle) => {
|
||||
const uuid = target.getAttribute('id') || ''
|
||||
const updateData: TUpdateWidgetMultiplePayload['data'] = []
|
||||
const updateData: TUpdateWidgetMultiplePayload['data'] = []
|
||||
if (transform) updateData.push({ key: 'transform', value: transform })
|
||||
if (transformOrigin) updateData.push({ key: 'transformOrigin', value: transformOrigin })
|
||||
if (width) updateData.push({ key: 'width', value: width })
|
||||
@ -112,6 +114,7 @@ const updateStoreModifierStyle = (target: HTMLElement | SVGElement, { transform,
|
||||
widgetStore.updateWidgetMultiple({
|
||||
uuid,
|
||||
data: updateData,
|
||||
pushHistory: true,
|
||||
})
|
||||
}
|
||||
|
||||
@ -251,12 +254,33 @@ const rotateOptions = reactive<RotatableProps>({
|
||||
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 opt = { ...dragOptions }
|
||||
if (showRotatable.value) Object.assign(opt, rotateOptions)
|
||||
if (warpable.value) Object.assign(opt, warpOptions)
|
||||
if (scalable.value) Object.assign(opt, scaleOptions)
|
||||
if (resizable.value) Object.assign(opt, resizeOptions)
|
||||
if (clippable.value) Object.assign(opt, clipOptions)
|
||||
return opt
|
||||
})
|
||||
|
||||
|
@ -412,8 +412,8 @@ function getChilds(uuid: string) {
|
||||
// },
|
||||
|
||||
const widgetStyle = (widgetData: TdWidgetData): CSSProperties => {
|
||||
const { transform, transformOrigin, width, height } = widgetData
|
||||
const style: CSSProperties = { position: 'absolute' }
|
||||
const { transform, transformOrigin, width, height, opacity, clipPath } = widgetData
|
||||
const style: CSSProperties = { position: 'absolute', opacity, clipPath }
|
||||
if (transform) style.transform = transform
|
||||
if (transformOrigin) style.transformOrigin = transformOrigin
|
||||
if (width !== undefined) style.width = `${width}px`
|
||||
@ -422,11 +422,11 @@ const widgetStyle = (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 { x: gX, y: gY } = getOffsetFromTransform(gTransform || '')
|
||||
const { x, y } = getOffsetFromTransform(transform || '')
|
||||
const style: CSSProperties = { position: 'absolute' }
|
||||
const style: CSSProperties = { position: 'absolute', opacity, clipPath }
|
||||
if (transform) style.transform = removeTranslate(transform)
|
||||
if (transformOrigin) style.transformOrigin = transformOrigin
|
||||
if (width !== undefined) style.width = `${width}px`
|
||||
|
@ -3,7 +3,7 @@
|
||||
* @Date: 2021-08-09 11:41:53
|
||||
* @Description:
|
||||
* @LastEditors: xi_zi
|
||||
* @LastEditTime: 2024-04-04 11:13:38
|
||||
* @LastEditTime: 2024-04-04 11:47:25
|
||||
-->
|
||||
<template>
|
||||
<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 style="width: 100%; margin-bottom: 12px" plain @click="openPicBox">替换图片</el-button>
|
||||
<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-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>
|
||||
<!-- <uploader class="options__upload" @done="uploadImgDone">
|
||||
<el-button size="small" plain>替换图片</el-button>
|
||||
@ -137,7 +139,7 @@ const canvasStore = useCanvasStore()
|
||||
// dActiveElement, dWidgets
|
||||
// } = useSetupMapGetters(['dActiveElement', 'dWidgets'])
|
||||
const controlStore = useControlStore()
|
||||
const { dMoving } = storeToRefs(controlStore)
|
||||
const { dMoving, clippable } = storeToRefs(controlStore)
|
||||
const { dActiveElement, dWidgets } = storeToRefs(widgetStore)
|
||||
|
||||
let lastUuid: string | undefined = undefined
|
||||
@ -317,14 +319,15 @@ async function selectDone(img: TGetImageListResult) {
|
||||
}
|
||||
|
||||
async function imgCrop(val: boolean) {
|
||||
// TODO: 画布内图像裁剪
|
||||
/* // TODO: 画布内图像裁剪
|
||||
const el = document.getElementById(state.innerElement.uuid || '')
|
||||
if (!el) return
|
||||
const { left, top } = el.getBoundingClientRect()
|
||||
toolBarStyle = { left: left + 'px', top: top + 'px' }
|
||||
state.innerElement.cropEdit = val
|
||||
await nextTick()
|
||||
controlStore.setShowRotatable(!val)
|
||||
controlStore.setShowRotatable(!val) */
|
||||
controlStore.setClippable(val)
|
||||
}
|
||||
|
||||
function cropHandle() {
|
||||
|
@ -1,18 +1,17 @@
|
||||
|
||||
/*
|
||||
* @Author: Jeremy Yu
|
||||
* @Date: 2024-03-18 21:00:00
|
||||
* @Description:
|
||||
* @LastEditors: xi_zi
|
||||
* @LastEditTime: 2024-04-03 10:26:38
|
||||
* @LastEditTime: 2024-04-04 11:50:51
|
||||
*/
|
||||
|
||||
import { useHistoryStore } from "@/store";
|
||||
import { Store, defineStore } from "pinia";
|
||||
import { useHistoryStore } from '@/store'
|
||||
import { Store, defineStore } from 'pinia'
|
||||
|
||||
type TControlState = {
|
||||
/** 是否正在移动组件 */
|
||||
dMoving: boolean
|
||||
dMoving: boolean
|
||||
/** 是否正在抓取组件 */
|
||||
dDraging: boolean
|
||||
/** 是否正在调整组件宽高 */
|
||||
@ -29,9 +28,10 @@ type TControlState = {
|
||||
dSpaceDown: boolean
|
||||
/** 正在编辑or裁剪的组件id **/
|
||||
dCropUuid: string
|
||||
warpable: boolean, // 是否开启斜切
|
||||
scalable: boolean, // 是否开启缩放
|
||||
resizable: boolean, // 是否开启拖拽
|
||||
warpable: boolean // 是否开启斜切
|
||||
scalable: boolean // 是否开启缩放
|
||||
resizable: boolean // 是否开启可调整大小
|
||||
clippable: boolean // 是否裁剪
|
||||
}
|
||||
|
||||
type TControlAction = {
|
||||
@ -50,27 +50,38 @@ type TControlAction = {
|
||||
setCropUuid: (uuid: string) => void
|
||||
/**
|
||||
* 设置斜切
|
||||
* @param able
|
||||
* @returns
|
||||
* @param able
|
||||
* @returns
|
||||
*/
|
||||
setWarpable: (able: boolean) => void
|
||||
/**
|
||||
* 设置尺寸控制
|
||||
* @param able
|
||||
* @returns
|
||||
* @param able
|
||||
* @returns
|
||||
*/
|
||||
setResizable: (able: boolean) => void
|
||||
/**
|
||||
* 设置缩放
|
||||
* @param able
|
||||
* @returns
|
||||
* @param able
|
||||
* @returns
|
||||
*/
|
||||
setScalable: (able: boolean) => void
|
||||
/**
|
||||
* 设置裁剪
|
||||
* @param able
|
||||
* @returns
|
||||
*/
|
||||
setClippable: (able: boolean) => void
|
||||
/**
|
||||
* 初始化moveable控制
|
||||
* @returns
|
||||
*/
|
||||
initMoveableControl: () => void
|
||||
setSpaceDown: (uuid: boolean) => void // 设置是否按下空格键
|
||||
}
|
||||
|
||||
/** 全局控制配置 */
|
||||
const ControlStore = defineStore<"controlStore", TControlState, {}, TControlAction>("controlStore", {
|
||||
const ControlStore = defineStore<'controlStore', TControlState, {}, TControlAction>('controlStore', {
|
||||
state: () => ({
|
||||
dMoving: false, // 是否正在移动组件
|
||||
dDraging: false, // 是否正在抓取组件
|
||||
@ -82,7 +93,8 @@ const ControlStore = defineStore<"controlStore", TControlState, {}, TControlActi
|
||||
dCropUuid: '-1', // 正在编辑or裁剪的组件id
|
||||
warpable: false, // 是否开启斜切
|
||||
scalable: false, // 是否开启缩放
|
||||
resizable: true, // 是否开启拖拽
|
||||
resizable: true, // 是否开启可调整大小
|
||||
clippable: false, // 是否开启裁剪
|
||||
dSpaceDown: false, // 记录是否按下空格键
|
||||
}),
|
||||
getters: {},
|
||||
@ -127,7 +139,7 @@ const ControlStore = defineStore<"controlStore", TControlState, {}, TControlActi
|
||||
stopDMove() {
|
||||
if (this.dMoving) {
|
||||
const historyStore = useHistoryStore()
|
||||
historyStore.pushHistory("stopDMove")
|
||||
historyStore.pushHistory('stopDMove')
|
||||
// store.dispatch('pushHistory', 'stopDMove')
|
||||
}
|
||||
this.dMoving = false
|
||||
@ -161,12 +173,22 @@ const ControlStore = defineStore<"controlStore", TControlState, {}, TControlActi
|
||||
this.resizable = true
|
||||
}
|
||||
},
|
||||
setClippable(able: boolean) {
|
||||
this.clippable = able
|
||||
},
|
||||
setSpaceDown(val: boolean) {
|
||||
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
|
||||
|
@ -3,7 +3,7 @@
|
||||
* @Date: 2024-03-18 21:00:00
|
||||
* @Description: Store方法export
|
||||
* @LastEditors: xi_zi
|
||||
* @LastEditTime: 2024-04-03 15:26:46
|
||||
* @LastEditTime: 2024-04-04 11:32:47
|
||||
*/
|
||||
|
||||
import { Store, defineStore } from "pinia";
|
||||
@ -29,6 +29,7 @@ export type TdWidgetData = TPageState & Partial<TCommonItemData> & {
|
||||
rotate?: string
|
||||
transform?: string
|
||||
transformOrigin?: string
|
||||
clipPath?: string
|
||||
sliceData?: Record<string, any>
|
||||
flip?: boolean
|
||||
cropEdit?: boolean
|
||||
|
Loading…
x
Reference in New Issue
Block a user