常用的两种填充策略:fit 和 fill

大家好,我是前端西瓜哥。

我们经常需要将图形放到到特定的矩形容器中,常用的的有两种策略:fit 和 fill。

fit

fit,合身、适应、匹配之意。

也有些地方叫做 contain(比如 CSS),差不多的意思,也是让目标图形装到容器下。

fit 的场景是希望看到完整的内容,比如 zoom to fit 让画布调整 zoom 值可以看到所有的图形,或者展示图标、公司的 logo,裁剪掉一部分就不太好了。

其策略的关键在于,将图形等比放大和缩小后,刚好把这个图形 "完整地" 放到容器下。

可能会留下缝隙,只要图形和容器的宽高比不同,就一定会发生。

也就是说,缩放后的图形的宽高 "都要" 小于等于容器的宽高。

为了让图形能刚刚好放入,通常是图形的一侧和容器对应的一侧是相等的,另一侧则是小于等于容器对应侧。

另外要让图形的中心点和容器的中心点对齐。

求需要应用的缩放比,算法实现就是分别计算图形的宽高到容器的宽高,分别要缩放的值 scaleWidth 和 scaleHeight,取 其中小的值 即可。

ts 复制代码
const calcFitScale = (
  container: {
    width: number;
    height: number;
  },
  content: {
    width: number;
    height: number;
  },
) => {
const scaleX = container.width / content.width;
const scaleY = container.height / content.height;
const scale = Math.min(scaleX, scaleY);
return scale;
};

我们可以写成求一个矩阵的形式,图形移动到容器需要应用的矩阵。

这样的话需要再提供 x 和 y 的值(图形的左上角位置),这样才知道要怎么移动。

ts 复制代码
const calcFitMatrix = (
  container: {
    x: number;
    y: number;
    width: number;
    height: number;
  },
  content: {
    x: number;
    y: number;
    width: number;
    height: number;
  },
) => {
const scaleX = container.width / content.width;
const scaleY = container.height / content.height;
const scale = Math.min(scaleX, scaleY);

const contentCenterX = content.x + content.width / 2;
const contentCenterY = content.y + content.height / 2;

const containerCenterX = container.x + container.width / 2;
const containerCenterY = container.y + container.height / 2;

const matrix = new Matrix()
    // content 的中心移动到原点
    .translate(-contentCenterX, -contentCenterY)
   // 缩放
    .scale(scale, scale)
   // 中心对齐到 container
    .translate(containerCenterX, containerCenterY);

return matrix;
};

这种写法还有个额外的好处:对矩阵做逆矩阵,就得到保持图形位置不变,让容器去移动缩放实现 fit 效果的矩阵。

fill

fill,填充、充满之意 。也有地方叫做 cover(覆盖),让图形完全地 "填满" 容器,不留一点缝隙。

fill 的场景是让内容充满,不留一点空隙,通常应用于图片,比如给 3D 模型的面贴图。

我们把裁剪效果去掉,看下图形和容器的大小和位置关系。

可以看到,缩放后的图形的宽高 "都要" 大于等于容器的宽高。至少有一边是和容器对应边是一致的。

和前面的 fit 策略的唯一区别,就是将原来的取 scaleX 和 scaleY 中的小值,改为 取大值 即可。

最后再裁剪掉多余的地方(或者做蒙版 mask),这个需要你自己用自己的渲染引擎处理,和我们要实现的算法无关。

计算缩放比的写法:

ts 复制代码
const calcFillScale = (
  container: {
    width: number;
    height: number;
  },
  content: {
    width: number;
    height: number;
  },
) => {
const scaleX = container.width / content.width;
const scaleY = container.height / content.height;
const scale = Math.max(scaleX, scaleY);
return scale;
};

计算矩阵的写法:

ts 复制代码
const calcFillMatrix = (
  container: {
    x: number;
    y: number;
    width: number;
    height: number;
  },
  content: {
    x: number;
    y: number;
    width: number;
    height: number;
  },
) => {
const scaleX = container.width / content.width;
const scaleY = container.height / content.height;
const scale = Math.max(scaleX, scaleY);

const contentCenterX = content.x + content.width / 2;
const contentCenterY = content.y + content.height / 2;

const containerCenterX = container.x + container.width / 2;
const containerCenterY = container.y + container.height / 2;
const matrix = new Matrix()
    // content 的中心移动到原点
    .translate(-contentCenterX, -contentCenterY)
    // 缩放
    .scale(scale, scale)
    // 中心对齐到 container
    .translate(containerCenterX, containerCenterY);

return matrix;
};

线上 demo

写了个简单 demo,感兴趣的读者可以体验一番。

stackblitz.com/edit/dtfd8d...

结尾

fit 是做适应的,让容器刚好装下,可留空隙,但要完整。

fill 是尽量填充,让容器充满,不要留缝隙,但也不要放太大。

我是前端西瓜哥,关注我,学习更多平面几何知识。


相关阅读,

图形编辑器:绘制图形需要用到的填充算法

图形编辑器开发:基于矩阵的画布缩放和移动实现

相关推荐
计算机魔术师37 分钟前
DeepSeek 据报道接近完成至少 800 亿元融资,腾讯与宁德时代参与
前端
可乐ea1 小时前
多模型编排的三层:框架、模型路由与提供商路由
前端·网络·人工智能·ai智能体·多智能体协作·多模型编排·大模型路由
青柠之夏cc3 小时前
前端拖拽功能原生实现,不引入拖拽库完成业务
开发语言·前端·python
liangshanbo12153 小时前
前端大文件切片上传怎么做?
前端
Sand(ContextGate)4 小时前
Python Agent 测试实战:测试与评估,让 Agent 像传统软件一样可交付
前端·javascript·python·microsoft·ai
广州华水科技4 小时前
单北斗GNSS变形监测系统在城市安全与地质灾害中的应用前景
前端
颜进强4 小时前
20 · NestJs循环依赖与 forwardRef:容器为什么在环面前会死,"占位再补齐"怎么救
前端·后端·ai编程
颜进强4 小时前
19 · NestJs @Global 落地账:装饰器与 `isGlobal` 参数,各在什么场景上岗
前端·后端·ai编程
码艺-Alimjan4 小时前
Tauri 网站To桌面应用实战总结
前端·javascript·vue
颜进强4 小时前
18 · NestJS动态模块与 forRoot:`imports: [ConfigModule.forRoot({...})]` 到底在 import 什么
前端·后端·ai编程