鼠标控制画曲线及扇形问题的分析(一)
独自行走
知识点:鼠标关联动作的识别与分析,程序逻辑的逐步实现,画线及填充命令的应用。
画直线、方框、整圆的方法,在我的《Flash 画线功能的讨论》一文中已经解决了,现在有一个新问题,怎样来画曲线和扇形呢?
这两个问题与前面的问题看起来很近似,但程序逻辑上要更复杂一些。在画直线的过程中,我们只要不停的清除、再重新画线,鼠标抬起时,就结束画线,就可以了。也就是说在鼠标按下、拖动、抬起这一次过程中,就可以完成任务。但是在画曲线时,我们在第一轮的鼠标按下、拖动、抬起时,只能先确定一个起始点、终结点,第二轮的鼠标操作时,才能完成把直线拉开成曲线的步骤。而后,在下次鼠标操作之前,鼠标应该是不对屏幕进行操作的。
从前面的对比中我们可以看到,鼠标需要进行两轮操作才能实现画一次曲线,加上鼠标画完线后,在屏幕上空跑的状态,可以将这些情形汇总成三个状态,如果用一个数值变量来表示它可以是myStep = 0,1,2,如果用两个逻辑变量来表示(可以组合成四种状态),它可以是 isA&&isB, !isA&&isB, !isA&&!isB,在这里我采用数值变量来表示三种状态。
很容易想到的一个结构就是switch(){}结构,我们可以在鼠标的各个事件中都加入这一结构,优化代码的问题我们暂时先不考虑。
示意代码如下(依旧是用侦听器模式来画线):
var myStep:Number = 2;
var myListener:Object = new Object();
myListener.onMouseDown = function() {
switch (myStep) {
case 0 :
//记录起点位置
break;
case 1 :
//记录控制点位置
break;
case 2 :
//记录起点位置
myStep = 0;
break;
default :
}
};
myListener.onMouseMove = function() {
switch (myStep) {
case 0 :
//从起点画直线到鼠标当前位置
break;
case 1 :
//从起点画曲线到结束点,鼠标当前位置为控制点的位置
break;
case 2 :
//什么都不做
default :
}
};
myListener.onMouseUp = function() {
switch (myStep) {
case 0 :
//记录结束点位置
myStep++;
break;
case 1 :
//结束画曲线
myStep++;
break;
case 2 :
//什么都不做
default :
}
};
Mouse.addListener(myListener);
如果myStep初始值为0,那么一开始,鼠标运动时,系统会自动的画起线来,所以,我们让myStep的初始值为2,按下后变成0,这样做之后,鼠标按下时永远都不会进入myStep为0的状态,这对我们的程序没有影响,属于代码优化的问题,所以也暂时不去考虑,总之每个事件中都加入switch结构,便于我们对不同状态的理解。
现在加入画直线的代码,完成后效果如下:
this.createEmptyMovieClip("circle_mc", this.getNextHighestDepth());
circle_mc._x = 200;
circle_mc._y = 208;
var cur_mc = circle_mc;
//
var start_pt:Object = new Object();
var end_pt:Object = new Object();
//
var myStep:Number = 2;
var myListener:Object = new Object();
myListener.onMouseDown = function() {
switch (myStep) {
case 0 :
//记录起点位置
start_pt._x = cur_mc._xmouse;
start_pt._y = cur_mc._ymouse;
break;
case 1 :
//记录控制点位置
break;
case 2 :
//记录起点位置
start_pt._x = cur_mc._xmouse;
start_pt._y = cur_mc._ymouse;
myStep = 0;
break;
default :
}
};
myListener.onMouseMove = function() {
switch (myStep) {
case 0 :
//从起点画直线到鼠标当前位置
cur_mc.clear();
cur_mc.lineStyle(1, 0x00ff00, 80);
cur_mc.moveTo(start_pt._x, start_pt._y);
cur_mc.lineTo(cur_mc._xmouse, cur_mc._ymouse);
break;
case 1 :
//从起点画曲线到结束点,鼠标当前位置为控制点的位置
break;
default :
}
};
myListener.onMouseUp = function() {
switch (myStep) {
case 0 :
//记录结束点位置
end_pt._x = cur_mc._xmouse;
end_pt._y = cur_mc._ymouse;
myStep++;
break;
case 1 :
//结束画曲线
myStep++;
break;
default :
}
};
Mouse.addListener(myListener);
可以看到现在实现的效果是按下画线,抬起、再按下时,什么也没做,第三轮按下时,画一个直线,这让你很容易想到第二轮的画线时该画曲线了。OK加入曲线的代码。
直接加入画曲线的代码:
cur_mc.clear();
cur_mc.lineStyle(1, 0x00ff00, 80);
cur_mc.moveTo(start_pt._x, start_pt._y);
cur_mc.curveTo(cur_mc._xmouse, cur_mc._ymouse, end_pt._x, end_pt._y);
可以发现,第一轮画完直线后,鼠标再移动时,直接就变成曲线了,还没等到第二轮的开始,这问题怎么解决呢?再加个变量canDrawCur,初始值为false,表示不允许画曲线,第二轮按下鼠标时,才允许画线令canDrawCur= true,第二轮抬起时,停止画曲线 canDrawCur = false。这样以来,画曲线的动作就完成了。
为了让曲线的效果更好看,还可以在移动曲线的时候,画两个辅助线,抬起时清屏再画一次,起到隐藏辅助线的效果。完成的代码如下:
this.createEmptyMovieClip("circle_mc", this.getNextHighestDepth());
circle_mc._x = 0;
circle_mc._y = 0;
var cur_mc = circle_mc;
var canDrawCur = false;
//
var start_pt:Object = new Object();
var end_pt:Object = new Object();
//
var myStep:Number = 2;
var myListener:Object = new Object();
myListener.onMouseDown = function() {
switch (myStep) {
case 0 :
//记录起点位置
//start_pt._x = cur_mc._xmouse;
//start_pt._y = cur_mc._ymouse;
break;
case 1 :
//记录控制点位置
canDrawCur = true;
break;
case 2 :
//记录起点位置
start_pt._x = cur_mc._xmouse;
start_pt._y = cur_mc._ymouse;
myStep = 0;
break;
default :
}
};
myListener.onMouseMove = function() {
switch (myStep) {
case 0 :
//从起点画直线到鼠标当前位置
cur_mc.clear();
cur_mc.lineStyle(1, 0x00ff00, 80);
cur_mc.moveTo(start_pt._x, start_pt._y);
cur_mc.lineTo(cur_mc._xmouse, cur_mc._ymouse);
break;
case 1 :
//从起点画曲线到结束点,鼠标当前位置为控制点的位置
if (canDrawCur) {
cur_mc.clear();
cur_mc.lineStyle(1, 0x00ff00, 80);
cur_mc.moveTo(start_pt._x, start_pt._y);
cur_mc.curveTo(cur_mc._xmouse, cur_mc._ymouse, end_pt._x, end_pt._y);
//添加辅助线
cur_mc.lineStyle(1, 0x000000, 50);
cur_mc.lineTo(cur_mc._xmouse, cur_mc._ymouse);
cur_mc.lineTo(start_pt._x, start_pt._y);
}
break;
default :
}
};
myListener.onMouseUp = function() {
switch (myStep) {
case 0 :
//记录结束点位置
end_pt._x = cur_mc._xmouse;
end_pt._y = cur_mc._ymouse;
myStep++;
break;
case 1 :
//结束画曲线
//重新画曲线,起到消除辅助线的目的。
cur_mc.clear();
cur_mc.lineStyle(1, 0x00ff00, 80);
cur_mc.moveTo(start_pt._x, start_pt._y);
cur_mc.curveTo(cur_mc._xmouse, cur_mc._ymouse, end_pt._x, end_pt._y);
canDrawCur = false;
myStep++;
break;
default :
}
};
Mouse.addListener(myListener);
这里涉及的代码结构或许显得有些啰嗦,而且从完成后来看流程有些模糊,但这是实际测试后的结果,在我们开始设计时,只能够大致设计一个框架,最后再根据实际的效果在细节上下点功夫,最终的代码还有简化的余地,但这不是我在这里想讨论的,留给大家来处理。
尽管我尽量想用switch结构来避免使用逻辑变量,但在前面的代码里还是用到了一个逻辑变量canDrawCur,分析原因,这个逻辑变量是可以省略的,因为我将鼠标空跑当成一个状态来理解,实际上却有两种状态,其一是画完直线后,鼠标在下次按下之前需要空跑一下,其二,画完曲线后,鼠标需要空跑一下,也就是说总共有四种状态,再将代码改一下,结果如下:
this.createEmptyMovieClip("circle_mc", this.getNextHighestDepth());
circle_mc._x = 0;
circle_mc._y = 0;
var cur_mc = circle_mc;
//
var start_pt:Object = new Object();
var end_pt:Object = new Object();
//
var myStep:Number = 3;
var myListener:Object = new Object();
myListener.onMouseDown = function() {
switch (myStep) {
case 0 :
break;
case 1 :
//第二次点击发生时,这里只起到一个中转的作用。
myStep++;
break;
case 2 :
break;
case 3 :
//首次点击发生时,记录起点位置
start_pt._x = cur_mc._xmouse;
start_pt._y = cur_mc._ymouse;
myStep = 0;
break;
default :
}
};
myListener.onMouseMove = function() {
switch (myStep) {
case 0 :
//从起点画直线到鼠标当前位置
cur_mc.clear();
cur_mc.lineStyle(1, 0x00ff00, 80);
cur_mc.moveTo(start_pt._x, start_pt._y);
cur_mc.lineTo(cur_mc._xmouse, cur_mc._ymouse);
break;
case 1 :
break;
case 2 :
//从起点画曲线到结束点,鼠标当前位置为控制点的位置
cur_mc.clear();
cur_mc.lineStyle(1, 0x00ff00, 80);
cur_mc.moveTo(start_pt._x, start_pt._y);
cur_mc.curveTo(cur_mc._xmouse, cur_mc._ymouse, end_pt._x, end_pt._y);
//添加辅助线
cur_mc.lineStyle(1, 0x000000, 50);
cur_mc.lineTo(cur_mc._xmouse, cur_mc._ymouse);
cur_mc.lineTo(start_pt._x, start_pt._y);
break;
default :
}
};
myListener.onMouseUp = function() {
switch (myStep) {
case 0 :
//第一轮画线结束,记录结束点位置
end_pt._x = cur_mc._xmouse;
end_pt._y = cur_mc._ymouse;
myStep++;
break;
case 1 :
break;
case 2 :
//第二轮画线结束,结束画曲线
//重新画曲线,起到消除辅助线的目的。
cur_mc.clear();
cur_mc.lineStyle(1, 0x00ff00, 80);
cur_mc.moveTo(start_pt._x, start_pt._y);
cur_mc.curveTo(cur_mc._xmouse, cur_mc._ymouse, end_pt._x, end_pt._y);
canDrawCur = false;
myStep++;
break;
default :
}
};
Mouse.addListener(myListener);
鼠标按下时共有四种状态可供选择,虽然对于我们这个例子,很多状态是用不着的,但这个结构却可以很容易的将连续的几步操作分隔开来,使我们能够很清楚的看到每一步该做什么,如用两个逻辑变量来控制实现画曲线的效果,那么代码的优化比较容易进行,因为两个逻辑变量之间有相关性,对它们的相关性的分析就可以将代码进一步简化,但从读代码的角度来看,却会增加一些额外的困难。