实现的效果:
1.选择直线的起始或结束节点时,可以移动直线
2.当按住Ctrl后,再点击直线,可以创建出一个折线,如下图:
基本思路:
当直线被MouseDown后,判断选择的位置,如果是起始或结束节点的范围,则移动该节点,重新绘制直线。
如果有Ctrl按下,则在MouseMove事件中根据鼠标的位置,绘制出一条拆线,当MouseUp后,添加一个新的UIComponent把那条拆线重新绘制一次。
UXLine 简单地封装了画直线的类
package cn.ui { import flash.events.MouseEvent; import flash.geom.Point; import flash.utils.Dictionary; import mx.collections.ArrayCollection; import mx.core.UIComponent; import spark.filters.GlowFilter; /** * 线条类 * [email protected] **/ public class UXLine extends UIComponent { /** 常量0,未选择节点 */ public static const SELECTED_NONE_NODE:int = 0; /** 常量1,选择了From节点 */ public static const SELECTED_FROM_NODE:int = 1; /** 常量2,选择了To节点 */ public static const SELECTED_TO_NODE:int = 2; /** 选择了From 或To节点 LineMove的事件 */ public static const EVENT_LINE_NODE_MOVE:String = "EVENT_LINE_NODE_MOVE"; /** 拆线形成拆线的事件 */ public static const EVENT_LINE_BROKEN:String = "EVENT_LINE_BROKEN"; private var _xFrom:Number = 0; private var _xTo:Number = 0; private var _yFrom:Number = 0; private var _yTo:Number = 0; private var _selectedNode:int = SELECTED_NONE_NODE; private var _lineStyle:int = 2; private var _lineColor:uint = 0x0099ff; // 关联的线条 //private var _linkLines:ArrayCollection = new ArrayCollection; private var _linkLines:Dictionary = new Dictionary; /** 有效范围(半径) */ private const _EFFECT_RANGE:int = 3; public function UXLine() { super(); this._selectedNode = SELECTED_NONE_NODE; this.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); } /** 0 未选择,1选择了起始节点,2选择了结束节点 */ public function get selectedNode():int { return _selectedNode; } public function get yTo():Number { return _yTo; } public function set yTo(value:Number):void { _yTo = value; } public function get yFrom():Number { return _yFrom; } public function set yFrom(value:Number):void { _yFrom = value; } public function get xTo():Number { return _xTo; } public function set xTo(value:Number):void { _xTo = value; } public function get xFrom():Number { return _xFrom; } public function set xFrom(value:Number):void { _xFrom = value; } public function set lineStyle(value:int):void{ _lineStyle = value; } public function get lineStyle():int{ return _lineStyle; } public function set lineColor(value:uint):void{ _lineColor = value } public function get lineColor():uint{ return _lineColor; } /** * 绑定MouseDown事件 **/ private function onMouseDown(event:MouseEvent):void{ var eventType:String = EVENT_LINE_NODE_MOVE;// 线节点移动 this._selectedNode = SELECTED_NONE_NODE; // 判断鼠标的位置,如果位于起点或终点的一个有效区域范围内时,触发可移动事件 var mousePoint:Point = new Point(event.localX, event.localY); if(isInRange(_xFrom, _yFrom, event.localX, event.localY)){ this._selectedNode = SELECTED_FROM_NODE; }else if(isInRange(_xTo, _yTo, event.localX, event.localY)){ this._selectedNode = SELECTED_TO_NODE; }else if(event.ctrlKey){ // 生成新的线条的时候 eventType = EVENT_LINE_BROKEN; }else{ // 其他事件,由外层容器去实现 return; } // 阻击冒泡,并派发自己的事件 event.stopPropagation(); var newEvent:MouseEvent = new MouseEvent(eventType, true); newEvent.localX = event.stageX; newEvent.localY = event.stageY; dispatchEvent(newEvent); } /** * 检测某一点是否在另一个点的范围内 * @param x * @param y * @param testX 测试点X坐标 * @param testY 测试点Y坐标 * @param effectRange 有效半径 **/ private function isInRange(x:int, y:int, testX:int, testY:int, effectRange:int=_EFFECT_RANGE):Boolean{ return ((x - effectRange <= testX) && (x + effectRange >= testX) && (y - effectRange <= testY) && (y + effectRange >= testY)); } /** * 绘制直线 **/ public function draw():void{ this.graphics.clear(); this.graphics.lineStyle(_lineStyle,_lineColor,1); this.graphics.moveTo(_xFrom, _yFrom); this.graphics.lineTo(_xTo, _yTo); } /** * 绘制拆线 **/ public function draw2Line(pointX:int, pointY:int):void{ this.graphics.clear(); this.graphics.lineStyle(_lineStyle,_lineColor,1); this.graphics.moveTo(_xFrom, _yFrom); this.graphics.lineTo(pointX, pointY); this.graphics.lineTo(_xTo, _yTo); } } }
不废话,直接上MXML
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600" creationComplete="onLoad();" > <fx:Style source="BrokenLineDemo.css"/> <fx:Script> <![CDATA[ import cn.ui.UXCanvas; import cn.ui.UXLine; import flash.display.Sprite; import flash.geom.Point; import mx.core.DragSource; import mx.core.UIComponent; import mx.events.DragEvent; import mx.events.MenuEvent; import mx.managers.DragManager; // 最原始的线条对象 private var uxLine:UXLine = new UXLine; // 拖动初始点 private var dragPoint:Point = new Point; // 当前线条对象 private var currentLine:UXLine = null; private function onLoad():void{ // 初始化表格线 initGridLine(mxCanvas); // 初始化uxLine uxLine.xFrom = 50; uxLine.yFrom = 50; uxLine.xTo = 250; uxLine.yTo = 250; uxLine.draw(); mxCanvas.addElement(uxLine); mxCanvas.addEventListener(UXLine.EVENT_LINE_BROKEN, onLineBroken); mxCanvas.addEventListener(UXLine.EVENT_LINE_NODE_MOVE, onLineNodeMove); } // 线条分割事件 private function onLineBroken(event:MouseEvent):void{ if(event.target is UXLine){ currentLine = UXLine(event.target); systemManager.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove4LB, true); systemManager.addEventListener(MouseEvent.MOUSE_UP, onMouseUp4LB, true); } } // 线条分割选择点后 开始移动 private function onMouseMove4LB(event:MouseEvent):void{ event.stopImmediatePropagation(); if(currentLine != null){ currentLine.draw2Line(event.localX, event.localY); } } // 线条分割选择点后 移动完成 private function onMouseUp4LB(event:MouseEvent):void{ event.stopImmediatePropagation(); systemManager.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove4LB, true); systemManager.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp4LB, true ); var pointX:Number = event.localX; var pointY:Number = event.localY; // 把线条拆分开,即新增加一条线来实现 if(currentLine != null){ var newLine:UXLine = new UXLine; newLine.xFrom = currentLine.xFrom; newLine.yFrom = currentLine.yFrom; newLine.xTo = pointX; newLine.yTo = pointY; newLine.draw(); mxCanvas.addElement(newLine); currentLine.graphics.clear(); currentLine.xFrom = pointX; currentLine.yFrom = pointY; currentLine.draw(); //mxCanvas.removeElement(currentLine); } currentLine = null; } // 选择线起点或终点的移动事件 private function onLineNodeMove(event:MouseEvent):void{ if(event.target is UXLine){ currentLine = UXLine(event.target); systemManager.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove4Line, true); systemManager.addEventListener(MouseEvent.MOUSE_UP, onMouseUp4Line, true); } } // 选择线起点或终点的移动事件 private function onMouseMove4Line(event:MouseEvent):void{ event.stopImmediatePropagation(); if(currentLine != null){ if(currentLine.selectedNode == UXLine.SELECTED_FROM_NODE){ currentLine.xFrom = event.localX; currentLine.yFrom = event.localY; }else if(currentLine.selectedNode == UXLine.SELECTED_TO_NODE){ currentLine.xTo = event.localX; currentLine.yTo = event.localY; } currentLine.draw(); } } // 释放移动动作 private function onMouseUp4Line(event:MouseEvent):void{ event.stopImmediatePropagation(); systemManager.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove4Line, true); systemManager.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp4Line, true ); currentLine = null; } /** * 初始化表格线条 **/ private function initGridLine(uiComponent:UIComponent):void{ const LINE_LENGTH:int = 4; const LINE_MARGIN:int = 2; const LINE_OFFSET:int = 24; var rowIndex:int; var colIndex:int; var rowCount:int = Math.floor(this.height / LINE_OFFSET); var colCount:int = Math.floor(this.width / LINE_OFFSET); var startX:Number; var startY:Number; var endX:Number; var endY:Number; var lineIndex:int; var lineCount:int; var lineLength:int = (LINE_LENGTH + LINE_MARGIN); var thickness:Number = 0.5; var lineColor:uint = 0x000000; uiComponent.graphics.clear(); uiComponent.graphics.beginFill(0xFFFFFF); uiComponent.graphics.drawRect(0, 0, this.width, this.height); uiComponent.graphics.endFill(); uiComponent.graphics.lineStyle(thickness, lineColor, 0.5); // 画出横线 lineCount = Math.floor(uiComponent.width / lineLength); for(rowIndex = 1; rowIndex <= rowCount; rowIndex ++){ startY = endY = rowIndex * LINE_OFFSET; if(rowIndex%2 == 0){ thickness = 1; uiComponent.graphics.lineStyle(thickness, lineColor, 0.3); }else{ thickness = 0.5; uiComponent.graphics.lineStyle(thickness, lineColor, 0.1); } for(lineIndex = 0; lineIndex < lineCount; lineIndex ++){ startX = lineIndex * lineLength; endX = startX + LINE_LENGTH; uiComponent.graphics.moveTo(startX, startY); uiComponent.graphics.lineTo(endX, endY); } } // 画出竖线 lineCount = Math.floor(uiComponent.height / lineLength); for(colIndex = 1; colIndex <= colCount; colIndex ++){ startX = endX = colIndex * LINE_OFFSET; if(colIndex % 2 == 0){ thickness = 1; uiComponent.graphics.lineStyle(thickness, lineColor, 0.3); }else{ thickness = 0.5; uiComponent.graphics.lineStyle(thickness, lineColor, 0.1); } for(lineIndex = 0; lineIndex < lineCount; lineIndex ++){ startY = lineIndex * lineLength; endY = startY + LINE_LENGTH; uiComponent.graphics.moveTo(startX, startY); uiComponent.graphics.lineTo(endX, endY); } } } ]]> </fx:Script> <mx:Canvas id="mxCanvas" height="100%" width="100%"> <s:Label text="提示:按住Ctrl再选择线条画折线,选择线的两端可移动线条" toolTip="按住Ctrl再选择线条画折线" x="278" y="11"/> </mx:Canvas> </s:Application>