垂直的波浪號

 好的,我明白你的意思了。你是想讓使用者在 <input type="text"> 框中輸入文字,然後 Canvas 會動態地將這些文字(包括可能出現的波浪號 ~)以垂直方式渲染出來,並且你無法預先知道波浪號的確切位置。

這種情況下,你需要:

  1. 監聽 <input> 的變化。

  2. 獲取輸入的完整文字內容。

  3. 逐字元渲染到 Canvas 上。

  4. 當遇到 ~ 字元時,對該字元進行旋轉繪製。

以下是如何實現這個功能的範例:

      <!DOCTYPE html>
<html>
<head>
<title>Dynamic Vertical Tilde on Canvas</title>
<style>
  body { margin: 20px; font-family: sans-serif; }
  canvas { border: 1px solid black; margin-top: 10px; }
  input[type="text"] { width: 300px; padding: 5px; margin-bottom: 10px; }
</style>
</head>
<body>

<label for="textInput">輸入文字 (包含 ~):</label><br>
<input type="text" id="textInput" value="Hello ~ World ~ Test">

<canvas id="myCanvas" width="400" height="300"></canvas>

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

  const fontSize = 24; // 字型大小
  const lineHeight = fontSize * 1.2; // 行高,可以調整
  const charSpacing = 2; // 垂直排列時,字元之間的額外間距 (如果需要)
  const padding = 10; // Canvas 邊距

  function drawTextVertical(text) {
    // 清除畫布
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    ctx.font = `${fontSize}px Arial`;
    ctx.fillStyle = 'black';
    ctx.textAlign = 'center'; // 方便旋轉後的定位
    ctx.textBaseline = 'middle'; // 方便旋轉後的定位

    let currentX = padding + fontSize / 2; // 第一列的 X 中心位置
    let currentY = padding + fontSize / 2; // 第一個字元的 Y 中心位置

    for (let i = 0; i < text.length; i++) {
      const char = text[i];

      if (currentY + fontSize / 2 > canvas.height - padding) {
        // 超出畫布高度,換列
        currentY = padding + fontSize / 2;
        currentX += fontSize + charSpacing * 2; // 移到下一列
      }

      if (char === '~') {
        // 繪製垂直的波浪號
        ctx.save();
        ctx.translate(currentX, currentY);
        ctx.rotate(Math.PI / 2); // 旋轉90度
        ctx.fillText(char, 0, 0); // 在旋轉後的新原點繪製
        ctx.restore();
      } else {
        // 繪製其他普通字元 (也是垂直排列)
        // 如果其他字元也需要水平顯示,則不需要旋轉
        // 這裡假設所有字元都垂直排列,如果只有~垂直,則需要調整
        ctx.save();
        ctx.translate(currentX, currentY);
        // 如果希望其他字元也旋轉顯示,取消下一行的註解
        // ctx.rotate(Math.PI / 2);
        ctx.fillText(char, 0, 0); // 如果不旋轉,這裡的(0,0)就是相對於 currentX, currentY
        ctx.restore();
      }

      currentY += fontSize + charSpacing; // 移動到下一個字元的位置
    }
  }

  // 初始繪製
  drawTextVertical(textInput.value);

  // 監聽輸入框的 input 事件
  textInput.addEventListener('input', function() {
    drawTextVertical(this.value);
  });

  // 也可以監聽 change 事件,但 input 事件更即時
  // textInput.addEventListener('change', function() {
  //   drawTextVertical(this.value);
  // });

</script>

</body>
</html>
    

