需求:
公司为了使得和客户领导签字的时候记录下来,签字过程,可以以后动态回放演示,最好是gif图片,在网页上也容易展示,文件也小。
解决过程:
始我们去寻找各种app,最终也没有找到合适的,后来我在flash8上看到有个在线的手绘板,很适合我们的需求,但是我尝试了很多次
绘画完成后,导出的是swf文件,我想从这个swf文件中抽出图片,最终发现这个导出的swf里面根本不存在我绘画的记录过程。
网上确实有手绘板的代码,但是没有动态回放的功能及到处gif图片的功能,还是不满足我们需求,
索性自己写了一个flash小程序实现手绘画板及动态回放功能。我把具体的思路及详细代码都贴出来,
希望能给有用到的人带来方便。
整体思路:
- 编写一个flash程序,实现基本手绘板功能
- 在基础上实现回放功能(这里是难点)
- 创建一个asp.ne站点,写个一般处理程序GetSignImg.ashx,用于存储flash发布时候动态创建的jpg图片
- 当swf程序发布后点击下载的时候,后台程序将所有的jpg图片拼成gif图片导出(使用了Gif.Components.dll)。
截图:
具体过程
一、实现手绘板功能,在网上能找到很多代码,这块也简单。
二、在基础上实现回放功能,我重点讲下这里的实现,起初我是这么判断的,当鼠标MOUSE_DOWN时候开始记录画笔当前位置信息存入一个数组中(包含X,Y坐标值),
鼠标MOUSE_UP的时候,这个数组存储结束,开启一个新的数组用于点的信息。当回放的时候,将这些所有数组中的点的信息重新绘制一遍,包括flash8.net的实现都是这么干的
如果一笔绘制很长,当它回放的时候一下子全显示出来,没有一个绘制的过程。后来领导说这样不行,因为领导签字的时候很多都是一笔走完,最后我想了一个解决的办法,
就是在回放的时候设置一个阀值,比如50个点,够50个点就回放一次,这样一来,即使领导签字是一笔,回放的时候效果也会很好。
下面是所有ActinScipt源码
1 import flash.ui.Mouse; 2 import flash.display.BitmapData; 3 import com.adobe.images.JPGEncoder; 4 import flash.events.MouseEvent; 5 6 var linesize:uint = 2;//画笔大小 7 var isDown:Boolean = false; 8 var oldX:Number; 9 var oldY:Number; 10 var arrayBig:Array=new Array();//存放笔画的数组,外层数组 11 var num:uint = 0;//当前数组个数 12 var playNum:uint = 0;//当前回放的数字(代表当前回放到第几个数组了) 13 var intervalDuration:Number = 500;// 回放延时时间间隔 14 var relaseDuration:Number = 1000;//发布时时间间隔 15 var intervalId:uint;//回放循环的ID 16 var relaseId:uint;//发布的ID 17 var url = "http://www.wispdawn.com/";//暂用我的网站做服务器 18 var serviceFile = "GetSignImg.ashx";//服务文件 19 20 mc_canvas.addEventListener(MouseEvent.MOUSE_DOWN ,onDown); 21 mc_canvas.addEventListener(MouseEvent.MOUSE_UP ,onUp); 22 mc_canvas.addEventListener(MouseEvent.MOUSE_MOVE ,onMove); 23 mc_canvas.addEventListener(MouseEvent.MOUSE_OVER,onMouseOver); 24 mc_canvas.addEventListener(MouseEvent.MOUSE_OUT,onMouseOut); 25 26 27 28 function onDown(event:MouseEvent):void 29 { 30 arrayBig[num]=new Array();//这里设置为数组 31 isDown = true; 32 oldX = mouseX; 33 oldY = mouseY; 34 } 35 function onMove(event:MouseEvent):void 36 { 37 if (isDown) 38 { 39 mc_canvas.graphics.lineStyle(linesize); 40 mc_canvas.graphics.moveTo(oldX,oldY); 41 mc_canvas.graphics.lineTo(mouseX,mouseY); 42 oldX = mouseX; 43 oldY = mouseY; 44 arrayBig[num].push([oldX,oldY]); 45 if (arrayBig[num].length > 80) 46 { 47 //isDown = false; 48 trace(num); 49 num++;//数组个数+1 50 arrayBig[num]=new Array(); 51 arrayBig[num].push([oldX,oldY]); 52 } 53 } 54 } 55 56 function onUp(event:MouseEvent):void 57 { 58 isDown = false; 59 trace(num); 60 num++;//数组个数+1 61 } 62 63 function onMouseOver(event:MouseEvent):void 64 { 65 mc_canvas.addEventListener(Event.ENTER_FRAME, onEnter); 66 } 67 68 69 function onMouseOut(event:MouseEvent):void 70 { 71 mc_canvas.removeEventListener(Event.ENTER_FRAME, onEnter); 72 Mouse.show(); 73 mc_pen.visible = false; 74 } 75 76 //画布监听事件 77 function onEnter(event:Event):void 78 { 79 Mouse.hide(); 80 mc_pen.visible = true; 81 mc_pen.x = mouseX + 22; 82 mc_pen.y = mouseY + 22; 83 } 84 85 86 87 //清空 88 btn_clear.addEventListener(MouseEvent.CLICK,onClear); 89 function onClear(event:MouseEvent):void 90 { 91 arrayBig = []; 92 playNum = 0; 93 num = 0; 94 mc_canvas.graphics.clear(); 95 } 96 //保存; 97 btn_save.addEventListener(MouseEvent.CLICK,onSave); 98 function onSave(event:MouseEvent):void 99 { 100 var imager:BitmapData = new BitmapData(mc_canvas.width,mc_canvas.height); 101 imager.draw(mc_canvas); 102 var jpg:JPGEncoder = new JPGEncoder(100); 103 var file:FileReference = new FileReference(); 104 file.save(jpg.encode(imager),"sign.jpg"); 105 106 } 107 108 //回放按钮 109 btn_replay.addEventListener(MouseEvent.CLICK,onReplay); 110 function onReplay(event:MouseEvent):void 111 { 112 if (arrayBig.length > 0) 113 { 114 mc_canvas.graphics.clear(); 115 //清除画面; 116 playNum = 0;//初始为0 117 intervalId = setInterval(onReplayDelay,intervalDuration); 118 } 119 } 120 121 //用于设置循环的回放 122 function onReplayDelay():void 123 { 124 //当前笔画 125 var arr:Array = arrayBig[playNum]; 126 //当前笔画下面所有的点 127 for (var i:uint=0; i<arr.length-1; i++) 128 { 129 var curPoint = arr[i]; 130 var nexPoint = arr[i + 1]; 131 mc_canvas.graphics.lineStyle(linesize); 132 mc_canvas.graphics.moveTo(curPoint[0],curPoint[1]); 133 mc_canvas.graphics.lineTo(nexPoint[0],nexPoint[1]); 134 } 135 136 playNum++; 137 if (playNum==num) 138 { 139 clearInterval(intervalId); 140 } 141 } 142 143 //上传图片 144 function UpImg(seq:uint):void 145 { 146 var imager:BitmapData = new BitmapData(mc_canvas.width,mc_canvas.height); 147 imager.draw(mc_canvas); 148 var jpg:JPGEncoder = new JPGEncoder(100); 149 var bytes:ByteArray = jpg.encode(imager); 150 151 var signName:String = mc_mask.txt_name.text; 152 var req:URLRequest = new URLRequest(url+serviceFile+"?name="+signName+"&seq="+seq+"&total="+arrayBig.length); 153 req.data = bytes; 154 req.method = URLRequestMethod.POST; 155 req.contentType = "application/octet-stream"; 156 157 var loader:URLLoader = new URLLoader(); 158 loader.dataFormat = URLLoaderDataFormat.BINARY; 159 loader.load(req); 160 } 161 //loader.addEventListener(Event.COMPLETE, completeHandler); 162 163 //function completeHandler(evt:Event):void 164 //{ 165 //trace(evt.target.data); 166 //} 167 168 //发布 169 btn_release.addEventListener(MouseEvent.CLICK,onReleas); 170 function onReleas(event:MouseEvent):void 171 { 172 if (arrayBig.length > 0) 173 { 174 mc_mask.x = mc_mask.y = 0; 175 mc_mask.mc_mask_bg.alpha = 0.8; 176 mc_mask.btn_complete.visible = false;//隐藏完成 177 mc_mask.btn_down.visible = false;//隐藏下载 178 } 179 } 180 //真正发布事件执行 181 function Release():void 182 { 183 //当前笔画 184 var arr:Array = arrayBig[playNum]; 185 //当前笔画下面所有的点 186 for (var i:uint=0; i<arr.length-1; i++) 187 { 188 var curPoint = arr[i]; 189 var nexPoint = arr[i + 1]; 190 mc_canvas.graphics.lineStyle(linesize); 191 mc_canvas.graphics.moveTo(curPoint[0],curPoint[1]); 192 mc_canvas.graphics.lineTo(nexPoint[0],nexPoint[1]); 193 } 194 //上传图片;; 195 UpImg(playNum); 196 playNum++; 197 if (playNum==num) 198 { 199 clearInterval(relaseId); 200 AfterRelease(); 201 } 202 } 203 //取消发布 204 mc_mask.btn_cancel.addEventListener(MouseEvent.CLICK,onCancel); 205 function onCancel(event:MouseEvent):void 206 { 207 mc_mask.x = 10000; 208 mc_mask.mc_mask_bg.alpha = 0; 209 } 210 //真正发布按钮 211 mc_mask.btn_release.addEventListener(MouseEvent.CLICK,onTrueRelease); 212 function onTrueRelease(event:MouseEvent):void 213 { 214 var name1 = mc_mask.txt_name.text; 215 if (name1!="") 216 { 217 BeginRelease(); 218 mc_canvas.graphics.clear(); 219 playNum = 0; 220 relaseId = setInterval(Release,relaseDuration); 221 } 222 else 223 { 224 mc_mask.txt_state.text = "请输入姓名"; 225 } 226 } 227 //发布前设置 228 function BeginRelease():void 229 { 230 mc_mask.btn_release.visible = false; 231 mc_mask.btn_cancel.visible = false; 232 mc_mask.txt_name.visible = false; 233 mc_mask.txt_nameTile.visible = false; 234 mc_mask.txt_state.text = "正在发布..."; 235 } 236 //发布后设置 237 function AfterRelease():void 238 { 239 mc_mask.btn_complete.visible = true; 240 mc_mask.btn_down.visible = true; 241 mc_mask.btn_complete.addEventListener(MouseEvent.CLICK,onComplete); 242 mc_mask.txt_state.text = "发布完成"; 243 244 } 245 //发布完成 246 function onComplete(event:MouseEvent):void 247 { 248 mc_mask.x = 10000; 249 mc_mask.mc_mask_bg.alpha = 0; 250 mc_mask.btn_down.visible = true; 251 mc_mask.btn_release.visible = true; 252 mc_mask.btn_cancel.visible = true; 253 mc_mask.txt_name.visible = true; 254 mc_mask.txt_nameTile.visible = true; 255 mc_mask.txt_state.text = ""; 256 mc_mask.txt_name.text = ""; 257 } 258 259 //下载gif 260 mc_mask.btn_down.addEventListener(MouseEvent.CLICK,onDownGif); 261 function onDownGif(event:MouseEvent):void 262 { 263 var today:Date =new Date(); 264 var todayStr = getDateFormat(today);//获取当天日期例如 20131130 265 var signName:String = mc_mask.txt_name.text; 266 267 var gifUrl:URLRequest = new URLRequest(url+"/Img/"+signName+"_"+todayStr+"/"+signName+".gif"); 268 var fileRef:FileReference=new FileReference(); 269 //fileRef.addEventListener(ProgressEvent.PROGRESS,onProgress); 270 fileRef.download(gifUrl,"sign.gif"); 271 } 272 function onProgress(event:ProgressEvent):void 273 { 274 var loaded:uint = event.bytesLoaded; 275 var total:uint = event.bytesTotal; 276 } 277 278 //获取当天日期 279 function getDateFormat( date:Date):String 280 { 281 var dYear:String = String(date.getFullYear()); 282 var dMonth:String = String((date.getMonth() + 1 < 10) ? "0" : "") + (date.getMonth() + 1); 283 var dDate:String = String(date.getDate() < 10 ? "0":"") + date.getDate(); 284 return dYear+dMonth+dDate; 285 }
三、创建一个asp.ne站点,写个一般处理程序GetSignImg.ashx
这里就是用于的获取flash传递过来的图片,获取后导出jpg图片序列,然后将这些序列图片用程序生成一个gif图片存放在目录下,当flash点击到处gif图片时候,执行下载
源文件如下
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.IO; 6 using System.Drawing.Drawing2D; 7 using System.Drawing.Imaging; 8 using Gif.Components; 9 10 namespace Web 11 { 12 /// <summary> 13 /// GetSignImg1 的摘要说明 14 /// </summary> 15 public class GetSignImg1 : IHttpHandler 16 { 17 public void ProcessRequest(HttpContext context) 18 { 19 string name = context.Request["name"]; 20 int seq = Convert.ToInt32(context.Request["seq"]); 21 int total =Convert.ToInt32(context.Request["total"]); 22 int length = context.Request.TotalBytes; 23 byte[] buffer = context.Request.BinaryRead(length); 24 string newPath=HttpContext.Current.Server.MapPath("Img/" + name + "_"+DateTime.Now.ToString("yyyyMMdd")); 25 if (!Directory.Exists(newPath)) 26 { 27 Directory.CreateDirectory(newPath); 28 }; 29 string path = string.Format("{0}/{1}.jpg", newPath, seq); 30 FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write); 31 BinaryWriter bw = new BinaryWriter(fs); 32 bw.Write(buffer); 33 bw.Close(); 34 fs.Close(); 35 36 //下载gif 37 if (seq + 1 == total) 38 { 39 //生成gif图片 40 List<string> fileList = new List<string>(); 41 for (int i = 0; i < total; i++) 42 { 43 fileList.Add(newPath + "/" + i + ".jpg"); 44 } 45 string[] imageFilePaths = fileList.ToArray(); 46 string outputFilePath = newPath + "/" + name + ".gif"; 47 AnimatedGifEncoder e = new AnimatedGifEncoder(); 48 e.Start(outputFilePath); 49 //图片转换时间 50 e.SetDelay(50); 51 //1表示只动一次,0:表示循环,n:表示循环n次 52 e.SetRepeat(1); 53 for (int i = 0, count = imageFilePaths.Length; i < count; i++) 54 { 55 e.AddFrame(System.Drawing.Image.FromFile(imageFilePaths[i])); 56 } 57 e.Finish(); 58 } 59 } 60 61 public bool IsReusable 62 { 63 get 64 { 65 return false; 66 } 67 } 68 } 69 }
四、所有程序打包下载 Download