Ho creato un po' di tempo fa, partendo da una classe di and80, un classe che maschera e smaschera un clip con le proprietà scale della maschera.

Ieri c'ho rimesso mano aggiungendo la possibilità di rilanciare la classe sullo stesso target, anche se la tween non è finita.

Forse non è elegante nel codice, magari potete dargli una sistemata, per il resto mi pare piuttosto affidabile. Un problema che non ho risolto è che se il clip da mascherare non è a 0,0. La maschera non si posizione sopra, ma su 0,0.

Ho anche tradotto in AS3.
Ah, io faccio uso di TweenLite (greensock.com), se usate un'altra piattaforma di Tween dovete arrangiarvi :)

(Di recente ho scoperto la classe TransitionManager, che fa la stessa cosa, ma se rilanciate un'altra tween a metà animazione riparte dagli estremi)

Il costruttore è

new MascheraSlide(target:MovieClip/Display, verso:String, inOut:String, easeType:Function, duration:Number, delay:Number) {

il verso può essere "left/top/right/bottom"
inOut è "in" o "out"


codice:
import mx.transitions.easing.Regular;
import gs.TweenLite;

class MascheraSlide {
	private var __target:MovieClip;
	private var __verso:String;	
	private var __width:Number;
	private var __height:Number;
	private var __time:Number;
	private var __easeType:Function;
	private var __intv:Number;
	private var __inOut:String;
	private var __delay:Number;	
	private var objTween:Object; 
	private var xscale:Number;
	private var yscale:Number;	
	private var x:Number;
	private var y:Number;

	public var onMotionFinished:Function;	

	function MascheraSlide(target:MovieClip, verso:String, inOut:String, easeType:Function, duration:Number, delay:Number) {	
		var path = this;
		if (target != undefined) {			
			__target = target;
			
			// inizializzo le variabili lasciate undefined			
			if (verso==undefined){__verso = "left";}else{__verso = verso;}
			if (inOut==undefined){__inOut="in";}else{__inOut=inOut;}
			if (easeType==undefined){__easeType=Regular.easeOut;}else{__easeType = easeType;}
			if (duration==undefined){__time = 1;}else{__time = duration;}
			if (delay==undefined){__delay=0;}else{__delay=delay;}			

			//primi parametri oggetto tween
			objTween = new Object();
			objTween.onComplete = chiudi;
			objTween.ease = __easeType;
			objTween.onCompleteParams = [__target, __inOut, path];
			objTween.delay = __delay;
			
			//posizione finale della maschera in base al verso, se in verso, l'origine del target, se out, verso gli estremi // scale finale
			if (__inOut=="in"){
				x = target._x
				y = target._y
				
				xscale=100;
				yscale=100;				
			}else{
				x = target._width+target._x
				y = target._height+target._y
				
				xscale=0;
				yscale=0;				
			}
			
			switch (__verso){
				default:				
				case "left":					
					objTween._xscale=xscale;
					break;
				case "top":
					objTween._yscale=yscale;
					break;
				case "right":
					objTween._xscale=xscale;
					objTween._x = x;
					break;
				case "bottom":
					objTween._yscale=yscale;
					objTween._y = y;
					break;
			}
			
			//se la maschera già esiste (perchè ho già lanciato la classe su questo target e deve finire la tween)
			if (target._parent["cmask_"+target._name]!=undefined){ //eseguo la nuova tween
				animateMask(__target, __time)
			}else{		
				//altrimenti disegno la maschera
				drawMask(__target, __verso, __easeType, __time, __delay, __inOut)			
			}
		}else{
			endOff(__target, __inOut);			
		}
	}
	
	//disegna un rettangolo
	private function drawSquare(target:MovieClip, name:String, level:Number, wdt:Number, hgt:Number):MovieClip {
		var rt:MovieClip=target.createEmptyMovieClip(name, level);
		rt.lineStyle(0, 0, 0);
		rt.moveTo(0, 0);
		rt.beginFill(0xCC0000,0);
		rt.lineTo(wdt, 0);
		rt.lineTo(wdt, hgt);
		rt.lineTo(0, hgt);
		rt.lineTo(0, 0);
		rt.endFill();
		return rt;
		
	}
		
	private function drawMask(target:MovieClip, verso:String, easeType:Function, duration:Number, delay:Number, inOut:String) {	
		
		var mk:MovieClip=drawSquare(target._parent, "cmask_"+target._name, target._parent.getNextHighestDepth(), target._width, target._height);

		mk._x = target._x;
		mk._y = target._y;
		
		target.setMask(mk);

		if (inOut=="in"){
			switch (verso){
				default:				
				case "left":
					mk._xscale=0;
					break;
				case "top":
					mk._yscale=0;
					break;
				case "right":
					mk._xscale=0;
					mk._x = target._width+target._x;
					break;
				case "bottom":
					mk._yscale=0;
					mk._y = target._height+target._y;
					break;
			}
		}
		animateMask(target, duration, "to")		
	}
	
	private function animateMask(trg, dur){
		trg._visible=true;
		TweenLite.to(trg._parent["cmask_"+trg._name], dur, objTween);
	}


	private function endOff(target:MovieClip, inOut:String) {
		target.setMask(null);
		if (inOut!="in"){
			target._visible=false;
		}
		if (target._parent["cmask_"+target._name]!=undefined){
			target._parent["cmask_"+target._name].removeMovieClip();
		}
		this['onMotionFinished']();
	}
	public function getMask():MovieClip{
		return __target._parent["cmask_"+__target._name];
	}
	
	private function chiudi(target, inOut, path){
		path['endOff'](target, inOut);
	}	
}
Utilizzo

codice:
import gs.TweenLite
import mx.transitions.easing.*


verso = "bottom"
var MS =new MascheraSlide(pippo, verso, "in", Strong.easeOut, 2)
MS.onMotionionFinished=function(){
   trace("1")
}


/* decommentare per vedere lanciare altre  tween

function chiudi(){
	var cc = new MascheraSlide(pippo, verso, "out", Strong.easeOut, 2)	
	cc.onMotionFinished=function(){
		trace("2")
	}
	
}
function apri(){
	dd = new MascheraSlide(pippo, verso, "in", Strong.easeOut, 2)
	dd.onMotionFinished=function(){
		trace("2")
	}
}
TweenLite.delayedCall(.5, chiudi)
TweenLite.delayedCall(1, apri)
*/

****************
Versione AS3

codice:
package {
	import flash.display.*;	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	
	import gs.TweenLite;
	import fl.transitions.easing.*;
	
	public class MascheraSlide extends EventDispatcher{
	
		private var __target:DisplayObjectContainer;
		private var __verso:String;	
		private var __time:Number;
		private var __easeType:Function;
		private var __intv:Number;
		private var __inOut:String;
		private var __delay:Number;
		private var __toFrom:String;
		
		private var objTween:Object; 
		private var __xscale:Number;
		private var __yscale:Number;	
		private var __x:Number;
		private var __y:Number;		
		
		private var box:MovieClip;
		
		function MascheraSlide(target:DisplayObjectContainer=null, verso:String ="left", inOut:String="in", easeType:Function = null, duration:Number=.5, delay:Number=0) {

			__inOut=inOut;			
			__target = target;
			__verso = verso;
			__easeType = easeType;
			__time = duration;
			__delay=delay;
			
			drawMask(__target, __verso, __inOut, __easeType, __time, __delay);
			
		}
		
		private function drawMask(target:DisplayObjectContainer, verso:String,inOut:String, easeType:Function, duration:Number, delay:Number) {
	
			if (target is DisplayObjectContainer){
							
				//primi parametri oggetto tween
				objTween = new Object();
				objTween.onComplete = endOff;
				objTween.ease = easeType;
				objTween.onCompleteParams = [target, inOut];
				objTween.delay = delay;
			

				//posizione finale della maschera in base al verso, se "in", verso l'origine del target, se out, verso gli estremi 
				//posizione dello scale finale scale finale
				if (inOut=="in"){
					__x = target.x
					__y = target.y
					
					__xscale=1;
					__yscale=1;	
				}else{
					__x = target.width+target.x
					__y = target.height+target.y
					
					__xscale=0;
					__yscale=0;
				}
				
				switch (__verso){
					default:				
					case "left":					
						objTween.scaleX=__xscale;
						break;
					case "top":
						objTween.scaleY=__yscale;
						break;
					case "right":
						objTween.scaleX=__xscale;
						objTween.x = __x;
						break;
					case "bottom":
						objTween.scaleY=__yscale;
						objTween.y = __y;
						break;
				}

				var box:MovieClip;
				if (target["__refMS"]==undefined){
					box = new MovieClip();
					box.graphics.beginFill(0,.5)
					box.graphics.drawRect(0,0, target.width, target.height);
					box.graphics.endFill();	
	
					target.parent.addChild(box);
					target["__refMS"] = box;
					
					box.x=target.x
					box.y=target.y
					
					if (inOut=="in"){
						switch (verso){
							default:				
							case "left":
								box.scaleX=0;
								break;
							case "top":
								box.scaleY=0;
								break;
							case "right":
								box.scaleX=0;
								box.x = target.width+target.x;
								break;
							case "bottom":
								box.scaleY=0;
								box.y = target.height+target.y;
								break;
						}
					}
					
					target.mask = box;
					
				}else{
					box = target["__refMS"]
				}
				
				target.visible=true;
				TweenLite.to(box, duration, objTween);
			}else{
				endOff(__target, __inOut)
			}
		}

		private function endOff(target:DisplayObjectContainer, inOut:String) {
			target.mask = null;
			target.parent.removeChild(target["__refMS"])
			target["__refMS"] = undefined;
			
			if (inOut!="in"){
				target.visible = false;
			}

			dispatchEvent(new Event("COMPLETED"));
		}
	}
}
USO
codice:

import gs.*
import fl.transitions.easing.*


var verso:String = "left"

var pp =new MascheraSlide(pippo, verso, "in", None.easeNone, 1)


function fatto(e:Event){
	trace("!")
}
pp.addEventListener("COMPLETED", fatto)


function chiudi(){
	var cc = new MascheraSlide(pippo, "top", "out", Strong.easeOut, 5)	
	

	
}
function apri(){
	var dd = new MascheraSlide(pippo, verso, "in", Strong.easeOut, 5)

}
TweenLite.delayedCall(.5, chiudi)
TweenLite.delayedCall(2, apri)