廖力工作日志 2026-02-06 09:26:33 星期五

本周主线:1.开始做图片编辑

##今天的任务:

1.完成业务框架的搭建

        业务框架的设计思路:
            目前已经将编辑器的静态组件都实现好了。现在要开始做功能了。
            目前主要想到这几种设计思路:
            1.实时渲染的思路(数据驱动):
                    就是在画布载入了图片以后,由一个“主画布控制器”渲染这个位图,这个控制器本来就加上了各种设置,只是都是初始值,在控制面板上调整设置时,就是调整这些初始值,使其实时渲染到画布上。而历史记录就保存调整的值就行。

            2.图片驱动模式
            用户载入图片以后,图片的数据被载入到内存里,比如由某个变量存储:“mainImage”,但是不被任何控制器控制
                    在用户进入某个调整模块之前,图片在预览器中渲染预览器配有导航器,可以局部放大等
                    每个调整模块都有自己的渲染器,用户进入到某个调整模块后,就使用这个模块的渲染器将图片载入
                    比如“剪裁”模块,进入后,裁剪模块的渲染器将图片载入,然后显示专用工具的操作柄,比如出现一个旋转和缩放大小的框,用户调整完之后,需要点击确定。这个图片就会按照用户调整的样子,重新渲染一个位图,并输出到“mainImage”这个变量上。这时历史记录只记录“裁剪”+输出时这个图片的样子 作为历史记录。
                    每次历史记录恢复时,恢复的就是输出时这个图片的样子
                    所以这个方案的中心思想是 f(mainImage)_=> modifiedImage






            3.结论:
                和gpt讨论了一下,目前打算这样来设计这个模块:
                一张图片的编辑,主要通过若干个不同的调整函数来进行,比如:
                旋转函数
                尺寸裁剪函数
                缩放函数
                位置变换函数
                等等

                但是所有这些函数都不是实时运行的,是在每个编辑模块完成后点击提交时运行的。
                在每个编辑模块里都有一个编辑渲染器,用于给用户操作得到实时预览,并产生参数
                提交时再把参数送给以上哪些单元函数进行计算并的到处理好的图片
            图片从每个编辑模块里出来的状态再进入到历史数据里保存




            编辑器的设计
            编辑器的常量
            需要满足的边界条件:
                每次resize的时候:算出当前置入的图片能占据画面的最大大小,这需要根据当前画布大小进行计算
                                            算出当前置入图片的居中位置
            画布的常量:
                            canvasWidth
                            canvasHeight

            四个"最大"常量:
                            最大高度: canvasObjectMaxHeight
                            最大宽度:   canvasObjectMaxWidth
                            居中top: canvasObjectCenterTop
                            居中left: canvasObjectCenterleft

            图片对象的常量
                            图片高度:imageheight
                            图片宽度:  imagewidth


            真实渲染的大小和位置
                            currentheight
                            currentwidth
                            currentTop
                            currentLeft
            相对原图的比例
                            relScaleRate

            curren组的计算方法:
            height/width
            先让 imagewidth/canvasObjectMaxWidth = widthRate
            然后 imageheight * widthRate = 在宽度缩放到可现实区域宽度时,高度是多少(rcheight)
            如果 rcheight > canvasObjectMaxHeight
                            那就
                                currentheight = canvasObjectMaxHeight
                                currentwidth =imagewidth * canvasObjectMaxHeight /imageheight
                                relScaleRate = canvasObjectMaxHeight /imageheight
            如果不是
                            那就
                                currentheight = imageheight * canvasObjectMaxWidth /imagewidth
                                currentwidth = imageWidth
                                relScaleRate = canvasObjectMaxWidth / imagewidth

            top/left
                    currenttop = canvasHeight / 2 - currentheight / 2
                    currentleft = canvasWidth / 2 - currentwidth / 2



            调整蓝框大小
                    editBlueBoxHeight = currentheight
                    editBlueBoxWidth = currentwidth
                    editBlueBoxtop = currenttop
                    editBlueBoxleft = currentleft
            到了这个阶段,canvas里实际需要渲染两个图层:
            1.底图 使用curren组
            2.蓝框里的图片 使用editBlueBox组
            为什么显示两个层
            是因为每次调整可能需要剪去多余的图像,多余的图像(也就是蓝框外面的图像)需要显示成一种暗色的样式,
            蓝框里的图像正常色显
            然后底图和蓝框里的图片都要随着调整进行同步更新,底图自然计算,蓝框里的图片的top和left都是负数
                    篮筐内图像大小和位置
                    editBlueBox_Image_top = 0
                    editBlueBox_Image_left = 0
                    editBlueBox_Image_width = editBlueBoxWidth
                    editBlueBox_Image_height = editBlueBoxHeight

            调整蓝框初始化的位置和大小都用current组里的
                1.调整蓝框需要实现四个角的拖拽,和四条边的拖拽,拖拽时需要静态拖拽,图片别动来动去
                2.调整篮筐的右上角要显示一个当前图片的大小(图片的实际大小,不是图片在渲染器里的大小)

            调整蓝框大小在拖拽时的计算方法:
                鼠标在运动的时候,只能获得相对整个窗口的xy数据,所以需要将鼠标的xy - 当前画布在dom里的xy,获得鼠标在
                画布上的xy:
                        mouseCanvasX
                        mouseCanvasY
                这时候需要看鼠标拖拽的是哪个手柄,例如是左上角的手柄:
                        这是计算蓝框大小
                        temp_top = (mouseCanvasY - editBlueBoxTop)
                        temp_left = (mouseCanvasX - editBlueBoxLeft)
                        temp_editBlueBoxtop = editBlueBoxtop + temp_top
                        temp_editBlueBoxHeight = editBlueBoxHeight -  temp_top
                        temp_editBlueBoxWidth = editBlueBoxWidth - temp_left 
                        temp_editBlueBoxleft = editBlueBoxleft + temp_left 
                        计算内部图像位置和大小:
                        temp_editBlueBox_Image_top =  - (mouseCanvasY - editBlueBoxTop)
                        temp_editBlueBox_Image_left = - (mouseCanvasX - editBlueBoxLeft)
                        temp_editBlueBox_Image_width = editBlueBoxWidth
                        temp_editBlueBox_Image_height = editBlueBoxHeight

                其它7个位置的计算以此类推..




                用户触发mouseUp后的计算:

                1.需要获得裁剪后的图形缩放中心(也就是说缩放中心在这个图的哪个像素位置):
                        渲染中心:
                                ren_center_top = temp_top + temp_editBlueBoxHeight/2
                                ren_center_left = temp_left + temp_editBlueBoxWidth/2
                        图片本身实际的中心点
                                real_center_top = ren_center_top / relScaleRate
                                real_center_left = ren_center_left / relScaleRate

                2.将调整后的蓝框算到画布中心位置(保留原比例,但需要缩放)
                        先算底图的大小/位置
                                先让 temp_editBlueBoxWidth/canvasObjectMaxWidth = widthRate
                                    然后 temp_editBlueBoxHeight * widthRate = 在宽度缩放到可现实区域宽度时,高度是多少(rcheight)
                                    如果 rcheight > canvasObjectMaxHeight
                                                    那就
                                                        currentheight = canvasObjectMaxHeight
                                                        currentwidth = temp_editBlueBoxWidth * canvasObjectMaxHeight / temp_editBlueBoxHeight
                                                        relScaleRate = 这个我没想好要怎么算
                                    如果不是
                                                    那就
                                                        currentheight = temp_editBlueBoxHeight * canvasObjectMaxWidth /temp_editBlueBoxWidth
                                                        currentwidth = temp_editBlueBoxWidth
                                                        relScaleRate = 这个我没想好要怎么算

                                    top和left也没想好怎么算
                                    以及这么算完底图和蓝筐里的图片的缩放也没想好怎么算
                                    你先帮忙看看这串想法是不是对的




        考虑快速迭代和计算的复杂度
        感觉可以直接集成其它工具来解决这些问题
        需要整合多个图像处理工具来实现这个功能
        裁剪/尺寸比例:可以用react-advanced-cropper来解决
        旋转/矫正 : 需要使用 R3F(可能偏重,需要自己控制一下大小)


         那图片编辑的store我就这么设计了:
         orgImageKey: 原图的储存在内存里的图像key
         historyList:[
                 {
                    name:"编辑了图片裁剪" ,
                    imageKey:储存在内存里的图像key
                    moduleName:模块名称
                    ,moduleOptions;结束编辑时使用的参数

                }
         ],
          historyIndex:永远在最前面,除非undo了
         currentModuleName:当前正在操作/打开的编辑模块
        currentModuleOptions:{}当前正在操作/打开的编辑模块的参数集合,每个模块不一样,操作面板通过这个对象集和预览渲染器进行交互
        optionsUpdateStamp: 数字,用于控制面板对于更新预览渲染器的节奏用的。


        图片存储不要直接放store里 内存会炸
        做一个图片管理器,使用imageBitMap来做:
