mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
update
This commit is contained in:
parent
f100f2cd9e
commit
653bf033a7
@ -1,24 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div class="image-element-operate" :class="{ 'cliping': isCliping }">
|
||||||
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>
|
|
||||||
<BorderLine
|
<BorderLine
|
||||||
class="operate-border-line"
|
class="operate-border-line"
|
||||||
v-for="line in borderLines"
|
v-for="line in borderLines"
|
||||||
@ -47,16 +28,14 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, PropType } from 'vue'
|
import { computed, defineComponent, PropType } from 'vue'
|
||||||
import { useStore } from 'vuex'
|
import { useStore } from 'vuex'
|
||||||
import { MutationTypes, State } from '@/store'
|
import { State } from '@/store'
|
||||||
import { PPTImageElement } from '@/types/slides'
|
import { PPTImageElement } from '@/types/slides'
|
||||||
import { OperateResizeHandler, ImageClipedEmitData } from '@/types/edit'
|
import { OperateResizeHandler } from '@/types/edit'
|
||||||
import { CLIPPATHS, ClipPathTypes } from '@/configs/imageClip'
|
|
||||||
import useCommonOperate from '../hooks/useCommonOperate'
|
import useCommonOperate from '../hooks/useCommonOperate'
|
||||||
|
|
||||||
import RotateHandler from './RotateHandler.vue'
|
import RotateHandler from './RotateHandler.vue'
|
||||||
import ResizeHandler from './ResizeHandler.vue'
|
import ResizeHandler from './ResizeHandler.vue'
|
||||||
import BorderLine from './BorderLine.vue'
|
import BorderLine from './BorderLine.vue'
|
||||||
import ImageClipHandler from './ImageClipHandler.vue'
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'image-element-operate',
|
name: 'image-element-operate',
|
||||||
@ -65,7 +44,6 @@ export default defineComponent({
|
|||||||
RotateHandler,
|
RotateHandler,
|
||||||
ResizeHandler,
|
ResizeHandler,
|
||||||
BorderLine,
|
BorderLine,
|
||||||
ImageClipHandler,
|
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
elementInfo: {
|
elementInfo: {
|
||||||
@ -93,52 +71,24 @@ export default defineComponent({
|
|||||||
const store = useStore<State>()
|
const store = useStore<State>()
|
||||||
const canvasScale = computed(() => store.state.canvasScale)
|
const canvasScale = computed(() => store.state.canvasScale)
|
||||||
const clipingImageElementId = computed(() => store.state.clipingImageElementId)
|
const clipingImageElementId = computed(() => store.state.clipingImageElementId)
|
||||||
|
const isCliping = computed(() => clipingImageElementId.value === props.elementInfo.id)
|
||||||
|
|
||||||
const scaleWidth = computed(() => props.elementInfo.width * canvasScale.value)
|
const scaleWidth = computed(() => props.elementInfo.width * canvasScale.value)
|
||||||
const scaleHeight = computed(() => props.elementInfo.height * canvasScale.value)
|
const scaleHeight = computed(() => props.elementInfo.height * canvasScale.value)
|
||||||
const { resizeHandlers, borderLines } = useCommonOperate(scaleWidth, scaleHeight)
|
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 {
|
return {
|
||||||
clipShape,
|
isCliping,
|
||||||
scaleWidth,
|
scaleWidth,
|
||||||
resizeHandlers,
|
resizeHandlers,
|
||||||
borderLines,
|
borderLines,
|
||||||
isCliping,
|
|
||||||
clip,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.clip-wrapper {
|
.image-element-operate.cliping {
|
||||||
position: relative;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
@ -1,494 +1,496 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="image-clip-handler"
|
class="image-clip-handler"
|
||||||
:style="clipWrapperPositionStyle"
|
:style="clipWrapperPositionStyle"
|
||||||
v-click-outside="clip"
|
v-click-outside="handleClip"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
class="bottom-img"
|
class="bottom-img"
|
||||||
:src="src"
|
:src="src"
|
||||||
:draggable="false"
|
:draggable="false"
|
||||||
alt=""
|
alt=""
|
||||||
:style="bottomImgPositionStyle"
|
:style="bottomImgPositionStyle"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="top-image-content"
|
class="top-image-content"
|
||||||
:style="{
|
:style="{
|
||||||
...topImgWrapperPositionStyle,
|
...topImgWrapperPositionStyle,
|
||||||
clipPath,
|
clipPath,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
class="top-img"
|
class="top-img"
|
||||||
:src="src"
|
:src="src"
|
||||||
:draggable="false"
|
:draggable="false"
|
||||||
alt=""
|
alt=""
|
||||||
:style="topImgPositionStyle"
|
:style="topImgPositionStyle"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="operate"
|
class="operate"
|
||||||
:style="topImgWrapperPositionStyle"
|
:style="topImgWrapperPositionStyle"
|
||||||
@mousedown.stop="$event => moveClipRange($event)"
|
@mousedown.stop="$event => moveClipRange($event)"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
:class="['clip-point', point]"
|
:class="['clip-point', point]"
|
||||||
v-for="point in ['t-l', 't-r', 'b-l', 'b-r']"
|
v-for="point in ['t-l', 't-r', 'b-l', 'b-r']"
|
||||||
:key="point"
|
:key="point"
|
||||||
@mousedown.stop="$event => scaleClipRange($event, point)"
|
@mousedown.stop="$event => scaleClipRange($event, point)"
|
||||||
>
|
>
|
||||||
<SvgWrapper width="12" height="12" fill="#fff" stroke="#666">
|
<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>
|
<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>
|
</SvgWrapper>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, onMounted, onUnmounted, PropType, reactive, ref } from 'vue'
|
import { computed, defineComponent, onMounted, onUnmounted, PropType, reactive, ref } from 'vue'
|
||||||
import { useStore } from 'vuex'
|
import { useStore } from 'vuex'
|
||||||
import { State } from '@/store'
|
import { State } from '@/store'
|
||||||
import { KEYS } from '@/configs/hotkey'
|
import { KEYS } from '@/configs/hotkey'
|
||||||
import { ImageClipData, ImageClipDataRange, ImageClipedEmitData } from '@/types/edit'
|
import { ImageClipData, ImageClipDataRange, ImageClipedEmitData } from '@/types/edit'
|
||||||
|
|
||||||
type ScaleClipRangeType = 't-l' | 't-r' | 'b-l' | 'b-r'
|
type ScaleClipRangeType = 't-l' | 't-r' | 'b-l' | 'b-r'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'image-clip-handler',
|
name: 'image-clip-handler',
|
||||||
props: {
|
props: {
|
||||||
src: {
|
src: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
clipData: {
|
clipData: {
|
||||||
type: Object as PropType<ImageClipData>,
|
type: Object as PropType<ImageClipData>,
|
||||||
},
|
},
|
||||||
clipPath: {
|
clipPath: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
width: {
|
width: {
|
||||||
type: Number,
|
type: Number,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
height: {
|
height: {
|
||||||
type: Number,
|
type: Number,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
top: {
|
top: {
|
||||||
type: Number,
|
type: Number,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
left: {
|
left: {
|
||||||
type: Number,
|
type: Number,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
setup(props, { emit }) {
|
setup(props, { emit }) {
|
||||||
const store = useStore<State>()
|
const store = useStore<State>()
|
||||||
const canvasScale = computed(() => store.state.canvasScale)
|
const canvasScale = computed(() => store.state.canvasScale)
|
||||||
|
|
||||||
const topImgWrapperPosition = reactive({
|
const topImgWrapperPosition = reactive({
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
})
|
})
|
||||||
const clipWrapperPositionStyle = reactive({
|
const clipWrapperPositionStyle = reactive({
|
||||||
top: '0',
|
top: '0',
|
||||||
left: '0',
|
left: '0',
|
||||||
})
|
})
|
||||||
const isSettingClipRange = ref(false)
|
const isSettingClipRange = ref(false)
|
||||||
const currentRange = ref<ImageClipDataRange | null>(null)
|
const currentRange = ref<ImageClipDataRange | null>(null)
|
||||||
|
|
||||||
const getClipDataTransformInfo = () => {
|
const getClipDataTransformInfo = () => {
|
||||||
const [start, end] = props.clipData ? props.clipData.range : [[0, 0], [100, 100]]
|
const [start, end] = props.clipData ? props.clipData.range : [[0, 0], [100, 100]]
|
||||||
|
|
||||||
const widthScale = (end[0] - start[0]) / 100
|
const widthScale = (end[0] - start[0]) / 100
|
||||||
const heightScale = (end[1] - start[1]) / 100
|
const heightScale = (end[1] - start[1]) / 100
|
||||||
const left = start[0] / widthScale
|
const left = start[0] / widthScale
|
||||||
const top = start[1] / heightScale
|
const top = start[1] / heightScale
|
||||||
|
|
||||||
return { widthScale, heightScale, left, top }
|
return { widthScale, heightScale, left, top }
|
||||||
}
|
}
|
||||||
|
|
||||||
const imgPosition = computed(() => {
|
const imgPosition = computed(() => {
|
||||||
const { widthScale, heightScale, left, top } = getClipDataTransformInfo()
|
const { widthScale, heightScale, left, top } = getClipDataTransformInfo()
|
||||||
return {
|
return {
|
||||||
left: -left,
|
left: -left,
|
||||||
top: -top,
|
top: -top,
|
||||||
width: 100 / widthScale,
|
width: 100 / widthScale,
|
||||||
height: 100 / heightScale,
|
height: 100 / heightScale,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const bottomImgPositionStyle = computed(() => {
|
const bottomImgPositionStyle = computed(() => {
|
||||||
return {
|
return {
|
||||||
top: imgPosition.value.top + '%',
|
top: imgPosition.value.top + '%',
|
||||||
left: imgPosition.value.left + '%',
|
left: imgPosition.value.left + '%',
|
||||||
width: imgPosition.value.width + '%',
|
width: imgPosition.value.width + '%',
|
||||||
height: imgPosition.value.height + '%',
|
height: imgPosition.value.height + '%',
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const topImgWrapperPositionStyle = computed(() => {
|
const topImgWrapperPositionStyle = computed(() => {
|
||||||
return {
|
return {
|
||||||
top: topImgWrapperPosition.top + '%',
|
top: topImgWrapperPosition.top + '%',
|
||||||
left: topImgWrapperPosition.left + '%',
|
left: topImgWrapperPosition.left + '%',
|
||||||
width: topImgWrapperPosition.width + '%',
|
width: topImgWrapperPosition.width + '%',
|
||||||
height: topImgWrapperPosition.height + '%',
|
height: topImgWrapperPosition.height + '%',
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const topImgPositionStyle = computed(() => {
|
const topImgPositionStyle = computed(() => {
|
||||||
const bottomWidth = imgPosition.value.width
|
const bottomWidth = imgPosition.value.width
|
||||||
const bottomHeight = imgPosition.value.height
|
const bottomHeight = imgPosition.value.height
|
||||||
|
|
||||||
const topLeft = topImgWrapperPosition.left
|
const topLeft = topImgWrapperPosition.left
|
||||||
const topTop = topImgWrapperPosition.top
|
const topTop = topImgWrapperPosition.top
|
||||||
const topWidth = topImgWrapperPosition.width
|
const topWidth = topImgWrapperPosition.width
|
||||||
const topHeight = topImgWrapperPosition.height
|
const topHeight = topImgWrapperPosition.height
|
||||||
|
|
||||||
return {
|
return {
|
||||||
left: -topLeft * (100 / topWidth) + '%',
|
left: -topLeft * (100 / topWidth) + '%',
|
||||||
top: -topTop * (100 / topHeight) + '%',
|
top: -topTop * (100 / topHeight) + '%',
|
||||||
width: bottomWidth / topWidth * 100 + '%',
|
width: bottomWidth / topWidth * 100 + '%',
|
||||||
height: bottomHeight / topHeight * 100 + '%',
|
height: bottomHeight / topHeight * 100 + '%',
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const initClipPosition = () => {
|
const initClipPosition = () => {
|
||||||
const { left, top } = getClipDataTransformInfo()
|
const { left, top } = getClipDataTransformInfo()
|
||||||
topImgWrapperPosition.left = left
|
topImgWrapperPosition.left = left
|
||||||
topImgWrapperPosition.top = top
|
topImgWrapperPosition.top = top
|
||||||
topImgWrapperPosition.width = 100
|
topImgWrapperPosition.width = 100
|
||||||
topImgWrapperPosition.height = 100
|
topImgWrapperPosition.height = 100
|
||||||
|
|
||||||
clipWrapperPositionStyle.top = -top + '%'
|
clipWrapperPositionStyle.top = -top + '%'
|
||||||
clipWrapperPositionStyle.left = -left + '%'
|
clipWrapperPositionStyle.left = -left + '%'
|
||||||
}
|
}
|
||||||
|
|
||||||
const clip = () => {
|
const handleClip = () => {
|
||||||
if(isSettingClipRange.value || !currentRange.value) {
|
if(isSettingClipRange.value) return
|
||||||
emit('clip', null)
|
|
||||||
return
|
if(!currentRange.value) {
|
||||||
}
|
emit('clip', null)
|
||||||
|
return
|
||||||
const { left, top } = getClipDataTransformInfo()
|
}
|
||||||
|
|
||||||
const position = {
|
const { left, top } = getClipDataTransformInfo()
|
||||||
left: (topImgWrapperPosition.left - left) / 100 * props.width,
|
|
||||||
top: (topImgWrapperPosition.top - top) / 100 * props.height,
|
const position = {
|
||||||
width: (topImgWrapperPosition.width - 100) / 100 * props.width,
|
left: (topImgWrapperPosition.left - left) / 100 * props.width,
|
||||||
height: (topImgWrapperPosition.height - 100) / 100 * props.height,
|
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,
|
const clipedEmitData: ImageClipedEmitData = {
|
||||||
}
|
range: currentRange.value,
|
||||||
emit('clip', clipedEmitData)
|
position,
|
||||||
}
|
}
|
||||||
|
emit('clip', clipedEmitData)
|
||||||
const keyboardClip = (e: KeyboardEvent) => {
|
}
|
||||||
const key = e.key.toUpperCase()
|
|
||||||
if(key === KEYS.ENTER) clip()
|
const keyboardClip = (e: KeyboardEvent) => {
|
||||||
}
|
const key = e.key.toUpperCase()
|
||||||
|
if(key === KEYS.ENTER) handleClip()
|
||||||
onMounted(() => {
|
}
|
||||||
initClipPosition()
|
|
||||||
document.addEventListener('keydown', keyboardClip)
|
onMounted(() => {
|
||||||
})
|
initClipPosition()
|
||||||
onUnmounted(() => {
|
document.addEventListener('keydown', keyboardClip)
|
||||||
document.removeEventListener('keydown', keyboardClip)
|
})
|
||||||
})
|
onUnmounted(() => {
|
||||||
|
document.removeEventListener('keydown', keyboardClip)
|
||||||
const getRange = () => {
|
})
|
||||||
const retPosition = {
|
|
||||||
left: parseInt(topImgPositionStyle.value.left),
|
const getRange = () => {
|
||||||
top: parseInt(topImgPositionStyle.value.top),
|
const retPosition = {
|
||||||
width: parseInt(topImgPositionStyle.value.width),
|
left: parseInt(topImgPositionStyle.value.left),
|
||||||
height: parseInt(topImgPositionStyle.value.height),
|
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 widthScale = 100 / retPosition.width
|
||||||
const start: [number, number] = [
|
const heightScale = 100 / retPosition.height
|
||||||
-retPosition.left * widthScale,
|
|
||||||
-retPosition.top * heightScale,
|
const start: [number, number] = [
|
||||||
]
|
-retPosition.left * widthScale,
|
||||||
const end: [number, number] = [
|
-retPosition.top * heightScale,
|
||||||
widthScale * 100 + start[0],
|
]
|
||||||
heightScale * 100 + start[1],
|
const end: [number, number] = [
|
||||||
]
|
widthScale * 100 + start[0],
|
||||||
|
heightScale * 100 + start[1],
|
||||||
currentRange.value = [start, end]
|
]
|
||||||
}
|
|
||||||
|
currentRange.value = [start, end]
|
||||||
const moveClipRange = (e: MouseEvent) => {
|
}
|
||||||
isSettingClipRange.value = true
|
|
||||||
let isMouseDown = true
|
const moveClipRange = (e: MouseEvent) => {
|
||||||
|
isSettingClipRange.value = true
|
||||||
const startPageX = e.pageX
|
let isMouseDown = true
|
||||||
const startPageY = e.pageY
|
|
||||||
const bottomPosition = imgPosition.value
|
const startPageX = e.pageX
|
||||||
const originPositopn = {
|
const startPageY = e.pageY
|
||||||
left: topImgWrapperPosition.left,
|
const bottomPosition = imgPosition.value
|
||||||
top: topImgWrapperPosition.top,
|
const originPositopn = {
|
||||||
width: topImgWrapperPosition.width,
|
left: topImgWrapperPosition.left,
|
||||||
height: topImgWrapperPosition.height,
|
top: topImgWrapperPosition.top,
|
||||||
}
|
width: topImgWrapperPosition.width,
|
||||||
|
height: topImgWrapperPosition.height,
|
||||||
document.onmousemove = e => {
|
}
|
||||||
if(!isMouseDown) return
|
|
||||||
|
document.onmousemove = e => {
|
||||||
const currentPageX = e.pageX
|
if(!isMouseDown) return
|
||||||
const currentPageY = e.pageY
|
|
||||||
|
const currentPageX = e.pageX
|
||||||
const moveX = (currentPageX - startPageX) / canvasScale.value / props.width * 100
|
const currentPageY = e.pageY
|
||||||
const moveY = (currentPageY - startPageY) / canvasScale.value / props.height * 100
|
|
||||||
|
const moveX = (currentPageX - startPageX) / canvasScale.value / props.width * 100
|
||||||
let targetLeft = originPositopn.left + moveX
|
const moveY = (currentPageY - startPageY) / canvasScale.value / props.height * 100
|
||||||
let targetTop = originPositopn.top + moveY
|
|
||||||
|
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(targetLeft < 0) targetLeft = 0
|
||||||
}
|
else if(targetLeft + originPositopn.width > bottomPosition.width) {
|
||||||
if(targetTop < 0) targetTop = 0
|
targetLeft = bottomPosition.width - originPositopn.width
|
||||||
else if(targetTop + originPositopn.height > bottomPosition.height) {
|
}
|
||||||
targetTop = bottomPosition.height - originPositopn.height
|
if(targetTop < 0) targetTop = 0
|
||||||
}
|
else if(targetTop + originPositopn.height > bottomPosition.height) {
|
||||||
|
targetTop = bottomPosition.height - originPositopn.height
|
||||||
topImgWrapperPosition.left = targetLeft
|
}
|
||||||
topImgWrapperPosition.top = targetTop
|
|
||||||
}
|
topImgWrapperPosition.left = targetLeft
|
||||||
|
topImgWrapperPosition.top = targetTop
|
||||||
document.onmouseup = () => {
|
}
|
||||||
isMouseDown = false
|
|
||||||
document.onmousemove = null
|
document.onmouseup = () => {
|
||||||
document.onmouseup = null
|
isMouseDown = false
|
||||||
|
document.onmousemove = null
|
||||||
getRange()
|
document.onmouseup = null
|
||||||
|
|
||||||
setTimeout(() => {
|
getRange()
|
||||||
isSettingClipRange.value = false
|
|
||||||
}, 0)
|
setTimeout(() => {
|
||||||
}
|
isSettingClipRange.value = false
|
||||||
}
|
}, 0)
|
||||||
|
}
|
||||||
const scaleClipRange = (e: MouseEvent, type: ScaleClipRangeType) => {
|
}
|
||||||
isSettingClipRange.value = true
|
|
||||||
let isMouseDown = true
|
const scaleClipRange = (e: MouseEvent, type: ScaleClipRangeType) => {
|
||||||
|
isSettingClipRange.value = true
|
||||||
const minWidth = 32 / props.width * 100
|
let isMouseDown = true
|
||||||
const minHeight = 32 / props.height * 100
|
|
||||||
|
const minWidth = 32 / props.width * 100
|
||||||
const startPageX = e.pageX
|
const minHeight = 32 / props.height * 100
|
||||||
const startPageY = e.pageY
|
|
||||||
const bottomPosition = imgPosition.value
|
const startPageX = e.pageX
|
||||||
const originPositopn = {
|
const startPageY = e.pageY
|
||||||
left: topImgWrapperPosition.left,
|
const bottomPosition = imgPosition.value
|
||||||
top: topImgWrapperPosition.top,
|
const originPositopn = {
|
||||||
width: topImgWrapperPosition.width,
|
left: topImgWrapperPosition.left,
|
||||||
height: topImgWrapperPosition.height,
|
top: topImgWrapperPosition.top,
|
||||||
}
|
width: topImgWrapperPosition.width,
|
||||||
|
height: topImgWrapperPosition.height,
|
||||||
document.onmousemove = e => {
|
}
|
||||||
if(!isMouseDown) return
|
|
||||||
|
document.onmousemove = e => {
|
||||||
const currentPageX = e.pageX
|
if(!isMouseDown) return
|
||||||
const currentPageY = e.pageY
|
|
||||||
|
const currentPageX = e.pageX
|
||||||
let moveX = (currentPageX - startPageX) / canvasScale.value / props.width * 100
|
const currentPageY = e.pageY
|
||||||
let moveY = (currentPageY - startPageY) / canvasScale.value / props.height * 100
|
|
||||||
|
let moveX = (currentPageX - startPageX) / canvasScale.value / props.width * 100
|
||||||
let targetLeft, targetTop, targetWidth, targetHeight
|
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(type === 't-l') {
|
||||||
}
|
if(originPositopn.left + moveX < 0) {
|
||||||
if(originPositopn.top + moveY < 0) {
|
moveX = -originPositopn.left
|
||||||
moveY = -originPositopn.top
|
}
|
||||||
}
|
if(originPositopn.top + moveY < 0) {
|
||||||
if(originPositopn.width - moveX < minWidth) {
|
moveY = -originPositopn.top
|
||||||
moveX = originPositopn.width - minWidth
|
}
|
||||||
}
|
if(originPositopn.width - moveX < minWidth) {
|
||||||
if(originPositopn.height - moveY < minHeight) {
|
moveX = originPositopn.width - minWidth
|
||||||
moveY = originPositopn.height - minHeight
|
}
|
||||||
}
|
if(originPositopn.height - moveY < minHeight) {
|
||||||
targetWidth = originPositopn.width - moveX
|
moveY = originPositopn.height - minHeight
|
||||||
targetHeight = originPositopn.height - moveY
|
}
|
||||||
targetLeft = originPositopn.left + moveX
|
targetWidth = originPositopn.width - moveX
|
||||||
targetTop = originPositopn.top + moveY
|
targetHeight = originPositopn.height - moveY
|
||||||
}
|
targetLeft = originPositopn.left + moveX
|
||||||
else if(type === 't-r') {
|
targetTop = originPositopn.top + moveY
|
||||||
if(originPositopn.left + originPositopn.width + moveX > bottomPosition.width) {
|
}
|
||||||
moveX = bottomPosition.width - (originPositopn.left + originPositopn.width)
|
else if(type === 't-r') {
|
||||||
}
|
if(originPositopn.left + originPositopn.width + moveX > bottomPosition.width) {
|
||||||
if(originPositopn.top + moveY < 0) {
|
moveX = bottomPosition.width - (originPositopn.left + originPositopn.width)
|
||||||
moveY = -originPositopn.top
|
}
|
||||||
}
|
if(originPositopn.top + moveY < 0) {
|
||||||
if(originPositopn.width + moveX < minWidth) {
|
moveY = -originPositopn.top
|
||||||
moveX = minWidth - originPositopn.width
|
}
|
||||||
}
|
if(originPositopn.width + moveX < minWidth) {
|
||||||
if(originPositopn.height - moveY < minHeight) {
|
moveX = minWidth - originPositopn.width
|
||||||
moveY = originPositopn.height - minHeight
|
}
|
||||||
}
|
if(originPositopn.height - moveY < minHeight) {
|
||||||
targetWidth = originPositopn.width + moveX
|
moveY = originPositopn.height - minHeight
|
||||||
targetHeight = originPositopn.height - moveY
|
}
|
||||||
targetLeft = originPositopn.left
|
targetWidth = originPositopn.width + moveX
|
||||||
targetTop = originPositopn.top + moveY
|
targetHeight = originPositopn.height - moveY
|
||||||
}
|
targetLeft = originPositopn.left
|
||||||
else if(type === 'b-l') {
|
targetTop = originPositopn.top + moveY
|
||||||
if(originPositopn.left + moveX < 0) {
|
}
|
||||||
moveX = -originPositopn.left
|
else if(type === 'b-l') {
|
||||||
}
|
if(originPositopn.left + moveX < 0) {
|
||||||
if(originPositopn.top + originPositopn.height + moveY > bottomPosition.height) {
|
moveX = -originPositopn.left
|
||||||
moveY = bottomPosition.height - (originPositopn.top + originPositopn.height)
|
}
|
||||||
}
|
if(originPositopn.top + originPositopn.height + moveY > bottomPosition.height) {
|
||||||
if(originPositopn.width - moveX < minWidth) {
|
moveY = bottomPosition.height - (originPositopn.top + originPositopn.height)
|
||||||
moveX = originPositopn.width - minWidth
|
}
|
||||||
}
|
if(originPositopn.width - moveX < minWidth) {
|
||||||
if(originPositopn.height + moveY < minHeight) {
|
moveX = originPositopn.width - minWidth
|
||||||
moveY = minHeight - originPositopn.height
|
}
|
||||||
}
|
if(originPositopn.height + moveY < minHeight) {
|
||||||
targetWidth = originPositopn.width - moveX
|
moveY = minHeight - originPositopn.height
|
||||||
targetHeight = originPositopn.height + moveY
|
}
|
||||||
targetLeft = originPositopn.left + moveX
|
targetWidth = originPositopn.width - moveX
|
||||||
targetTop = originPositopn.top
|
targetHeight = originPositopn.height + moveY
|
||||||
}
|
targetLeft = originPositopn.left + moveX
|
||||||
else {
|
targetTop = originPositopn.top
|
||||||
if(originPositopn.left + originPositopn.width + moveX > bottomPosition.width) {
|
}
|
||||||
moveX = bottomPosition.width - (originPositopn.left + originPositopn.width)
|
else {
|
||||||
}
|
if(originPositopn.left + originPositopn.width + moveX > bottomPosition.width) {
|
||||||
if(originPositopn.top + originPositopn.height + moveY > bottomPosition.height) {
|
moveX = bottomPosition.width - (originPositopn.left + originPositopn.width)
|
||||||
moveY = bottomPosition.height - (originPositopn.top + originPositopn.height)
|
}
|
||||||
}
|
if(originPositopn.top + originPositopn.height + moveY > bottomPosition.height) {
|
||||||
if(originPositopn.width + moveX < minWidth) {
|
moveY = bottomPosition.height - (originPositopn.top + originPositopn.height)
|
||||||
moveX = minWidth - originPositopn.width
|
}
|
||||||
}
|
if(originPositopn.width + moveX < minWidth) {
|
||||||
if(originPositopn.height + moveY < minHeight) {
|
moveX = minWidth - originPositopn.width
|
||||||
moveY = minHeight - originPositopn.height
|
}
|
||||||
}
|
if(originPositopn.height + moveY < minHeight) {
|
||||||
targetWidth = originPositopn.width + moveX
|
moveY = minHeight - originPositopn.height
|
||||||
targetHeight = originPositopn.height + moveY
|
}
|
||||||
targetLeft = originPositopn.left
|
targetWidth = originPositopn.width + moveX
|
||||||
targetTop = originPositopn.top
|
targetHeight = originPositopn.height + moveY
|
||||||
}
|
targetLeft = originPositopn.left
|
||||||
|
targetTop = originPositopn.top
|
||||||
topImgWrapperPosition.left = targetLeft
|
}
|
||||||
topImgWrapperPosition.top = targetTop
|
|
||||||
topImgWrapperPosition.width = targetWidth
|
topImgWrapperPosition.left = targetLeft
|
||||||
topImgWrapperPosition.height = targetHeight
|
topImgWrapperPosition.top = targetTop
|
||||||
}
|
topImgWrapperPosition.width = targetWidth
|
||||||
|
topImgWrapperPosition.height = targetHeight
|
||||||
document.onmouseup = () => {
|
}
|
||||||
isMouseDown = false
|
|
||||||
document.onmousemove = null
|
document.onmouseup = () => {
|
||||||
document.onmouseup = null
|
isMouseDown = false
|
||||||
|
document.onmousemove = null
|
||||||
getRange()
|
document.onmouseup = null
|
||||||
|
|
||||||
setTimeout(() => isSettingClipRange.value = false, 0)
|
getRange()
|
||||||
}
|
|
||||||
}
|
setTimeout(() => isSettingClipRange.value = false, 0)
|
||||||
|
}
|
||||||
return {
|
}
|
||||||
clipWrapperPositionStyle,
|
|
||||||
bottomImgPositionStyle,
|
return {
|
||||||
topImgWrapperPositionStyle,
|
clipWrapperPositionStyle,
|
||||||
topImgPositionStyle,
|
bottomImgPositionStyle,
|
||||||
clip,
|
topImgWrapperPositionStyle,
|
||||||
moveClipRange,
|
topImgPositionStyle,
|
||||||
scaleClipRange,
|
handleClip,
|
||||||
}
|
moveClipRange,
|
||||||
},
|
scaleClipRange,
|
||||||
})
|
}
|
||||||
</script>
|
},
|
||||||
|
})
|
||||||
<style lang="scss" scoped>
|
</script>
|
||||||
.image-clip-handler {
|
|
||||||
width: 100%;
|
<style lang="scss" scoped>
|
||||||
height: 100%;
|
.image-clip-handler {
|
||||||
position: relative;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
.bottom-img {
|
position: relative;
|
||||||
top: 0;
|
|
||||||
left: 0;
|
.bottom-img {
|
||||||
width: 100%;
|
top: 0;
|
||||||
height: 100%;
|
left: 0;
|
||||||
opacity: .5;
|
width: 100%;
|
||||||
}
|
height: 100%;
|
||||||
|
opacity: .5;
|
||||||
img {
|
}
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
img {
|
||||||
}
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
.top-image-content {
|
}
|
||||||
position: absolute;
|
|
||||||
overflow: hidden;
|
.top-image-content {
|
||||||
|
position: absolute;
|
||||||
img {
|
overflow: hidden;
|
||||||
position: absolute;
|
|
||||||
}
|
img {
|
||||||
}
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.operate {
|
}
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
.operate {
|
||||||
height: 100%;
|
position: absolute;
|
||||||
top: 0;
|
width: 100%;
|
||||||
left: 0;
|
height: 100%;
|
||||||
cursor: move;
|
top: 0;
|
||||||
}
|
left: 0;
|
||||||
|
cursor: move;
|
||||||
.clip-point {
|
}
|
||||||
position: absolute;
|
|
||||||
width: 12px;
|
.clip-point {
|
||||||
height: 12px;
|
position: absolute;
|
||||||
left: 0;
|
width: 12px;
|
||||||
top: 0;
|
height: 12px;
|
||||||
transform-origin: 0 0;
|
left: 0;
|
||||||
display: flex;
|
top: 0;
|
||||||
justify-content: center;
|
transform-origin: 0 0;
|
||||||
align-items: center;
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
svg {
|
align-items: center;
|
||||||
overflow: visible;
|
|
||||||
}
|
svg {
|
||||||
|
overflow: visible;
|
||||||
&.t-l {
|
}
|
||||||
cursor: nwse-resize;
|
|
||||||
left: 0;
|
&.t-l {
|
||||||
top: 0;
|
cursor: nwse-resize;
|
||||||
}
|
left: 0;
|
||||||
&.t-r {
|
top: 0;
|
||||||
cursor: nesw-resize;
|
}
|
||||||
left: 100%;
|
&.t-r {
|
||||||
top: 0;
|
cursor: nesw-resize;
|
||||||
transform: rotate(90deg);
|
left: 100%;
|
||||||
}
|
top: 0;
|
||||||
&.b-l {
|
transform: rotate(90deg);
|
||||||
cursor: nesw-resize;
|
}
|
||||||
left: 0;
|
&.b-l {
|
||||||
top: 100%;
|
cursor: nesw-resize;
|
||||||
transform: rotate(-90deg);
|
left: 0;
|
||||||
}
|
top: 100%;
|
||||||
&.b-r {
|
transform: rotate(-90deg);
|
||||||
cursor: nwse-resize;
|
}
|
||||||
left: 100%;
|
&.b-r {
|
||||||
top: 100%;
|
cursor: nwse-resize;
|
||||||
transform: rotate(180deg);
|
left: 100%;
|
||||||
}
|
top: 100%;
|
||||||
}
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
@ -1,10 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="editable-element-image"
|
class="editable-element-image"
|
||||||
:class="{
|
:class="{ 'lock': elementInfo.lock }"
|
||||||
'lock': elementInfo.lock,
|
|
||||||
'cliping': clipingImageElementId === elementInfo.id,
|
|
||||||
}"
|
|
||||||
:style="{
|
:style="{
|
||||||
top: elementInfo.top + 'px',
|
top: elementInfo.top + 'px',
|
||||||
left: elementInfo.left + 'px',
|
left: elementInfo.left + 'px',
|
||||||
@ -14,8 +11,20 @@
|
|||||||
}"
|
}"
|
||||||
@mousedown="$event => handleSelectElement($event)"
|
@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
|
<div
|
||||||
class="element-content"
|
class="element-content"
|
||||||
|
v-else
|
||||||
v-contextmenu="contextmenus"
|
v-contextmenu="contextmenus"
|
||||||
:style="{
|
:style="{
|
||||||
filter: shadowStyle ? `drop-shadow(${shadowStyle})` : '',
|
filter: shadowStyle ? `drop-shadow(${shadowStyle})` : '',
|
||||||
@ -64,7 +73,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, PropType } from 'vue'
|
import { computed, defineComponent, PropType } from 'vue'
|
||||||
import { useStore } from 'vuex'
|
import { useStore } from 'vuex'
|
||||||
import { State } from '@/store'
|
import { MutationTypes, State } from '@/store'
|
||||||
import { PPTImageElement } from '@/types/slides'
|
import { PPTImageElement } from '@/types/slides'
|
||||||
import { ContextmenuItem } from '@/components/Contextmenu/types'
|
import { ContextmenuItem } from '@/components/Contextmenu/types'
|
||||||
import { CLIPPATHS, ClipPathTypes } from '@/configs/imageClip'
|
import { CLIPPATHS, ClipPathTypes } from '@/configs/imageClip'
|
||||||
@ -73,7 +82,8 @@ import useElementShadow from '@/views/components/element/hooks/useElementShadow'
|
|||||||
import ImageRectOutline from './ImageRectOutline.vue'
|
import ImageRectOutline from './ImageRectOutline.vue'
|
||||||
import ImageEllipseOutline from './ImageEllipseOutline.vue'
|
import ImageEllipseOutline from './ImageEllipseOutline.vue'
|
||||||
import ImagePolygonOutline from './ImagePolygonOutline.vue'
|
import ImagePolygonOutline from './ImagePolygonOutline.vue'
|
||||||
|
import ImageClipHandler from './ImageClipHandler.vue'
|
||||||
|
import { ImageClipedEmitData } from '@/types/edit'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'editable-element-image',
|
name: 'editable-element-image',
|
||||||
@ -81,6 +91,7 @@ export default defineComponent({
|
|||||||
ImageRectOutline,
|
ImageRectOutline,
|
||||||
ImageEllipseOutline,
|
ImageEllipseOutline,
|
||||||
ImagePolygonOutline,
|
ImagePolygonOutline,
|
||||||
|
ImageClipHandler,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
elementInfo: {
|
elementInfo: {
|
||||||
@ -98,6 +109,7 @@ export default defineComponent({
|
|||||||
setup(props) {
|
setup(props) {
|
||||||
const store = useStore<State>()
|
const store = useStore<State>()
|
||||||
const clipingImageElementId = computed(() => store.state.clipingImageElementId)
|
const clipingImageElementId = computed(() => store.state.clipingImageElementId)
|
||||||
|
const isCliping = computed(() => clipingImageElementId.value === props.elementInfo.id)
|
||||||
|
|
||||||
const shadow = computed(() => props.elementInfo.shadow)
|
const shadow = computed(() => props.elementInfo.shadow)
|
||||||
const { shadowStyle } = useElementShadow(shadow)
|
const { shadowStyle } = useElementShadow(shadow)
|
||||||
@ -157,7 +169,27 @@ export default defineComponent({
|
|||||||
return ''
|
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 {
|
return {
|
||||||
|
isCliping,
|
||||||
|
clip,
|
||||||
clipingImageElementId,
|
clipingImageElementId,
|
||||||
shadowStyle,
|
shadowStyle,
|
||||||
handleSelectElement,
|
handleSelectElement,
|
||||||
@ -177,10 +209,6 @@ export default defineComponent({
|
|||||||
&.lock .element-content {
|
&.lock .element-content {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.cliping {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.element-content {
|
.element-content {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user