如何从代码看出软件框架(协作绘图应用程序)
如何从代码看出软件框架(协作绘图应用程序)如果您只是稍微玩一下该应用程序,您最终可以弄清楚它是如何工作的。但是如果程序本身提供一些简单的指令不是很好吗?例如,在制作圆圈时,先选择中心,然后选择边缘并不明显。一些用户可能认为您应该单击圆圈的两个相对边缘。其他人可能认为您可以绘制多段线。该应用程序不这样做,但对于好奇的程序员来说,稍后添加它会是一个很好的改进。让我们添加一些方便的说明。显示说明现在将此方法添加到 Canvas 对象:displayMode() { const modeDiv = document.querySelector('#mode'); modeDiv.innerHTML = this.mode; }现在在构造函数和 setMode 的末尾调用它。 例如,完成后 setMode 应该如下所示:setMode() { this.mode = mode; this.di
在本文中,我们添加了一些方便的功能让用户知道:
- 他们在画什么形状
- 画出他们所处的形状的步骤
显示模式
首先添加我们将向用户展示他们所处的形状模式的区域。在画布下添加以下行:
<b>Mode:</b> <span id="mode"></span><br />
现在将此方法添加到 Canvas 对象:
displayMode() {
const modeDiv = document.querySelector('#mode');
modeDiv.innerHTML = this.mode;
}
现在在构造函数和 setMode 的末尾调用它。 例如,完成后 setMode 应该如下所示:
setMode() {
this.mode = mode;
this.displayMode();
}
通过此更改,该模式将在应用程序启动时首先出现(默认为“Line”),并在您单击其中一个形状按钮时更新。
显示说明
如果您只是稍微玩一下该应用程序,您最终可以弄清楚它是如何工作的。但是如果程序本身提供一些简单的指令不是很好吗?例如,在制作圆圈时,先选择中心,然后选择边缘并不明显。一些用户可能认为您应该单击圆圈的两个相对边缘。其他人可能认为您可以绘制多段线。该应用程序不这样做,但对于好奇的程序员来说,稍后添加它会是一个很好的改进。让我们添加一些方便的说明。
首先,HTML。在显示模式的代码下,添加:
<b>Message:</b> <span id="message"></span>
在 Canvas 构造函数中,我们需要定义给用户什么指令。我们需要第一次单击形状的说明,以及第二次单击的说明。此外,由于圆形和矩形模式的工作方式相同,我们可以按形状类型概括说明。
this.shapeMessages = [
{ type: 'line' start: 'Select the starting point' end: 'Select the ending point' }
{ type: 'rectangle' start: 'Select the first corner' end: 'Select the second corner' }
{ type: 'circle' start: 'Select the middle of the circle' end: 'Select the edge of the circle' }
];
然后我们需要一种方法来确定模式是哪种形状类型:
getModeType(mode) {
let type = '';
switch (mode) {
case 'Line':
type = 'line';
break;
case 'Hollow Rectangle':
case 'Filled Rectangle':
type = 'rectangle';
break;
case 'Hollow Circle':
case 'Filled Circle':
type = 'circle';
break;
}
return type;
}
然后是实际显示消息的方法:
displayMessage() {
const type = this.getModeType(this.mode);
const find = this.shapeMessages.find(m => m.type === type);
const msgDiv = document.querySelector('#message');
msgDiv.innerHTML = find[this.pointMode];
}
作为复习,this.pointMode 被设置为记录我们是在形状的起点还是终点。 displayMessage 方法因此找到我们想要使用 shapeMessages 数组的哪个元素,并根据需要返回开始或结束消息。
和 displayMode 一样,这个方法应该在构造函数的末尾和 setMode 的末尾调用。 它也应该在 handleDraw 结束时调用,因为每次点击后我们都想更新指令。
把它们放在一起
如下所示,模式和消息现在应该出现在画布下:
当您单击按钮选择形状并单击画布时,请注意“模式”和“消息”区域。 他们会改变以反映你在做什么。
下面是 HTML 和 Javascript 此时的样子:
<html>
<head>
<title>Collaborative Drawing App</title>
<style type="text/css">
canvas {
border: 1px solid black;
}
.palette {
border: 1px solid black;
display: inline-block;
margin: 2px;
height: 25px;
width: 25px;
}
</style>
</head>
<body>
<h1>Collaborative Drawing App</h1>
<div>
<canvas id="canvas" height="500px" width="500px"></canvas><br />
<b>Mode:</b> <span id="mode"></span><br />
<b>Message:</b> <span id="message"></span>
</div>
<div id="palette">
<div id="row-1">
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
</div>
<div id="row-2">
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
</div>
</div>
<div id="draw-methods">
<button onclick="canvas.setMode('Line')">Line</button>
<button onclick="canvas.setMode('Hollow Rectangle')">Hollow Rectangle</button>
<button onclick="canvas.setMode('Filled Rectangle')">Filled Rectangle</button>
<button onclick="canvas.setMode('Hollow Circle')">Hollow Circle</button>
<button onclick="canvas.setMode('Filled Circle')">Filled Circle</button>
</div>
<script type="text/javascript" src="./script.js"></script>
<script type="text/javascript">
const canvas = new Canvas();
const palette = new Palette();
palette.draw(canvas);
</script>
</body>
</html>
class Canvas {
constructor() {
this.canvas = document.querySelector('#canvas');
this.ctx = this.canvas.getContext('2d');
this.activeColor = '#000000';
this.startPoint = null;
this.endPoint = null;
this.pointMode = 'start';
this.mode = 'Line';
this.handleDraw = this.handleDraw.bind(this);
this.canvas.addEventListener('click' this.handleDraw);
this.shapeMessages = [
{ type: 'line' start: 'Select the starting point' end: 'Select the ending point' }
{ type: 'rectangle' start: 'Select the first corner' end: 'Select the second corner' }
{ type: 'circle' start: 'Select the middle of the circle' end: 'Select the edge of the circle' }
];
this.displayMode();
this.displayMessage();
}
setColor(color) {
this.activeColor = color;
this.ctx.strokeStyle = color;
this.ctx.fillStyle = color;
}
setMode(mode) {
this.mode = mode;
this.displayMode();
this.displayMessage();
}
handleDraw(e) {
const rect = this.canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
if (this.pointMode == 'start') {
this.startPoint = [x y];
this.pointMode = 'end';
} else if (this.pointMode == 'end') {
this.pointMode = 'start';
this.endPoint = [x y];
// do the drawing
if (this.mode == 'Line') {
this.drawLine(this.startPoint this.endPoint);
} else if (this.mode == 'Hollow Rectangle') {
this.drawHollowRectangle(this.startPoint this.endPoint);
} else if (this.mode == 'Filled Rectangle') {
this.drawFilledRectangle(this.startPoint this.endPoint);
} else if (this.mode == 'Hollow Circle') {
this.drawHollowCircle(this.startPoint this.endPoint);
} else if (this.mode == 'Filled Circle') {
this.drawFilledCircle(this.startPoint this.endPoint);
}
this.startPoint = null;
this.endPoint = null;
}
this.displayMessage();
}
drawLine(startPoint endPoint) {
this.ctx.beginPath();
this.ctx.moveTo(startPoint[0] startPoint[1]);
this.ctx.lineTo(endPoint[0] endPoint[1]);
this.ctx.stroke();
}
drawHollowRectangle(startPoint endPoint) {
this.ctx.beginPath();
this.ctx.strokeRect(
startPoint[0]
startPoint[1]
endPoint[0] - startPoint[0]
endPoint[1] - startPoint[1]
);
}
drawFilledRectangle(startPoint endPoint) {
this.ctx.beginPath();
this.ctx.fillRect(
startPoint[0]
startPoint[1]
endPoint[0] - startPoint[0]
endPoint[1] - startPoint[1]
);
}
drawHollowCircle(startPoint endPoint) {
const x = startPoint[0] - endPoint[0];
const y = startPoint[1] - endPoint[1];
const radius = Math.sqrt(x * x y * y);
this.ctx.beginPath();
this.ctx.arc(startPoint[0] startPoint[1] radius 0 2 * Math.PI false);
this.ctx.stroke();
}
drawFilledCircle(startPoint endPoint) {
const x = startPoint[0] - endPoint[0];
const y = startPoint[1] - endPoint[1];
const radius = Math.sqrt(x * x y * y);
this.ctx.beginPath();
this.ctx.arc(startPoint[0] startPoint[1] radius 0 2 * Math.PI false);
this.ctx.fill();
}
displayMode() {
const modeDiv = document.querySelector('#mode');
modeDiv.innerHTML = this.mode;
}
getModeType(mode) {
let type = '';
switch (mode) {
case 'Line':
type = 'line';
break;
case 'Hollow Rectangle':
case 'Filled Rectangle':
type = 'rectangle';
break;
case 'Hollow Circle':
case 'Filled Circle':
type = 'circle';
break;
}
return type;
}
displayMessage() {
const type = this.getModeType(this.mode);
const find = this.shapeMessages.find(m => m.type === type);
const msgDiv = document.querySelector('#message');
msgDiv.innerHTML = find[this.pointMode];
}
}
class Palette {
constructor() {
this.colors = [
['#000000' '#FFFFFF' '#7F7F7F' '#C3C3C3' '#880015' '#B97A57' '#ED1C24' '#FFAEC9' '#FF7F27' '#FFC90E']
['#FFF200' '#EFE4B0' '#22B14C' '#B5E61D' '#00A2E8' '#99D9EA' '#3F48CC' '#7092BE' '#A349A4' '#C8BFE7']
];
}
draw(canvas) {
const row1 = document.querySelectorAll('#row-1 .palette');
const row2 = document.querySelectorAll('#row-2 .palette');
row1.forEach((div idx) => {
div.style.backgroundColor = this.colors[0][idx];
div.onclick = e => canvas.setColor(this.colors[0][idx]);
});
row2.forEach((div idx) => {
div.style.backgroundColor = this.colors[1][idx];
div.onclick = e => canvas.setColor(this.colors[1][idx]);
});
}
}
我们正在接近添加套接字和后端功能以使应用程序具有协作性。 不过,接下来,我们将添加几个按钮来清除绘图并将其下载为图像文件。 我们将在下一篇文章中介绍。