C-DRAWS画图程序(三):画图核心
发表于8年前(Dec 12, 2014 10:22:45 PM)  阅读 15837  评论 0
标签: c-draws DrawCanvas CGraphics
一个多月没有更新了,先说几句废话。之前病了一次,导致两周没有更新,然后开发上遇到困难,导致我对C-DRAWS失去了信心。最开始的设想是做个类似Windows画图样的程序,但是要比那个要强大,支持的样式要多,但开发过程中,发现并不是那么容易的一件事。
首先,画图方面,因为想增加线条样式功能,所以设想,用户可以选择实线以及虚线,乃至其他样式的线,但Flex本身不支持虚线的样式(至少现在未发现方便可行的方法),期间我研究过一阵图形算法,使用Bresenham画圆算法画了一种虚线圆,差强人意,还有椭圆,圆角矩形,箭头等暂时为做研究,这是一样耗时的工作,所以我暂时不想花费在上面。
其次,一个难点是剪切,对于图形的剪切,现在我的实现是用画布的颜色填充剪切的区域,这并不是一个好的处理方法,因为当初的设想,画布的颜色并不是固定的,是可以供用户设置的,但用户选择的画布是透明的时候,这种剪切就尴尬了。理想的做法当然是挖掉选中的部分,但是我没有发现可行的api。参考windows画图,我发现他的画图是不允许选择画布颜色的,那他的剪切是不是用画布颜色填充我就不得而知了。
其实本来是想把C-DRAWS当成一个产品来做,毕竟自己并未单独做过累世成熟的产品,但基于以上两点,我只好把C-DRAWS当成一个练习来做了。现在文件操作功能已经完成,画图功能能画矩形和圆,样式工具栏上没有按钮选择,但代码上我已经做了部分实现,可以说框架已经搭好了,所以我不准备再做其他画图功能了。末了我会附上工程源码,有不懂的可以留言问我。
废话讲完,开始今天的内容,这一节的内容主要是完成核心的画图。
上一节我们主要讲了布局,这一节我们先看下工具栏。众所周知,工具栏一般都是有一个选中状态的,实现方法有几种,一种是准备两组图片,一个是选中状态,一个是未选中状态,状态改变时替换相应的图片。还一种是改变按钮背景图片或颜色。在这里我们使用Flex中的Glow特效。
首先,我们看下源码(UtileBox.mxml):
看61-65行,定义的glowImage特效会给图片加上一圈蓝色的边框。unglowImage则会移去,这里我们移去边框特效并不是采用unglowImage实现。我们看rectangleDraw()方法,首先我们将graph的鼠标当前状态置换成画矩形的状态,然后对四个工具栏按钮的特效进行清空,再给画矩形的工具栏按钮加上glowImage特效,这样就实现了按钮选中状态。
我们再来看如何绘制矩形。首先我建立了一个CGraphics类,用来扩充系统的Graphics。现在CGraphics类实现了画点,画线,画矩形,画圆的方法,并支持虚线样式。所有的样式我封装到了自定义的Style类里面了。CGraphics还有很多地方要完善,在这里如何画图形的我就不讨论了,大家有兴趣的自己研究扩充。画图方面我是这样设计的,每种图形都是一个AS类,他们又个共同的父类Sketch,Sketch对图形的一些公共属性如,坐标,长宽,样式等做处理,每个图形有单独的实现类,只负责如何画该类图形。如Rectangle类里除构造函数外,只有一个draw方法,即画矩形的方法:this.cgraphics.clear();
this.cgraphics.drawRect(0, 0, width, height, style);
package com.cangzhitao.util
{
import com.cangzhitao.util.style.Style;
import flash.display.Graphics;
import flash.geom.Point;
//画图工具,扩充系统的Graphics
public class CGraphics
{
public function CGraphics()
{
}
//系统的Graphics
private var _graphics:Graphics;
private var _defaultStyle:Style = new Style();
public function get graphics():Graphics {
return this._graphics;
}
public function set graphics(value:Graphics):void {
this._graphics = value;
}
//画矩形
public function drawRect(x:Number, y:Number, width:Number, height:Number, style:Style=null):void {
if(style==null) {
style = _defaultStyle;
}
//画虚线矩形
if(Style.DASHED==style.lineStyle) {
this.lineStyle(style.thickness, style.color, 0);
this.beginFill(style.fillColor, style.fillAlpha);
this.graphics.drawRect(x, y, width, height);
this.endFill();
this.lineStyle(style.thickness, style.color, style.alpha);
this.graphics.moveTo(x, y);
this.drawDashedLine(new Point(x, y), new Point(x+width, y), style.solidLength, style.brokenLength);
this.drawDashedLine(new Point(x+width, y), new Point(x+width, y+height), style.solidLength, style.brokenLength);
this.drawDashedLine(new Point(x+width, y+height), new Point(x, y+height), style.solidLength, style.brokenLength);
this.drawDashedLine(new Point(x, y+height), new Point(x, y), style.solidLength, style.brokenLength);
//实线矩形
} else {
this.lineStyle(style.thickness, style.color, style.alpha);
this.beginFill(style.fillColor, style.fillAlpha);
this.graphics.drawRect(x, y, width, height);
this.endFill();
}
}
//画线
public function lineTo(startX:Number, startY:Number, endX:Number, endY:Number, style:Style=null):void {
if(style==null) {
style = _defaultStyle;
}
if(Style.DASHED==style.lineStyle) {
this.drawDashedLine(new Point(startX, startY), new Point(endX, endY), style.solidLength, style.brokenLength);
} else {
this.graphics.moveTo(startX, startY);
this.graphics.lineTo(endX, endY);
}
}
//画虚线,参数,起始坐标,实线长度,空格长度
public function drawDashedLine(fP:Point, tP:Point, solidLength:Number=10, brokenLength:Number=3):void {
var g:Graphics=this.graphics;
if(!fP){
fP=new Point(0,0);
}
if(!tP){
tP=new Point(0,0);
}
var lineAngle:Number;
lineAngle = Math.atan2(tP.y - fP.y,tP.x - fP.x);
var xSolidLength:Number=solidLength*Math.cos(lineAngle);
var ySolidLength:Number=solidLength*Math.sin(lineAngle);
var xBrokenLength:Number=brokenLength*Math.cos(lineAngle);
var yBrokenLength:Number=brokenLength*Math.sin(lineAngle);
var forwardFlag:Boolean=true;
if(tP.x<fP.x){
forwardFlag=false;
}
var tempP:Point=new Point(fP.x,fP.y);
var tempToP:Point=new Point(tP.x,tP.y);
var lineType:String="solid";
if(fP.x!=tP.x){
while(checkIn(tempP.x,tP.x,forwardFlag)){
g.beginFill(0x555555);
if(lineType=="solid"){
tempToP.x=tempP.x+xSolidLength;
tempToP.y=tempP.y+ySolidLength;
if(!checkIn(tempToP.x,tP.x,forwardFlag)){
tempToP.x = tP.x;
tempToP.y = tP.y;
}
g.moveTo(tempP.x, tempP.y);
g.lineTo(tempToP.x, tempToP.y);
lineType="space";
}else{
tempToP.x=tempP.x+xBrokenLength;
tempToP.y=tempP.y+yBrokenLength;
g.moveTo(tempToP.x, tempToP.y);
lineType="solid";
}
tempP.x=tempToP.x;
tempP.y=tempToP.y;
g.endFill();
}
}else {
if(tP.y < fP.y){
forwardFlag=false;
}
while(checkIn(tempP.y,tP.y,forwardFlag)){
g.beginFill(0x555555);
if(lineType=="solid"){
tempToP.x=tempP.x+xSolidLength;
tempToP.y=tempP.y+ySolidLength;
if(!checkIn(tempToP.y,tP.y,forwardFlag)){
tempToP.x = tP.x;
tempToP.y = tP.y;
}
g.moveTo(tempP.x, tempP.y);
g.lineTo(tempToP.x, tempToP.y);
lineType="space";
}else{
tempToP.x=tempP.x+xBrokenLength;
tempToP.y=tempP.y+yBrokenLength;
g.moveTo(tempToP.x, tempToP.y);
lineType="solid";
}
tempP.x=tempToP.x;
tempP.y=tempToP.y;
g.endFill();
}
}
}
public function clear():void {
this.graphics.clear();
}
public function lineStyle(thickness:Number = NaN, color:uint = 0, alpha:Number = 1.0):void {
this.graphics.lineStyle(thickness, color, alpha);
}
public function beginFill(color:uint, alpha:Number = 1.0):void {
this.graphics.beginFill(color, alpha);
}
public function endFill():void {
this.graphics.endFill();
}
//判断是否还在开始点和结束点之间
private function checkIn(x:Number,toX:Number,forwardFlag:Boolean):Boolean{
var result:Boolean=true;
if(forwardFlag){//向前
if(x<toX){
}else{
result=false;
}
}else{
if(x>toX){
}else{
result=false;
}
}
return result;
}
public function drawCircle(x:Number, y:Number, radius:Number, style:Style=null):void {
if(style==null) {
style = _defaultStyle;
}
if(Style.DASHED== style.lineStyle) {
//填充
this.lineStyle(style.thickness, style.color, 0);
this.beginFill(style.fillColor, style.fillAlpha);
this.graphics.drawCircle(x, y, radius);
this.endFill();
//bresenham画圆
var linkPoints:Array = new Array();
var p:Number;
var rx:Number = 0;
var ry:Number = radius;
p = 3 - 2*radius;
//求1/8圆各点
while(rx<ry) {
//判断哪些点需要画进去
var length:Number = style.solidLength+style.brokenLength;
if(rx%length<style.solidLength) {
linkPoints.push(new Point(rx, ry));
}
if(p<0) {
p = p + 4*rx +6;
} else {
p = p + 4*(rx-ry) + 10;
ry -= 1;
}
rx += 1;
}
if(rx == ry) {
linkPoints.push(new Point(rx, ry));
}
for(var i:int=0;i<linkPoints.length;i++) {
var point:Point = linkPoints[i] as Point;
this.drawPoint(point.x+x,point.y+y,style);
this.drawPoint(point.x*(-1)+x, point.y+y,style);
this.drawPoint(point.x*(-1)+x, point.y*(-1)+y,style);
this.drawPoint(point.x+x, point.y*(-1)+y,style);
this.drawPoint(point.y+x, point.x+y,style);
this.drawPoint(point.y*(-1)+x, point.x*(-1)+y,style);
this.drawPoint(point.y+x, point.x*(-1)+y,style);
this.drawPoint(point.y*(-1)+x, point.x+y,style);
}
} else {
this.lineStyle(style.thickness, style.color, style.alpha);
this.beginFill(style.fillColor, style.fillAlpha);
this.graphics.drawCircle(x, y, radius);
this.endFill();
}
}
public function drawPoint(x:Number, y:Number, style:Style=null):void {
if(style==null) {
style = _defaultStyle;
}
this.graphics.lineStyle(style.thickness, style.color, style.alpha);
this.graphics.moveTo(x-1, y-1);
this.graphics.lineTo(x, y);
/* this.graphics.drawCircle(x, y, 1) */
}
}
}
我们如何通过鼠标操作,来获得图形的相关操作呢?首先我们必须在画布(DrawCanvas)里监听鼠标事件,但鼠标按下时,我们必须记下,当前鼠标的坐标,作为图形的开始位置,然后开始监听鼠标移动事件(注意,鼠标移动事件不能一开始就监听,必须在鼠标按下时才开始添加监听),移动过程中, 记下鼠标的当前位置,作为图形的结束位置,开始画图,因为鼠标移动是个连续的过程,所以这样做的话,画图也会是一个连续的过程,能实时展现出来。
画图就讲到这,很笼统,需要大家结合代码看。下一节我打算结束他,主要讲下复制,粘贴,打开,保存。