程式碼解釋:

  1. HTML 結構:

    • 一個 <input type="text"> 讓使用者輸入。

    • 一個 <canvas> 用於繪製。

  2. JavaScript:

    • fontSize, lineHeight, charSpacing, padding: 用於控制文字佈局的常數。

    • drawTextVertical(text) 函數:

      • ctx.clearRect(0, 0, canvas.width, canvas.height);: 每次重繪前清除畫布。

      • ctx.font, ctx.fillStyle, ctx.textAlign, ctx.textBaseline: 設定繪圖屬性。textAligntextBaseline 設為 centermiddle 有助於在 translaterotate 後更容易將字元中心對準目標點。

      • currentX, currentY: 追蹤當前要繪製字元的中心座標。

      • 遍歷輸入文字 (for (let i = 0; i < text.length; i++))

        • const char = text[i];: 取得當前字元。

        • 換列邏輯if (currentY + fontSize / 2 > canvas.height - padding) 檢查是否需要換到下一列。

        • 判斷是否為波浪號 ~

          • if (char === '~'): 如果是波浪號:

            • ctx.save(): 儲存狀態。

            • ctx.translate(currentX, currentY): 移動原點到字元中心。

            • ctx.rotate(Math.PI / 2): 旋轉90度。

            • ctx.fillText(char, 0, 0): 在旋轉後的 (0,0) 繪製波浪號。

            • ctx.restore(): 恢復狀態。

          • else: 如果是其他字元:

            • 這裡的範例也是將其他字元垂直排列(即每個字元單獨繪製在自己的垂直位置)。

            • 如果你希望其他字元是正常的水平方向,但整體文字流是從上到下、從左到右,那麼你需要修改這部分的邏輯,可能不需要為每個非 ~ 字元都進行 translate,而是直接調整 fillText 的 Y 座標,並且在遇到 ~ 時才特殊處理。

            • 目前的範例是將所有字元都視為獨立的垂直單元,如果 ~ 是一個長字串的一部分,且其他字元要水平顯示,那麼這個邏輯需要大幅調整。

        • currentY += fontSize + charSpacing;: 更新 Y 座標,準備繪製下一個字元。

    • 事件監聽:

      • textInput.addEventListener('input', ...): 當使用者在輸入框中輸入內容時,會觸發 input 事件,並重新呼叫 drawTextVertical 函數來更新 Canvas。

重要考慮點和可能的調整:

  1. 其他字元的顯示方式:

    • 目前範例:所有字元都是垂直排列的。 也就是說,"Hello" 會顯示成:

            H
      e
      l
      l
      o
          
    • 如果你的需求是: 文字整體是水平閱讀的,只有 ~ 這個符號本身是垂直的,例如:"Hello ~ World" 應該顯示為 "Hello" (水平) 然後一個垂直的 ~,然後 "World" (水平)。
      這種情況下,邏輯會複雜得多。你需要:

      • 測量每個字元的寬度 (ctx.measureText(char).width)。

      • 維護一個水平的 currentX 和垂直的 currentY (用於換行)。

      • 當遇到 ~ 時,在其當前水平位置 currentX 繪製一個垂直的 ~,然後 currentX 需要增加 ~ 旋轉後所佔的「高度」(即原始字型大小)。

      • 對於其他字元,正常水平繪製,並更新 currentX

  2. 精確的字元定位和間距:

    • ctx.measureText(char).width 可以用來獲取字元的寬度,這對於更精確的佈局(尤其是在混合水平和垂直字元時)非常重要。

    • 對於垂直的 ~,它旋轉後佔據的「寬度」實際上是原始字型的高度,而它佔據的「高度」是原始字元的寬度 (雖然 ~ 的寬度通常不大)。

  3. 複雜排版(如從右到左的文字或混合語言):

    • 如果涉及到更複雜的排版需求,Canvas 的原生文字 API 可能會顯得吃力,你可能需要考慮使用更專業的文字渲染庫。

如果你希望文字是水平流動,只有 ~ 垂直,請告訴我,我可以提供一個針對該情況的調整版本。

目前的範例是基於「所有字元都垂直排列,遇到 ~ 就讓它旋轉90度」的假設。

留言

這個網誌中的熱門文章

Offscreen Canvas

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

計算 Canvas 文字最大字體