裁剪图片并上传,这个功能,在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。可以直接搜或扫描右边的二维码呀。