Tween Shape
ベクタ画像をトゥイーンで変形させてみました。
/**
* Copyright shohei909 ( http://wonderfl.net/user/shohei909 )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/3BQW
*/
package {
import flash.geom.Matrix;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.filters.BlurFilter;
import flash.geom.Point;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.display.Sprite;
import net.kawa.tween.KTween;
import frocessing.color.ColorHSV;
import net.kawa.tween.easing.*;
import net.hires.debug.Stats;
[SWF(backgroundColor="0x000000", frameRate="60")]
public class FlashTest extends Sprite {
private const SHAPE_SIZE:int = 50;
private const SHAPE_NUM:int = 20;
private var bitmap:Bitmap;
private var black:BitmapData;
private var filter:BlurFilter = new BlurFilter(2,2);
private var shapes:Vector.<BlendImage> = new Vector.<BlendImage>();
private var dir:Vector.<Point> = new Vector.<Point>();
private var count:int = 0;
private var count2:int = 0;
private var easing:Array = [ null, Bounce.easeOut, Elastic.easeOut, Back.easeIn, Back.easeInOut, Sine.easeOut ];
function FlashTest(){
bitmap = new Bitmap( new BitmapData(465,465,false,0) );
black = new BitmapData(465,465,false,0);
addChild(bitmap)
for(var i:int = 0; i<SHAPE_NUM; i++) {
var color:ColorHSV = new ColorHSV(i/SHAPE_NUM*600,1,0.5,1);
var shape:BlendImage = new BlendImage();
shape.addImage( new Circle(SHAPE_SIZE,new PathStyle(true,0,0,0,0,0,color.r,color.g,color.b) ) );
shape.addImage( new Star(SHAPE_SIZE*0.6) );
shape.addImage( new Heart(SHAPE_SIZE*0.8) );
shape.x = 200 + 65*Math.random();
shape.y = 200 + 65*Math.random();
shape.blendMode = "add";
shapes.push( shape );
shape.repeat = true;
shape.lock();
dir[i] = new Point;
}
shapes.fixed = true;
dir.fixed = true;
addChild(new Stats)
this.addEventListener("enterFrame",onFrame);
}
private function onFrame(e:Event):void{
if(count % 95 == 0){
var rand:BlendImage = shapes[ count2 % SHAPE_NUM ];
rand.unlock();
var r:int = 180;
var deg:Number = Math.PI * Math.random() * 2;
KTween.to( rand,1.5,{x:r*Math.sin(deg) + 232, y:r*Math.cos(deg) + 232, value: rand.value+1 }, easing[int(Math.random()*easing.length)], function a():void{rand.lock()} );
if( Math.random() < 0.5 ){ count2++; }
}
count++;
var b:BitmapData = bitmap.bitmapData;
b.lock();
b.merge(black, b.rect, new Point, 30,30,30,30 );
b.applyFilter( b, b.rect, new Point, filter );
for each(var shape:BlendImage in shapes){
b.draw( shape, new Matrix(1,0,0,1,shape.x,shape.y), null, "add" );
}
b.unlock();
}
}
}
import flash.display.Shape;
import flash.display.Graphics;
import flash.events.Event;
/**
* Anchorクラスは図形の頂点となるアンカーのクラスです。
*/
class Anchor extends Object {
public var x:Number, y:Number;
private var _curve:Number;
public function set curve(c:Number):void { c = (c < 0)?0:c; c = (c > 1)?1:c; _curve = c }
public function get curve():Number { return _curve; }
/**
* コンストラクタです。
* @param x アンカーの位置を指定します。
* @param y アンカーの位置を指定します。
* @param curve アンカーの曲線度を指定します。(0-1)
*/
function Anchor(x:Number, y:Number, curve:Number):void {
this.x = x; this.y = y; this.curve = curve;
}
/**
* Anchorの複製を返します。
* @return このオブジェクトの複製です。
*/
public function clone():Anchor { return (new Anchor(x, y, curve)); }
}
/**
* パスブロックの線のスタイルや塗りのスタイルを保持するクラスです。
*
* @param close パスの開閉を表す値です。
* @param thickness 線の太さを表す値です。(0-)
* @param lineRed 線の色を表す値です。(0-255)
* @param lineGreen 線の色を表す値です。(0-255)
* @param lineBlue 線の色を表す値です。(0-255)
* @param lineAlpha 線のアルファ値を表す値です。(0-1)
* @param fillRed 塗りの色を表す値です。(0-255)
* @param fillGreen 塗りの色を表す値です。(0-255)
* @param fillBlue 塗りの色を表す値です。(0-255)
* @param fillAlpha 塗りのアルファ値を表す値です。(0-1)
* @param compounded パスの複合化をするかどうかを表す値です。
* @param compID パスの複合化IDを表す値です。(0-255)
*/
class PathStyle extends Object {
public var thickness:Number;
private var _lineRed:uint,_lineGreen:uint,_lineBlue:uint,_lineAlpha:Number;
private var _fillRed:uint,_fillGreen:uint, _fillBlue:uint,_fillAlpha:Number;
public function set lineRed(i:uint):void { i = (i < 0)?0:i; i = (i > 255)?255:i; _lineRed = i }
public function get lineRed():uint { return _lineRed; }
public function set lineGreen(i:uint):void { i = (i < 0)?0:i; i = (i > 255)?255:i; _lineGreen = i }
public function get lineGreen():uint { return _lineGreen; }
public function set lineBlue(i:uint):void { i = (i < 0)?0:i; i = (i > 255)?255:i; _lineBlue = i }
public function get lineBlue():uint { return _lineBlue; }
public function set lineAlpha(i:Number):void { i = (i < 0)?0:i; i = (i > 1)?1:i; _lineAlpha = i }
public function get lineAlpha():Number { return _lineAlpha; }
public function set fillRed(i:uint):void { i = (i < 0)?0:i; i = (i > 255)?255:i; _fillRed = i }
public function get fillRed():uint { return _fillRed; }
public function set fillGreen(i:uint):void { i = (i < 0)?0:i; i = (i > 255)?255:i; _fillGreen = i }
public function get fillGreen():uint { return _fillGreen; }
public function set fillBlue(i:uint):void { i = (i < 0)?0:i; i = (i > 255)?255:i; _fillBlue = i }
public function get fillBlue():uint { return _fillBlue; }
public function set fillAlpha(i:Number):void { i = (i < 0)?0:i; i = (i > 1)?1:i; _fillAlpha = i }
public function get fillAlpha():Number { return _fillAlpha; }
public var close:Boolean;
public var compounded:Boolean;
public var compID:int;
//public var useGradation:Boolean = false;
//public var gradation:Gradation;
/**
* コンストラクタです。
* @param close パスの開閉を指定します。
* @param thickness 線の太さを指定します。(0-)
* @param lineRed 線の色を指定します。(0-255)
* @param lineGreen 線の色を指定します。(0-255)
* @param lineBlue 線の色を指定します。(0-255)
* @param lineAlpha 線のアルファ値を指定します。(0-1)
* @param fillRed 塗りの色を指定します。(0-255)
* @param fillGreen 塗りの色を指定します。(0-255)
* @param fillBlue 塗りの色を指定します。(0-255)
* @param fillAlpha 塗りのアルファ値を指定します。(0-1)
* @param compounded パスの複合化をするかどうかを指定します。
* @param compID パスの複合化IDを指定します。(0-255)
*/
function PathStyle( close:Boolean = true, thickness:Number = 2,
lineRed:uint = 0, lineGreen:uint = 0, lineBlue:uint = 0, lineAlpha:Number = 0,
fillRed:uint = 0xFF, fillGreen:uint=0xDD, fillBlue:uint=0x33, fillAlpha:Number = 0.2,
compounded:Boolean = false, compID:int = 0
):void {
this.close = close; this.thickness = thickness;
this.lineRed = lineRed; this.lineGreen = lineGreen; this.lineBlue = lineBlue; this.lineAlpha = lineAlpha;
this.fillRed = fillRed; this.fillGreen = fillGreen; this.fillBlue = fillBlue; this.fillAlpha = fillAlpha;
this.compounded = compounded; this.compID = compID;
}
/**
* PathStyleの複製を返します
* @return このオブジェクトの複製
*/
public function clone():PathStyle { return new PathStyle(close, thickness, lineRed, lineGreen, lineBlue, lineAlpha,fillRed, fillGreen, fillBlue, fillAlpha, compounded, compID); }
}
class Path extends Object {
public var anchors:Vector.<Anchor> = new Vector.<Anchor>();
public var style:PathStyle;
function Path(style:PathStyle = null) {
if(style == null ){ this.style = new PathStyle() }
else{ this.style = style }
}
public function addAnchor(anchor:Anchor):void{ anchors.push(anchor); }
/**
* 指定したグラフィックスに対して,このパスブロックを描画します
* @param g:描画を行うグラフィックス
*/
public function draw(g:Graphics, comp:Boolean = false):void {
if (anchors.length > 1) {
var s:PathStyle = style;
var a0:Anchor = anchors[0];
var a1:Anchor = anchors[1];
if(comp == false){ g.lineStyle( s.thickness, (s.lineRed << 16) + (s.lineGreen << 8) + (s.lineBlue << 0), s.lineAlpha ); }
var curve:Number = 0;
if ( s.close ) {
if(comp == false){ g.beginFill( (s.fillRed << 16) + (s.fillGreen << 8) + (s.fillBlue << 0), s.fillAlpha); }
if( a0.curve > 0 ) { g.moveTo(( a0.x + a0.curve * a1.x ) / ( a0.curve + 1 ) , ( a0.y + a0.curve * a1.y ) / ( a0.curve + 1 ) ); }
else { g.moveTo( a0.x, a0.y ); }
}else {
g.moveTo( a0.x , a0.y );
}
g.lineTo( ( a1.x + a1.curve * a0.x ) / ( a1.curve + 1 ) , ( a1.y + a1.curve * a0.y ) / ( a1.curve + 1 ) );
for ( var i:int = 1; i < anchors.length - 2; i++ ) {
a0 = anchors[i]; a1 = anchors[i + 1];
_drawLine(g, a0, a1);
}
a0 = anchors[i]; a1 = anchors[i + 1];
if ( s.close ) {
_drawLine(g, a0, a1);
a0 = anchors[i + 1]; a1 = anchors[0];
_drawLine(g, a0, a1);
a0 = anchors[0]; a1 = anchors[1];
_drawLine(g, a0, a1);
if(comp == false){ g.endFill(); }
}else {
if( a0.curve > 0 ) { g.curveTo(a0.x, a0.y, a1.x, a1.y); }
else{ g.lineTo( a0.x, a0.y );g.lineTo( a1.x, a1.y ); }
}
}
}
/**
* Pathの複製を返します
* @return このオブジェクトの複製
*/
public function clone():Path {
var cl:Path = new Path( style.clone() );
cl.anchors = objClone(anchors);
return cl
}
private function _drawLine(g:Graphics, a0:Anchor, a1:Anchor):void {
if( a0.curve > 0 ) { g.curveTo(a0.x, a0.y, ( a0.x + a0.curve * a1.x ) / ( a0.curve + 1 ) , ( a0.y + a0.curve * a1.y ) / ( a0.curve + 1 ) ); }
else { g.lineTo( a0.x, a0.y ); }
g.lineTo( ( a1.x + a1.curve * a0.x ) / ( a1.curve + 1 ) , ( a1.y + a1.curve * a0.y ) / ( a1.curve + 1 ) );
}
}
/**
* VectorImageクラスはすべてのベクタイメージの基本クラスです。
* @author shohei909
*/
class VectorImage extends Shape {
public var paths:Vector.<Path> = new Vector.<Path>();
public function get numPath():uint { return paths.length; }
private var _locked:Boolean = false;
/**
* このイメージをロックします。イメージをロックすると再描画がおこなわれなくなり、
* プログラムの高速化をおこなうことができます。
*/
public function lock():void { _locked = true; }
/**
* このイメージをロックを解除します。イメージの変更が直ちに反映されるようになりますが、
* 毎フレーム描画が行われるので処理は重くなります。
*/
public function unlock():void { _locked = false; draw( graphics ); }
/**
* このイメージがロックされているかどうかのbool値です。
*/
public function get locked():Boolean { return _locked; }
/**
* コンストラクタです。
*/
function VectorImage(){
addEventListener(Event.ENTER_FRAME, onFrame);
addEventListener(Event.ADDED_TO_STAGE, addedToStage);
}
/**
* このイメージにパスを追加します。
* @param path 追加するパス。
*/
public function addPath(path:Path):void{ paths.push(path); draw( graphics ); }
/**
* 指定したグラフィックスに対して,このイメージを描画します
* @param g:描画を行うグラフィックス
*/
public function draw(g:Graphics):void {
g.clear();
var comp:Boolean;
for each( var path:Path in paths ) {
if ( bf != null && path.style.compounded == true && bf.style.compounded == true && path.style.compID == bf.style.compID ) { comp = true }
else { comp = false; }
path.draw( g, comp );
var bf:Path = path;
}
}
/**
* ロックを解除して、このベクタイメージに描かれたものを消去します。
*/
public function clear():void {
paths = new Vector.<Path>();
unlock();
}
/**
* このイメージの複製を返します
*/
public function clone():VectorImage { var img:VectorImage = new VectorImage(); paths = objClone(paths); return img; }
private function onFrame(e:Event):void { if( !_locked ){ draw( graphics ); } }
private function addedToStage(e:Event):void { removeEventListener(Event.ADDED_TO_STAGE, addedToStage);draw( graphics ); }
}
/**
* 複数のベクタ画像をブレンドしたイメージを作成します。
* <p>
* BlendImage に 3つのベクタ画像を設定した場合。<br/>
* blendValue = 0 のとき、1つ目の画像、blendValue = 1 のとき、2つ目の画像、blendValue = 2 のとき、3つ目の画像,<br/>
* blendValue = 0.5 のとき、1つ目の画像と2つ目の画像の中間の画像、blendValue = 1.5 のとき、2つ目の画像と3つ目の画像の中間の画像が表示されます。<br/>
* <br/>
* また、blendValue が 2より大きいときの画像は、repeat,yoyoの値によって変わります。<br/>
* <br/>
* BlendImage に 設定されたベクタ画像はパスの数とアンカーの数が一致していなければなりません。<br/>
* </p>
*/
class BlendImage extends VectorImage {
public var images:Vector.<VectorImage>;
private var _blendValue:Number = 0;
public function get value():Number { return _blendValue; }
public function set value(v:Number):void { _blendValue = v; makeBlend(); }
private var _yoyo:Boolean = false;
public function get yoyo():Boolean { return _yoyo; }
public function set yoyo(y:Boolean):void { _yoyo = y; makeBlend(); }
private var _repeat:Boolean = false;
public function get repeat():Boolean { return _repeat; }
public function set repeat(r:Boolean):void { _repeat = r; makeBlend(); }
/**
* コンストラクタです。
* @param imgs このBlendImageに設定するVectorImageの配列です。
*/
function BlendImage( imgs:Array = null ){
if(imgs == null){ imgs = []; }
images = Vector.<VectorImage>(imgs);
if( images.length > 0 ){ this.paths = objClone(images[0].paths); }
}
/**
* BlendImageにVectorImageを追加します。
* @param img 追加するイメージです。
*/
public function addImage( img:VectorImage ):void {
if( images.length == 0 ){ this.paths = objClone(img.paths); }
images.push(img);
makeBlend();
}
private function makeBlend():void {
if( 1 < images.length ){
var startImg:VectorImage = images[ position( Math.floor(_blendValue), true ) ];
var endImg:VectorImage = images[ position( Math.floor(_blendValue+1), false ) ];
var rate:Number = morphNum();
for (var i:uint = 0; i < paths.length; i++ ) {
for (var j:uint = 0; j < paths[i].anchors.length; j++ ) {
blend( paths[i].anchors[j] , startImg.paths[i].anchors[j], endImg.paths[i].anchors[j], rate, ["x", "y", "curve"], [] );
}
blend( paths[i].style , startImg.paths[i].style, endImg.paths[i].style, rate, ["thickness", "lineRed", "lineGreen", "lineBlue", "lineAlpha", "fillRed", "fillGreen", "fillBlue", "fillAlpha"], ["compounded", "compID", "close"] );
}
}
}
private function repeatLength():int {
if (_yoyo) { return images.length + ( images.length - 2 < 0 ? 0 : images.length - 2 ) }
else { return images.length }
}
private function position(i:int, start:Boolean):int {
if (_repeat && i > 0) {
i = i % repeatLength();
}else{
if ( i < 0 ){ i = 0 }
if ( i > repeatLength() - 1 ) { i = repeatLength()-1 }
if ( i == 0 && !start ) { i = 1 }
if ( i == repeatLength()-1 && start ) { i = repeatLength()-2 }
}
if (_yoyo && i > images.length-1 ){ i = repeatLength()-i }
return i;
}
private function morphNum():Number {
var i:Number = _blendValue;
if ( i > 0 ) {
if ( _repeat || i < repeatLength()-1 ) {
i = i % 1
if( i < 0 ){ i = -i; }
}else { i -= repeatLength()-2; }
}
return i;
}
}
/**
* Starクラスは星形のベクタイメージを生成するクラスです。
* このイメージは生成後、ロックされます。
*/
class Star extends VectorImage {
/**
* コンストラクタです。
* @param r 半径を指定します。
* @param amp 切り込みの深さを指定します。
* @param deg 0から360の値で回転させる角度を指定します。
* @param anchorNumアンカー(頂点)の数を指定します。
* @param innerRound trueにすると内側のアンカーを丸めます。
* @param outerRound trueを設定すると外側のアンカーを丸めます。
*/
function Star( r:Number = 30, style:PathStyle = null, pathNum:int = 10, deg:Number = 0, amp:Number = 0.4, innerRound:Boolean = false, outerRound:Boolean = false ) {
if (style == null) { style = new PathStyle(); style.fillRed = 0xFF; style.fillGreen = 0xFF; style.fillBlue = 0x33; }
var path:Path = new Path(style);
for ( var i:int = 0; i < pathNum; i++ ) {
path.addAnchor( new Anchor(
( r * ( 1 + 2 * amp * (0.5 - (i % 2)) ) ) * Math.sin( Math.PI * (2 * i / pathNum + (deg / 180) ) ) ,
- ( r * ( 1 + 2 * amp * (0.5 -(i % 2)) ) ) * Math.cos( Math.PI * (2 * i / pathNum + (deg / 180) ) ) ,
( ( i % 2 == 0 && outerRound ) || ( i % 2 == 1 && innerRound ) ) ? 1 : 0
) );
}
addPath(path);
lock();
}
}
/**
* Heartクラスはハート形のベクタイメージを生成するクラスです。
* このイメージは生成後、ロックされます。
*/
class Heart extends VectorImage {
/**
* コンストラクタです。
* @param r 半径を指定します。
* @param amp 切り込みの深さを指定します。
* @param deg 0から360の値で回転させる角度を指定します。
* @param anchorNumアンカー(頂点)の数を指定します。
*/
function Heart( r:Number = 30, style:PathStyle = null, pathNum:int = 10, deg:Number = 0, amp:Number = 0.4, innerRound:Boolean = false, outerRound:Boolean = false ) {
if (style == null) { style = new PathStyle(); style.fillRed = 0xFF; style.fillGreen = 0x66; style.fillBlue = 0xCC; }
var path:Path = new Path(style);
for ( var i:int = 0; i < pathNum; i++ ) {
var deg2:Number = deg + Math.PI * (2 * i / pathNum + ( deg / 180) );
var r2:Number = (1.70 - Math.abs( (i / pathNum - 0.5) * 3.5 ) ) * r * ( 1 - Math.cos( (deg2 - deg)*2 )*0.1 );
path.addAnchor( new Anchor( r2 * Math.sin(deg2), - r2 * ( Math.cos(deg2) ) - 0.7 * r, ( i == 0 || i == pathNum / 2 ) ? 0 : 1) );
}
addPath(path);
lock();
}
}
/**
* Circleクラスは円形のベクタイメージを生成するクラスです。
* このイメージは生成後、ロックされます。
*/
class Circle extends VectorImage {
/**
* コンストラクタです。
* @param r 半径を指定します。
* @param amp 切り込みの深さを指定します。
* @param deg 0から360の値で回転させる角度を指定します。
* @param anchorNumアンカー(頂点)の数を指定します。
*/
function Circle( r:Number = 30, style:PathStyle = null, pathNum:int = 10, deg:Number = 0 ) {
if (style == null) { style = new PathStyle(); style.fillRed = 0x33; style.fillGreen = 0xCC; style.fillBlue = 0xFF; }
var path:Path = new Path(style);
for ( var i:int = 0; i < pathNum; i++ ) {
path.addAnchor( new Anchor(
r * Math.sin( Math.PI * (2 * i / pathNum + (deg / 180) ) ) ,
- r * Math.cos( Math.PI * (2 * i / pathNum + (deg / 180) ) ) ,
1
) );
}
addPath(path);
lock();
}
}
import flash.utils.getQualifiedClassName;
/**
* オブジェクトの複製を行います。
*/
function objClone(arg:*):*{
var cl:*;
var name:String = getQualifiedClassName(arg);
if( name == "Object" || name == "Array" || name.match("__AS3__.vec::Vector") != null ){
if ( name == "Object" ) { cl = { };
}else if( name == "Array" || name.match("__AS3__.vec::Vector") != null ) { cl = arg.slice(0, 0); }
for( var s:String in arg ){ cl[s] = objClone( arg[s] ) }
return cl;
}else if( arg is Object && arg.hasOwnProperty("clone") && arg.clone is Function ){
return arg.clone();
}
return arg;
}
/**
* targetにscr1と scr2 を ブレンドした値を設定します。
*/
function blend( target:*, scr1:*, scr2:*, rate:Number, blendParams:Array, passParams:Array = null ):void{
var a:Number = 1 - rate;
for each( var str:String in blendParams ) {
target[str] = a * scr1[str] + rate * scr2[str];
}
for each( var str2:String in passParams ) {
target[str2] = scr1[str2];
}
}