绘制几何图形
拿矩形举个例子吧,当鼠标在canvas按下的时候你记录下当前的坐标点,当鼠标拖动一段距离(但是不能拖出canvas),然后松开的时候再记录一个坐标,就可以计算出width和height了,然后你就得到了画一个矩形所需要的所有参数。你当然可以像铅笔绘图一样,保存进去4个数组,分别记录beginX, beginY, width, height。但是还有好多别的图形要做,每个都弄一个数组,用起来总归有点冗杂。而且几何图形和铅笔的区别在于,几何图形是以个存在的,比如:(50, 50)处开始,有一个宽为200px,高为100px的矩形,在(250, 300)处为圆心,有一个半径83px的圆……这时我们就想,可不可以用一点OO(面向对象)的思想呢,为每一个形状设计一个类,类里面有该形状所必须的参数作为成员变量,然后这样的类都有一个方法叫draw();可以把这个几何图形画出来。所幸js还是支持面向对象的,虽然实现的方式有点让我有点感觉不是很直观。
声明一个Rect类的js代码:
function Rect(){
this.beginX = 0;
this.beginY = 0;
this.endX = 0;
this.endY = 0;
this.color = "#000000";
//绘制方法
this.draw = function(){
context.beginPath();
context.strokeStyle = this.color;
context.rect(this.beginX,his.beginY, this.endX-this.beginX, this.endY-this.beginY);
context.stroke();
};
}
该类拥有五个成员变量,beginX,beginY,endX,endY, color,和一个方法draw().。这样我们每一次鼠标事件(mousedown, mosemove, mouseup..)的时候,就不是存进数组里了,而是声明一个Rect的实例,放进一个专门存放几何图形的shapes数组里。然后redraw的时候除了画出clickX,clickY所描绘的线,还画出shapes数组里的每一个几何图形,具体就是调用它们的draw()方法。
其他的直线、多边形和圆可以类推。
然后就牵扯到不同绘图工具之间的转换,可以声明一个变量叫curTool,默认为”pencil”,然后你给个方法改变它,支持”rectangular”,”circle”,”line”等选项。然后在鼠标事件的监听里面加上一层判断curTool的代码。
下面是实现了铅笔和矩形绘图并且可以换一些颜色的代码:
shape.html:
<!DOCTYPE html>
<html>
<head>
<title>Simple Demo</title>
<style type = "text/css">
body{
margin: 0px;
padding: 0px;
}
#myCanvas {
border: 1px solid #9C9898;
}
</style>
</head>
<body>
<canvas id = "myCanvas" width = "800px" height = "500px"></canvas>
<div id = "colorBox">
<button id = "black">黑</button>
<button id = "red">红</button>
<button id = "yellow">黄</button>
<button id = "blue">蓝</button>
</div>
<div id = "toolBox">
<button id = "pencil">铅笔</button>
<button id = "line">直线</button>
<button id = "rect">矩形</button>
<button id = "circle">圆形</button>
<button id = "multiLine">多边形</button>
</div>
<div id = "debug">
</div>
<script type = "text/javascript" language="javascript" src = "main.js"></script>
</body>
</html>
main.js:
var canvas, context,
isPainting,
clickX, clickY, clickDrag, clickColor,
curColor, curTool, //当前的颜色和所使用的工具
shapes, //存储几何形状
tempX1,tempX2,tempY1,tempY2;//临时工
var mouseDown = function(e){
var mouseX = e.pageX;
var mouseY = e.pageY;
isPainting = true;
if(curTool == "pencil"){
addClick(mouseX, mouseY, false);
redraw();
}else if(curTool == "rect"){
tempX1 = mouseX;
tempY1 = mouseY;
}
}
var mouseUp = function(e){
isPainting = false;
if(curTool == "rect"){
var mouseX = e.pageX;
var mouseY = e.pageY;
tempX2 = mouseX;
tempY2 = mouseY;
var aRect = new Rect();
aRect.beginX = tempX1;
aRect.beginY = tempY1;
aRect.endX = tempX2;
aRect.endY = tempY2;
aRect.color = curColor;
shapes.push(aRect);
}
redraw();
}
var mouseMove = function(e){
if(isPainting){
var mouseX = e.pageX;
var mouseY = e.pageY;
if(curTool == "pencil"){
addClick(mouseX, mouseY, true);
redraw();
}else if(curTool == "rect"){
redraw();
context.beginPath();
context.strokeStyle = curColor;
context.rect(tempX1,tempY1, mouseX-tempX1, mouseY-tempY1);
context.stroke();
}
}
}
var mouseOut = function(e){
isPainting = false;
}
function addClick(mouseX, mouseY, isDragging){
clickX.push(mouseX);
clickY.push(mouseY);
clickDrag.push(isDragging);
clickColor.push(curColor);
}
//声明矩形类
function Rect(){
this.beginX = 0;
this.beginY = 0;
this.endX = 0;
this.endY = 0;
this.color = "#000000";
//绘制方法
this.draw = function(){
context.beginPath();
context.strokeStyle = this.color;
context.rect(this.beginX, this.beginY, this.endX-this.beginX, this.endY-this.beginY);
context.stroke();
};
}
//重绘
function redraw(){
context.clearRect (0 , 0, canvas.width , canvas.height );
var numOfPts = clickX.length;
var numOfShapes = shapes.length;
for(var i=0; i<numOfPts; i++){
context.beginPath();
context.strokeStyle = clickColor[i];
if(clickDrag[i]){
context.moveTo(clickX[i-1], clickY[i-1]);
context.lineTo(clickX[i], clickY[i]);
}else{
context.arc(clickX[i], clickY[i], 0.5, 0, 2*Math.PI, true);
}
context.closePath();
context.stroke();
}
for(var j=0; j<numOfShapes; j++){
shapes[j].draw();
}
}
//初始化
function init(){
canvas = document.getElementById("myCanvas");
context = canvas.getContext("2d");
isPainting = false;
clickX = new Array();
clickY = new Array();
clickDrag = new Array();
clickColor = new Array();
shapes = new Array();
curColor = "#000000";
curTool = "pencil";
//鼠标事件
canvas.onmousedown = mouseDown;
canvas.onmouseup = mouseUp;
canvas.onmousemove = mouseMove;
canvas.onmouseout = mouseOut;
var black = document.getElementById("black");
black.onclick = function(){
curColor = "#000000";
}
var red = document.getElementById("red");
red.onclick = function(){
curColor = "#ff0000";
}
var yellow = document.getElementById("yellow");
yellow.onclick = function(){
curColor = "#ffff37";
}
var blue = document.getElementById("blue");
blue.onclick = function(){
curColor = "#2894ff";
}
var pencil = document.getElementById("pencil");
pencil.onclick = function(){
curTool = "pencil";
}
var rect = document.getElementById("rect");
rect.onclick = function(){
curTool = "rect";
}
}
window.onload = init();
想加上更多的颜色和更多的形状,就是简单地重复了。想把界面做的好看点?那也不是本文要探讨的主要内容了。至此我们已经实现了一开始所说的全部功能。
-end
小插曲:今天在写这个代码的时候(文章里的代码都是我在写博客时重写的,不是从已有项目里直接粘贴下来的哦~),遇到了一个很囧的问题:我的canvas的border莫名其妙不见了。开chrome的审查元素发现canvas压根就没有css格式,百思不得其解后,我就和上一篇博文里的代码一字一行的比较,都准备去stackoverflow上面去问问题了的时候才发现原来是我的代码写成了这样:#myCanvas{border: 1px solid "#9c9898";} 大家一眼看上去是不是差不多呢?其实是颜色部分多写了引号。然后我回想到自己之前把strokeStyle写成了strokeType并抓狂好久的事情,不禁感叹,细节还是很值得用心注意的。bug之所以成为bug,就是因为它本身就如同小虫一般细微而不易察觉吧。但是须知千里之堤溃于蚁穴,时刻注意对细节的推敲,才是一个程序员的良好修养。