diff --git a/components.d.ts b/components.d.ts index 1c57248a..b2d38451 100644 --- a/components.d.ts +++ b/components.d.ts @@ -9,31 +9,19 @@ export {} declare module 'vue' { export interface GlobalComponents { ABadge: typeof import('ant-design-vue/es')['Badge'] - ABreadcrumb: typeof import('ant-design-vue/es')['Breadcrumb'] ACheckbox: typeof import('ant-design-vue/es')['Checkbox'] - ADatePicker: typeof import('ant-design-vue/es')['DatePicker'] ADrawer: typeof import('ant-design-vue/es')['Drawer'] - AImage: typeof import('ant-design-vue/es')['Image'] AInputNumber: typeof import('ant-design-vue/es')['InputNumber'] - AMenu: typeof import('ant-design-vue/es')['Menu'] - AMenuItem: typeof import('ant-design-vue/es')['MenuItem'] AModal: typeof import('ant-design-vue/es')['Modal'] - APagination: typeof import('ant-design-vue/es')['Pagination'] APopover: typeof import('ant-design-vue/es')['Popover'] ARangePicker: typeof import('ant-design-vue/es')['RangePicker'] ASelect: typeof import('ant-design-vue/es')['Select'] ASelectOption: typeof import('ant-design-vue/es')['SelectOption'] ASlider: typeof import('ant-design-vue/es')['Slider'] - ASpace: typeof import('ant-design-vue/es')['Space'] ASpin: typeof import('ant-design-vue/es')['Spin'] - ASubMenu: typeof import('ant-design-vue/es')['SubMenu'] ASwitch: typeof import('ant-design-vue/es')['Switch'] ATable: typeof import('ant-design-vue/es')['Table'] - ATabPane: typeof import('ant-design-vue/es')['TabPane'] - ATabs: typeof import('ant-design-vue/es')['Tabs'] - ATimeRangePicker: typeof import('ant-design-vue/es')['TimeRangePicker'] AUpload: typeof import('ant-design-vue/es')['Upload'] - ElCascader: typeof import('element-plus/es')['ElCascader'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] } diff --git a/src/component/Canvas/CanvasEditor/index.vue b/src/component/Canvas/CanvasEditor/index.vue index 7247885a..0e4b7844 100644 --- a/src/component/Canvas/CanvasEditor/index.vue +++ b/src/component/Canvas/CanvasEditor/index.vue @@ -456,6 +456,13 @@ onMounted(async () => { // 使用window的resize事件代替ResizeObserver // 只有当窗口大小变化时才更新画布尺寸 // window.addEventListener("resize", handleWindowResize); + if(props.config.initZoom) { + const width = canvasManager.width; + const height = canvasManager.height; + const cwidth = props.config.width; + const cheight = props.config.height; + setZoom(Math.min(width/cwidth,height/cheight)); // 设置画布缩放 + } }); watchEffect(() => { @@ -521,6 +528,19 @@ function resetZoom() { canvasManager.resetZoom(); } +function setZoom(zoom) { + setTimeout(()=>{ + if (!canvasManager) return; + const newZoom = Math.max(zoom / 1.1, 0.1); // 减少10%,最小0.1倍 + // 使用画布中心作为缩放点 + const centerPoint = { + x: canvasManager.canvas.width / 2, + y: canvasManager.canvas.height / 2, + }; + canvasManager.animateZoom(centerPoint, newZoom); + }) +} + function zoomIn() { if (!canvasManager) return; @@ -882,6 +902,7 @@ defineExpose({ layerId = "", // 导出具体图层ID layerIdArray = [], // 导出多个图层ID数组 expPicType = "png", // 导出图片类型 JPG 或 PNG ,SVG + isEnhanceImg, // 是否是增强图片 } = {}) => { return canvasManager.exportImage({ isContainBg, @@ -890,6 +911,7 @@ defineExpose({ layerId, layerIdArray, expPicType, + isEnhanceImg, }); }, /** diff --git a/src/component/Canvas/CanvasEditor/managers/CanvasManager.js b/src/component/Canvas/CanvasEditor/managers/CanvasManager.js index 6c4083da..a0abed05 100644 --- a/src/component/Canvas/CanvasEditor/managers/CanvasManager.js +++ b/src/component/Canvas/CanvasEditor/managers/CanvasManager.js @@ -811,6 +811,7 @@ export class CanvasManager { * @param {Array} options.layerIdArray 导出多个图层ID数组 * @param {String} options.expPicType 导出图片类型 (png/jpg/svg) * @param {Boolean} options.restoreOpacityInRedGreen 红绿图模式下是否恢复透明度为1 + * @param {Boolean} options.isEnhanceImg 是否是增强图片 * @returns {String} 导出的图片数据URL */ async exportImage(options = {}) { diff --git a/src/component/Canvas/CanvasEditor/managers/ExportManager.js b/src/component/Canvas/CanvasEditor/managers/ExportManager.js index 41d1007d..0b474d05 100644 --- a/src/component/Canvas/CanvasEditor/managers/ExportManager.js +++ b/src/component/Canvas/CanvasEditor/managers/ExportManager.js @@ -18,10 +18,12 @@ export class ExportManager { * @param {Object} options 导出选项 * @param {Boolean} options.isContainBg 是否包含背景图层 * @param {Boolean} options.isContainFixed 是否包含固定图层 + * @param {Boolean} options.isCropByBg 是否使用背景大小裁剪 * @param {String} options.layerId 导出具体图层ID * @param {Array} options.layerIdArray 导出多个图层ID数组 * @param {String} options.expPicType 导出图片类型 (png/jpg/svg) * @param {Boolean} options.restoreOpacityInRedGreen 红绿图模式下是否恢复透明度为1 + * @param {Boolean} options.isEnhanceImg 是否是增强图片 * @returns {String} 导出的图片数据URL */ exportImage(options = {}) { @@ -33,6 +35,7 @@ export class ExportManager { layerIdArray = [], expPicType = "png", restoreOpacityInRedGreen = true, + isEnhanceImg, // 是否是增强图片 } = options; try { // 检查是否为红绿图模式 @@ -44,7 +47,8 @@ export class ExportManager { expPicType, isRedGreenMode, restoreOpacityInRedGreen, - isCropByBg + isCropByBg, + isEnhanceImg, // 是否是增强图片 ); } @@ -57,7 +61,8 @@ export class ExportManager { isContainFixed, isRedGreenMode, restoreOpacityInRedGreen, - isCropByBg + isCropByBg, + isEnhanceImg, // 是否是增强图片 ); } @@ -68,7 +73,8 @@ export class ExportManager { isContainFixed, isRedGreenMode, restoreOpacityInRedGreen, - isCropByBg + isCropByBg, + isEnhanceImg, // 是否是增强图片 ); } catch (error) { console.error("导出图片失败:", error); @@ -82,6 +88,8 @@ export class ExportManager { * @param {String} expPicType 导出类型 * @param {Boolean} isRedGreenMode 是否为红绿图模式 * @param {Boolean} restoreOpacityInRedGreen 红绿图模式下是否恢复透明度为1 + * @param {Boolean} isCropByBg 是否使用背景大小裁剪 + * @param {Boolean} isEnhanceImg 是否是增强图片 * @returns {String} 图片数据URL * @private */ @@ -89,7 +97,9 @@ export class ExportManager { layerId, expPicType, isRedGreenMode, - restoreOpacityInRedGreen + restoreOpacityInRedGreen, + isCropByBg, // 是否使用背景大小裁剪 + isEnhanceImg, // 是否是增强图片 ) { if (!this.layerManager) { throw new Error("图层管理器未初始化"); @@ -117,7 +127,9 @@ export class ExportManager { return this._exportWithRedGreenMode( objectsToExport, expPicType, - restoreOpacityInRedGreen + restoreOpacityInRedGreen, + isCropByBg, // 是否使用背景大小裁剪 + isEnhanceImg, // 是否是增强图片 ); } @@ -137,6 +149,8 @@ export class ExportManager { * @param {Boolean} isContainFixed 是否包含固定图层 * @param {Boolean} isRedGreenMode 是否为红绿图模式 * @param {Boolean} restoreOpacityInRedGreen 红绿图模式下是否恢复透明度为1 + * @param {Boolean} isCropByBg 是否使用背景大小裁剪 + * @param {Boolean} isEnhanceImg 是否是增强图片 * @returns {String} 图片数据URL * @private */ @@ -147,7 +161,8 @@ export class ExportManager { isContainFixed, isRedGreenMode, restoreOpacityInRedGreen, - isCropByBg + isCropByBg, // 是否使用背景大小裁剪 + isEnhanceImg, // 是否是增强图片 ) { if (!this.layerManager) { throw new Error("图层管理器未初始化"); @@ -178,7 +193,9 @@ export class ExportManager { return await this._exportWithCanvasSize( objectsToExport, expPicType, - restoreOpacityInRedGreen + restoreOpacityInRedGreen, + isCropByBg, // 是否使用背景大小裁剪 + isEnhanceImg, // 是否是增强图片 ); } @@ -189,6 +206,8 @@ export class ExportManager { * @param {Boolean} isContainFixed 是否包含固定图层 * @param {Boolean} isRedGreenMode 是否为红绿图模式 * @param {Boolean} restoreOpacityInRedGreen 红绿图模式下是否恢复透明度为1 + * @param {Boolean} isCropByBg 是否使用背景大小裁剪 + * @param {Boolean} isEnhanceImg 是否是增强图片 * @returns {String} 图片数据URL * @private */ @@ -198,7 +217,8 @@ export class ExportManager { isContainFixed, isRedGreenMode, restoreOpacityInRedGreen, - isCropByBg + isCropByBg, // 是否使用背景大小裁剪 + isEnhanceImg, // 是否是增强图片 ) { // 按图层顺序收集对象(从底到顶) const objectsToExport = this._collectObjectsByLayerOrder( @@ -251,7 +271,9 @@ export class ExportManager { objectsToExport, expPicType, restoreOpacityInRedGreen, - canvasClipPath + canvasClipPath, + isCropByBg, // 是否使用背景大小裁剪 + isEnhanceImg, // 是否是增强图片 ); } @@ -603,6 +625,9 @@ export class ExportManager { * @param {Array} objectsToExport 要导出的对象数组 * @param {String} expPicType 导出类型 * @param {Boolean} restoreOpacityInRedGreen 是否恢复透明度为1 + * @param {Object} maskObject 裁剪对象 + * @param {Boolean} isCropByBg 是否使用背景大小裁剪 + * @param {Boolean} isEnhanceImg 是否是增强图片 * @returns {String} 图片数据URL * @private */ @@ -610,7 +635,9 @@ export class ExportManager { objectsToExport, expPicType, restoreOpacityInRedGreen, - maskObject + maskObject, // 裁剪对象 + isCropByBg, // 是否使用背景大小裁剪 + isEnhanceImg, // 是否是增强图片 ) { // 使用当前画布尺寸 // const canvasWidth = @@ -629,6 +656,8 @@ export class ExportManager { trimWhitespace: true, // 裁剪空白 trimPadding: 0, // 裁剪边距 restoreOpacityInRedGreen, + isCropByBg, // 是否使用背景大小裁剪 + isEnhanceImg, // 是否是增强图片 }); console.log("导出图片数据URL:", dataURL); diff --git a/src/component/Canvas/CanvasEditor/utils/selectionToImage.js b/src/component/Canvas/CanvasEditor/utils/selectionToImage.js index 848f6916..2f518624 100644 --- a/src/component/Canvas/CanvasEditor/utils/selectionToImage.js +++ b/src/component/Canvas/CanvasEditor/utils/selectionToImage.js @@ -21,6 +21,7 @@ export const createRasterizedImage = async ({ preserveOriginalQuality = true, // 是否保持原始质量(新增) selectionManager = null, // 选区管理器,用于获取羽化值等设置 restoreOpacityInRedGreen, // 是否在红绿图模式下恢复透明度 + isEnhanceImg, // 是否是增强图片 } = {}) => { try { console.log(`📊 开始栅格化 ${fabricObjects.length} 个对象`); @@ -41,6 +42,7 @@ export const createRasterizedImage = async ({ clippingObject, isReturenDataURL, selectionManager, // 传递选区管理器 + isEnhanceImg, // 是否是增强图片 }); } @@ -81,6 +83,7 @@ const createClippedObjects = async ({ clippingObject, isReturenDataURL, selectionManager = null, // 新增选区管理器参数 + isEnhanceImg, // 是否是增强图片 }) => { try { console.log("🎯 使用新的图像遮罩裁剪方法创建对象"); @@ -110,6 +113,7 @@ const createClippedObjects = async ({ clippingObject, selectionBounds: optimizedBounds, // 使用优化后的边界框 featherAmount, + isEnhanceImg, // 是否是增强图片 }); } @@ -120,6 +124,7 @@ const createClippedObjects = async ({ clippingObject, selectionBounds: optimizedBounds, // 使用优化后的边界框 featherAmount, + isEnhanceImg, // 是否是增强图片 }); // 将DataURL转换为fabric.Image对象 @@ -173,6 +178,7 @@ const createClippedDataURLByCanvas = async ({ clippingObject, selectionBounds, featherAmount = 0, + isEnhanceImg = false, // 是否是增强图片 }) => { try { console.log("🖼️ 使用图像遮罩裁剪方法生成DataURL"); @@ -185,7 +191,9 @@ const createClippedDataURLByCanvas = async ({ // 使用高分辨率以保证质量 const pixelRatio = window.devicePixelRatio || 1; - const qualityMultiplier = Math.max(2, pixelRatio); + const qualityMultiplier = !!isEnhanceImg ? Math.max(2, pixelRatio) : 1; + + console.log("使用高分辨率以保证质量:" + isEnhanceImg, optimizedBounds); const canvasWidth = Math.ceil(optimizedBounds.width * qualityMultiplier); const canvasHeight = Math.ceil(optimizedBounds.height * qualityMultiplier); @@ -455,6 +463,8 @@ const createLegacyRasterization = async ({ quality, format, isReturenDataURL, + isCropByBg, // 是否根据背景裁剪 + isEnhanceImg, // 是否是增强图片 }) => { console.log("⚠️ 使用兼容的离屏渲染方法"); @@ -481,6 +491,8 @@ const createLegacyRasterization = async ({ format, currentZoom, isReturenDataURL, + isCropByBg, // 是否根据背景裁剪 + isEnhanceImg, // 是否是增强图片 }); }; @@ -571,6 +583,8 @@ const createOffscreenRasterization = async ({ format, currentZoom, isReturenDataURL, + isCropByBg, // 是否根据背景裁剪 + isEnhanceImg, // 是否是增强图片 }) => { try { // 创建离屏画布,使用绝对尺寸以保证高质量 diff --git a/src/component/home/tools/deReconstruction/canvas/index.vue b/src/component/home/tools/deReconstruction/canvas/index.vue index 0c6b8277..3e6ef56c 100644 --- a/src/component/home/tools/deReconstruction/canvas/index.vue +++ b/src/component/home/tools/deReconstruction/canvas/index.vue @@ -113,12 +113,16 @@ export default defineComponent({ return new Promise((res,rev)=>{ let img = new Image() img.onload = ()=>{ - let wH = [1,1] - let domHeight = dataDom.canvasBox.offsetHeight - 200 - let imgHeight = img.height - wH = [1,domHeight/imgHeight] - data.canvasConfig.width = img.width * wH[1] - data.canvasConfig.height = domHeight + // let wH = [1,1] + // let domHeight = dataDom.canvasBox.offsetHeight - 200 + // let imgHeight = img.height + // wH = [1,domHeight/imgHeight] + // data.canvasConfig.width = img.width * wH[1] + // data.canvasConfig.height = domHeight + data.canvasConfig.height = img.height + data.canvasConfig.width = img.width + data.canvasConfig.initZoom = true + data.canvasLoad = true res('') } diff --git a/src/component/modules/generalMiniCanvas.vue b/src/component/modules/generalMiniCanvas.vue index e9463578..eaf2be3d 100644 --- a/src/component/modules/generalMiniCanvas.vue +++ b/src/component/modules/generalMiniCanvas.vue @@ -87,7 +87,7 @@ export default defineComponent({ // dataDom.editCanvas.exportImage({isContainBg:props.source == 'detail',isContainFixed:true}).then((rv)=>{ // emit('submitBase64Data',rv) // }) - dataDom.editCanvas.exportImage({isContainBg:true,isContainFixed:true}).then((rv)=>{ + dataDom.editCanvas.exportImage({isContainBg:true,isContainFixed:true,isCropByBg:true}).then((rv)=>{ emit('submitBase64Data',rv) }) } @@ -135,12 +135,16 @@ export default defineComponent({ if(props.imgUrl){ let img = new Image() img.onload = ()=>{ - let wH = [1,1] - let domHeight = dataDom.canvasBox.offsetHeight - 200 - let imgHeight = img.height - wH = [1,domHeight/imgHeight] - data.canvasConfig.height = domHeight - data.canvasConfig.width = wH[1] * img.width + // let wH = [1,1] + // let domHeight = dataDom.canvasBox.offsetHeight - 200 + // let imgHeight = img.height + // wH = [1,domHeight/imgHeight] + // data.canvasConfig.height = domHeight + // data.canvasConfig.width = wH[1] * img.width + data.canvasConfig.height = img.height + data.canvasConfig.width = img.width + data.canvasConfig.initZoom = true + data.canvasLoad = true // setTimeout(()=>{ // canvasLoadAddImg()