mirror of
https://github.com/palxiao/poster-design.git
synced 2025-07-28 04:10:31 +08:00
feat: convert lineguides component to composition API
This commit is contained in:
parent
e87f1ebfcb
commit
baa83adfa9
107
src/components/business/cropper/CropImage/CropImage 2.vue
Normal file
107
src/components/business/cropper/CropImage/CropImage 2.vue
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<!--
|
||||||
|
* @Author: ShawnPhang
|
||||||
|
* @Date: 2021-09-28 20:06:25
|
||||||
|
* @Description: 裁剪组件
|
||||||
|
* @LastEditors: ShawnPhang <site: book.palxp.com>
|
||||||
|
* @LastEditTime: 2023-06-29 17:58:00
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<el-dialog v-model="dialogVisible" title="裁剪图片" width="80%" :before-close="handleClose" @close="cancel">
|
||||||
|
<div id="wrap" v-loading="loading" style="height: 50vh">
|
||||||
|
<img v-show="url" ref="imgBox" style="visibility: hidden" alt="imgBox" :src="url" />
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="cancel">取消</el-button>
|
||||||
|
<el-button :loading="loading" plain type="primary" @click="ok">确认</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import api from '@/api'
|
||||||
|
import { ElDialog } from 'element-plus'
|
||||||
|
import { ref, defineComponent, toRefs, reactive, nextTick } from 'vue'
|
||||||
|
import { useStore } from 'vuex'
|
||||||
|
import 'cropperjs/dist/cropper.css'
|
||||||
|
import Cropper from 'cropperjs'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: { ElDialog },
|
||||||
|
emits: ['done'],
|
||||||
|
setup(props, context) {
|
||||||
|
const store = useStore()
|
||||||
|
const state = reactive({
|
||||||
|
loading: false,
|
||||||
|
url: '',
|
||||||
|
})
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const imgBox = ref<HTMLImageElement | any>()
|
||||||
|
let cropData: any = null
|
||||||
|
let cropper: any = null
|
||||||
|
|
||||||
|
const handleClose = (done: any) => {
|
||||||
|
done()
|
||||||
|
}
|
||||||
|
const open = async (item: any, data = {}) => {
|
||||||
|
state.loading = true
|
||||||
|
item.rawImg = item.rawImg ? item.rawImg : item.imgUrl
|
||||||
|
cropData = data
|
||||||
|
state.url = item.rawImg
|
||||||
|
store.commit('setShowMoveable', false)
|
||||||
|
dialogVisible.value = true
|
||||||
|
await nextTick()
|
||||||
|
setEdit()
|
||||||
|
}
|
||||||
|
const setEdit = () => {
|
||||||
|
cropper = new Cropper(imgBox.value, {
|
||||||
|
// aspectRatio: imgBox.value.width / imgBox.value.height,
|
||||||
|
dragMode: 'move',
|
||||||
|
viewMode: 1,
|
||||||
|
cropBoxMovable: false,
|
||||||
|
// cropBoxResizable: false,
|
||||||
|
highlight: false,
|
||||||
|
background: true,
|
||||||
|
// crop(event) {
|
||||||
|
// console.log(event);
|
||||||
|
// },
|
||||||
|
})
|
||||||
|
imgBox.value.addEventListener('ready', function() {
|
||||||
|
state.loading = false
|
||||||
|
if (this.cropper === cropper) {
|
||||||
|
cropData && cropper.setData(cropData)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const ok = () => {
|
||||||
|
state.loading = true
|
||||||
|
setTimeout(async () => {
|
||||||
|
const newImg = cropper.getCroppedCanvas({ maxWidth: 4096, minWidth: 4096 }).toDataURL('image/jpeg', 0.8)
|
||||||
|
const { width, height } = cropper.getCropBoxData()
|
||||||
|
const { preview_url } = await api.material.uploadBase64({ file: newImg })
|
||||||
|
context.emit('done', { newImg: preview_url, data: cropper.getData(), width, height })
|
||||||
|
cancel()
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancel = () => {
|
||||||
|
store.commit('setShowMoveable', true)
|
||||||
|
dialogVisible.value = false
|
||||||
|
state.url = ''
|
||||||
|
cropData = null
|
||||||
|
state.loading = false
|
||||||
|
cropper.destroy()
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...toRefs(state),
|
||||||
|
dialogVisible,
|
||||||
|
handleClose,
|
||||||
|
open,
|
||||||
|
ok,
|
||||||
|
cancel,
|
||||||
|
imgBox,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
114
src/components/business/cropper/CropImage/index 2.vue
Normal file
114
src/components/business/cropper/CropImage/index 2.vue
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
<!--
|
||||||
|
* @Author: ShawnPhang
|
||||||
|
* @Date: 2024-03-02 13:32:00
|
||||||
|
* @Description: 裁剪组件
|
||||||
|
* @LastEditors: ShawnPhang <site: book.palxp.com>, Jeremy Yu <https://github.com/JeremyYu-cn>
|
||||||
|
* @LastEditTime: 2024-03-02 13:32:00
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<el-dialog v-model="dialogVisible" title="裁剪图片" width="80%" :before-close="handleClose" @close="cancel">
|
||||||
|
<div id="wrap" v-loading="state.loading" style="height: 50vh">
|
||||||
|
<img v-show="state.url" ref="imgBox" style="visibility: hidden" alt="imgBox" :src="state.url" />
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="cancel">取消</el-button>
|
||||||
|
<el-button :loading="state.loading" plain type="primary" @click="ok">确认</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import api from '@/api'
|
||||||
|
import { ElDialog } from 'element-plus'
|
||||||
|
import { ref, defineEmits, reactive, nextTick, toRefs } from 'vue'
|
||||||
|
import { useStore } from 'vuex'
|
||||||
|
import 'cropperjs/dist/cropper.css'
|
||||||
|
import Cropper from 'cropperjs'
|
||||||
|
|
||||||
|
type TDoneParams = {
|
||||||
|
newImg: string,
|
||||||
|
data: Cropper.Data,
|
||||||
|
width: string,
|
||||||
|
height: string
|
||||||
|
}
|
||||||
|
type TDoneFunc = (event: "done", data: TDoneParams) => void
|
||||||
|
|
||||||
|
type TOpenItem = {
|
||||||
|
rawImg?: string
|
||||||
|
imgUrl: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const store = useStore()
|
||||||
|
const state = reactive({
|
||||||
|
loading: false,
|
||||||
|
url: '',
|
||||||
|
})
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const imgBox = ref<HTMLImageElement>()
|
||||||
|
let cropData: Cropper.Data | null
|
||||||
|
let cropper: Cropper
|
||||||
|
|
||||||
|
const emit = defineEmits<TDoneFunc>()
|
||||||
|
|
||||||
|
const handleClose = (done: () => void) => {
|
||||||
|
done()
|
||||||
|
}
|
||||||
|
|
||||||
|
const open = async (item: TOpenItem, data: Cropper.Data) => {
|
||||||
|
state.loading = true
|
||||||
|
item.rawImg = item.rawImg ? item.rawImg : item.imgUrl
|
||||||
|
cropData = data
|
||||||
|
state.url = item.rawImg
|
||||||
|
store.commit('setShowMoveable', false)
|
||||||
|
dialogVisible.value = true
|
||||||
|
await nextTick()
|
||||||
|
setEdit()
|
||||||
|
}
|
||||||
|
|
||||||
|
const setEdit = () => {
|
||||||
|
if (!imgBox || !imgBox.value) return
|
||||||
|
cropper = new Cropper(imgBox.value, {
|
||||||
|
// aspectRatio: imgBox.value.width / imgBox.value.height,
|
||||||
|
dragMode: 'move',
|
||||||
|
viewMode: 1,
|
||||||
|
cropBoxMovable: false,
|
||||||
|
// cropBoxResizable: false,
|
||||||
|
highlight: false,
|
||||||
|
background: true,
|
||||||
|
// crop(event) {
|
||||||
|
// console.log(event);
|
||||||
|
// },
|
||||||
|
})
|
||||||
|
imgBox.value.addEventListener('ready', function() {
|
||||||
|
state.loading = false
|
||||||
|
// if (this.cropper === cropper) {
|
||||||
|
cropData && cropper.setData(cropData)
|
||||||
|
// }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const ok = () => {
|
||||||
|
state.loading = true
|
||||||
|
setTimeout(async () => {
|
||||||
|
// const newImg = cropper.getCroppedCanvas({ maxWidth: 4096, minWidth: 4096 }).toDataURL('image/jpeg', 0.8)
|
||||||
|
// const { width, height } = cropper.getCropBoxData()
|
||||||
|
// const { preview_url } = await api.material.uploadBase64({ file: newImg })
|
||||||
|
// context.emit('done', { newImg: preview_url, data: cropper.getData(), width, height })
|
||||||
|
cancel()
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancel = () => {
|
||||||
|
store.commit('setShowMoveable', true)
|
||||||
|
dialogVisible.value = false
|
||||||
|
state.url = ''
|
||||||
|
cropData = null
|
||||||
|
state.loading = false
|
||||||
|
cropper.destroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
toRefs(state),
|
||||||
|
|
||||||
|
</script>
|
@ -10,63 +10,83 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, watch } from 'vue'
|
import { watch, defineProps } from 'vue'
|
||||||
import { useStore } from 'vuex'
|
import { useStore } from 'vuex'
|
||||||
import Guides from '@scena/guides'
|
import Guides, { GuideOptions } from '@scena/guides'
|
||||||
|
|
||||||
export default defineComponent({
|
type TProps = {
|
||||||
props: ['show'],
|
show: boolean
|
||||||
setup(props) {
|
}
|
||||||
const store = useStore()
|
|
||||||
const container = 'page-design' // page-design out-page
|
|
||||||
let guidesTop: any = null
|
|
||||||
let guidesLeft: any = null
|
|
||||||
|
|
||||||
watch(
|
type TSameParams = {
|
||||||
|
backgroundColor: string,
|
||||||
|
lineColor: string
|
||||||
|
textColor: string
|
||||||
|
// direction: 'start',
|
||||||
|
// height: 30,
|
||||||
|
displayDragPos: boolean,
|
||||||
|
dragPosFormat: (v: string | number) => string,
|
||||||
|
}
|
||||||
|
|
||||||
|
type TGuidesData = Guides & GuideOptions
|
||||||
|
|
||||||
|
const props = defineProps<TProps>()
|
||||||
|
|
||||||
|
const store = useStore()
|
||||||
|
const container = 'page-design' // page-design out-page
|
||||||
|
let guidesTop: TGuidesData | null = null
|
||||||
|
let guidesLeft: TGuidesData | null = null
|
||||||
|
|
||||||
|
watch(
|
||||||
() => props.show,
|
() => props.show,
|
||||||
(open) => {
|
(open) => {
|
||||||
open ? render() : destroy()
|
open ? render() : destroy()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => store.getters.dZoom,
|
() => store.getters.dZoom,
|
||||||
() => {
|
() => {
|
||||||
changeScroll()
|
changeScroll()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// onMounted(() => {
|
// onMounted(() => {
|
||||||
// // let scrollX = 0
|
// // let scrollX = 0
|
||||||
// // let scrollY = 0
|
// // let scrollY = 0
|
||||||
// // window.addEventListener('resize', () => {
|
// // window.addEventListener('resize', () => {
|
||||||
// // guides.resize()
|
// // guides.resize()
|
||||||
// // })
|
// // })
|
||||||
// // window.addEventListener('wheel', (e) => {
|
// // window.addEventListener('wheel', (e) => {
|
||||||
// // scrollX += e.deltaX
|
// // scrollX += e.deltaX
|
||||||
// // scrollY += e.deltaY
|
// // scrollY += e.deltaY
|
||||||
// // guides.scrollGuides(scrollY)
|
// // guides.scrollGuides(scrollY)
|
||||||
// // guides.scroll(scrollX)
|
// // guides.scroll(scrollX)
|
||||||
// // })
|
// // })
|
||||||
// })
|
// })
|
||||||
|
|
||||||
function destroy() {
|
function destroy() {
|
||||||
guidesTop.destroy()
|
guidesTop?.destroy()
|
||||||
guidesLeft.destroy()
|
guidesLeft?.destroy()
|
||||||
guidesTop = null
|
guidesTop = null
|
||||||
guidesLeft = null
|
guidesLeft = null
|
||||||
}
|
}
|
||||||
function render() {
|
|
||||||
const sameParams: any = {
|
function render() {
|
||||||
|
const sameParams: TSameParams = {
|
||||||
backgroundColor: '#f9f9fa',
|
backgroundColor: '#f9f9fa',
|
||||||
lineColor: '#bec2c7',
|
lineColor: '#bec2c7',
|
||||||
textColor: '#999999',
|
textColor: '#999999',
|
||||||
// direction: 'start',
|
// direction: 'start',
|
||||||
// height: 30,
|
// height: 30,
|
||||||
displayDragPos: true,
|
displayDragPos: true,
|
||||||
dragPosFormat: (v: any) => v + 'px',
|
dragPosFormat: (v) => v + 'px',
|
||||||
}
|
}
|
||||||
guidesTop = new Guides(document.getElementById(container), {
|
|
||||||
|
const containerEl = document.getElementById(container)
|
||||||
|
if (!containerEl) return
|
||||||
|
|
||||||
|
guidesTop = new Guides(containerEl, {
|
||||||
...sameParams,
|
...sameParams,
|
||||||
type: 'horizontal',
|
type: 'horizontal',
|
||||||
className: 'my-horizontal',
|
className: 'my-horizontal',
|
||||||
@ -77,7 +97,7 @@ export default defineComponent({
|
|||||||
// store.commit('updateGuidelines', { horizontalGuidelines: e.guides.map((x) => x + top) })
|
// store.commit('updateGuidelines', { horizontalGuidelines: e.guides.map((x) => x + top) })
|
||||||
})
|
})
|
||||||
|
|
||||||
guidesLeft = new Guides(document.getElementById(container), {
|
guidesLeft = new Guides(containerEl, {
|
||||||
...sameParams,
|
...sameParams,
|
||||||
type: 'vertical',
|
type: 'vertical',
|
||||||
className: 'my-vertical',
|
className: 'my-vertical',
|
||||||
@ -87,8 +107,9 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
changeScroll()
|
changeScroll()
|
||||||
}
|
}
|
||||||
function changeScroll() {
|
|
||||||
|
function changeScroll() {
|
||||||
if (guidesTop && guidesLeft) {
|
if (guidesTop && guidesLeft) {
|
||||||
const zoom = store.getters.dZoom / 100
|
const zoom = store.getters.dZoom / 100
|
||||||
guidesTop.zoom = zoom
|
guidesTop.zoom = zoom
|
||||||
@ -98,18 +119,18 @@ export default defineComponent({
|
|||||||
guidesLeft.unit = Math.floor(1 / zoom) * 50
|
guidesLeft.unit = Math.floor(1 / zoom) * 50
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
if (guidesTop && guidesLeft) {
|
||||||
const el = document.getElementById('out-page')
|
const el = document.getElementById('out-page')
|
||||||
const left = 60 + (el?.offsetLeft || 0)
|
const left = 60 + (el?.offsetLeft ?? 0)
|
||||||
const top = 30 + (el?.offsetTop || 0)
|
const top = 30 + (el?.offsetTop ?? 0)
|
||||||
guidesTop.scroll(-left / zoom)
|
guidesTop.scroll(-left / zoom)
|
||||||
guidesTop.scrollGuides(-top / zoom)
|
guidesTop.scrollGuides(-top / zoom)
|
||||||
guidesLeft.scroll(-top / zoom)
|
guidesLeft.scroll(-top / zoom)
|
||||||
guidesLeft.scrollGuides(-(left - 30) / zoom)
|
guidesLeft.scrollGuides(-(left - 30) / zoom)
|
||||||
|
}
|
||||||
}, 300)
|
}, 300)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user