垂直的波浪號
好的,我明白你的意思了。你是想讓使用者在 <input type="text"> 框中輸入文字,然後 Canvas 會動態地將這些文字(包括可能出現的波浪號 ~)以垂直方式渲染出來,並且你無法預先知道波浪號的確切位置。
監聽 <input> 的變化。 獲取輸入的完整文字內容。 逐字元渲染到 Canvas 上。 當遇到 ~ 字元時,對該字元進行旋轉繪製。
<!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>
HTML 結構: 一個 <input type="text"> 讓使用者輸入。 一個 <canvas> 用於繪製。
JavaScript: fontSize, lineHeight, charSpacing, padding: 用於控制文字佈局的常數。 drawTextVertical(text) 函數: ctx.clearRect(0, 0, canvas.width, canvas.height);: 每次重繪前清除畫布。 ctx.font, ctx.fillStyle, ctx.textAlign, ctx.textBaseline: 設定繪圖屬性。textAlign 和 textBaseline 設為 center 和 middle 有助於在 translate 和 rotate 後更容易將字元中心對準目標點。 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。
其他字元的顯示方式: 目前範例:所有字元都是垂直排列的。 也就是說,"Hello" 會顯示成:H e l l o如果你的需求是: 文字整體是水平閱讀的,只有 ~ 這個符號本身是垂直的,例如:"Hello ~ World" 應該顯示為 "Hello" (水平) 然後一個垂直的 ~,然後 "World" (水平)。這種情況下,邏輯會複雜得多。你需要: 測量每個字元的寬度 (ctx.measureText(char).width)。 維護一個水平的 currentX 和垂直的 currentY (用於換行)。 當遇到 ~ 時,在其當前水平位置 currentX 繪製一個垂直的 ~,然後 currentX 需要增加 ~ 旋轉後所佔的「高度」(即原始字型大小)。 對於其他字元,正常水平繪製,並更新 currentX。
精確的字元定位和間距: ctx.measureText(char).width 可以用來獲取字元的寬度,這對於更精確的佈局(尤其是在混合水平和垂直字元時)非常重要。 對於垂直的 ~,它旋轉後佔據的「寬度」實際上是原始字型的高度,而它佔據的「高度」是原始字元的寬度 (雖然 ~ 的寬度通常不大)。
複雜排版(如從右到左的文字或混合語言): 如果涉及到更複雜的排版需求,Canvas 的原生文字 API 可能會顯得吃力,你可能需要考慮使用更專業的文字渲染庫。
留言
張貼留言