mirror of
https://github.com/palxiao/poster-design.git
synced 2025-07-15 16:02:19 +08:00
code: optimize image cutout
This commit is contained in:
parent
d2df5c488b
commit
91088b98e3
@ -14,7 +14,6 @@
|
|||||||
"publish-fast": "git add . && git commit -m 'build: auto publish' && sh script/publish.sh"
|
"publish-fast": "git add . && git commit -m 'build: auto publish' && sh script/publish.sh"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@gradio/client": "^0.1.4",
|
|
||||||
"@palxp/color-picker": "^1.3.1",
|
"@palxp/color-picker": "^1.3.1",
|
||||||
"@scena/guides": "^0.18.1",
|
"@scena/guides": "^0.18.1",
|
||||||
"@webtoon/psd": "^0.4.0",
|
"@webtoon/psd": "^0.4.0",
|
||||||
|
24
src/api/ai.ts
Normal file
24
src/api/ai.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* @Author: ShawnPhang
|
||||||
|
* @Date: 2021-08-27 14:42:15
|
||||||
|
* @Description: AI相关接口
|
||||||
|
* @LastEditors: ShawnPhang <https://m.palxp.cn>
|
||||||
|
* @LastEditTime: 2023-09-30 12:08:01
|
||||||
|
*/
|
||||||
|
import fetch from '@/utils/axios'
|
||||||
|
|
||||||
|
// 上传接口
|
||||||
|
export const upload = (file: File, cb: Function) => {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
const extra = {
|
||||||
|
responseType: 'blob',
|
||||||
|
onUploadProgress: (progress: any) => {
|
||||||
|
cb(Math.floor((progress.loaded / progress.total) * 100), 0)
|
||||||
|
},
|
||||||
|
onDownloadProgress: (progress: any) => {
|
||||||
|
cb(100, Math.floor((progress.loaded / progress.total) * 100))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return fetch('https://res.palxp.cn/ai/upload', formData, 'post', {}, extra)
|
||||||
|
}
|
@ -2,13 +2,15 @@
|
|||||||
* @Author: ShawnPhang
|
* @Author: ShawnPhang
|
||||||
* @Date: 2021-08-19 18:43:22
|
* @Date: 2021-08-19 18:43:22
|
||||||
* @Description:
|
* @Description:
|
||||||
* @LastEditors: ShawnPhang <site: book.palxp.com>
|
* @LastEditors: ShawnPhang <https://m.palxp.cn>
|
||||||
* @LastEditTime: 2023-07-21 13:06:46
|
* @LastEditTime: 2023-09-28 15:46:56
|
||||||
*/
|
*/
|
||||||
import * as home from './home'
|
import * as home from './home'
|
||||||
import * as material from './material'
|
import * as material from './material'
|
||||||
|
import * as ai from './ai'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
home,
|
home,
|
||||||
material,
|
material,
|
||||||
|
ai,
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
/*
|
/*
|
||||||
* @Author: ShawnPhang
|
* @Author: ShawnPhang
|
||||||
* @Date: 2021-08-27 14:42:15
|
* @Date: 2021-08-27 14:42:15
|
||||||
* @Description:
|
* @Description: 媒体相关接口
|
||||||
* @LastEditors: ShawnPhang <site: book.palxp.com>
|
* @LastEditors: ShawnPhang <https://m.palxp.cn>
|
||||||
* @LastEditTime: 2023-07-21 11:19:04
|
* @LastEditTime: 2023-09-28 11:24:13
|
||||||
*/
|
*/
|
||||||
import fetch from '@/utils/axios'
|
import fetch from '@/utils/axios'
|
||||||
|
|
||||||
|
@ -135,13 +135,4 @@
|
|||||||
box-shadow: 0 0 0 5000px rgba(255, 255, 255, 0.95);
|
box-shadow: 0 0 0 5000px rgba(255, 255, 255, 0.95);
|
||||||
z-index: 8;
|
z-index: 8;
|
||||||
}
|
}
|
||||||
.shelter-bg {
|
|
||||||
// background-color: #ffffff;
|
|
||||||
background-color: #f0f0f0;
|
|
||||||
background-image: linear-gradient(to top right, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff), linear-gradient(to top right, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff);
|
|
||||||
background-position: 0 0, 8px 8px;
|
|
||||||
background-size: 16px 16px;
|
|
||||||
overflow: hidden;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -25,3 +25,12 @@
|
|||||||
.line-clamp-2 {
|
.line-clamp-2 {
|
||||||
-webkit-line-clamp: 2;
|
-webkit-line-clamp: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.transparent-bg {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
background-image: linear-gradient(to top right, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff), linear-gradient(to top right, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff);
|
||||||
|
background-position: 0 0, 8px 8px;
|
||||||
|
background-size: 16px 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
@ -3,21 +3,25 @@
|
|||||||
* @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-15 12:38:49
|
* @LastEditTime: 2023-09-30 12:10:31
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<el-dialog v-model="show" title="AI抠图(测试版)" width="650" @close="handleClose">
|
<el-dialog v-model="show" title="AI 智能抠图" width="650" @close="handleClose">
|
||||||
<uploader v-if="!rawImage" :hold="true" :drag="true" :multiple="true" class="uploader" @load="selectFile">
|
<uploader v-if="!rawImage" :hold="true" :drag="true" :multiple="true" class="uploader" @load="selectFile">
|
||||||
<div class="uploader__box">
|
<div class="uploader__box">
|
||||||
<upload-filled style="width: 64px; height: 64px" />
|
<upload-filled style="width: 64px; height: 64px" />
|
||||||
<div class="el-upload__text">在此拖入或选择<em>上传图片</em></div>
|
<div class="el-upload__text">在此拖入或选择<em>上传图片</em></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="el-upload__tip">支持 jpg 或 png 格式的文件,大小不超过 2M</div>
|
<div class="el-upload__tip">由于服务器带宽过低,上传大小限制在 1M 内</div>
|
||||||
</uploader>
|
</uploader>
|
||||||
|
<el-progress v-if="!cutImage && progressText" :percentage="progress">
|
||||||
|
<el-button text>
|
||||||
|
{{ progressText }} <span v-show="progress">{{ progress }}%</span>
|
||||||
|
</el-button>
|
||||||
|
</el-progress>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div v-show="rawImage" v-loading="!cutImage" :style="{ width: offsetWidth ? offsetWidth + 'px' : '100%' }" class="scan-effect">
|
<div v-show="rawImage" v-loading="!cutImage" :style="{ width: offsetWidth ? offsetWidth + 'px' : '100%' }" class="scan-effect transparent-bg">
|
||||||
<img ref="raw" :src="rawImage" alt="" />
|
<img ref="raw" :style="{ 'clip-path': 'inset(0 0 0 ' + percent + '%)' }" :src="rawImage" alt="" />
|
||||||
<div :style="{ right: 100 - percent + '%' }" class="bg"></div>
|
|
||||||
<img v-show="cutImage" :src="cutImage" alt="结果图像" @mousemove="mousemove" />
|
<img v-show="cutImage" :src="cutImage" alt="结果图像" @mousemove="mousemove" />
|
||||||
<div v-show="cutImage" :style="{ left: percent + '%' }" class="scan-line"></div>
|
<div v-show="cutImage" :style="{ left: percent + '%' }" class="scan-line"></div>
|
||||||
</div>
|
</div>
|
||||||
@ -33,14 +37,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, reactive, toRefs, onMounted, nextTick } from 'vue'
|
import { defineComponent, reactive, toRefs } from 'vue'
|
||||||
import { useStore } from 'vuex'
|
import { useStore } from 'vuex'
|
||||||
|
import { ElProgress } from 'element-plus'
|
||||||
import { UploadFilled } from '@element-plus/icons-vue'
|
import { UploadFilled } from '@element-plus/icons-vue'
|
||||||
import uploader from '@/components/common/Uploader/index.vue'
|
import uploader from '@/components/common/Uploader/index.vue'
|
||||||
import _dl from '@/common/methods/download'
|
import _dl from '@/common/methods/download'
|
||||||
|
import * as api from '@/api/ai'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { uploader, UploadFilled },
|
components: { uploader, UploadFilled, ElProgress },
|
||||||
setup() {
|
setup() {
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
const state: any = reactive({
|
const state: any = reactive({
|
||||||
@ -50,18 +56,15 @@ export default defineComponent({
|
|||||||
raw: null,
|
raw: null,
|
||||||
offsetWidth: 0,
|
offsetWidth: 0,
|
||||||
percent: 0,
|
percent: 0,
|
||||||
|
progress: 0,
|
||||||
|
progressText: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
let app: any = null
|
|
||||||
let fileName: string = 'unknow'
|
let fileName: string = 'unknow'
|
||||||
let isRuning: boolean = false
|
let isRuning: boolean = false
|
||||||
onMounted(async () => {
|
|
||||||
app = await store.getters.app
|
|
||||||
})
|
|
||||||
|
|
||||||
const selectFile = async (file: File) => {
|
const selectFile = async (file: File) => {
|
||||||
if (file.size > 1024 * 1024 * 2) {
|
if (file.size > 1024 * 1024) {
|
||||||
alert('请上传小于 2M 的图片')
|
alert('上传图片超出限制')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// 显示选择的图片
|
// 显示选择的图片
|
||||||
@ -70,10 +73,22 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
state.rawImage = URL.createObjectURL(file)
|
state.rawImage = URL.createObjectURL(file)
|
||||||
fileName = file.name
|
fileName = file.name
|
||||||
// 返回抠图
|
// 返回抠图结果
|
||||||
const result = await app.predict('/predict', [file, 'u2netp', ''])
|
const result: any = await api.upload(file, (up: number, dp: number) => {
|
||||||
state.rawImage && (state.cutImage = result?.data[0])
|
if (dp) {
|
||||||
|
state.progressText = '导入中..'
|
||||||
|
state.progress = dp
|
||||||
|
} else {
|
||||||
|
state.progressText = up < 100 ? '上传中..' : '正在处理,请稍候..'
|
||||||
|
state.progress = up < 100 ? up : 0
|
||||||
|
}
|
||||||
|
dp === 100 && (state.progressText = '')
|
||||||
|
})
|
||||||
|
if (result.type !== 'application/json') {
|
||||||
|
const resultImage = URL.createObjectURL(result)
|
||||||
|
state.rawImage && (state.cutImage = resultImage)
|
||||||
requestAnimationFrame(run)
|
requestAnimationFrame(run)
|
||||||
|
} else alert('服务器繁忙,请稍等下重新尝试~')
|
||||||
}
|
}
|
||||||
|
|
||||||
const open = () => {
|
const open = () => {
|
||||||
@ -146,12 +161,6 @@ export default defineComponent({
|
|||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
.bg {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: #ffffff;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.scan-line {
|
.scan-line {
|
||||||
@ -163,4 +172,8 @@ export default defineComponent({
|
|||||||
// background-image: linear-gradient(to top, transparent, rgba(255, 255, 255, 0.7), transparent);
|
// background-image: linear-gradient(to top, transparent, rgba(255, 255, 255, 0.7), transparent);
|
||||||
box-shadow: 0 0 2px rgba(0, 0, 0, 0.3);
|
box-shadow: 0 0 2px rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,10 +1,3 @@
|
|||||||
/*
|
|
||||||
* @Author: ShawnPhang
|
|
||||||
* @Date: 2023-09-07 22:56:09
|
|
||||||
* @Description: 配置文件
|
|
||||||
* @LastEditors: ShawnPhang <https://m.palxp.cn>
|
|
||||||
* @LastEditTime: 2023-09-20 16:09:59
|
|
||||||
*/
|
|
||||||
// const prefix = import.meta.env
|
// const prefix = import.meta.env
|
||||||
const prefix = process.env
|
const prefix = process.env
|
||||||
|
|
||||||
@ -21,7 +14,6 @@ export default {
|
|||||||
API_URL: 'https://palxp.cn:8887', // 服务端地址
|
API_URL: 'https://palxp.cn:8887', // 服务端地址
|
||||||
SCREEN_URL: isDev ? 'http://localhost:7001' : '#{SCREEN_URL}', // 截图服务地址
|
SCREEN_URL: isDev ? 'http://localhost:7001' : '#{SCREEN_URL}', // 截图服务地址
|
||||||
IMG_URL: 'https://store.palxp.com/', // 七牛云资源地址
|
IMG_URL: 'https://store.palxp.com/', // 七牛云资源地址
|
||||||
KT_URL: 'https://res.palxp.cn:5001', // 抠图服务地址
|
|
||||||
// ICONFONT_URL: '//at.alicdn.com/t/font_3223711_74mlzj4jdue.css',
|
// ICONFONT_URL: '//at.alicdn.com/t/font_3223711_74mlzj4jdue.css',
|
||||||
ICONFONT_URL: '//at.alicdn.com/t/font_2717063_ypy8vprc3b.css?display=swap',
|
ICONFONT_URL: '//at.alicdn.com/t/font_2717063_ypy8vprc3b.css?display=swap',
|
||||||
ICONFONT_EXTRA: '//at.alicdn.com/t/c/font_3228074_6qsac4kteu7.css?&display=swap',
|
ICONFONT_EXTRA: '//at.alicdn.com/t/c/font_3228074_6qsac4kteu7.css?&display=swap',
|
||||||
|
@ -3,11 +3,10 @@
|
|||||||
* @Date: 2021-12-16 16:20:16
|
* @Date: 2021-12-16 16:20:16
|
||||||
* @Description:
|
* @Description:
|
||||||
* @LastEditors: ShawnPhang <https://m.palxp.cn>
|
* @LastEditors: ShawnPhang <https://m.palxp.cn>
|
||||||
* @LastEditTime: 2023-09-15 12:47:57
|
* @LastEditTime: 2023-09-28 17:42:25
|
||||||
*/
|
*/
|
||||||
import mutations from './mutations'
|
import mutations from './mutations'
|
||||||
import actions from './actions'
|
import actions from './actions'
|
||||||
import { client } from '@gradio/client'
|
|
||||||
import _config from '@/config'
|
import _config from '@/config'
|
||||||
|
|
||||||
const all = {
|
const all = {
|
||||||
@ -39,10 +38,6 @@ const all = {
|
|||||||
fonts: (state: Type.Object) => {
|
fonts: (state: Type.Object) => {
|
||||||
return state.fonts
|
return state.fonts
|
||||||
},
|
},
|
||||||
app: async (state: Type.Object) => {
|
|
||||||
!state.app && (state.app = await client(_config.KT_URL))
|
|
||||||
return state.app
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
...mutations,
|
...mutations,
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
* @Author: ShawnPhang
|
* @Author: ShawnPhang
|
||||||
* @Date: 2021-07-13 02:48:38
|
* @Date: 2021-07-13 02:48:38
|
||||||
* @Description: 接口规则:只有正确code为200时返回result结果对象,错误返回整个结果对象
|
* @Description: 接口规则:只有正确code为200时返回result结果对象,错误返回整个结果对象
|
||||||
* @LastEditors: ShawnPhang <site: book.palxp.com>
|
* @LastEditors: ShawnPhang <https://m.palxp.cn>
|
||||||
* @LastEditTime: 2023-07-31 09:34:34
|
* @LastEditTime: 2023-09-28 15:58:41
|
||||||
*/
|
*/
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
<!-- 用于挡住画布溢出部分,因为使用overflow有bug. PS:如shadow没有透明度则可以完全遮挡元素 -->
|
<!-- 用于挡住画布溢出部分,因为使用overflow有bug. PS:如shadow没有透明度则可以完全遮挡元素 -->
|
||||||
<div class="shelter" :style="{ width: Math.floor((dPage.width * dZoom) / 100) + 'px', height: Math.floor((dPage.height * dZoom) / 100) + 'px' }"></div>
|
<div class="shelter" :style="{ width: Math.floor((dPage.width * dZoom) / 100) + 'px', height: Math.floor((dPage.height * dZoom) / 100) + 'px' }"></div>
|
||||||
<!-- 提供一个背景图层以免遮挡穿帮 -->
|
<!-- 提供一个背景图层以免遮挡穿帮 -->
|
||||||
<div class="shelter-bg" :style="{ width: Math.floor((dPage.width * dZoom) / 100) + 'px', height: Math.floor((dPage.height * dZoom) / 100) + 'px' }"></div>
|
<div class="shelter-bg transparent-bg" :style="{ width: Math.floor((dPage.width * dZoom) / 100) + 'px', height: Math.floor((dPage.height * dZoom) / 100) + 'px' }"></div>
|
||||||
</design-board>
|
</design-board>
|
||||||
<style-panel></style-panel>
|
<style-panel></style-panel>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user