Stories

Detail Return Return

開源 | Warpvas 實現扭曲的畫布 - Stories Detail

介紹

名字靈感源於 warped + canvas,扭曲的畫布。

畫布可以被扭曲嗎?

如果你使用過canvas,可能你對setTransform API也有所瞭解,那你可能會給出一個偏消極的答案:只能整體傾斜變形,但做不到扭曲變形。

但這真的做不到嗎?

我們可以先來想一個問題:如果對於一個方方正正的圖像來説,setTransform API施加的變形效果只能作用於整體,那如果把它從斜對角切成兩份三角形,再分別應用setTransform API施加變形呢?

不知道你是否能驚喜地發現,“只能整體變形”的魔咒就這樣被輕易破除了

這是非常容易理解的,但是你可能會疑惑那又如何,"扭曲"一樣無法實現。

但我們要意識到圖像是可以無限分割的,我們可以將其分割成網格狀(如10行10列),然後每個網格中的部分圖像再根據它自己斜對角分割成兩個三角形進行獨立變形,而當這些獨立變形的三角圖元重新整合在一起成為一張大圖的時候,“扭曲的畫布”不就誕生了嗎?

所以,扭曲畫布是能夠實現的。但我知道,你此刻內心在打鼓,因為這原理好理解,但實現起來的話,估計得吃上不少苦頭吧...


如果你今日還在等待好消息出現的話,那我想這可能就是你今天聽到的第一個好消息。

Warpvas是一個可以快速實現畫布扭曲效果的工具庫,它小而美,它靈活且高效,而且它是開源的。

官網地址:Warpvas

image.png

在Warpvas庫的代碼設計中,雖然它內部實際上更傾向應用性能更好的WebGL實現,而不是應用2D中setTransform API實現,但也不妨礙它展示上面化零為整的藝術(圖像分割變形)。

特性

  • 網格分割策略:支持自定義網格分割算法,通過不同策略實現透視/波浪/曲面等變形效果。
  • 邊界曲線控制:通過貝塞爾曲線控制點調整,實現精細的邊界變形效果控制。
  • 輔助顯示效果:提供選項以在變形圖像上添加分隔線和分割點等輔助顯示效果,以協助觀察圖像變形效果。
  • 輸入輸出限制:提供選項以控制變形圖像的輸入和輸出尺寸,更好把控變形圖像的生成質量和效率,也可防止超大圖像導致性能問題。
  • 多渲染引擎支持:可在 WebGL (高性能) 和 Canvas2D (高兼容) 渲染模式間自由切換。
  • 安全渲染模式:WebGL 渲染失敗時自動降級至 Canvas2D 渲染,保障基礎功能可用性。
  • 狀態序列化:支持將變形狀態序列化為 base64 字符串,便於保存/恢復複雜變形狀態。

安裝方法

npm install warpvas
# or
pnpm add warpvas

使用教程

1. 創建新的變形畫布實例

// 使用圖片創建
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. 更新變形區域頂點座標

// 將變形區域(0,0)的左上角頂點移動到(100,100)
const warpvas = new Warpvas(canvas);
warpvas.updateVertexCoord(
    0,                  // 區域行索引
    0,                  // 區域列索引
    'tl',               // 頂點位置(左上角)
    { x: 100, y: 100 }, // 新位置
    true                // 重新計算曲線
);

image.png

第五個參數當設置為true時,臨近曲線的控制點會重新計算,換句話説就是保持直線狀態。

3. 更新變形區域指定方向的邊界曲線

// 將變形區域(0,0)的底部邊界變為曲線
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 },                // 曲線終點
    ]
);

image.png

4. 添加分割點以進一步分割變形區域

// 在第一個變形區域(0,0)的中心添加分割點,會分割成2×2的區域
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%容差,容差越大,分割點越容易直接作用於邊界上
);

如果對不同區域分別施加變形操作,可實現以下效果(分隔線僅供參考):

image.png

5. 移除指定的變形區域

// 移除位於第1行第1列的變形區域
const warpvas = new Warpvas(canvas);
warpvas.removeRegion({
    row: 1,
    column: 1
});

6. 設置渲染配置選項

// 控制渲染效果和調試視圖顯示
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 } // 使用珊瑚紅色網格
});

image.png

7. 設置變形網格細分的最大比例

控制分割網格的密度,值範圍 0-1。這個值越小,網格越密,變形效果越精細,但同時也會增加計算量。例如,值為 0.1 時,每個網格的最大尺寸為圖像寬度的 10%。

// 設置緊密網格以提高變形質量
const warpvas = new Warpvas(canvas);
warpvas
    .setSplitUnit(0.01)
    .setRenderingConfig({
        enableGridDisplay: true,
        gridColor: { r: 206, g: 102, b: 91, a: 1 }
    });

image.png

8. 設置網格細分點的計算策略

const warpvas = new Warpvas(canvas);

// 使用自定義策略
warpvas.setSplitStrategy({
    name: 'custom',
    execute: (warpvas) => {
        // 返回自定義網格點計算結果
        // return [[[{ x: 0, y: 0 }]]];

        // 使用默認策略計算的結果
        return Warpvas.strategy(warpvas);
    }
});

應用不同的策略,將會得到不同的變形效果,為了讓大家更好地感受到“策略”的強大效果,這裏“附送”透視效果的實現,這裏需要引入另一個庫:warpvas-perspective

image.png

9. 設置輸入畫布的尺寸限制

當處理大尺寸圖像時,可以通過此方法限制輸入畫布的最大尺寸,系統會自動將圖像等比縮放到限制範圍內進行處理,以提高性能和減少內存佔用。

const warpvas = new Warpvas(canvas);

// 限制輸入高度為100px,寬度按比例縮放
warpvas.setInputLimitSize({
    height: 100
});

10. 設置輸出畫布的尺寸限制

限制變形後輸出畫布的最大尺寸。當變形導致畫布尺寸過大時,系統會自動將結果等比縮放到限制範圍內,避免因內存限制導致渲染失敗。

const warpvas = new Warpvas(canvas);

// 限制輸出高度為100px,寬度按比例縮放
warpvas.setOutputLimitSize({
    height: 100
});

11. 設置渲染引擎類型

配置渲染引擎使用 Canvas2D 或 WebGL 模式。模式比較:

  • Canvas2D:兼容性更好(支持大多數瀏覽器),但性能較慢
  • WebGL:大圖像性能更好(GPU 加速),但兼容性有限
const warpvas = new Warpvas(canvas);

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

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

12. 通過Web Worker異步生成變形畫布

const canvas = document.createElement('canvas');
const warpvas = new Warpvas(canvas);
await warpvas.renderWithWorker();

最後

這個庫源於我的工作經驗和總結,這個庫我會持續維護,如果這個庫能幫助到你,歡迎到 GitHub 留下寶貴的⭐️。

如果你有極具創新的獨特變形策略,歡迎在 Issues 區留言,我也會將它們補充到 README.md 中。

這裏還有一個好消息,結合fabricjs快速搭建圖像扭曲編輯工具的開源庫也在籌備中了,如果你感興趣,可持續關注~

來自掘金文章:開源 | Warpvas 實現扭曲的畫布

Add a new Comments

Some HTML is okay.