Files
JMuseum/public/js/drawing_tools.js
2018-02-26 14:09:18 +08:00

340 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @typedef CPosition
* @prop {number} X 平面座標上的X。
* @prop {number} Y 平面座標上的Y。
*/
/**
* 滑鼠狀態的列舉。
* @readonly
* @enum {number}
*/
const MouseStatus = Object.freeze({MouseDown: 0, Drawing: 1, MouseUp: 2, MouseMoving: 3});
/** @type {HTMLElement} */
let cvsCanvas = document.getElementById("cvsCanvas"); // 畫布(Canvas)的HTML物件
/** @type {CanvasRenderingContext2D} */
let context = cvsCanvas.getContext("2d"); // 操縱繪圖動作的相關函式集。
/** 以Id對應不同的筆刷模式。有: 筆刷、滑筆、燕筆、刺筆、印筆 */
let BrushMethods = {
"normalBrush": NormalBrush_Drawing,
"slideBrush": SlideBrush_Drawing,
"featherBrush": FeatherBrush_Drawing,
"furBrush": FurBrush_Drawing,
"stampBrush": StampBrush_Drawing
};
/* ============================ 紀錄HTML物件 ============================ */
let sizeGroupText = document.getElementById("sizeGroupText");
let strokeSizeGroupText = document.getElementById("strokeSizeGroupText");
/* ======================== 紀錄繪圖工具的相關狀態 ======================== */
let isMouseDown = false; // 紀錄滑鼠是否為壓下的狀態
let lastPosition = {X: 0, Y: 0}; // 紀錄上一次的點的座標
let curPosition = {X: 0, Y: 0}; // 紀錄目前的滑鼠座標位置
let brushWidth = 1; // 紀錄目前使用者設定的筆刷大小
let fillWidth = 1; // 紀錄目前使用者設定的填充大小
let strokeColor = "#000000"; // 紀錄目前使用者所設定的比刷顏色
let fillColor = "#000000"; // 紀錄目前使用者所設定的填滿顏色
let activedBrush = document.getElementById("normalBrush"); // 紀錄目前在使用的比刷模式的HTML物件
let DrawingMethod = NormalBrush_Drawing; // 紀錄目前的繪畫模式
/* ============================== 簽署事件 ============================== */
cvsCanvas.addEventListener("mousedown", cvsCanvas_MouseDown);
cvsCanvas.addEventListener("mousemove", cvsCanvas_MouseMove, false);
cvsCanvas.addEventListener("mouseup", cvsCanvas_MouseUp, false);
document.getElementById("resetCanvas").addEventListener("click", ClearCanvas);
document.getElementById("normalBrush").addEventListener("click", ChangeBrushMethod);
document.getElementById("slideBrush").addEventListener("click", ChangeBrushMethod);
document.getElementById("featherBrush").addEventListener("click", ChangeBrushMethod);
document.getElementById("furBrush").addEventListener("click", ChangeBrushMethod);
document.getElementById("stampBrush").addEventListener("click", ChangeBrushMethod);
document.getElementById("mainColorPicker").addEventListener("change", ChangeColor, false);
document.getElementById("subColorPicker").addEventListener("change", ChangeColor, false);
document.getElementById("subColorPicker").addEventListener("click", e => e.stopPropagation() ); //防止按下按鈕時會動到上一層的sub-color-picker
document.getElementById("sizeUp").addEventListener("click", () => ChangeFillSize(1));
document.getElementById("sizeDown").addEventListener("click", () => ChangeFillSize(-1));
document.getElementById("sizeUp10").addEventListener("click", () => ChangeFillSize(10));
document.getElementById("sizeDown10").addEventListener("click", () => ChangeFillSize(-10));
document.getElementById("strokeSizeUp").addEventListener("click", () => ChangeStrokeSize(1));
document.getElementById("strokeSizeDown").addEventListener("click", () => ChangeStrokeSize(-1));
document.getElementById("strokeSizeUp10").addEventListener("click", () => ChangeStrokeSize(10));
document.getElementById("strokeSizeDown10").addEventListener("click", () => ChangeStrokeSize(-10));
/**
* 畫布初始化。
*/
if (paintingId) {
let paintImg = document.createElement("img");
paintImg.style = "display: none;";
paintImg.onload = () => context.drawImage(paintImg, 0, 0, 800, 450);
paintImg.src = "/db/paintings/" + paintingId + ".png";
document.body.appendChild(paintImg);
}
else {
context.fillStyle = "#FFFFFF";
context.fillRect(0, 0, 800, 450);
context.fill();
}
/**
* 當「重置」按鈕按下之後,清除畫布。
* @param {Event} event 事件物件。
*/
function ClearCanvas(event) {
context.fillStyle = "#FFFFFF";
context.fillRect(0, 0, 800, 450);
context.fill();
}
/**
* 變更筆刷模式。當筆刷模式按鈕按下時所做的處理。
* @param {Event} event 事件物件。
*/
function ChangeBrushMethod(event) {
activedBrush.classList.remove("active");
activedBrush = event.target;
activedBrush.classList.add("active");
DrawingMethod = BrushMethods[activedBrush.id];
}
/**
* 變更顏色。當筆刷顏色或填充顏色按鈕按下時,並選取完顏色後的事件處理。
* @param {Event} event 事件物件。
*/
function ChangeColor(event) {
let target = event.target ? event.srcElement : event.target;
if (target.id == "mainColorPicker") {
fillColor = target.value;
}
else {
strokeColor = target.value;
}
}
/**
* 變更筆刷的粗細大小。
* @param {number} value 變更量。
*/
function ChangeStrokeSize(value) {
brushWidth += value;
if (brushWidth > 128) brushWidth = 128;
if (brushWidth < 1) brushWidth = 1;
strokeSizeGroupText.innerText = "筆刷粗細 " + brushWidth;
}
/**
* 變更填充的粗細大小。
* @param {number} value 變更量。
*/
function ChangeFillSize(value) {
fillWidth += value;
if (fillWidth > 128) fillWidth = 128;
if (fillWidth < 1) fillWidth = 1;
sizeGroupText.innerText = "填色大小 " + fillWidth;
}
/**
* 取得滑鼠在畫布中的位置。
* @param {Event} event 事件物件。限定滑鼠動作相關的事件。
* @return {Position} 目前游標的座標。
*/
function GetMousePosition(event) {
let rect = cvsCanvas.getBoundingClientRect();
return {X: event.clientX - rect.left, Y: event.clientY - rect.top};
}
/**
* 當滑鼠在畫布上按下時,所觸發的事件。
* @param {Event} event 事件物件。
*/
function cvsCanvas_MouseDown(event) {
isMouseDown = true;
DrawingMethod(event, MouseStatus.MouseDown);
}
/**
* 當滑鼠在畫布上移動時,所觸發的事件。
* @param {Event} event 事件物件。
*/
function cvsCanvas_MouseMove(event) {
DrawingMethod(event, isMouseDown ? MouseStatus.Drawing : MouseStatus.MouseMoving);
}
/**
* 當滑鼠在畫布上放開時,所觸發的事件。
* @param {Event} event 事件物件。
*/
function cvsCanvas_MouseUp(event) {
isMouseDown = false;
DrawingMethod(event, MouseStatus.MouseUp);
}
/**
* 在指定位置畫上圓。
* @param {CPosition} pos 圓心位置。
* @param {number} radius 圓的半徑。
* @param {string?} color 顏色。
*/
function FillCircle(pos, radius, color) {
if (color) context.fillStyle = color;
context.beginPath();
context.arc(pos.X, pos.Y, radius, 0, 2 * Math.PI);
context.closePath();
context.fill();
}
/**
* 在指定兩個座標之間畫線。
* @param {CPosition} posA 起點座標位置。
* @param {CPosition} posB 終點座標位置。
* @param {string?} color 顏色。
*/
function DrawLine(posA, posB, color) {
if (color) context.strokeStyle = color;
context.beginPath();
context.moveTo(posA.X, posA.Y);
context.lineTo(posB.X, posB.Y);
context.closePath();
context.stroke();
}
/**
* 在指定坐標上繪製外內圓。
* @param {CPosition} pos 圓心座標位置。
* @param {number} radius 圓的半徑。
* @param {string} strokeColor 外圓的顏色。
* @param {string} fillColor 內圓的顏色。
*/
function StampCircle(pos, radius, strokeColor, fillColor) {
if (strokeColor) context.strokeStyle = strokeColor;
if (fillColor) context.fillStyle = fillColor;
context.beginPath();
context.arc(pos.X, pos.Y, radius, 0, 6.28);
context.closePath();
context.fill();
context.stroke();
}
/**
* 筆刷模式「筆刷」下的繪圖動作。
* @param {Event} event 與滑鼠動作相關的事件物件。
* @param {MouseStatus} status 滑鼠的狀態。
*/
function NormalBrush_Drawing(event, status) {
switch(status) {
case MouseStatus.MouseDown:
lastPosition = GetMousePosition(event);
context.lineWidth = brushWidth;
context.strokeStyle = strokeColor;
FillCircle(lastPosition, brushWidth / 2, strokeColor);
break;
case MouseStatus.Drawing:
curPosition = GetMousePosition(event);
DrawLine(lastPosition, curPosition);
FillCircle(curPosition, brushWidth / 2);
lastPosition = curPosition;
}
}
/**
* 筆刷模式「滑筆」下的繪圖動作。
* @param {Event} event 與滑鼠動作相關的事件物件。
* @param {MouseStatus} status 滑鼠的狀態。
*/
function SlideBrush_Drawing(event, status) {
switch(status) {
case MouseStatus.MouseDown:
lastPosition = GetMousePosition(event);
curPosition = lastPosition;
context.lineWidth = brushWidth;
context.strokeStyle = strokeColor;
FillCircle(lastPosition, brushWidth / 2, strokeColor);
setTimeout(SlideBrush_Chasing, 10);
break;
case MouseStatus.Drawing:
curPosition = GetMousePosition(event);
}
}
/**
* 筆刷模式「滑筆」下的滑筆繪製動作。
*/
function SlideBrush_Chasing() {
let dx = (curPosition.X - lastPosition.X) / 100, dy = (curPosition.Y - lastPosition.Y) / 100;
let nextPosition = {X: lastPosition.X + dx, Y: lastPosition.Y + dy};
DrawLine(lastPosition, nextPosition);
FillCircle(nextPosition, brushWidth / 2);
lastPosition = nextPosition;
if (isMouseDown && DrawingMethod == SlideBrush_Drawing)
setTimeout(SlideBrush_Chasing, 10);
}
let featherEnable = false;
/**
* 筆刷模式「燕筆」下的繪圖動作。 (燕尾筆)
* @param {Event} event 與滑鼠動作相關的事件物件。
* @param {MouseStatus} status 滑鼠的狀態。
*/
function FeatherBrush_Drawing(event, status) {
switch(status) {
case MouseStatus.MouseDown:
lastPosition = GetMousePosition(event);
context.strokeStyle = strokeColor;
context.lineWidth = brushWidth;
FillCircle(lastPosition, brushWidth / 2, strokeColor);
featherEnable = true;
break;
case MouseStatus.Drawing:
if (!featherEnable) break;
curPosition = GetMousePosition(event);
DrawLine(lastPosition, curPosition);
FillCircle(curPosition, context.lineWidth / 2);
lastPosition = curPosition;
context.lineWidth -= 0.5;
featherEnable = context.lineWidth - 0.5 > 0;
}
}
/**
* 筆刷模式「刺筆」下的繪圖動作。
* @param {Event} event 與滑鼠動作相關的事件物件。
* @param {MouseStatus} status 滑鼠的狀態。
*/
function FurBrush_Drawing(event, status) {
switch(status) {
case MouseStatus.MouseDown:
lastPosition = GetMousePosition(event);
context.strokeStyle = strokeColor;
context.lineWidth = brushWidth;
FillCircle(lastPosition, brushWidth / 2, strokeColor);
break;
case MouseStatus.Drawing:
curPosition = GetMousePosition(event);
let dx = curPosition.X - lastPosition.X, dy = curPosition.Y - lastPosition.Y;
dx = dx > 40 ? 2 : dx / 20;
dy = dy > 40 ? 2 : dy / 20;
DrawLine(lastPosition, curPosition);
FillCircle(curPosition, brushWidth / 2);
lastPosition.X += dx;
lastPosition.Y += dy;
}
}
/**
* 筆刷模式「印筆」下的繪圖動作。
* @param {Event} event 與滑鼠動作相關的事件物件。
* @param {MouseStatus} status 滑鼠的狀態。
*/
function StampBrush_Drawing(event, status) {
switch(status) {
case MouseStatus.MouseDown:
context.strokeStyle = strokeColor;
context.fillStyle = fillColor;
context.lineWidth = brushWidth;
StampCircle(GetMousePosition(event), fillWidth / 2);
break;
case MouseStatus.Drawing:
StampCircle(GetMousePosition(event), fillWidth / 2);
}
}