feat: image support fast matting

This commit is contained in:
ShawnPhang 2023-10-05 16:25:56 +08:00
parent d2f171e368
commit 53df4edd76
5 changed files with 57 additions and 18 deletions

View File

@ -2,8 +2,8 @@
* @Author: ShawnPhang * @Author: ShawnPhang
* @Date: 2021-08-29 20:35:31 * @Date: 2021-08-29 20:35:31
* @Description: * @Description:
* @LastEditors: ShawnPhang <site: book.palxp.com> * @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2023-07-11 22:02:12 * @LastEditTime: 2023-10-05 16:11:55
*/ */
import dayjs from 'dayjs' import dayjs from 'dayjs'
import api from '@/api/album' import api from '@/api/album'
@ -18,7 +18,7 @@ export default {
upload: async (file: File, options: Options, cb?: Function) => { upload: async (file: File, options: Options, cb?: Function) => {
const win: any = window const win: any = window
let name = '' let name = ''
const suffix = file.type.split('/')[1] // 文件后缀 const suffix = file.type.split('/')[1] || 'png' // 文件后缀
if (!options.fullPath) { if (!options.fullPath) {
// const DT: any = await exifGetTime(file) // 照片时间 // const DT: any = await exifGetTime(file) // 照片时间
const DT: any = new Date() const DT: any = new Date()

View File

@ -3,7 +3,7 @@
* @Date: 2023-07-11 23:50:22 * @Date: 2023-07-11 23:50:22
* @Description: 抠图组件 * @Description: 抠图组件
* @LastEditors: ShawnPhang <https://m.palxp.cn> * @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2023-09-30 12:23:39 * @LastEditTime: 2023-10-05 16:19:11
--> -->
<template> <template>
<el-dialog v-model="show" title="AI 智能抠图" width="650" @close="handleClose"> <el-dialog v-model="show" title="AI 智能抠图" width="650" @close="handleClose">
@ -29,15 +29,16 @@
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button v-show="rawImage" @click="clear">重新选择图片</el-button> <el-button v-show="rawImage && toolModel" @click="clear">重新选择图片</el-button>
<el-button v-show="cutImage" type="primary" plain @click="download"> 下载 </el-button> <el-button v-show="cutImage && toolModel" type="primary" plain @click="download"> 下载 </el-button>
<el-button v-show="cutImage && !toolModel" type="primary" plain @click="cutDone"> 完成 </el-button>
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, reactive, toRefs } from 'vue' import { defineComponent, reactive, toRefs, nextTick } from 'vue'
import { useStore } from 'vuex' import { useStore } from 'vuex'
import { ElProgress } from 'element-plus' import { ElProgress } from 'element-plus'
import { UploadFilled } from '@element-plus/icons-vue' import { UploadFilled } from '@element-plus/icons-vue'
@ -47,7 +48,8 @@ import * as api from '@/api/ai'
export default defineComponent({ export default defineComponent({
components: { uploader, UploadFilled, ElProgress }, components: { uploader, UploadFilled, ElProgress },
setup() { emits: ['done'],
setup(props, { emit }) {
const store = useStore() const store = useStore()
const state: any = reactive({ const state: any = reactive({
show: false, show: false,
@ -58,6 +60,7 @@ export default defineComponent({
percent: 0, percent: 0,
progress: 0, progress: 0,
progressText: '', progressText: '',
toolModel: true,
}) })
let fileName: string = 'unknow' let fileName: string = 'unknow'
let isRuning: boolean = false let isRuning: boolean = false
@ -90,9 +93,15 @@ export default defineComponent({
} else alert('服务器繁忙,请稍等下重新尝试~') } else alert('服务器繁忙,请稍等下重新尝试~')
} }
const open = () => { const open = (file: File) => {
state.show = true state.show = true
store.commit('setShowMoveable', false) store.commit('setShowMoveable', false)
nextTick(() => {
if (file) {
selectFile(file)
state.toolModel = false
}
})
} }
const handleClose = () => { const handleClose = () => {
@ -122,6 +131,15 @@ export default defineComponent({
state.percent < 100 ? requestAnimationFrame(run) : (isRuning = false) state.percent < 100 ? requestAnimationFrame(run) : (isRuning = false)
} }
const cutDone = async () => {
const response = await fetch(state.cutImage)
const buffer = await response.arrayBuffer()
const file = new File([buffer], `cut_image_${Math.random()}.png`)
emit('done', file)
state.show = false
handleClose()
}
return { return {
clear, clear,
download, download,
@ -130,6 +148,7 @@ export default defineComponent({
open, open,
handleClose, handleClose,
...toRefs(state), ...toRefs(state),
cutDone,
} }
}, },
}) })

View File

@ -2,8 +2,8 @@
* @Author: ShawnPhang * @Author: ShawnPhang
* @Date: 2021-08-29 18:17:13 * @Date: 2021-08-29 18:17:13
* @Description: 二次封装上传组件 * @Description: 二次封装上传组件
* @LastEditors: ShawnPhang <site: book.palxp.com> * @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2023-07-12 15:12:07 * @LastEditTime: 2023-10-05 15:46:02
--> -->
<template> <template>
<el-upload action="" accept="image/*" :http-request="upload" :show-file-list="false" multiple> <el-upload action="" accept="image/*" :http-request="upload" :show-file-list="false" multiple>
@ -75,7 +75,7 @@ export default defineComponent({
if (file.size <= 1024 * 1024) { if (file.size <= 1024 * 1024) {
tempSimpleRes = await qiNiuUpload(file) // tempSimpleRes = await qiNiuUpload(file) //
const { width, height }: any = await getImage(file) const { width, height }: any = await getImage(file)
useNotification('上传成功', '目前没有用户系统,请注意别上传隐私照片哦!', { position: 'bottom-left' }) useNotification('上传成功', '公共测试账户,上传请注意保护隐私哦!', { position: 'bottom-left' })
context.emit('done', { width, height, url: _config.IMG_URL + tempSimpleRes.key }) // context.emit('done', { width, height, url: _config.IMG_URL + tempSimpleRes.key }) //
} else useNotification('爱护小水管', '请上传小于 1M 的图片哦!', { type: 'error', position: 'bottom-left' }) } else useNotification('爱护小水管', '请上传小于 1M 的图片哦!', { type: 'error', position: 'bottom-left' })
uploading = false uploading = false

View File

@ -3,7 +3,7 @@
* @Date: 2022-02-23 15:48:52 * @Date: 2022-02-23 15:48:52
* @Description: 图片列表组件 Bookshelf Layout * @Description: 图片列表组件 Bookshelf Layout
* @LastEditors: ShawnPhang <https://m.palxp.cn> * @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2023-10-04 22:05:21 * @LastEditTime: 2023-10-05 16:10:21
--> -->
<template> <template>
<ul ref="listRef" class="img-list-wrap" :style="{ paddingBottom: isShort ? '15px' : '200px' }" @scroll="scrollEvent($event)"> <ul ref="listRef" class="img-list-wrap" :style="{ paddingBottom: isShort ? '15px' : '200px' }" @scroll="scrollEvent($event)">
@ -222,6 +222,7 @@ export default defineComponent({
} }
&__mask { &__mask {
position: absolute; position: absolute;
z-index: 2;
width: 100%; width: 100%;
height: 100%; height: 100%;
background: rgba(0, 0, 0, 0.7); background: rgba(0, 0, 0, 0.7);

View File

@ -2,8 +2,8 @@
* @Author: ShawnPhang * @Author: ShawnPhang
* @Date: 2021-08-09 11:41:53 * @Date: 2021-08-09 11:41:53
* @Description: * @Description:
* @LastEditors: ShawnPhang <site: book.palxp.com> * @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2023-07-12 12:50:39 * @LastEditTime: 2023-10-05 16:13:56
--> -->
<template> <template>
<div id="w-image-style"> <div id="w-image-style">
@ -22,7 +22,7 @@
<div class="options"> <div class="options">
<el-button v-if="innerElement.cropEdit" plain type="primary" @click="imgCrop(false)">完成</el-button> <el-button v-if="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 v-else plain type="primary" @click="imgCrop(true)"><i class="icon sd-caijian" />裁剪</el-button>
<el-button @click="openImageCutout" plain>抠图</el-button> <el-button plain @click="openImageCutout">抠图</el-button>
<!-- <uploader class="options__upload" @done="uploadImgDone"> <!-- <uploader class="options__upload" @done="uploadImgDone">
<el-button size="small" plain>替换图片</el-button> <el-button size="small" plain>替换图片</el-button>
</uploader> --> </uploader> -->
@ -52,7 +52,7 @@
<i style="padding: 0 8px; cursor: pointer" class="icon sd-queren" @click="imgCrop(false)" /> <i style="padding: 0 8px; cursor: pointer" class="icon sd-queren" @click="imgCrop(false)" />
</inner-tool-bar> </inner-tool-bar>
<picBox ref="picBox" @select="selectDone" /> <picBox ref="picBox" @select="selectDone" />
<imageCutout ref="imageCutout" /> <imageCutout ref="imageCutout" @done="cutImageDone" />
</div> </div>
</template> </template>
@ -73,6 +73,8 @@ import layerIconList from '@/assets/data/LayerIconList'
import alignIconList from '@/assets/data/AlignListData' import alignIconList from '@/assets/data/AlignListData'
import picBox from '@/components/business/picture-selector' import picBox from '@/components/business/picture-selector'
import imageCutout from '@/components/business/image-cutout' import imageCutout from '@/components/business/image-cutout'
import Qiniu from '@/common/methods/QiNiu'
import _config from '@/config'
export default { export default {
name: NAME, name: NAME,
@ -242,7 +244,24 @@ export default {
this.$refs.picBox.open() this.$refs.picBox.open()
}, },
openImageCutout() { openImageCutout() {
this.$refs.imageCutout.open() fetch(this.innerElement.imgUrl)
.then((response) => response.blob())
.then((blob) => {
const file = new File([blob], `image_${Math.random()}.jpg`, { type: 'image/jpeg' })
this.$refs.imageCutout.open(file)
})
.catch((error) => {
console.error('获取图片失败:', error)
})
},
//
async cutImageDone(file) {
const qnOptions = { bucket: 'xp-design', prePath: 'user' }
const result = await Qiniu.upload(file, qnOptions)
const { width, height } = await getImage(file)
const url = _config.IMG_URL + result.key
await api.material.addMyPhoto({ width, height, url })
this.innerElement.imgUrl = url
}, },
}, },
} }