开源 | Warpvas 实现扭曲的画布

介绍

名字灵感源于 warped + canvas,扭曲的画布。

画布可以被扭曲吗?

如果你使用过canvas,可能你对setTransform API也有所了解,那你可能会给出一个偏消极的答案:只能整体倾斜变形,但做不到扭曲变形。

但这真的做不到吗?

我们可以先来想一个问题:如果对于一个方方正正的图像来说,setTransform API施加的变形效果只能作用于整体,那如果把它从斜对角切成两份三角形,再分别应用setTransform API施加变形呢?

不知道你是否能惊喜地发现,"只能整体变形"的魔咒就这样被轻易破除了

这是非常容易理解的,但是你可能会疑惑那又如何,"扭曲"一样无法实现。

但我们要意识到图像是可以无限分割的,我们可以将其分割成网格状(如10行10列),然后每个网格中的部分图像再根据它自己斜对角分割成两个三角形进行独立变形,而当这些独立变形的三角图元重新整合在一起成为一张大图的时候,"扭曲的画布"不就诞生了吗?

所以,扭曲画布是能够实现的。但我知道,你此刻内心在打鼓,因为这原理好理解,但实现起来的话,估计得吃上不少苦头吧...


如果你今日还在等待好消息出现的话,那我想这可能就是你今天听到的第一个好消息。

Warpvas是一个可以快速实现画布扭曲效果的工具库,它小而美,它灵活且高效,而且它是开源的。

官网地址:Warpvas

在Warpvas库的代码设计中,虽然它内部实际上更倾向应用性能更好的WebGL实现,而不是应用2D中setTransform API实现,但也不妨碍它展示上面化零为整的艺术(图像分割变形)。

特性

  • 网格分割策略:支持自定义网格分割算法,通过不同策略实现透视/波浪/曲面等变形效果。
  • 边界曲线控制:通过贝塞尔曲线控制点调整,实现精细的边界变形效果控制。
  • 辅助显示效果:提供选项以在变形图像上添加分隔线和分割点等辅助显示效果,以协助观察图像变形效果。
  • 输入输出限制:提供选项以控制变形图像的输入和输出尺寸,更好把控变形图像的生成质量和效率,也可防止超大图像导致性能问题。
  • 多渲染引擎支持:可在 WebGL (高性能) 和 Canvas2D (高兼容) 渲染模式间自由切换。
  • 安全渲染模式:WebGL 渲染失败时自动降级至 Canvas2D 渲染,保障基础功能可用性。
  • 状态序列化:支持将变形状态序列化为 base64 字符串,便于保存/恢复复杂变形状态。

安装方法

shell 复制代码
npm install warpvas
# or
pnpm add warpvas

使用教程

1. 创建新的变形画布实例

typescript 复制代码
// 使用图片创建
const img = new Image();
img.src = 'example.jpg';
img.onload = () => new Warpvas(img);

// 使用画布创建并设置2x2网格
const canvas = document.createElement('canvas');
const warpvas = new Warpvas(canvas, 2, 2);

2. 更新变形顶点坐标

typescript 复制代码
// 将区域(0,0)的左上角顶点移动到(50,50)
const warpvas = new Warpvas(canvas);
warpvas.updateVertexCoord(
    0,                  // 行索引
    0,                  // 列索引
    'tl',               // 顶点位置(左上角)
    { x: 50, y: 50 },   // 新位置
    true                // 重新计算曲线
);

3. 更新指定方向的边界曲线坐标

typescript 复制代码
// 创建一个弧形的底部边界
const warpvas = new Warpvas(canvas);
warpvas.updateRegionBoundCoords(
    0,                  // 行索引
    0,                  // 列索引
    'bottom',           // 方向
    [
        { x: 0, y: canvas.height },                          // 起始点
        { x: canvas.width / 3, y: canvas.height / 2 },        // 控制点1
        { x: (canvas.width / 3) * 2, y: canvas.height / 2 },  // 控制点2
        { x: canvas.width, y: canvas.height },                // 终点
    ]
);

第五个参数当设置为true时,临近曲线的控制点会重新计算,换句话说就是保持直线状态。

4. 添加分割点以划分区域

typescript 复制代码
// 在第一个区域(0,0)的中心添加分割点,容差为10%
const warpvas = new Warpvas(canvas);
const region = warpvas.originalRegions[0][0];

