裁剪图片并上传,这个功能,在pc上经常会遇到。比如,你注册一个网站,要求你上传头像。这个时候,就会用到裁剪图片上传的功能。
一般,裁剪的图片有两种来源。一种来自你上传的图片,另一种就是来自摄像头拍摄的图片。
裁剪图片上传到服务器这个功能,之前就已经总结过。现在再温顾下。先看效果吧。
这个是我自己的虚拟空间,服务器端语音是PHP。该功能是模仿网上比较流行的图片上传裁剪做的。我去掉了部分功能。比如旋转丶添加滤镜丶八个拖动点。下边来分析下这个的制作流程。
获取源图片(本地上传或摄像头) → 编辑裁剪源图片 → 命名,上传图片到服务器
1.本地上传图片。
因为不是AIR,所以我们用的API是FileReference。第一步,先选图片,调用的方法是browse。其参数是一个FileFilter的数组,表示你想选择的文件类型。假如你只想选中系统里边的图片文件可以这样写。
var _fileReference:FileReference = new FileReference(); _fileReference.addEventListener(Event.SELECT , selectHandler); _fileReference.browse([new FileFilter("*.jpg;*.png;*.gif", "*.jpg;*.png;*.gif")]);
记住,FileFilter里边的文件后缀名类型之间用“;”分开,而非“,”。
我们要对FileReference的事件进行侦听。知道它干嘛了,干到哪里了。比如先对其SELECT事件就行侦听。当你选择了一个文件后,会触发这个事件。这个时候,event.target是FileReference对象,其data为空。因为数据还没加载到内存中来。这个时候,需要进行第二步。将图片数据加载到内存中来。这些都是以二进制的形式进行着。同样,需要对其COMPLETE事件进行侦听。当,将图片数据加载到内存中后,会触发这个事件。然后,我们会做下一步的事情。将这些二进制的数据还原成显示对象,也就是Bitmap。这样,我们才能看到。将二进制数据还原成Bitmap,我们可以用URLLoader,其方法loadBytes就是干这号事的。还是要对其完成状态进行侦听。当最后加载完成后。我们将Bitmap显示到舞台上。
好啦。FileReference的使用过程就是先SELECT,再Load,然会再Load。最后一个Load不是FileReference的。但要显示出来,还是得Load。
package com.vini123.tool { import com.vini123.view.UploadPanel; import flash.display.Bitmap; import flash.display.Loader; import flash.events.Event; import flash.net.FileFilter; import flash.net.FileReference; import flash.utils.ByteArray; public class SelectImage { private var _fileReference:FileReference; private var _byteArray:ByteArray; private var _loader:Loader; private var _bitmap:Bitmap; private var _receive:UploadPanel; public function SelectImage(receive:UploadPanel) { _receive = receive ; } public function select():void { _fileReference = new FileReference(); _fileReference.addEventListener(Event.SELECT , selectHandler); _fileReference.browse([new FileFilter("*.jpg;*.png;*.gif", "*.jpg;*.png;*.gif")]) } private function selectHandler(e:Event):void { (e.target as FileReference).removeEventListener(Event.SELECT , selectHandler); (e.target as FileReference).addEventListener(Event.COMPLETE , completeHandler); (e.target as FileReference).load(); } private function completeHandler(e:Event):void { (e.target as FileReference).removeEventListener(Event.COMPLETE , completeHandler); _loader = new Loader(); _loader.loadBytes(e.target.data); _loader.contentLoaderInfo.addEventListener(Event.COMPLETE , loadCompleteHandler); } private function loadCompleteHandler(e:Event):void { e.target.removeEventListener(Event.COMPLETE , loadCompleteHandler); dispose(); _bitmap = e.target.content as Bitmap ; _receive.receiveImage(_bitmap); _fileReference = null; } public function dispose():void { if(_bitmap && _bitmap.bitmapData) { _bitmap.bitmapData.dispose(); } } } }
2.裁剪图片。
源图片既然已经获得了,那么我们就要对图片进行裁剪了。这里,仅仅对图片进行正方形式裁剪,且不旋转和加滤镜。
裁剪图片展现在我们面前的是这样一个场景。在图片的上一图层,有一个带尾巴的矩形。矩形为镂空的。矩形外围到图片之间的区域填充进了半透明的黑丝。按下矩形,我们可以拖动矩形。按下尾巴,我们可以拖拽尾巴,以改变矩形的面积。
做这里多,其实就是想裁切图片。裁切成跟矩形相同范围的图片。在各种拖动中,我们要保证不能拖到错误的地方(脱出范围了,或该拖到的地方没有拖到),并且保证拖动的过程中,边上的小图及时更新。
在这里,因为有放大缩小,变换旋转(只是我没做),这里用BitmapData的draw方法。因为要实现灵活,这里仅仅存一个原始的图片Bitmap和一个裁剪draw出来的BitmapData。三个小图是三个Bitmap,它们共同拥有着裁剪的BitmapData。
这样,当我们裁剪的BitmapData更新了,三个小图也会更新。
Image面板:
package com.vini123.view { import com.vini123.ui.Cover; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.MovieClip; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.geom.Matrix; import flash.geom.Rectangle; import flash.ui.Mouse; public class ImagePanel extends Sprite { public var deep:Sprite ; public var dragIcon:MovieClip; private var _bitmap:Bitmap; private var _cover:Cover; private var _supplyBitmapData:BitmapData; private var _mask:Sprite; public function ImagePanel() { deep.visible = false; _cover = new Cover(); _cover.visible = false; _mask = new Sprite(); dragIcon.visible = false; dragIcon.mouseChildren = false; dragIcon.mouseEnabled = false; _cover.background.addEventListener(MouseEvent.ROLL_OVER , overHandler); _cover.background.addEventListener(MouseEvent.ROLL_OUT , outHandler); _cover.background.addEventListener(MouseEvent.MOUSE_DOWN , mouseDownHandler); } private function mouseMoveHandler(e:MouseEvent):void { dragIcon.x = mouseX; dragIcon.y = mouseY; } private function overHandler(e:MouseEvent):void { Mouse.hide(); dragIcon.visible = true; addChild(dragIcon); _cover.addEventListener(MouseEvent.MOUSE_MOVE , mouseMoveHandler); } private function outHandler(e:MouseEvent):void { _cover.removeEventListener(MouseEvent.MOUSE_MOVE , mouseMoveHandler); dragIcon.visible = false; Mouse.show(); } private function mouseDownHandler(e:MouseEvent):void { stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler); addEventListener(Event.ENTER_FRAME , enterHandler); _cover.background.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler); _cover.startDrag(false,new Rectangle(_bitmap.x , _bitmap.y , _bitmap.width - _cover.bgW , _bitmap.height - _cover.bgW)); } private function enterHandler(e:Event):void { updateBitmapData(); } private function mouseUpHandler(e:MouseEvent):void { stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler); removeEventListener(Event.ENTER_FRAME , enterHandler); _cover.background.removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler); _cover.stopDrag(); updateBitmapData(); } public function updateBitmapData():void { readyBitmapData(); coverBitmapData(); resizeMask(); } public function resizeMask():void { addChild(_mask); _mask.x = _bitmap.x; _mask.y = _bitmap.y; _mask.graphics.clear(); _mask.graphics.beginFill(0x000000,0.4); _mask.graphics.drawRect(0,0,_bitmap.width ,_bitmap.height); _mask.graphics.drawRect((_cover.x - _bitmap.x),(_cover.y - _bitmap.y) ,_cover.background.width ,_cover.background.height); _mask.graphics.endFill(); } public function set bitmap(value:Bitmap):void { _bitmap = value; addChild(_bitmap); addChild(_cover); deep.visible = true; _cover.visible = true; resizeBitmap(); } public function get bitmapData():BitmapData { return _supplyBitmapData; } public function get bitmap():Bitmap { return _bitmap; } private function resizeBitmap():void { if(_bitmap.width > _bitmap.height) { if(_bitmap.width > deep.width) { _bitmap.width = deep.width; _bitmap.scaleY = _bitmap.scaleX; _cover.bgW = _bitmap.height; } else { _cover.bgW = _bitmap.width; } } else { if(_bitmap.height > deep.height) { _bitmap.height = deep.height ; _bitmap.scaleX = _bitmap.scaleY; _cover.bgW = _bitmap.width; } else { _cover.bgW = _bitmap.height; } } _bitmap.x = deep.width * 0.5 - _bitmap.width * 0.5 ; _bitmap.y = deep.height * 0.5 - _bitmap.height * 0.5 ; _cover.x = _bitmap.x + _bitmap.width * 0.5 - _cover.bgW * 0.5 ; _cover.y = _bitmap.y + _bitmap.height * 0.5 - _cover.bgW * 0.5; updateBitmapData(); } private function readyBitmapData():void { if(_supplyBitmapData)_supplyBitmapData.dispose(); _supplyBitmapData = new BitmapData(_cover.bgW,_cover.bgW,true,0); //_supplyBitmapData.copyPixels(_bitmap.bitmapData,new Rectangle((_cover.x),(_cover.y),_cover.bgW,_cover.bgW),new Point()); _supplyBitmapData.draw(_bitmap,new Matrix(_bitmap.scaleX,0,0,_bitmap.scaleY,-(_cover.x - _bitmap.x),-(_cover.y - _bitmap.y))); } private function coverBitmapData():void { (this.parent as UploadPanel).image180.bitmapData = _supplyBitmapData; (this.parent as UploadPanel).image64.bitmapData = _supplyBitmapData; (this.parent as UploadPanel).image32.bitmapData = _supplyBitmapData; } } }
裁剪小块面板:
package com.vini123.ui { import com.vini123.view.ImagePanel; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.geom.Point; import flash.geom.Rectangle; import flash.ui.Mouse; public class Cover extends Sprite { private var _background:Sprite; private var _leftTopButton:Button; private var _rightTopButton:Button; private var _rightBottomButton:Button; private var _leftBottomButton:Button; private var _buttonList:Vector.<Button> = new Vector.<Button>(); private var _buttonPosList:Vector.<Point> = new Vector.<Point>(); private var _lineColor:int = 0xffffff; private var _bgW:Number = 150 ; private var _bgH:Number = 150 ; private var _maxW:Number = 0; private var _scaleIcon:ScaleIcon; private var _isInDown:Boolean = false; public function Cover(w:int = 150 ,h:int = 150) { _bgW = w ; _bgH = h ; _background = new Sprite(); addChild(_background); draw(); _leftTopButton = new Button(); _rightTopButton = new Button(); _rightBottomButton = new Button(); _leftBottomButton = new Button(); _buttonList.push(_leftTopButton , _rightTopButton , _rightBottomButton , _leftBottomButton); for(var i:int = 0 ; i< 4 ; i++) { _buttonList[i].buttonMode = true; _buttonList[i].id = i; _buttonList[i].addEventListener(MouseEvent.ROLL_OVER , overHandler); _buttonList[i].addEventListener(MouseEvent.ROLL_OUT , outHandler); _buttonList[i].addEventListener(MouseEvent.MOUSE_DOWN , mouseDownHandler); if(i!=2) { _buttonList[i].visible = false; } addChild(_buttonList[i]); } resizePos(); _scaleIcon = new ScaleIcon(); addChild(_scaleIcon); _scaleIcon.visible = false; } public function get background():Sprite { return _background; } private function resizePos():void { _buttonPosList.splice(0 , _buttonList.length); _buttonPosList.push(new Point(0,0) , new Point(_bgW ,0) , new Point(_bgW ,_bgH) , new Point(0 , _bgH)); for(var i:int = 0 ; i<4 ; i++) { _buttonList[i].x = _buttonPosList[i].x; _buttonList[i].y = _buttonPosList[i].y; } } public function set bgW(value:Number):void { _maxW = _bgW = _bgH = value; resizeDraw(); } private function resizeDraw():void { draw(); resizePos(); } public function get bgW():Number { return _bgW; } private function overHandler(e:MouseEvent):void { _scaleIcon.visible = true; Mouse.hide(); _scaleIcon.x = _buttonList[2].x; _scaleIcon.y = _buttonList[2].y; } private function outHandler(e:MouseEvent):void { if(!_isInDown) { _scaleIcon.visible = false; Mouse.show(); } } private function mouseDownHandler(e:MouseEvent):void { _isInDown = true; _background.mouseEnabled = false; Mouse.hide(); stage.addEventListener(MouseEvent.MOUSE_UP , mouseUpHandler); e.currentTarget.addEventListener(MouseEvent.MOUSE_UP , mouseUpHandler); addEventListener(Event.ENTER_FRAME , enterHandler); var xLen:Number = Math.min((this.parent as ImagePanel).bitmap.width - (this.x - (this.parent as ImagePanel).bitmap.x),_maxW); var yLen:Number = Math.min((this.parent as ImagePanel).bitmap.height - (this.y - (this.parent as ImagePanel).bitmap.y),_maxW); var xyLen:Number = Math.min(xLen , yLen); _buttonList[2].startDrag(false,new Rectangle(30,30, (xyLen - 30) , (xyLen - 30))); } private function enterHandler(e:Event):void { _bgW = _bgH = _buttonList[2].x; resizeDraw(); _scaleIcon.x = this.mouseX; //_buttonList[2].x; _scaleIcon.y = this.mouseY; //_buttonList[2].y; (this.parent as ImagePanel).updateBitmapData(); } private function mouseUpHandler(e:MouseEvent):void { stage.removeEventListener(MouseEvent.MOUSE_UP , mouseUpHandler); removeEventListener(Event.ENTER_FRAME , enterHandler); e.currentTarget.removeEventListener(MouseEvent.MOUSE_UP , mouseUpHandler); _isInDown = false; _buttonList[2].stopDrag(); _scaleIcon.visible = false; _background.mouseEnabled = true; Mouse.show(); (this.parent as ImagePanel).updateBitmapData(); } private function draw():void { _background.graphics.clear(); _background.graphics.lineStyle(1 , _lineColor); _background.graphics.beginFill(0xffff00,0.0); _background.graphics.drawRect(0,0,_bgW,_bgH); _background.graphics.endFill(); } } } import flash.display.Sprite; class Button extends Sprite { private var _radius:int = 3; private var _lineColor:int = 0xffffff; private var _alpha:Number = 0.3; public var id:int = 0; public function Button():void { this.graphics.lineStyle(1 , _lineColor, 0); this.graphics.beginFill(0x000000, 0); this.graphics.drawRect( -_radius * 4 , -_radius * 4 , _radius * 8 , _radius * 8 ); this.graphics.endFill(); this.graphics.lineStyle(1 , _lineColor); this.graphics.beginFill(0x000000, _alpha); this.graphics.drawRect( -_radius , -_radius , _radius * 2 , _radius * 2); this.graphics.endFill(); } }
3.上传图片。
图片裁剪好后,我们需要上传。说到上传,FileReference就有这样的方法。使用upload,FileReference就可以实现上传的功能。当我们将图片数据load进内存中后,然后使用FileReference的upload方法,就可以上传的。
可是,我们不能。因为我们对图片进行裁剪处理了。而FileReference没有这样的方法去上传别的二进制数据或BitmapData。这个时候,我们还是得用传统的URLLoader。
我们先将截取的BitmapData转化成ByteArray。这个怎么传了。现在好多了,从Flashplayer11.3开始,Flash自身就有API对BitmapData进行编码。我们使用PNGEncoderOptions和BitmapData的encode方法,就能将截取的BitmapData
写进ByteArray中。我们将ByteArray对象给URLRequest的data。最后load。php那边有方法接受Flash这边上传过去的ByteArray。最后生成图片,放到服务器的指定的位置。
这里需要注意的是图片的名字是get的方式传给PHP的。因为是3种大小的图片,这里上传处理了三次。不知道有朋友有其他方法没。
package com.vini123.tool { import com.vini123.controller.Controller; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.JPEGEncoderOptions; import flash.display.PNGEncoderOptions; import flash.events.Event; import flash.events.ProgressEvent; import flash.geom.Matrix; import flash.geom.Point; import flash.geom.Rectangle; import flash.net.URLLoader; import flash.net.URLRequest; import flash.net.URLRequestMethod; import flash.utils.ByteArray; import flash.utils.setTimeout; public class SaveImage { private var _loader:URLLoader; private var _urlRequest:URLRequest; private var _byteArray:ByteArray; private var _pngEncoderOptions:PNGEncoderOptions; private var _jpgEncoderOptions:JPEGEncoderOptions; private var _originalBitmapData:BitmapData; private var _bitmapData:BitmapData; private var _typeSize:Vector.<Point> = new Vector.<Point>(); private var _step:int = -1; private var _url:String = "http://localhost:8080/upload/upfile.php"; public function SaveImage() { _typeSize.push(new Point(180,180) , new Point(64,64) , new Point(32,32)); } public function Save(bitmapdata:BitmapData):void { if(_step != -1)return; _step = 0 ; _originalBitmapData = bitmapdata; upload(); } private function upload():void { if(_bitmapData) { _bitmapData.dispose(); _bitmapData = null; } var scaleX:Number = _typeSize[_step].x/_originalBitmapData.width; var scaleY:Number = _typeSize[_step].y/_originalBitmapData.height; _bitmapData = new BitmapData(_typeSize[_step].x , _typeSize[_step].y,true ,0); _bitmapData.draw(_originalBitmapData,new Matrix(scaleX,0,0,scaleY,0,0)); _byteArray = new ByteArray(); _pngEncoderOptions = new PNGEncoderOptions(); _bitmapData.encode(new Rectangle(0,0,_typeSize[_step].x,_typeSize[_step].y) , _pngEncoderOptions,_byteArray); dispose() ; _loader = new URLLoader(); _urlRequest = new URLRequest(); _urlRequest.url = _url + "?name=" + _typeSize[_step].x + "_" + _typeSize[_step].y + "_" + new Date().time +".png"; _urlRequest.data = _byteArray; _urlRequest.method = URLRequestMethod.POST; _urlRequest.contentType = "application/octet-stream"; _loader.load(_urlRequest); _loader.addEventListener(Event.COMPLETE , loadCompleteHandler); _loader.addEventListener(ProgressEvent.PROGRESS , progressHandler); Controller.instance.info = "整在上传第" + (_step + 1) + "张图片,请耐心等候" ; } private function loadCompleteHandler(e:Event):void { e.target.removeEventListener(Event.COMPLETE , loadCompleteHandler); e.target.removeEventListener(ProgressEvent.PROGRESS , progressHandler); _step ++ ; if(_step >=3) { _step = -1; _bitmapData.dispose(); _originalBitmapData.dispose(); dispose(); Controller.instance.info = "恭喜你,上传完毕!" ; setTimeout(function():void{Controller.instance.end();},1000); return; } upload(); } private function progressHandler(e:ProgressEvent):void { Controller.instance.percent = e.bytesLoaded/e.bytesTotal; } private function dispose():void { if(_loader) { _loader = null; _urlRequest = null; } } } }
php端代码:
<?php if (isset($GLOBALS["HTTP_RAW_POST_DATA"])) { // 得到 byteArray 数据 $png = $GLOBALS["HTTP_RAW_POST_DATA"]; /**写到服务器的images文件夹下**/ $file = fopen("images/".$_GET['name'],"w");//打开文件准备写入 fwrite($file,$png);//写入 fclose($file);//关闭 /**返回信息**/ echo "ok"; } ?>
最后,有一些细节要处理。上传图片的进度。以及上传的时候,舞台要暗下去。
有人要加我微信么,vinifly。可以直接搜或扫描右边的二维码呀。