根據基準框, 來放大縮小平移

 啊,了解了!這樣就更單純了。drawImage 只是你的參考線。

那麼,重點就完全放在如何將你定義好的 Bezier 曲線路徑,根據新的 x, y, w, h 進行縮放和平移。

我之前的第二種方法(使用 Canvas 轉換)仍然是最佳選擇。我們只需要把 drawImage 的部分拿掉,並確保 lineWidth 的行為是你期望的。

關於 lineWidth 的行為:
當你使用 ctx.scale(scaleX, scaleY) 後,再設定 ctx.lineWidth = N; 並執行 ctx.stroke();

  • 線條的寬度實際上會變成 N * scaleX(對於水平方向的線段)和 N * scaleY(對於垂直方向的線段)。

  • 如果 scaleXscaleY 不同(非均勻縮放),線條看起來會有不同的粗細。

  • 如果 scaleXscaleY 相同(均勻縮放),線條寬度會一致地變為 N * scaleFactor

如果你希望線條在視覺上有一個固定的寬度,不論縮放比例如何:
這比較複雜。一種方法是在設定 lineWidth 時,將其除以縮放因子:
ctx.lineWidth = desiredVisualLineWidth / Math.min(scaleX, scaleY);
(使用 Math.min 是為了避免在一個維度縮放很大時線條變得過粗;或者你可以用 Math.sqrt(scaleX * scaleY) 做幾何平均)。
但通常情況下,讓線條寬度隨圖形一起縮放是可接受的,也更簡單。

修改後的 drawCustomFrame 函數(不含圖片,專注於路徑):

      <!DOCTYPE html>
<html>
<head>
<title>Dynamic Canvas Shape (No Image)</title>
<style>
  body { margin: 20px; }
  canvas { border: 1px solid black; }
</style>
</head>
<body>

<canvas id="myCanvas" width="600" height="450"></canvas>

<script>
    const canvas = document.getElementById("myCanvas");
    const ctx = canvas.getContext("2d");

    // 繪製原始尺寸和位置 (方便比較)
    drawCustomFrame(ctx, 10, 10, 150, 100, "blue", 1.5);

    // 根據新的 x, y, w, h 繪製
    drawCustomFrame(ctx, 200, 30, 300, 200, "red", 2);    // 放大並移動
    drawCustomFrame(ctx, 50, 250, 75, 50, "green", 1);     // 縮小並移動到另一位置
    drawCustomFrame(ctx, 200, 280, 200, 150, "purple", 3); // 不同尺寸和線寬

    /**
     * 繪製可變形的自訂邊框路徑
     * @param {CanvasRenderingContext2D} context - Canvas 的 2D 渲染上下文
     * @param {number} targetX - 目標位置的 X 座標 (圖形左上角)
     * @param {number} targetY - 目標位置的 Y 座標 (圖形左上角)
     * @param {number} targetWidth - 目標寬度
     * @param {number} targetHeight - 目標高度
     * @param {string} [strokeColor="black"] - 線條顏色
     * @param {number} [baseLineWidth=1] - 線條的基礎寬度。注意:實際渲染的線寬會受到縮放影響。
     */
    function drawCustomFrame(context, targetX, targetY, targetWidth, targetHeight, strokeColor = "black", baseLineWidth = 1) {
        const originalWidth = 150;  // 你定義 Bezier 曲線時的原始寬度
        const originalHeight = 100; // 你定義 Bezier 曲線時的原始高度

        // 計算縮放比例
        // 如果 targetWidth 或 targetHeight 為 0,避免除以零,並將比例設為一個極小值或 1
        const scaleX = (originalWidth === 0) ? 1 : targetWidth / originalWidth;
        const scaleY = (originalHeight === 0) ? 1 : targetHeight / originalHeight;

        context.save(); // 儲存當前狀態 (轉換矩陣, 樣式等)

        // 1. 平移: 將畫布原點移動到目標位置的左上角
        context.translate(targetX, targetY);

        // 2. 縮放: 以新的原點為中心進行縮放
        context.scale(scaleX, scaleY);

        // --- 現在開始使用原始座標繪製路徑 ---
        // (0,0) 現在是 targetX, targetY 經過縮放後的位置
        // 所有繪圖指令的座標都是相對於這個原始的 150x100 框框

        context.beginPath();
        context.moveTo(0, 4);
        context.bezierCurveTo(50, 20, 56, 20, 59, 11);
        context.bezierCurveTo(88, 27, 143, 7, 150, 0);
        context.bezierCurveTo(139, 12, 127, 29, 142, 29);
        context.bezierCurveTo(136, 52, 138, 62, 145, 68);
        context.bezierCurveTo(130, 78, 146, 90, 150, 100);
        context.bezierCurveTo(133, 90, 116, 80, 105, 95);
        context.bezierCurveTo(70, 83, 36, 84, 0, 100);
        context.bezierCurveTo(11, 91, 27, 66, 7, 69);
        context.bezierCurveTo(18, 58, 25, 46, 5, 27);
        context.bezierCurveTo(19, 29, 23, 25, 0, 4); // 回到起點 (0,4)

        context.closePath(); // 明確閉合路徑,對於 stroke 可能不是絕對必要但好習慣

        context.strokeStyle = strokeColor;
        context.lineWidth = baseLineWidth; // 設定線條寬度,它會被 scaleX, scaleY 影響

        context.stroke(); // 描邊繪製路徑

        context.restore(); // 恢復到 save() 之前的狀態,移除本次的 translate 和 scale 影響
    }
</script>

</body>
</html>
    

主要變更和說明:

  1. 移除了 imageObject 相關的程式碼。

  2. originalWidthoriginalHeight: 這些值 (150, 100) 是你定義 Bezier 曲線時,這些座標點所參考的原始畫布或區域的尺寸。scaleXscaleY 就是基於這個原始尺寸和新的目標尺寸計算出來的。

  3. baseLineWidth 參數: 我將 lineWidth 參數名改為 baseLineWidth 並在註解中說明,這個寬度會受到 ctx.scale() 的影響。如果你傳入 baseLineWidth = 1,而圖形被放大兩倍 (scaleX=2, scaleY=2),那麼實際線條看起來會有 2px 寬。

  4. context.closePath(): 雖然你的最後一個 bezierCurveTo 已經回到了起點 (0,4),但使用 closePath() 是一個好習慣,它會明確地將當前點連接回路徑的起始點,確保圖形是封閉的。對於 stroke() 來說,如果路徑本身已經閉合,效果可能不明顯,但對於 fill() 則很重要。

  5. 增加了對 originalWidthoriginalHeight 為 0 的檢查:避免除以零的錯誤,雖然在你的情況下它們是固定的 150 和 100。

現在,你只需要呼叫 drawCustomFrame 函數,並傳入你想要的 targetX, targetY, targetWidth, targetHeight,以及可選的顏色和基礎線寬,它就會在你指定的位置以指定的尺寸繪製出那個邊框了。

留言

這個網誌中的熱門文章

Offscreen Canvas

外部網頁新增 Google 日曆行程(URL模板)

計算 Canvas 文字最大字體