warpvas.splitRegionByPoint(
    0,      // 行索引
    0,      // 列索引
    {
        x: (region.tl.x + region.br.x) / 2,  // 区域中心X坐标
        y: (region.tl.y + region.br.y) / 2,   // 区域中心Y坐标
    },
    0.1     // 10%容差
);

5. 移除指定的变形区域

typescript 复制代码
// 移除位于第1行第1列的区域
const warpvas = new Warpvas(canvas);
warpvas.removeRegion({
    row: 1,
    column: 1
});

6. Warpvas渲染配置选项

typescript 复制代码
// 控制渲染效果和调试视图显示
const warpvas = new Warpvas(canvas);
warpvas.setRenderingConfig({
    // padding: 10,                // 设置10px内边距
    // enableAntialias: true,      // 启用抗锯齿
    // enableSafeRendering: true,  // 启用安全渲染模式
    // enableContentDisplay: true,  // 显示变形内容
    enableGridDisplay: true,       // 显示变形网格
    enableGridVertexDisplay: true, // 显示网格顶点
    gridColor: { r: 255, g: 99, b: 71, a: 1 } // 使用珊瑚红色网格
});

7. 设置变形网格细分的最大比例

typescript 复制代码
// 设置稀疏网格以提高性能
const warpvas = new Warpvas(canvas);
warpvas
    .setSplitUnit(0.2)
    .setRenderingConfig({
        enableGridDisplay: true,
        gridColor: { r: 206, g: 102, b: 91, a: 1 }
    });

8. 设置网格细分点的计算策略

typescript 复制代码
const warpvas = new Warpvas(canvas);

// 使用自定义策略
warpvas.setSplitStrategy({
    name: 'custom',
    execute: (warpvas) => {
        // 返回自定义网格点计算结果
        // return [[[{ x: 0, y: 0 }]]];

        // 使用默认策略计算的结果
        return Warpvas.strategy();
    }
});

应用不同的策略,将会得到不同的变形效果,为了让大家更好地感受到"策略"的强大效果,这里"附送"透视效果的实现,这里需要引入另一个库:warpvas-perspective

9. 设置输入画布的尺寸限制

typescript 复制代码
const warpvas = new Warpvas(canvas);

// 限制输入高度为100px,宽度按比例缩放
warpvas.setInputLimitSize({
    height: 100
});

10. 设置输出画布的尺寸限制

typescript 复制代码
const warpvas = new Warpvas(canvas);

// 限制输出高度为100px,宽度按比例缩放
warpvas.setOutputLimitSize({
    height: 100
});

11. 设置渲染引擎类型

typescript 复制代码
const warpvas = new Warpvas(canvas);

// 启用Canvas2D渲染
warpvas.setRenderingContext('2d');

// 启用WebGL渲染
warpvas.setRenderingContext('webgl');

12. 通过Web Worker异步生成变形画布

typescript 复制代码
const canvas = document.createElement('canvas');
const warpvas = new Warpvas(canvas);
await warpvas.renderWithWorker();

最后

这个库源于我的工作经验和总结,这个库我会持续维护,如果这个库能帮助到你,欢迎到 GitHub 留下宝贵的⭐️。

如果你有极具创新的独特变形策略,欢迎在 Issues 区留言,我也会将它们补充到 README.md 中。

这里还有一个好消息,结合fabricjs快速搭建图像扭曲编辑工具的开源库也在筹备中了,如果你感兴趣,可持续关注~

相关推荐
该怎么办呢34 分钟前
webgl入门实例-矩阵在图形学中的作用
线性代数·矩阵·webgl
拉不动的猪1 小时前
无缝适配 PC 和移动端‌我们要注意哪些点呢
前端·javascript·面试
爱看书的小沐2 小时前
【小沐杂货铺】基于Three.JS绘制卫星轨迹Satellite(GIS 、WebGL、vue、react,提供全部源代码)
javascript·vue.js·webgl·three.js·卫星轨道·地球earth·satellite
王富贵的记录3 小时前
React 函数组件和类组件的区别
前端·javascript·react.js
巴巴_羊3 小时前
React Article模块
javascript·react.js·ecmascript
z_mazin3 小时前
正则表达式在爬虫中的应用:匹配 HTML 和 JSON 的技巧
javascript·爬虫·正则表达式
DevUI团队3 小时前
Electron 入门学习指南:快速搭建跨平台桌面应用
前端·javascript·electron
柳鲲鹏3 小时前
VUE3多国语言切换(国际化)
前端·javascript·vue.js
java_jun3 小时前
pdfjs库使用记录1
javascript
Cutey9164 小时前
前端SEO优化方案
前端·javascript