// assetPool.ts
export type AssetRecord = {
  blob: Blob;
  bitmap: ImageBitmap; // 用于快速渲染
  width: number;
  height: number;
};

const g = globalThis as any;
const pool: Map<string, AssetRecord> =
  g.__imgAssetPool ?? (g.__imgAssetPool = new Map());

export function putAsset(blob: Blob): Promise<string> {
  return new Promise(function (resolve, reject) {
    (async function () {
      try {
        const bitmap = await createImageBitmap(blob);
        const key = crypto.randomUUID();
        pool.set(key, {
          blob: blob,
          bitmap: bitmap,
          width: bitmap.width,
          height: bitmap.height,
        });
        resolve(key);
      } catch (err) {
        reject(err);
      }
    })();
  });
}

export function getAsset(key: string): AssetRecord | undefined {
  return pool.get(key);
}

export function deleteAsset(key: string): void {
  const rec = pool.get(key);
  if (rec) {
    // ImageBitmap 需要主动释放
    rec.bitmap.close();
  }
  pool.delete(key);
}

export function keys(): string[] {
  return Array.from(pool.keys());
}

内存管理设计大纲:
https://liaoliresume.com/manage/blogs/edit/476

        最终确定的store结构:
            orgImageKey
            historyList[]
            historyIndex
            currentModuleName
            currentModuleOptions
            optionsUpdateStamp


        1.完成store数据结构设计
            1.设计数据结构
                完成 -- 2026-02-06 17:57:32 星期五
            2.设计历史记录机制 --- 只保存图片快照和模块配置快照,其它数据直接丢,恢复时主要使用图片快照
                完成 --2026-02-06 17:57:46 星期五

        2.完成store - bis + query - view 的三层结构和目录设计
                 已经完成 --- 2026-02-06 18:19:17 星期五

        3.尝试设计一套控制面板自动布局的语义化json,试试看看可不可以让控制面板的组件在渲染时做成动态的,这样可以节省开发量,后期维护也更友好
                已经研究过了,非常不好做(很耗时),目前放弃

