「驚訝」、「吶喊」或「強調」對話框
程式更新為:
中心橢圓/圓形區域 :作為文字的背景,通常是白色。放射狀的尖刺線條 :從中心橢圓的邊緣向外輻射,形成「爆炸」或「毛刺」效果。
targetX, targetY: 對話框內容區域 (白色橢圓) 的左上角 bounding box 座標。 targetWidth, targetHeight: 對話框內容區域 (白色橢圓) 的寬度和高度。 你可能還需要一些參數來控制尖刺的外觀,例如: spikeLength: 尖刺的長度。 numSpikes: 尖刺的數量 (越多越密集)。 spikeColor: 尖刺的顏色 (通常是黑色)。 fillColor: 中心區域的填充色 (通常是白色)。
<!DOCTYPE html>
<html>
<head>
<title>Canvas Spiky Speech Bubble</title>
<style>
body { margin: 20px; display: flex; flex-direction: column; align-items: center; }
canvas { border: 1px solid black; margin-bottom: 10px; }
.controls label { display: inline-block; min-width: 100px; }
.controls input { margin-bottom: 5px; }
</style>
</head>
<body>
<canvas id="myCanvas" width="500" height="400"></canvas>
<div class="controls">
<div><label for="posX">X:</label><input type="number" id="posX" value="100"></div>
<div><label for="posY">Y:</label><input type="number" id="posY" value="100"></div>
<div><label for="pWidth">Width:</label><input type="number" id="pWidth" value="200"></div>
<div><label for="pHeight">Height:</label><input type="number" id="pHeight" value="120"></div>
<div><label for="spikeLen">Spike Length:</label><input type="number" id="spikeLen" value="40"></div>
<div><label for="numSpikes">Num Spikes:</label><input type="number" id="numSpikes" value="300"></div>
<div><label for="spikeColor">Spike Color:</label><input type="color" id="spikeColor" value="#000000"></div>
<div><label for="fillColor">Fill Color:</label><input type="color" id="fillColor" value="#FFFFFF"></div>
<button id="drawButton">Draw</button>
</div>
<script>
const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");
const posXInput = document.getElementById('posX');
const posYInput = document.getElementById('posY');
const pWidthInput = document.getElementById('pWidth');
const pHeightInput = document.getElementById('pHeight');
const spikeLenInput = document.getElementById('spikeLen');
const numSpikesInput = document.getElementById('numSpikes');
const spikeColorInput = document.getElementById('spikeColor');
const fillColorInput = document.getElementById('fillColor');
const drawButton = document.getElementById('drawButton');
/**
* 繪製帶尖刺的對話框
* @param {CanvasRenderingContext2D} context
* @param {number} x - 內部橢圓 bounding box 的左上角 x
* @param {number} y - 內部橢圓 bounding box 的左上角 y
* @param {number} width - 內部橢圓的寬度
* @param {number} height - 內部橢圓的高度
* @param {number} spikeLength - 尖刺的長度
* @param {number} numSpikes - 尖刺的數量
* @param {string} innerFillColor - 內部橢圓的填充色
* @param {string} spikeLineColor - 尖刺線條的顏色
* @param {number} spikeLineWidth - 尖刺線條的寬度 (設小一點,例如 0.5 或 1)
*/
function drawSpikyBubble(context, x, y, width, height, spikeLength, numSpikes, innerFillColor, spikeLineColor, spikeLineWidth = 1) {
const centerX = x + width / 2;
const centerY = y + height / 2;
const radiusX = width / 2;
const radiusY = height / 2;
// 儲存當前狀態
context.save();
context.clearRect(0, 0, canvas.width, canvas.height); // 清除畫布
// 1. 繪製中心的白色橢圓 (作為文字背景)
context.beginPath();
context.ellipse(centerX, centerY, radiusX, radiusY, 0, 0, 2 * Math.PI);
context.fillStyle = innerFillColor;
context.fill();
// (可選) 如果內部橢圓也需要邊框
// context.strokeStyle = "gray";
// context.lineWidth = 1;
// context.stroke();
// 2. 繪製放射狀的尖刺
context.beginPath(); // 開始一個新的路徑來畫所有的尖刺線條
context.strokeStyle = spikeLineColor;
context.lineWidth = spikeLineWidth;
for (let i = 0; i < numSpikes; i++) {
const angle = (i / numSpikes) * 2 * Math.PI; // 0 到 2π 的角度
// 尖刺在橢圓邊緣上的起點
const startX = centerX + radiusX * Math.cos(angle);
const startY = centerY + radiusY * Math.sin(angle);
// 尖刺的終點 (從中心沿角度向外延伸)
// 這裡我們假設尖刺是從橢圓邊緣向外固定長度
// 更精確的做法是讓尖刺的方向垂直於橢圓在該點的切線,但對於這種效果,直接從中心放射通常足夠
const endX = centerX + (radiusX + spikeLength) * Math.cos(angle);
const endY = centerY + (radiusY + spikeLength) * Math.sin(angle);
context.moveTo(startX, startY);
context.lineTo(endX, endY);
}
context.stroke(); // 一次性描邊所有尖刺線條
context.restore(); // 恢復之前儲存的狀態
}
function redraw() {
drawSpikyBubble(
ctx,
parseFloat(posXInput.value),
parseFloat(posYInput.value),
parseFloat(pWidthInput.value),
parseFloat(pHeightInput.value),
parseFloat(spikeLenInput.value),
parseInt(numSpikesInput.value, 10),
fillColorInput.value,
spikeColorInput.value,
0.7 // 可以調整尖刺線條的寬度
);
}
// 初始繪製
redraw();
// 為所有輸入框添加事件監聽器以重新繪製
[posXInput, posYInput, pWidthInput, pHeightInput, spikeLenInput, numSpikesInput, spikeColorInput, fillColorInput].forEach(input => {
input.addEventListener('input', redraw);
});
drawButton.addEventListener('click', redraw);
</script>
</body>
</html>
HTML 控制項 :提供輸入框讓使用者設定對話框的各種參數。drawSpikyBubble 函數 :接收 context (Canvas 的 2D 繪圖環境) 和各種參數。 計算橢圓的中心點 (centerX, centerY) 和半徑 (radiusX, radiusY)。 繪製中心橢圓 :使用 ctx.ellipse() 繪製一個橢圓。 用 innerFillColor (通常是白色) 填充它。
繪製尖刺 :開始一個新的路徑 (ctx.beginPath())。這樣可以將所有尖刺線條收集起來,最後用一次 ctx.stroke() 繪製,效能較好。 迴圈 numSpikes 次,每次計算一個角度 angle。 startX, startY:根據當前角度計算尖刺在橢圓邊緣上的起點。 endX, endY:根據當前角度和 spikeLength 計算尖刺的終點。這裡的計算方式是讓尖刺從橢圓中心點沿著角度向外延伸,經過橢圓邊緣後再延伸 spikeLength 的距離。 ctx.moveTo(startX, startY) 和 ctx.lineTo(endX, endY):定義一條尖刺線段。 迴圈結束後,ctx.stroke() 將所有定義的線段描邊繪製出來。
spikeLineWidth:控制每根尖刺的線條寬度。如果尖刺數量很多,設小一點 (例如 0.5 到 1) 效果會比較好,線條會顯得更密集,形成類似圖片中的效果。
numSpikes (尖刺數量) :圖片中的尖刺非常密集,所以你需要一個較大的值,例如 200、300 甚至更多。spikeLength (尖刺長度) :根據你想要的「爆炸」範圍來調整。spikeLineWidth (尖刺線寬) :設為一個較小的值,比如 0.5 或 0.7 或 1。如果線寬太大,尖刺之間可能不會有圖片中那種細密的感覺。width 和 height :決定了中心白色區域的大小和形狀。圖片中的看起來更接近圓形,所以 width 和 height 可以設得比較接近。

留言
張貼留言