Image的width, contentWidth ,content.width区别

本文详细解释了在图像处理中width和height属性的作用,即代表Image控件的原始宽度和高度。此外,还介绍了contentWidth和contentHeight这两个属性,它们表示经过缩放或调整后的图片尺寸。最后,文中明确了content.width和content.height属性是指图片本身的宽度和高度,并指出这些属性的值不受图片缩放的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. width和height指的是Image的控件的宽和高,当未设定width和height属性时,默认为图片的width和height;

2. contentWidth和contentHeight指的是缩放或调整后的图片大小;

3. content.width和content.height中content指的就是图片本身,
   而content.width和content.height就是图片的widh和height属性,图片缩放不会影响其值

import jsPDF from 'jspdf' import html2canvas from 'html2canvas' /* * 使用说明 * ele:需要导出pdf的容器元素(dom节点 不是id) * pdfFileName: 导出文件的名字 通过调用outPutPdfFn方法也可传参数改变 * splitClassName: 避免分段截断的类名 当pdf有多页时需要传入此参数 , 避免pdf分页时截断元素 如表格<tr class="itemClass"></tr> * 调用方式 先 let pdf = new PdfLoader(ele, 'pdf' ,'itemClass'); * 若想改变pdf名称 pdf.outPutPdfFn(fileName); outPutPdfFn方法返回一个promise 可以使用then方法处理pdf生成后的逻辑 * */ class PdfLoader { constructor(ele, pdfFileName, splitClassName) { console.log(ele) this.ele = ele this.pdfFileName = pdfFileName this.splitClassName = splitClassName this.A4_WIDTH = 595 this.A4_HEIGHT = 842 } async getPDF(resolve) { const ele = this.ele const pdfFileName = this.pdfFileName const eleW = ele.offsetWidth// 获得该容器的宽 const eleH = ele.scrollHeight// 获得该容器的高 const eleOffsetTop = ele.offsetTop// 获得该容器到文档顶部的距离 const eleOffsetLeft = ele.offsetLeft// 获得该容器到文档最左的距离 const canvas = document.createElement('canvas') let abs = 0 const win_in = document.documentElement.clientWidth || document.body.clientWidth// 获得当前可视窗口的宽度(不包含滚动条) const win_out = window.innerWidth// 获得当前窗口的宽度(包含滚动条) if (win_out > win_in) { abs = (win_out - win_in) / 2// 获得滚动条宽度的一半 } canvas.width = eleW * 2// 将画布宽&&高放大两倍 canvas.height = eleH * 2 const context = canvas.getContext('2d') context.scale(2, 2) // 增强图片清晰度 context.translate(-eleOffsetLeft - abs, -eleOffsetTop) html2canvas(ele, { useCORS: true// 允许canvas画布内可以跨域请求外部链接图片, 允许跨域请求。 }).then(async canvas => { const contentWidth = canvas.width const contentHeight = canvas.height // 一页pdf显示html页面生成的canvas高度; const pageHeight = (contentWidth / this.A4_WIDTH) * this.A4_HEIGHT // 这样写的目的在于保持宽高比例一致 pageHeight/canvas.width = a4纸高度/a4纸宽度// 宽度和canvas.width保持一致 // 未生成pdf的html页面高度 let leftHeight = contentHeight // 页面偏移 let position = 0 // a4纸的尺寸[595,842],单位像素,html页面生成的canvas在pdf中图片的宽高 const imgWidth = this.A4_WIDTH - 10 // -10为了页面有右边距 const imgHeight = (this.A4_WIDTH / contentWidth) * contentHeight const pageData = canvas.toDataURL('image/jpeg', 1.0) const pdf = jsPDF('', 'pt', 'a4') // 有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89) // 当内容未超过pdf一页显示的范围,无需分页 console.log(leftHeight) console.log(pageHeight) if (leftHeight < pageHeight) { // 在pdf.addImage(pageData, 'JPEG', 左,上,宽度,高度)设置在pdf中显示; pdf.addImage(pageData, 'JPEG', 5, 0, imgWidth, imgHeight) // pdf.addImage(pageData, 'JPEG', 20, 40, imgWidth, imgHeight); } else { // 分页 while (leftHeight > 0) { pdf.addImage(pageData, 'JPEG', 5, position, imgWidth, imgHeight) leftHeight -= pageHeight position -= this.A4_HEIGHT // 避免添加空白页 if (leftHeight > 0) { pdf.addPage() } } } pdf.save(pdfFileName + '.pdf', { returnPromise: true }).then(() => { // 去除添加的空div 防止页面混乱 const doms = document.querySelectorAll('.emptyDiv') for (let i = 0; i < doms.length; i++) { doms[i].remove() } }) this.ele.style.height = '' resolve() }) } //此方法是防止(图表之类)内容因为A4纸张问题被截断 async outPutPdfFn(pdfFileName) { return new Promise((resolve, reject) => { this.ele.style.height = 'initial' pdfFileName ? this.pdfFileName = pdfFileName : null const target = this.ele const pageHeight = target.scrollWidth / this.A4_WIDTH * this.A4_HEIGHT if(this.splitClassName && this.splitClassName.length){ //判断是否有分页 // 获取分割dom,此处为class类名为item的dom const domList = document.getElementsByClassName(this.splitClassName) // 进行分割操作,当dom内容已超出a4的高度,则将该dom前插入一个空dom,把他挤下去,分割 let pageNum = 1 // pdf页数 const eleBounding = this.ele.getBoundingClientRect() for (let i = 0; i < domList.length; i++) { const node = domList[i] const bound = node.getBoundingClientRect() const offset2Ele = bound.top - eleBounding.top const currentPage = Math.ceil((bound.bottom - eleBounding.top) / pageHeight) // 当前元素应该在哪一页 if (pageNum < currentPage) { pageNum++ const divParent = domList[i].parentNode // 获取该div的父节点 const newNode = document.createElement('div') newNode.className = 'emptyDiv' newNode.style.background = 'white' newNode.style.height = (pageHeight * (pageNum - 1) - offset2Ele + 30) + 'px' // +30为了在换下一页时有顶部的边距 newNode.style.width = '100%' divParent.insertBefore(newNode, node) //在每一个节点前面插入一个空的新节点,防止内容被分割截断 } } } // 异步函数,导出成功后处理交互 this.getPDF(resolve, reject) }) } } export default PdfLoader 在这个的基础改
最新发布
06-14
import type { DrawStyleSecondCategoryGridRef } from '@aiComponents/DrawStyleCategoryGrid/SecondGrid.tsx'; import type { UseDrawStyleCategoryConfigProps } from '@aiComponents/DrawStyleCategoryGrid/useDrawStyleCategoryConfig.ts'; import type { FeatureLeaferFrameLayoutCommonProps, FeatureLeaferFrameLayoutRef, } from '@aiComponents/FeatureLeaferFrameLayout/types.ts'; import type { AiToolGenerateModalRef } from '@aiComponents/GenerateModal'; import type { QueryTagItem } from '@aiComponents/QueryTagImagesSelector/types.ts'; import type { SemanticsSegmentImageControlRef, SemanticsSegmentImageType, } from '@aiComponents/SemanticsSegmentImage/types.ts'; import type { StyleConfigTextAreaRef } from '@aiComponents/StyleConfigTextArea'; import type { UploadCropperModalProps, UploadCropperModalRef, } from '@aiComponents/UploadCropperModal'; import type { AiVipEquityLimitModalRef } from '@aiComponents/VipEquityLimitModal'; import type { ToolEquityCountWrapRef } from '@components/ToolEquityCountWrap'; import type { RequestDecorationDesignParams } from '@services/comfyUI.ts'; import type { RequestJzxzDrawImageParams, RequestTextToImageGenerateParams, } from '@services/draw.ts'; import { aiAddEventTrack, combineSentenceBySplitSymbol, getRandomNum, getStyleDataIndexByRefs, } from '@utils/ai.ts'; import { userAuth } from '@utils/auth.ts'; import { computeContentObjectFit, delayFun } from '@utils/common.ts'; import { decodeImageUrl } from '@utils/string.ts'; import { microGetHuoshanUploadImageUrl } from '@utils/upload.ts'; import type { UploadImageResponse } from '@wander/base-utils'; import { fileToBase64 } from '@wander/base-utils'; import { base64ToBlob, blobToFile, getImageSizeInfo, isMobile } from '@wander/base-utils'; import { useMount } from 'ahooks'; import { message } from 'antd'; import type { AxiosRequestConfig } from 'axios'; import { t } from 'i18next'; import PubSub from 'pubsub-js'; import type { ReactNode } from 'react'; import { useEffect, useRef, useState } from 'react'; import { useLocation, useSearchParams } from 'react-router-dom'; import type { CustomUploadProps } from '@/components/CustomUpload'; import type { UploadBtnImageResponse } from '@/components/HuabanUploadBtn'; import type { ImageSelectorModalProps, ImageSelectorModalRef, } from '@/components/ImageSelectorModal'; import type { ThemeSegmentedProps } from '@/components/ThemeSegmented'; import type { UploadImageType } from '@/components/UploadImageCard'; import { imgToImgSizeConfig, JZXZ_DRAW_INIT_NEGATIVE_PROMPTS } from '@/config/aiTool'; import useGetConfigValue from '@/hooks/useGetConfigValue.ts'; import { DECORATION_DESIGN_CATEGORY_SELECT_CONFIG_KEY, DECORATION_DESIGN_CHARTLET_CATEGORY_KEY, DECORATION_DESIGN_COMFY_UI_SELECT_CATEGORY_CONFIG_KEY, decorationDesignSpeedModePrompts, decorationDesignToolKey, TRACK_DECORATION_DESIGN_TOOL_NAME, } from '@/pages/AiTools/DecorationDesign/config.tsx'; import { commonDrawControlNetModelMap } from '@/pages/AiTools/Draw/configs.ts'; import { useToolRouterStore } from '@/pages/AiTools/Router/store/createStore.ts'; import type { RequestDescribeRetouchingParams } from '@/services/aiTool'; import { requestDescribeRetouching } from '@/services/aiTool'; import { useStore } from '@/store/createStore.ts'; import type { AiToolProcessSuccessResponse, ControlNetEditorImageInfo } from '@/types/ai'; import type { CandidateItem, DrawSizeType, DrawStyleConfigItem, GeneratePriorityType, LoraItem, } from '@/types/draw.ts'; import type { ChartletCategoryItem, ChartletImageItem } from '@/types/image'; import { imageDataToBase64 } from '@/utils/image'; export type VersionValueType = 'STANDARD' | 'EXTREME_SPEED' | 'ADVANCED'; // 请求之间的间隔时长 const REQUEST_DELAY_DURATION = 2000; // 默认重试次数 const DEFAULT_RETRY_COUNT = 2; type VersionMobileValue = any; export type VersionOptionItem = { label: ReactNode; value: VersionMobileValue; disabled?: boolean; }; export type CategoriesItem = { key: string; // 风格名称 label: string; // 风格展示图片 thumbUrl: string; // 提示词 promptsList: CandidateItem[]; // 大模型名称 modelName: string; // 小模型 loraParamses?: LoraItem[]; }; export type DecorationDesignStyleItem = { alias: string; key: string; categories: CategoriesItem[]; }; type PromptConfig = { key: string; list: CandidateItem[]; }; export const useAiDecorationDesign = () => { const location = useLocation(); const [searchParams, setSearchParams] = useSearchParams(); const addResultImage = useToolRouterStore((state) => state.addResultImage); // 产品类型 const productType = useStore((state) => state.productInfo.productType); // 不限制权益 const noLimitFrequency = userAuth(['ORGANIZATION_VIP']); // 描述词获取 const styleConfigTextAreaRef = useRef<StyleConfigTextAreaRef>(null); // 底图图库 const imageSelectorModalRef = useRef<ImageSelectorModalRef>(null); // 生成弹窗 const generateModalRef = useRef<AiToolGenerateModalRef<RequestJzxzDrawImageParams>>(null); const drawStyleCategoryRef = useRef<DrawStyleSecondCategoryGridRef>(null); const toolEquityCountWrapRef = useRef<ToolEquityCountWrapRef>(null); // 画板Ref const featureLeaferFrameLayoutRef = useRef<FeatureLeaferFrameLayoutRef>(null); const semanticsSegmentImageControlRef = useRef<SemanticsSegmentImageControlRef>(null); // 刷新次数 const refreshToolEquityUserUseInfo = useStore((state) => state.refreshToolEquityUserUseInfo); // 创作模式优先级 const [generatePriorityType, setGeneratePriorityType] = useState<GeneratePriorityType>('CONTROLNET'); // 会员权益弹窗 const DecorationDesignipLimitModalRef = useRef<AiVipEquityLimitModalRef>(null); // 相似度 const [weight, setWeight] = useState(1); // 最终生成图尺寸大小 const [sizeType, setSizeType] = useState<DrawSizeType>('LARGE'); // 极速版-生成失败的信息 const [errorMsg, setErrorMsg] = useState(''); // 创作弹框-极速版 const generateSpeedModalRef = useRef<AiToolGenerateModalRef>(null); // comfyUI 创作弹框-高级版 const comfyUiGenerateModalRef = useRef<AiToolGenerateModalRef<RequestDecorationDesignParams>>(null); // 风格获取-高级版 const drawComfyUiStyleCategoryRef = useRef<DrawStyleSecondCategoryGridRef>(null); // 风格列表 const [comfyUiStyleList, setComfyUiStyleList] = useState<DecorationDesignStyleItem[]>([]); // 高级版-风格参考图信息 const [styleReferComfyImageInfo, setStyleReferComfyImageInfo] = useState<UploadImageResponse | null>(null); // 风格参考图图库 const comfySourceImageSelectorModalRef = useRef<ImageSelectorModalRef>(null); // comfyUI-XL版本参考图风格符合度 const [comfyStyleReferWeight, setComfyStyleReferWeight] = useState(0.8); // 上传裁剪弹窗 const uploadCropperModalRef = useRef<UploadCropperModalRef>(null); // 裁剪弹窗open const [cropperModalOpen, setCropperModalOpen] = useState(false); // 选中风格索引 const [currentStyleIndex, setCurrentStyleIndex] = useState(0); // 高级版-一级类目状态获取 const [activeAlias, setActiveAlias] = useState<string>(comfyUiStyleList[0]?.alias || ''); // 高级版-获取当前激活的二级类目 const activeCategories = comfyUiStyleList.find((item) => item.alias === activeAlias)?.categories || []; // 版本切换状态 const [versionValue, setVersionValue] = useState<VersionValueType>(() => { return userAuth(['USER', 'COMMON_VIP', 'TOURIST']) ? 'STANDARD' : 'EXTREME_SPEED'; }); // 标准版-该工具支持选择的类目组 const categoryRefsGroup: string[][] = useGetConfigValue({ productKeys: { JZXZ: DECORATION_DESIGN_CATEGORY_SELECT_CONFIG_KEY, JZDS: 'jzds_decoration_design_style_categories', }, }) || []; // 贴图类目列表 const chartletCategoryList = useGetConfigValue<ChartletCategoryItem[]>({ key: DECORATION_DESIGN_CHARTLET_CATEGORY_KEY }) || []; // 线稿库类目列表 const queryTagList = useGetConfigValue<QueryTagItem[]>({ key: 'jzxz_decoration_design_base_Image_query_tag_config', }) || []; // 高级版-该工具支持选择的类目组 const comfyUiCategoryRefsGroup: CategoriesItem[] = ( useGetConfigValue({ productKeys: { JZXZ: DECORATION_DESIGN_COMFY_UI_SELECT_CATEGORY_CONFIG_KEY, }, }) || [] ).filter( (category: CategoriesItem) => category.key === 'indoorHomeDecoration' || category.key === 'indoorWorkWear', ); // 高级版-描述词 const comfyUiPrompt: PromptConfig[] = ( useGetConfigValue({ productKeys: { JZXZ: 'jzxz_advance_draw_descriptor_config', }, }) || [] ).filter((p: PromptConfig) => p.key === 'indoorHomeDecoration' || p.key === 'indoorWorkWear'); console.log('comfyUiPrompt', comfyUiPrompt); // 版本切换-mobile const handleVersionMobileChange = (value: VersionValueType) => { if (userAuth(['TOURIST'])) { PubSub.publish('onLogin', true); return; } if (value !== 'STANDARD' && userAuth(['USER', 'COMMON_VIP'])) { console.log('value', value); DecorationDesignipLimitModalRef.current?.setLimitType('FOREVER_VIP'); return; } // 否则允许切换 setVersionValue(value); // 描述词类型获取 const promptType = styleConfigTextAreaRef.current?.getPromptType(); // 描述词自定义-版本切换判断 if (promptType === 'USER' || promptType === 'INNOVATIVE') { if (value === 'ADVANCED') { styleConfigTextAreaRef.current?.updateCandidateConfig( activeCategories[currentStyleIndex].promptsList, false, ); return; } if (value === 'EXTREME_SPEED') { styleConfigTextAreaRef.current?.updateCandidateConfig( decorationDesignSpeedModePrompts, false, ); return; } if (value === 'STANDARD') { const defaultRefs = categoryRefsGroup[currentStyleIndex]; // 获取对应的类目配置数据 const categories = drawStyleCategoryRef.current?.styleConfigList || []; // 手动触发选择逻辑 const ids = getStyleDataIndexByRefs('id', categories, defaultRefs); if (ids.length) { styleConfigTextAreaRef.current?.updateCandidateConfigByIds(ids, false); } } return; } if (value === 'ADVANCED') { styleConfigTextAreaRef.current?.updateCandidateConfig( activeCategories[currentStyleIndex].promptsList, true, ); return; } if (value === 'EXTREME_SPEED') { styleConfigTextAreaRef.current?.updateCandidateConfig(decorationDesignSpeedModePrompts, true); return; } if (value === 'STANDARD') { const defaultRefs = categoryRefsGroup[currentStyleIndex]; // 获取对应的类目配置数据 const categories = drawStyleCategoryRef.current?.styleConfigList || []; // 手动触发选择逻辑 const ids = getStyleDataIndexByRefs('id', categories, defaultRefs); if (ids.length) { styleConfigTextAreaRef.current?.updateCandidateConfigByIds(ids, true); } } }; // 版本切换-pc const handleVersionPcChange: ThemeSegmentedProps['onChange'] = (item) => { const value = item.value; if (userAuth(['TOURIST'])) { PubSub.publish('onLogin', true); return; } if (value !== 'STANDARD' && userAuth(['USER', 'COMMON_VIP'])) { DecorationDesignipLimitModalRef.current?.setLimitType('FOREVER_VIP'); return; } // 否则允许切换 setVersionValue(value); // 描述词类型获取 const promptType = styleConfigTextAreaRef.current?.getPromptType(); // 描述词自定义-版本切换判断 if (promptType === 'USER' || promptType === 'INNOVATIVE') { if (value === 'ADVANCED') { styleConfigTextAreaRef.current?.updateCandidateConfig( activeCategories[currentStyleIndex].promptsList, false, ); return; } if (value === 'EXTREME_SPEED') { styleConfigTextAreaRef.current?.updateCandidateConfig( decorationDesignSpeedModePrompts, false, ); return; } if (value === 'STANDARD') { const defaultRefs = categoryRefsGroup[currentStyleIndex]; // 获取对应的类目配置数据 const categories = drawStyleCategoryRef.current?.styleConfigList || []; // 手动触发选择逻辑 const ids = getStyleDataIndexByRefs('id', categories, defaultRefs); if (ids.length) { styleConfigTextAreaRef.current?.updateCandidateConfigByIds(ids, false); } } return; } if (value === 'ADVANCED') { styleConfigTextAreaRef.current?.updateCandidateConfig( activeCategories[currentStyleIndex].promptsList, true, ); return; } if (value === 'EXTREME_SPEED') { styleConfigTextAreaRef.current?.updateCandidateConfig(decorationDesignSpeedModePrompts, true); return; } if (value === 'STANDARD') { const defaultRefs = categoryRefsGroup[currentStyleIndex]; // 获取对应的类目配置数据 const categories = drawStyleCategoryRef.current?.styleConfigList || []; // 手动触发选择逻辑 const ids = getStyleDataIndexByRefs('id', categories, defaultRefs); if (ids.length) { styleConfigTextAreaRef.current?.updateCandidateConfigByIds(ids, true); } } }; // 高级版-风格类目描述词对应方法 function mapPromptsToCategories(categoryGroups: any, prompts: any) { return categoryGroups.map((categoryGroup: any) => { const promptConfig = prompts.find((prompt: any) => prompt.key === categoryGroup.key); if (!promptConfig) { return null; } return { alias: categoryGroup.alias, key: categoryGroup.key, categories: categoryGroup.categories.map((cat: any) => ({ key: cat.key, label: cat.alias, thumbUrl: cat.coverUrl, promptsList: promptConfig.list.map((item: any) => ({ placeholder: item.placeholder || '', keyword: item.keyword || '', })), modelName: cat.modelName, lora: cat.loraParamses?.[0], })), }; }); } // 高级版-设计领域与风格切换 const onComfyUiStyleIndexChange = (index: number) => { setCurrentStyleIndex(index); const currentItem = activeCategories[index]; const isUniversalCategory = activeCategories[0]; // 随机描述词 let randomPrompts: CandidateItem[] = []; if (currentItem.promptsList?.length) { const randomIndex = Math.floor(Math.random() * currentItem.promptsList.length); randomPrompts = [currentItem.promptsList[randomIndex]]; } // 更新候选配置 styleConfigTextAreaRef.current?.updateCandidateConfig(randomPrompts, true); if (isUniversalCategory) { styleConfigTextAreaRef.current?.updateCandidateConfig(currentItem.promptsList, true); } }; // 高级版-风格类目名称切换 const handleStyleAliasChange = (item: DecorationDesignStyleItem) => { setActiveAlias(item.alias); // 切换一级类目时重置二级类目索引为0 setCurrentStyleIndex(0); // 获取当前激活的二级类目列表 const currentCategories = comfyUiStyleList.find((style) => style.alias === item.alias)?.categories || []; // 确保存在有效的二级类目 if (currentCategories.length === 0) return; // 描述词类型获取 const promptType = styleConfigTextAreaRef.current?.getPromptType(); // 描述词自定义-版本切换判断 if (promptType === 'USER' || promptType === 'INNOVATIVE') { if (versionValue === 'ADVANCED') { styleConfigTextAreaRef.current?.updateCandidateConfig( activeCategories[currentStyleIndex].promptsList, false, ); return; } } // 合并子类的描述词 const allPrompts = currentCategories.flatMap((cat) => cat.promptsList); if (allPrompts.length === 0) return; // 随机选择一个描述词 const randomIndex = Math.floor(Math.random() * allPrompts.length); const randomPrompt = allPrompts[randomIndex]; console.log('randomPrompt', randomPrompt); // 更新到输入框 styleConfigTextAreaRef.current?.updateCandidateConfig([randomPrompt], true); onComfyUiStyleIndexChange(); }; // 获取语意分割组件和局部绘制组件内部参考图信息并转为ControlNet所需参数 const getControlsControlNetArgs = ({ localDrawImage, semanticsSegmentImage, }: { localDrawImage: ControlNetEditorImageInfo | null; semanticsSegmentImage: SemanticsSegmentImageType | null; }) => { const controlNetArgs: Pick< RequestTextToImageGenerateParams, 'localDrawImage' | 'semanticsSegmentImage' > = {}; if (localDrawImage?.enabled) { controlNetArgs.localDrawImage = { ...localDrawImage, ...commonDrawControlNetModelMap.inpaint, enable: true, // 开始迭代步数 guidanceStart: 0, guidanceEnd: 1, // 结束迭代步数 weight: 1, }; } if (semanticsSegmentImage?.enabled) { controlNetArgs.semanticsSegmentImage = { ...semanticsSegmentImage, ...commonDrawControlNetModelMap.semanticsSegment, enable: true, // 开始迭代步数 guidanceStart: 0, guidanceEnd: 1, // 结束迭代步数 weight: 1, }; } return controlNetArgs; }; // 获取画板图片 const getFrameImage = async (sizeInfo: Pick<UploadImageResponse, 'width' | 'height'>) => { // 导出画布方法 const exportFrame = featureLeaferFrameLayoutRef.current?.exportFrame; if (!exportFrame) { return Promise.reject(); } // 导出内容 const result = await exportFrame('canvas', { size: { width: sizeInfo.width, height: sizeInfo.height, }, }); const leaferCanvas = result.data; const context = leaferCanvas.context; // 获取画板图片 const imageData = context.getImageData(0, 0, leaferCanvas.width, leaferCanvas.height); const base64 = imageDataToBase64(imageData); const file = blobToFile(base64ToBlob(base64)); const imageInfo = await microGetHuoshanUploadImageUrl(file); featureLeaferFrameLayoutRef.current?.setCompareImage(imageInfo); return imageInfo; }; // comfyUI-有风格参考图 const generateComfyStyleReferenceTask = async (uploadImage: UploadImageResponse) => { comfyUiGenerateModalRef.current?.setModalOpen(true); let imageInfo: UploadImageResponse = uploadImage; const prompt = styleConfigTextAreaRef.current?.prompts.placeholder || ''; // 当前尺寸的阈值 const thresholdNum = imgToImgSizeConfig[sizeType]; // 尺寸信息 const sizeParams = computeContentObjectFit({ containerWidth: thresholdNum, containerHeight: thresholdNum, contentWidth: uploadImage.width, contentHeight: uploadImage.height, type: 'contain', }); // 风格参考图获取 const baseImage = styleReferComfyImageInfo?.largeUrl; try { if (!featureLeaferFrameLayoutRef.current?.verifyFrameIsEmpty()) { imageInfo = await getFrameImage(uploadImage); } comfyUiGenerateModalRef.current?.generateTask({ imageUrl: imageInfo.largeUrl, seed: getRandomNum(), ...sizeParams, // 描述词 prompt, // 底图符合度 baseImageWeight: weight, // 参考图片链接 styleReferImageUrl: baseImage, // 风格参考度-权重 styleReferWeight: comfyStyleReferWeight, }); } catch (e) { comfyUiGenerateModalRef.current?.setRequestStatus('GENERATE_ERROR'); } }; // comfyUI-无风格参考图 const generateComfyTask = async (uploadImage: UploadImageResponse) => { comfyUiGenerateModalRef.current?.setModalOpen(true); let imageInfo: UploadImageResponse = uploadImage; const prompt = styleConfigTextAreaRef.current?.prompts.placeholder || ''; // 当前尺寸的阈值 const thresholdNum = imgToImgSizeConfig[sizeType]; // 尺寸信息 const sizeParams = computeContentObjectFit({ containerWidth: thresholdNum, containerHeight: thresholdNum, contentWidth: uploadImage.width, contentHeight: uploadImage.height, type: 'contain', }); try { if (!featureLeaferFrameLayoutRef.current?.verifyFrameIsEmpty()) { imageInfo = await getFrameImage(uploadImage); } comfyUiGenerateModalRef.current?.generateTask({ imageUrl: imageInfo.largeUrl, seed: getRandomNum(), ...sizeParams, // 描述词 prompt, // 底图符合度 baseImageWeight: weight, }); } catch (e) { comfyUiGenerateModalRef.current?.setRequestStatus('GENERATE_ERROR'); } }; // 标准版-开始创作 const onSdStart: FeatureLeaferFrameLayoutCommonProps['onStart'] = async ({ uploadImage, localDrawImage, semanticsSegmentImage, }) => { if (!noLimitFrequency && !toolEquityCountWrapRef.current?.verifyUseStatus()) { return; } if (!uploadImage) { message.warning(t('AiTools-LocalDrawImage-yES4')); return; } let imageInfo: UploadImageResponse = uploadImage; generateModalRef.current?.setModalOpen(true); try { if (!featureLeaferFrameLayoutRef.current?.verifyFrameIsEmpty()) { imageInfo = await getFrameImage(uploadImage); } // 提取当前类目下下的参数 const { modelName = '', loraParamses = [] } = drawStyleCategoryRef.current?.getCategoryParams() || {}; let prompt = styleConfigTextAreaRef.current?.prompts.keyword || ''; // 启用了色块 获取色块描述 if (semanticsSegmentImage?.enabled) { // 去重描述词 const paintKeywords = [ ...new Set(semanticsSegmentImage.colorBlockList.map((item) => item.englishAlias)), ]; prompt = combineSentenceBySplitSymbol([...paintKeywords, prompt]); } // 参考图 const styleImage = styleReferComfyImageInfo; generateModalRef.current?.generateTask({ // 获取语意分割、局部重绘相关controlnet参数 ...getControlsControlNetArgs({ localDrawImage, semanticsSegmentImage }), prompt, modelName, loraParamses, negativePrompt: JZXZ_DRAW_INIT_NEGATIVE_PROMPTS, baseImage: { width: imageInfo.width, height: imageInfo.height, url: imageInfo.largeUrl || imageInfo.thumbUrl, // 开始迭代步数 guidanceStart: 0, // 结束迭代步数 guidanceEnd: 1, // 相似度 module: 'scribble_xdog', weight, model: 'control_v11p_sd15_scribble', }, ...(styleImage ? { styleReferImage: { guidanceEnd: 1, guidanceStart: 0, height: styleImage.height as number, model: 'ip-adapter_sd15_plus [32cd8f7f]', module: 'ip-adapter_clip_sd15', url: styleImage.largeUrl, weight: comfyStyleReferWeight, width: styleImage.width as number, }, } : {}), sizeType, generateMode: 'REPAINT', generatePriorityType, generateImageType: 'DECORATION_DESIGN', }); } catch (error) { message.error(t('AiTools-DecorationDesign-Operate-hook-trpaP')); } }; // comfy-高级版-开始创作 const onComfyStart: FeatureLeaferFrameLayoutCommonProps['onStart'] = async (params) => { if (!params?.uploadImage) { message.warn(t('AiTools-DetailEnhance-Operate-hook-PzPPn')); return; } if (styleReferComfyImageInfo) { await generateComfyStyleReferenceTask(params.uploadImage); } else { await generateComfyTask(params.uploadImage); } }; // 极速版 const sendRequest = async ( // 请求参数 params: RequestDescribeRetouchingParams, // 重试次数 retryCount: number, // 请求头配置 用于减次数 config?: AxiosRequestConfig, ): Promise<UploadImageType> => { // 没有重试次数了 直接抛出异常 if (retryCount <= 0) { throw { errorCode: 'MAX_RETRY_TIMES', errorMsg: '生成图片异常' }; } const response = await requestDescribeRetouching(params, config).catch((e) => { console.error('请求失败:', e); return null; }); // 本次生成没有结果 重试 if (!response) { await delayFun(REQUEST_DELAY_DURATION); return sendRequest(params, retryCount - 1, config); } // 拿到了结果 正常返回 if (response.imageList[0]) { return response.imageList[0]; } // 拿到了结果但是没有图片返回且有文案提示 if (!response.imageList.length && response.text) { setErrorMsg(response.text.replace(/\*/g, '')); // 特殊错误,不重试 throw { errorCode: 'NO_IMAGE', errorMsg: response.text }; } // 其他情况重试 await delayFun(REQUEST_DELAY_DURATION); return sendRequest(params, retryCount - 1, config); }; const batchRequest = async (params: RequestDescribeRetouchingParams) => { const result: UploadImageType[] = []; const data = await sendRequest(params, DEFAULT_RETRY_COUNT); result.push(data); return result; }; const onSpeedModeGenerateSuccess = (list: UploadImageType[]) => { const result = list[0].largeUrl; featureLeaferFrameLayoutRef.current?.setResult(result); // 添加进快捷使用 addResultImage({ ...list[0], url: list[0].largeUrl }); aiAddEventTrack(TRACK_DECORATION_DESIGN_TOOL_NAME, 'createSuccess'); refreshToolEquityUserUseInfo(decorationDesignToolKey); }; // 开始创作-极速版 const onSpeedModeStart: FeatureLeaferFrameLayoutCommonProps['onStart'] = async ({ uploadImage, }) => { if (!uploadImage) { message.warning({ content: '请先上传图片', key: 'emptyImage' }); return; } setErrorMsg(''); generateSpeedModalRef.current?.setModalOpen(true); generateSpeedModalRef.current?.setRequestStatus('EXECUTING'); // 底图 const uploadImageInfo = uploadImage.largeUrl; // 参考图 const baseImage = styleReferComfyImageInfo?.largeUrl; const imageUrlList: string[] = []; imageUrlList.push(uploadImageInfo); if (baseImage) { imageUrlList.push(baseImage); } // 描述词 const prompts = styleConfigTextAreaRef.current?.prompts.placeholder || ''; // 拼接描述词-有参考图 const baseImageDescription = `图一参考图二,图片要求按${uploadImage.width}:${uploadImage.height}比例生成`; const newPrompts = `${prompts},${baseImageDescription}`; // 拼接描述词-没有参考图 const noBaseImageDescription = `图片要求按${uploadImage.width}:${uploadImage.height}比例生成`; const promptsText = `${prompts},${noBaseImageDescription}`; // 根据baseImage是否存在,设置description const description = baseImage ? newPrompts : promptsText; try { // 发起请求 const response = await batchRequest({ description: combineSentenceBySplitSymbol([description]), imageUrls: imageUrlList, }); onSpeedModeGenerateSuccess(response); generateSpeedModalRef.current?.setRequestStatus('PENDING'); generateSpeedModalRef.current?.setModalOpen(false); } catch (e) { aiAddEventTrack(TRACK_DECORATION_DESIGN_TOOL_NAME, 'createError'); generateSpeedModalRef.current?.setRequestStatus('POLLING_ERROR'); } }; // 生成结果回调 const onGenerateSuccess = async (response: AiToolProcessSuccessResponse) => { try { const result = response.imageList[0]; // 刷新次数 refreshToolEquityUserUseInfo(decorationDesignToolKey); // 设置显示 featureLeaferFrameLayoutRef.current?.setResult(result); const sizeInfo = await getImageSizeInfo(result); // 添加生成记录 addResultImage({ url: result, ...sizeInfo, }); } catch (e) { console.error(e); } }; // 画板插入贴图 const onChartletItemClick = (imageInfo: ChartletImageItem) => { featureLeaferFrameLayoutRef.current?.insertPicture(imageInfo); }; // 画板插入本地上传图片 const onChartletUploadSuccess: CustomUploadProps['onUploadSuccess'] = async ({ url }) => { try { const size = await getImageSizeInfo(url); featureLeaferFrameLayoutRef.current?.insertPicture({ url, width: size.width, height: size.height, }); } catch (err) { console.error(err); message.error(t('AiTools-ImageEditorModal-hooks-mdQhK')); } }; // 创作优先级改变 const onGeneratePriorityTypeChange = (type: GeneratePriorityType) => { setGeneratePriorityType(type); setWeight(type === 'CONTROLNET' ? 1 : 1.2); }; // 标准版-类目改变回调 const onSelectRefsChange: UseDrawStyleCategoryConfigProps['onSelectRefsChange'] = ( refs, categories, ) => { if (versionValue !== 'STANDARD') { return; } drawStyleCategoryRef.current?.setSelectRefs(refs); const ids = getStyleDataIndexByRefs('id', categories, refs); if (ids.length) { styleConfigTextAreaRef.current?.updateCandidateConfigByIds(ids); } }; // 标准版 const onCategoryInit = (styleConfigItems: DrawStyleConfigItem[]) => { // 一级类目ref const firstCategoryRefs = categoryRefsGroup.map((item) => item[0]); // 过滤出相关的类目信息 const filterStyleConfigItems = styleConfigItems.filter((item) => firstCategoryRefs.includes(item.ref), ); drawStyleCategoryRef.current?.setStyleConfigList(filterStyleConfigItems); // 请求描述词 onSelectRefsChange(categoryRefsGroup[0], filterStyleConfigItems); }; // 当点击继续处理时 const onContinue = async (result: string) => { try { const sizeInfo = await getImageSizeInfo(result); featureLeaferFrameLayoutRef.current?.onUploadSuccess([ { ...sizeInfo, largeUrl: result, thumbUrl: result }, ]); } catch (e) { message.error(t('AiTools-DecorationDesign-Operate-hook-6ipKH')); } }; const onSelectorModalFinish: ImageSelectorModalProps['onFinish'] = async (item) => { featureLeaferFrameLayoutRef.current?.onUploadSuccess([item]); if (item.originFile) { aiAddEventTrack(TRACK_DECORATION_DESIGN_TOOL_NAME, isMobile() ? 'mobileUpload' : 'pcUpload'); } }; // 风格底图选择弹窗回调 const onDecorationDesignReferImageModalFinish: ImageSelectorModalProps['onFinish'] = async ({ largeUrl, thumbUrl, width, height, originFile, }) => { setStyleReferComfyImageInfo({ width, height, largeUrl, thumbUrl, }); let base64; // 原图有originFile直接转base64 if (originFile) { try { base64 = await fileToBase64(originFile); } catch (e) { console.error(e); } } // base64转换成功 打开裁剪弹框 失败则默认为原图 setCropperModalOpen(true); uploadCropperModalRef.current?.setCropperImageUrl(base64 || largeUrl); }; // 风格底图裁剪完成图片弹框回调 const onStyleReferImageCropperSuccess: UploadCropperModalProps['onCropperSuccess'] = async ( obj, ) => { setStyleReferComfyImageInfo(obj); setCropperModalOpen(false); }; const onStyleReferImageCropperError = () => { setCropperModalOpen(false); message.error(t('AiTools-TransferSmudge-Operate-hook-MiGY')); }; // 开始创作版本判断 const generateTask: FeatureLeaferFrameLayoutCommonProps['onStart'] = (params) => { if (userAuth(['TOURIST'])) { PubSub.publish('onLogin', true); return; } if (!noLimitFrequency && !toolEquityCountWrapRef.current?.verifyUseStatus()) { return; } if (versionValue === 'STANDARD') { onSdStart(params); return; } if (versionValue === 'EXTREME_SPEED') { onSpeedModeStart(params); return; } if (versionValue === 'ADVANCED') { onComfyStart(params); } }; useMount(() => { // 获取极速版描述词 styleConfigTextAreaRef.current?.updateCandidateConfig(decorationDesignSpeedModePrompts, true); // 获取高级版风格类目 const comfyUiStyle = mapPromptsToCategories(comfyUiCategoryRefsGroup, comfyUiPrompt); setComfyUiStyleList(comfyUiStyle); if (comfyUiStyle.length > 0) { setActiveAlias(comfyUiStyle[0].alias); setCurrentStyleIndex(0); } const image: UploadBtnImageResponse = location.state?.image; if (image) { featureLeaferFrameLayoutRef.current?.onUploadSuccess([image]); return; } // 地址栏参数传递 const url = decodeImageUrl(searchParams.get('url') || ''); const width = searchParams.get('width'); const height = searchParams.get('height'); if (url && width && height) { featureLeaferFrameLayoutRef.current?.onUploadSuccess([ { largeUrl: url, thumbUrl: url, width: Number(width), height: Number(height), }, ]); setSearchParams({}); } }); // 室内装修初始设置选中室内色块 useEffect(() => { semanticsSegmentImageControlRef.current?.setSelectCategoryIndex(1); }, []); return { noLimitFrequency, styleConfigTextAreaRef, imageSelectorModalRef, generateModalRef, featureLeaferFrameLayoutRef, drawStyleCategoryRef, toolEquityCountWrapRef, semanticsSegmentImageControlRef, generatePriorityType, categoryRefsGroup, weight, setWeight, sizeType, setSizeType, weightSliderMaxSize: generatePriorityType === 'PROMPTS' ? 2 : 1.3, chartletCategoryList, queryTagList, onChartletItemClick, onCategoryInit, onChartletUploadSuccess, onGenerateSuccess, onContinue, onGeneratePriorityTypeChange, onSelectRefsChange, onSelectorModalFinish, versionValue, handleVersionPcChange, productType, comfyUiStyleList, setActiveAlias, activeAlias, activeCategories, onComfyUiStyleIndexChange, currentStyleIndex, comfyUiGenerateModalRef, drawComfyUiStyleCategoryRef, styleReferComfyImageInfo, setStyleReferComfyImageInfo, comfySourceImageSelectorModalRef, comfyStyleReferWeight, setComfyStyleReferWeight, generateTask, onDecorationDesignReferImageModalFinish, uploadCropperModalRef, cropperModalOpen, setCropperModalOpen, onStyleReferImageCropperSuccess, onStyleReferImageCropperError, generateSpeedModalRef, errorMsg, DecorationDesignipLimitModalRef, handleVersionMobileChange, handleStyleAliasChange, }; }; onComfyUiStyleIndexChange();需要传入对应alias的index怎么实现
05-28
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值