This commit is contained in:
pipipi-pikachu 2021-01-11 21:30:55 +08:00
parent f100f2cd9e
commit 653bf033a7
3 changed files with 540 additions and 560 deletions

View File

@ -1,24 +1,5 @@
<template>
<div
class="clip-wrapper"
:style="{
width: elementInfo.width + 'px',
height: elementInfo.height + 'px',
}"
v-if="isCliping"
>
<ImageClipHandler
:src="elementInfo.src"
:clipData="elementInfo.clip"
:width="elementInfo.width"
:height="elementInfo.height"
:top="elementInfo.top"
:left="elementInfo.left"
:clipPath="clipShape.style"
@clip="range => clip(range)"
/>
</div>
<div class="image-element-operate" v-else>
<div class="image-element-operate" :class="{ 'cliping': isCliping }">
<BorderLine
class="operate-border-line"
v-for="line in borderLines"
@ -47,16 +28,14 @@
<script lang="ts">
import { computed, defineComponent, PropType } from 'vue'
import { useStore } from 'vuex'
import { MutationTypes, State } from '@/store'
import { State } from '@/store'
import { PPTImageElement } from '@/types/slides'
import { OperateResizeHandler, ImageClipedEmitData } from '@/types/edit'
import { CLIPPATHS, ClipPathTypes } from '@/configs/imageClip'
import { OperateResizeHandler } from '@/types/edit'
import useCommonOperate from '../hooks/useCommonOperate'
import RotateHandler from './RotateHandler.vue'
import ResizeHandler from './ResizeHandler.vue'
import BorderLine from './BorderLine.vue'
import ImageClipHandler from './ImageClipHandler.vue'
export default defineComponent({
name: 'image-element-operate',
@ -65,7 +44,6 @@ export default defineComponent({
RotateHandler,
ResizeHandler,
BorderLine,
ImageClipHandler,
},
props: {
elementInfo: {
@ -93,52 +71,24 @@ export default defineComponent({
const store = useStore<State>()
const canvasScale = computed(() => store.state.canvasScale)
const clipingImageElementId = computed(() => store.state.clipingImageElementId)
const isCliping = computed(() => clipingImageElementId.value === props.elementInfo.id)
const scaleWidth = computed(() => props.elementInfo.width * canvasScale.value)
const scaleHeight = computed(() => props.elementInfo.height * canvasScale.value)
const { resizeHandlers, borderLines } = useCommonOperate(scaleWidth, scaleHeight)
const clipShape = computed(() => {
if(!props.elementInfo || !props.elementInfo.clip) return CLIPPATHS.rect
const shape = props.elementInfo.clip.shape || ClipPathTypes.RECT
return CLIPPATHS[shape]
})
const isCliping = computed(() => clipingImageElementId.value === props.elementInfo.id)
const clip = (data: ImageClipedEmitData) => {
store.commit(MutationTypes.SET_CLIPING_IMAGE_ELEMENT_ID, '')
if(!data) return
const { range, position } = data
const originClip = props.elementInfo.clip || {}
const _props = {
clip: { ...originClip, range },
left: props.elementInfo.left + position.left,
top: props.elementInfo.top + position.top,
width: props.elementInfo.width + position.width,
height: props.elementInfo.height + position.height,
}
console.log(_props)
}
return {
clipShape,
isCliping,
scaleWidth,
resizeHandlers,
borderLines,
isCliping,
clip,
}
},
})
</script>
<style lang="scss" scoped>
.clip-wrapper {
position: relative;
.image-element-operate.cliping {
visibility: hidden;
}
</style>

View File

@ -1,494 +1,496 @@
<template>
<div
class="image-clip-handler"
:style="clipWrapperPositionStyle"
v-click-outside="clip"
>
<img
class="bottom-img"
:src="src"
:draggable="false"
alt=""
:style="bottomImgPositionStyle"
/>
<div
class="top-image-content"
:style="{
...topImgWrapperPositionStyle,
clipPath,
}"
>
<img
class="top-img"
:src="src"
:draggable="false"
alt=""
:style="topImgPositionStyle"
/>
</div>
<div
class="operate"
:style="topImgWrapperPositionStyle"
@mousedown.stop="$event => moveClipRange($event)"
>
<div
:class="['clip-point', point]"
v-for="point in ['t-l', 't-r', 'b-l', 'b-r']"
:key="point"
@mousedown.stop="$event => scaleClipRange($event, point)"
>
<SvgWrapper width="12" height="12" fill="#fff" stroke="#666">
<path d="M 12 0 L 0 0 L 0 12 L 3 12 L 3 3 L 12 3 L 12 0 Z"></path>
</SvgWrapper>
</div>
</div>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, onMounted, onUnmounted, PropType, reactive, ref } from 'vue'
import { useStore } from 'vuex'
import { State } from '@/store'
import { KEYS } from '@/configs/hotkey'
import { ImageClipData, ImageClipDataRange, ImageClipedEmitData } from '@/types/edit'
type ScaleClipRangeType = 't-l' | 't-r' | 'b-l' | 'b-r'
export default defineComponent({
name: 'image-clip-handler',
props: {
src: {
type: String,
required: true,
},
clipData: {
type: Object as PropType<ImageClipData>,
},
clipPath: {
type: String,
required: true,
},
width: {
type: Number,
required: true,
},
height: {
type: Number,
required: true,
},
top: {
type: Number,
required: true,
},
left: {
type: Number,
required: true,
},
},
setup(props, { emit }) {
const store = useStore<State>()
const canvasScale = computed(() => store.state.canvasScale)
const topImgWrapperPosition = reactive({
top: 0,
left: 0,
width: 0,
height: 0,
})
const clipWrapperPositionStyle = reactive({
top: '0',
left: '0',
})
const isSettingClipRange = ref(false)
const currentRange = ref<ImageClipDataRange | null>(null)
const getClipDataTransformInfo = () => {
const [start, end] = props.clipData ? props.clipData.range : [[0, 0], [100, 100]]
const widthScale = (end[0] - start[0]) / 100
const heightScale = (end[1] - start[1]) / 100
const left = start[0] / widthScale
const top = start[1] / heightScale
return { widthScale, heightScale, left, top }
}
const imgPosition = computed(() => {
const { widthScale, heightScale, left, top } = getClipDataTransformInfo()
return {
left: -left,
top: -top,
width: 100 / widthScale,
height: 100 / heightScale,
}
})
const bottomImgPositionStyle = computed(() => {
return {
top: imgPosition.value.top + '%',
left: imgPosition.value.left + '%',
width: imgPosition.value.width + '%',
height: imgPosition.value.height + '%',
}
})
const topImgWrapperPositionStyle = computed(() => {
return {
top: topImgWrapperPosition.top + '%',
left: topImgWrapperPosition.left + '%',
width: topImgWrapperPosition.width + '%',
height: topImgWrapperPosition.height + '%',
}
})
const topImgPositionStyle = computed(() => {
const bottomWidth = imgPosition.value.width
const bottomHeight = imgPosition.value.height
const topLeft = topImgWrapperPosition.left
const topTop = topImgWrapperPosition.top
const topWidth = topImgWrapperPosition.width
const topHeight = topImgWrapperPosition.height
return {
left: -topLeft * (100 / topWidth) + '%',
top: -topTop * (100 / topHeight) + '%',
width: bottomWidth / topWidth * 100 + '%',
height: bottomHeight / topHeight * 100 + '%',
}
})
const initClipPosition = () => {
const { left, top } = getClipDataTransformInfo()
topImgWrapperPosition.left = left
topImgWrapperPosition.top = top
topImgWrapperPosition.width = 100
topImgWrapperPosition.height = 100
clipWrapperPositionStyle.top = -top + '%'
clipWrapperPositionStyle.left = -left + '%'
}
const clip = () => {
if(isSettingClipRange.value || !currentRange.value) {
emit('clip', null)
return
}
const { left, top } = getClipDataTransformInfo()
const position = {
left: (topImgWrapperPosition.left - left) / 100 * props.width,
top: (topImgWrapperPosition.top - top) / 100 * props.height,
width: (topImgWrapperPosition.width - 100) / 100 * props.width,
height: (topImgWrapperPosition.height - 100) / 100 * props.height,
}
const clipedEmitData: ImageClipedEmitData = {
range: currentRange.value,
position,
}
emit('clip', clipedEmitData)
}
const keyboardClip = (e: KeyboardEvent) => {
const key = e.key.toUpperCase()
if(key === KEYS.ENTER) clip()
}
onMounted(() => {
initClipPosition()
document.addEventListener('keydown', keyboardClip)
})
onUnmounted(() => {
document.removeEventListener('keydown', keyboardClip)
})
const getRange = () => {
const retPosition = {
left: parseInt(topImgPositionStyle.value.left),
top: parseInt(topImgPositionStyle.value.top),
width: parseInt(topImgPositionStyle.value.width),
height: parseInt(topImgPositionStyle.value.height),
}
const widthScale = 100 / retPosition.width
const heightScale = 100 / retPosition.height
const start: [number, number] = [
-retPosition.left * widthScale,
-retPosition.top * heightScale,
]
const end: [number, number] = [
widthScale * 100 + start[0],
heightScale * 100 + start[1],
]
currentRange.value = [start, end]
}
const moveClipRange = (e: MouseEvent) => {
isSettingClipRange.value = true
let isMouseDown = true
const startPageX = e.pageX
const startPageY = e.pageY
const bottomPosition = imgPosition.value
const originPositopn = {
left: topImgWrapperPosition.left,
top: topImgWrapperPosition.top,
width: topImgWrapperPosition.width,
height: topImgWrapperPosition.height,
}
document.onmousemove = e => {
if(!isMouseDown) return
const currentPageX = e.pageX
const currentPageY = e.pageY
const moveX = (currentPageX - startPageX) / canvasScale.value / props.width * 100
const moveY = (currentPageY - startPageY) / canvasScale.value / props.height * 100
let targetLeft = originPositopn.left + moveX
let targetTop = originPositopn.top + moveY
//
if(targetLeft < 0) targetLeft = 0
else if(targetLeft + originPositopn.width > bottomPosition.width) {
targetLeft = bottomPosition.width - originPositopn.width
}
if(targetTop < 0) targetTop = 0
else if(targetTop + originPositopn.height > bottomPosition.height) {
targetTop = bottomPosition.height - originPositopn.height
}
topImgWrapperPosition.left = targetLeft
topImgWrapperPosition.top = targetTop
}
document.onmouseup = () => {
isMouseDown = false
document.onmousemove = null
document.onmouseup = null
getRange()
setTimeout(() => {
isSettingClipRange.value = false
}, 0)
}
}
const scaleClipRange = (e: MouseEvent, type: ScaleClipRangeType) => {
isSettingClipRange.value = true
let isMouseDown = true
const minWidth = 32 / props.width * 100
const minHeight = 32 / props.height * 100
const startPageX = e.pageX
const startPageY = e.pageY
const bottomPosition = imgPosition.value
const originPositopn = {
left: topImgWrapperPosition.left,
top: topImgWrapperPosition.top,
width: topImgWrapperPosition.width,
height: topImgWrapperPosition.height,
}
document.onmousemove = e => {
if(!isMouseDown) return
const currentPageX = e.pageX
const currentPageY = e.pageY
let moveX = (currentPageX - startPageX) / canvasScale.value / props.width * 100
let moveY = (currentPageY - startPageY) / canvasScale.value / props.height * 100
let targetLeft, targetTop, targetWidth, targetHeight
//
if(type === 't-l') {
if(originPositopn.left + moveX < 0) {
moveX = -originPositopn.left
}
if(originPositopn.top + moveY < 0) {
moveY = -originPositopn.top
}
if(originPositopn.width - moveX < minWidth) {
moveX = originPositopn.width - minWidth
}
if(originPositopn.height - moveY < minHeight) {
moveY = originPositopn.height - minHeight
}
targetWidth = originPositopn.width - moveX
targetHeight = originPositopn.height - moveY
targetLeft = originPositopn.left + moveX
targetTop = originPositopn.top + moveY
}
else if(type === 't-r') {
if(originPositopn.left + originPositopn.width + moveX > bottomPosition.width) {
moveX = bottomPosition.width - (originPositopn.left + originPositopn.width)
}
if(originPositopn.top + moveY < 0) {
moveY = -originPositopn.top
}
if(originPositopn.width + moveX < minWidth) {
moveX = minWidth - originPositopn.width
}
if(originPositopn.height - moveY < minHeight) {
moveY = originPositopn.height - minHeight
}
targetWidth = originPositopn.width + moveX
targetHeight = originPositopn.height - moveY
targetLeft = originPositopn.left
targetTop = originPositopn.top + moveY
}
else if(type === 'b-l') {
if(originPositopn.left + moveX < 0) {
moveX = -originPositopn.left
}
if(originPositopn.top + originPositopn.height + moveY > bottomPosition.height) {
moveY = bottomPosition.height - (originPositopn.top + originPositopn.height)
}
if(originPositopn.width - moveX < minWidth) {
moveX = originPositopn.width - minWidth
}
if(originPositopn.height + moveY < minHeight) {
moveY = minHeight - originPositopn.height
}
targetWidth = originPositopn.width - moveX
targetHeight = originPositopn.height + moveY
targetLeft = originPositopn.left + moveX
targetTop = originPositopn.top
}
else {
if(originPositopn.left + originPositopn.width + moveX > bottomPosition.width) {
moveX = bottomPosition.width - (originPositopn.left + originPositopn.width)
}
if(originPositopn.top + originPositopn.height + moveY > bottomPosition.height) {
moveY = bottomPosition.height - (originPositopn.top + originPositopn.height)
}
if(originPositopn.width + moveX < minWidth) {
moveX = minWidth - originPositopn.width
}
if(originPositopn.height + moveY < minHeight) {
moveY = minHeight - originPositopn.height
}
targetWidth = originPositopn.width + moveX
targetHeight = originPositopn.height + moveY
targetLeft = originPositopn.left
targetTop = originPositopn.top
}
topImgWrapperPosition.left = targetLeft
topImgWrapperPosition.top = targetTop
topImgWrapperPosition.width = targetWidth
topImgWrapperPosition.height = targetHeight
}
document.onmouseup = () => {
isMouseDown = false
document.onmousemove = null
document.onmouseup = null
getRange()
setTimeout(() => isSettingClipRange.value = false, 0)
}
}
return {
clipWrapperPositionStyle,
bottomImgPositionStyle,
topImgWrapperPositionStyle,
topImgPositionStyle,
clip,
moveClipRange,
scaleClipRange,
}
},
})
</script>
<style lang="scss" scoped>
.image-clip-handler {
width: 100%;
height: 100%;
position: relative;
.bottom-img {
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: .5;
}
img {
width: 100%;
height: 100%;
}
.top-image-content {
position: absolute;
overflow: hidden;
img {
position: absolute;
}
}
}
.operate {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
cursor: move;
}
.clip-point {
position: absolute;
width: 12px;
height: 12px;
left: 0;
top: 0;
transform-origin: 0 0;
display: flex;
justify-content: center;
align-items: center;
svg {
overflow: visible;
}
&.t-l {
cursor: nwse-resize;
left: 0;
top: 0;
}
&.t-r {
cursor: nesw-resize;
left: 100%;
top: 0;
transform: rotate(90deg);
}
&.b-l {
cursor: nesw-resize;
left: 0;
top: 100%;
transform: rotate(-90deg);
}
&.b-r {
cursor: nwse-resize;
left: 100%;
top: 100%;
transform: rotate(180deg);
}
}
<template>
<div
class="image-clip-handler"
:style="clipWrapperPositionStyle"
v-click-outside="handleClip"
>
<img
class="bottom-img"
:src="src"
:draggable="false"
alt=""
:style="bottomImgPositionStyle"
/>
<div
class="top-image-content"
:style="{
...topImgWrapperPositionStyle,
clipPath,
}"
>
<img
class="top-img"
:src="src"
:draggable="false"
alt=""
:style="topImgPositionStyle"
/>
</div>
<div
class="operate"
:style="topImgWrapperPositionStyle"
@mousedown.stop="$event => moveClipRange($event)"
>
<div
:class="['clip-point', point]"
v-for="point in ['t-l', 't-r', 'b-l', 'b-r']"
:key="point"
@mousedown.stop="$event => scaleClipRange($event, point)"
>
<SvgWrapper width="12" height="12" fill="#fff" stroke="#666">
<path d="M 12 0 L 0 0 L 0 12 L 3 12 L 3 3 L 12 3 L 12 0 Z"></path>
</SvgWrapper>
</div>
</div>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, onMounted, onUnmounted, PropType, reactive, ref } from 'vue'
import { useStore } from 'vuex'
import { State } from '@/store'
import { KEYS } from '@/configs/hotkey'
import { ImageClipData, ImageClipDataRange, ImageClipedEmitData } from '@/types/edit'
type ScaleClipRangeType = 't-l' | 't-r' | 'b-l' | 'b-r'
export default defineComponent({
name: 'image-clip-handler',
props: {
src: {
type: String,
required: true,
},
clipData: {
type: Object as PropType<ImageClipData>,
},
clipPath: {
type: String,
required: true,
},
width: {
type: Number,
required: true,
},
height: {
type: Number,
required: true,
},
top: {
type: Number,
required: true,
},
left: {
type: Number,
required: true,
},
},
setup(props, { emit }) {
const store = useStore<State>()
const canvasScale = computed(() => store.state.canvasScale)
const topImgWrapperPosition = reactive({
top: 0,
left: 0,
width: 0,
height: 0,
})
const clipWrapperPositionStyle = reactive({
top: '0',
left: '0',
})
const isSettingClipRange = ref(false)
const currentRange = ref<ImageClipDataRange | null>(null)
const getClipDataTransformInfo = () => {
const [start, end] = props.clipData ? props.clipData.range : [[0, 0], [100, 100]]
const widthScale = (end[0] - start[0]) / 100
const heightScale = (end[1] - start[1]) / 100
const left = start[0] / widthScale
const top = start[1] / heightScale
return { widthScale, heightScale, left, top }
}
const imgPosition = computed(() => {
const { widthScale, heightScale, left, top } = getClipDataTransformInfo()
return {
left: -left,
top: -top,
width: 100 / widthScale,
height: 100 / heightScale,
}
})
const bottomImgPositionStyle = computed(() => {
return {
top: imgPosition.value.top + '%',
left: imgPosition.value.left + '%',
width: imgPosition.value.width + '%',
height: imgPosition.value.height + '%',
}
})
const topImgWrapperPositionStyle = computed(() => {
return {
top: topImgWrapperPosition.top + '%',
left: topImgWrapperPosition.left + '%',
width: topImgWrapperPosition.width + '%',
height: topImgWrapperPosition.height + '%',
}
})
const topImgPositionStyle = computed(() => {
const bottomWidth = imgPosition.value.width
const bottomHeight = imgPosition.value.height
const topLeft = topImgWrapperPosition.left
const topTop = topImgWrapperPosition.top
const topWidth = topImgWrapperPosition.width
const topHeight = topImgWrapperPosition.height
return {
left: -topLeft * (100 / topWidth) + '%',
top: -topTop * (100 / topHeight) + '%',
width: bottomWidth / topWidth * 100 + '%',
height: bottomHeight / topHeight * 100 + '%',
}
})
const initClipPosition = () => {
const { left, top } = getClipDataTransformInfo()
topImgWrapperPosition.left = left
topImgWrapperPosition.top = top
topImgWrapperPosition.width = 100
topImgWrapperPosition.height = 100
clipWrapperPositionStyle.top = -top + '%'
clipWrapperPositionStyle.left = -left + '%'
}
const handleClip = () => {
if(isSettingClipRange.value) return
if(!currentRange.value) {
emit('clip', null)
return
}
const { left, top } = getClipDataTransformInfo()
const position = {
left: (topImgWrapperPosition.left - left) / 100 * props.width,
top: (topImgWrapperPosition.top - top) / 100 * props.height,
width: (topImgWrapperPosition.width - 100) / 100 * props.width,
height: (topImgWrapperPosition.height - 100) / 100 * props.height,
}
const clipedEmitData: ImageClipedEmitData = {
range: currentRange.value,
position,
}
emit('clip', clipedEmitData)
}
const keyboardClip = (e: KeyboardEvent) => {
const key = e.key.toUpperCase()
if(key === KEYS.ENTER) handleClip()
}
onMounted(() => {
initClipPosition()
document.addEventListener('keydown', keyboardClip)
})
onUnmounted(() => {
document.removeEventListener('keydown', keyboardClip)
})
const getRange = () => {
const retPosition = {
left: parseInt(topImgPositionStyle.value.left),
top: parseInt(topImgPositionStyle.value.top),
width: parseInt(topImgPositionStyle.value.width),
height: parseInt(topImgPositionStyle.value.height),
}
const widthScale = 100 / retPosition.width
const heightScale = 100 / retPosition.height
const start: [number, number] = [
-retPosition.left * widthScale,
-retPosition.top * heightScale,
]
const end: [number, number] = [
widthScale * 100 + start[0],
heightScale * 100 + start[1],
]
currentRange.value = [start, end]
}
const moveClipRange = (e: MouseEvent) => {
isSettingClipRange.value = true
let isMouseDown = true
const startPageX = e.pageX
const startPageY = e.pageY
const bottomPosition = imgPosition.value
const originPositopn = {
left: topImgWrapperPosition.left,
top: topImgWrapperPosition.top,
width: topImgWrapperPosition.width,
height: topImgWrapperPosition.height,
}
document.onmousemove = e => {
if(!isMouseDown) return
const currentPageX = e.pageX
const currentPageY = e.pageY
const moveX = (currentPageX - startPageX) / canvasScale.value / props.width * 100
const moveY = (currentPageY - startPageY) / canvasScale.value / props.height * 100
let targetLeft = originPositopn.left + moveX
let targetTop = originPositopn.top + moveY
//
if(targetLeft < 0) targetLeft = 0
else if(targetLeft + originPositopn.width > bottomPosition.width) {
targetLeft = bottomPosition.width - originPositopn.width
}
if(targetTop < 0) targetTop = 0
else if(targetTop + originPositopn.height > bottomPosition.height) {
targetTop = bottomPosition.height - originPositopn.height
}
topImgWrapperPosition.left = targetLeft
topImgWrapperPosition.top = targetTop
}
document.onmouseup = () => {
isMouseDown = false
document.onmousemove = null
document.onmouseup = null
getRange()
setTimeout(() => {
isSettingClipRange.value = false
}, 0)
}
}
const scaleClipRange = (e: MouseEvent, type: ScaleClipRangeType) => {
isSettingClipRange.value = true
let isMouseDown = true
const minWidth = 32 / props.width * 100
const minHeight = 32 / props.height * 100
const startPageX = e.pageX
const startPageY = e.pageY
const bottomPosition = imgPosition.value
const originPositopn = {
left: topImgWrapperPosition.left,
top: topImgWrapperPosition.top,
width: topImgWrapperPosition.width,
height: topImgWrapperPosition.height,
}
document.onmousemove = e => {
if(!isMouseDown) return
const currentPageX = e.pageX
const currentPageY = e.pageY
let moveX = (currentPageX - startPageX) / canvasScale.value / props.width * 100
let moveY = (currentPageY - startPageY) / canvasScale.value / props.height * 100
let targetLeft, targetTop, targetWidth, targetHeight
//
if(type === 't-l') {
if(originPositopn.left + moveX < 0) {
moveX = -originPositopn.left
}
if(originPositopn.top + moveY < 0) {
moveY = -originPositopn.top
}
if(originPositopn.width - moveX < minWidth) {
moveX = originPositopn.width - minWidth
}
if(originPositopn.height - moveY < minHeight) {
moveY = originPositopn.height - minHeight
}
targetWidth = originPositopn.width - moveX
targetHeight = originPositopn.height - moveY
targetLeft = originPositopn.left + moveX
targetTop = originPositopn.top + moveY
}
else if(type === 't-r') {
if(originPositopn.left + originPositopn.width + moveX > bottomPosition.width) {
moveX = bottomPosition.width - (originPositopn.left + originPositopn.width)
}
if(originPositopn.top + moveY < 0) {
moveY = -originPositopn.top
}
if(originPositopn.width + moveX < minWidth) {
moveX = minWidth - originPositopn.width
}
if(originPositopn.height - moveY < minHeight) {
moveY = originPositopn.height - minHeight
}
targetWidth = originPositopn.width + moveX
targetHeight = originPositopn.height - moveY
targetLeft = originPositopn.left
targetTop = originPositopn.top + moveY
}
else if(type === 'b-l') {
if(originPositopn.left + moveX < 0) {
moveX = -originPositopn.left
}
if(originPositopn.top + originPositopn.height + moveY > bottomPosition.height) {
moveY = bottomPosition.height - (originPositopn.top + originPositopn.height)
}
if(originPositopn.width - moveX < minWidth) {
moveX = originPositopn.width - minWidth
}
if(originPositopn.height + moveY < minHeight) {
moveY = minHeight - originPositopn.height
}
targetWidth = originPositopn.width - moveX
targetHeight = originPositopn.height + moveY
targetLeft = originPositopn.left + moveX
targetTop = originPositopn.top
}
else {
if(originPositopn.left + originPositopn.width + moveX > bottomPosition.width) {
moveX = bottomPosition.width - (originPositopn.left + originPositopn.width)
}
if(originPositopn.top + originPositopn.height + moveY > bottomPosition.height) {
moveY = bottomPosition.height - (originPositopn.top + originPositopn.height)
}
if(originPositopn.width + moveX < minWidth) {
moveX = minWidth - originPositopn.width
}
if(originPositopn.height + moveY < minHeight) {
moveY = minHeight - originPositopn.height
}
targetWidth = originPositopn.width + moveX
targetHeight = originPositopn.height + moveY
targetLeft = originPositopn.left
targetTop = originPositopn.top
}
topImgWrapperPosition.left = targetLeft
topImgWrapperPosition.top = targetTop
topImgWrapperPosition.width = targetWidth
topImgWrapperPosition.height = targetHeight
}
document.onmouseup = () => {
isMouseDown = false
document.onmousemove = null
document.onmouseup = null
getRange()
setTimeout(() => isSettingClipRange.value = false, 0)
}
}
return {
clipWrapperPositionStyle,
bottomImgPositionStyle,
topImgWrapperPositionStyle,
topImgPositionStyle,
handleClip,
moveClipRange,
scaleClipRange,
}
},
})
</script>
<style lang="scss" scoped>
.image-clip-handler {
width: 100%;
height: 100%;
position: relative;
.bottom-img {
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: .5;
}
img {
width: 100%;
height: 100%;
}
.top-image-content {
position: absolute;
overflow: hidden;
img {
position: absolute;
}
}
}
.operate {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
cursor: move;
}
.clip-point {
position: absolute;
width: 12px;
height: 12px;
left: 0;
top: 0;
transform-origin: 0 0;
display: flex;
justify-content: center;
align-items: center;
svg {
overflow: visible;
}
&.t-l {
cursor: nwse-resize;
left: 0;
top: 0;
}
&.t-r {
cursor: nesw-resize;
left: 100%;
top: 0;
transform: rotate(90deg);
}
&.b-l {
cursor: nesw-resize;
left: 0;
top: 100%;
transform: rotate(-90deg);
}
&.b-r {
cursor: nwse-resize;
left: 100%;
top: 100%;
transform: rotate(180deg);
}
}
</style>

View File

@ -1,10 +1,7 @@
<template>
<div
class="editable-element-image"
:class="{
'lock': elementInfo.lock,
'cliping': clipingImageElementId === elementInfo.id,
}"
:class="{ 'lock': elementInfo.lock }"
:style="{
top: elementInfo.top + 'px',
left: elementInfo.left + 'px',
@ -14,8 +11,20 @@
}"
@mousedown="$event => handleSelectElement($event)"
>
<ImageClipHandler
v-if="isCliping"
:src="elementInfo.src"
:clipData="elementInfo.clip"
:width="elementInfo.width"
:height="elementInfo.height"
:top="elementInfo.top"
:left="elementInfo.left"
:clipPath="clipShape.style"
@clip="range => clip(range)"
/>
<div
class="element-content"
v-else
v-contextmenu="contextmenus"
:style="{
filter: shadowStyle ? `drop-shadow(${shadowStyle})` : '',
@ -64,7 +73,7 @@
<script lang="ts">
import { computed, defineComponent, PropType } from 'vue'
import { useStore } from 'vuex'
import { State } from '@/store'
import { MutationTypes, State } from '@/store'
import { PPTImageElement } from '@/types/slides'
import { ContextmenuItem } from '@/components/Contextmenu/types'
import { CLIPPATHS, ClipPathTypes } from '@/configs/imageClip'
@ -73,7 +82,8 @@ import useElementShadow from '@/views/components/element/hooks/useElementShadow'
import ImageRectOutline from './ImageRectOutline.vue'
import ImageEllipseOutline from './ImageEllipseOutline.vue'
import ImagePolygonOutline from './ImagePolygonOutline.vue'
import ImageClipHandler from './ImageClipHandler.vue'
import { ImageClipedEmitData } from '@/types/edit'
export default defineComponent({
name: 'editable-element-image',
@ -81,6 +91,7 @@ export default defineComponent({
ImageRectOutline,
ImageEllipseOutline,
ImagePolygonOutline,
ImageClipHandler,
},
props: {
elementInfo: {
@ -98,6 +109,7 @@ export default defineComponent({
setup(props) {
const store = useStore<State>()
const clipingImageElementId = computed(() => store.state.clipingImageElementId)
const isCliping = computed(() => clipingImageElementId.value === props.elementInfo.id)
const shadow = computed(() => props.elementInfo.shadow)
const { shadowStyle } = useElementShadow(shadow)
@ -157,7 +169,27 @@ export default defineComponent({
return ''
})
const clip = (data: ImageClipedEmitData) => {
store.commit(MutationTypes.SET_CLIPING_IMAGE_ELEMENT_ID, '')
if(!data) return
const { range, position } = data
const originClip = props.elementInfo.clip || {}
const _props = {
clip: { ...originClip, range },
left: props.elementInfo.left + position.left,
top: props.elementInfo.top + position.top,
width: props.elementInfo.width + position.width,
height: props.elementInfo.height + position.height,
}
store.commit(MutationTypes.UPDATE_ELEMENT, { id: props.elementInfo.id, props: _props })
}
return {
isCliping,
clip,
clipingImageElementId,
shadowStyle,
handleSelectElement,
@ -177,10 +209,6 @@ export default defineComponent({
&.lock .element-content {
cursor: default;
}
&.cliping {
visibility: hidden;
}
}
.element-content {