2.继续完成剩下的ui静态组件的重构

        1.快捷选项列表 文字列表
            预置选项 -- 点击 onchange响应选项id
            也可以往里面添加选项  -- 点击 onchange响应选项id
            已经完成. --2026-02-06 10:58:21 星期五


        2.快捷选项列表 图片列表
            点击 onchange响应选项id
            已经完成. --2026-02-06 10:58:21 星期五

        3x轴滑动数值调整器
            已经完成 --2026-02-06 12:14:26 星期五

        4.导航器的实现

##明天的任务:
1.使用https://advanced-cropper.github.io/react-advanced-cropper/docs/tutorials/custom-stencil/组件完成图片编辑的裁剪模块
2.完善history系统
3.在模块间切换自动提交
4.验证线上的版本是不是真的完成了回复一大段时不吸底的问题

长期任务:
1.适配一个移动端版本
2.图片编辑模块的需求 — 延后
图片编辑模块的业务照搬美图秀秀,原因是美图秀秀提供的图片编辑功能可以用于图生视频的前置编辑
目前已有的大模块为:
调整 – 调整图像尺寸
抠图 – 获得前景
人像 – 人像美化
滤镜 – 一些照片风格滤镜

    2.1 了解图片编辑模块的子模块拆分是怎么做的

    2.2  了解图片编辑模块的代码风格

    2.3  了解图片编辑模块的单例模式实现和状态管理是怎么做的

    2.4   了解当前图片编辑模块缺失的内容有哪些
            1.调整模块
                    基础调整已经完成 但包含一些bug
                    没有高级调整功能


            2.抠图模块
            3.消除笔模块
            4.ai超清模块
            5.ai扩图模块
            6.无损放大模块

            总结,主要做调整和 抠图,人像和滤镜功能disable掉,暂时不做

    2.5   了解当前图片编辑模块为未来可以做哪些代码或结构上的优化
            1.当前图片编辑的模块配色没有使用色板
            2.当前图片编辑模块的尺寸没有使用rem导致尺寸在rem分辨率适配下失真
            3.界面需要对齐设计稿
                1.当前界面需要全屏显示
                2.当前界面的ui元素和设计稿不对齐
            4.目前一级菜单上的样式未对齐设计稿

设计稿规格为:1440*900

本周完成的内容:
1.完成工作台v2.0脚本编辑的部分功能
2.完成图片编辑的静态页重构和静态组件的编写
3.完成图片编辑的技术方案和store设计
下周预计完成:
1.完成图片编辑的裁剪模块 – 预计使用 react-advanced-cropper 来做
2.完成图片编辑的旋转/矫正模块 – 预计使用R3F来做
3.完成图片编辑的尺寸/比例模块 – 预计使用 react-advanced-cropper 来做
本来想自己写计算代码,但是感觉不够快和稳,所以感觉使用已经有的组件是最稳的,只要把体验和样式调好就好办了

—————————————————————-其它内容———————————————————————–