Xmas Message with Twitter
ネットで投稿されたメッセージがクリスマスの夜空を飾ります
クリスマスメッセージは「Twitter」と「はてなブックマーク」で送ることができます
※投稿はFlash内のボタンを押して、Twitterの場合は[]内にコメントをはてなブックマークの場合は、ブックマークコメントを記入下さい
/**
* Copyright clockmaker ( http://wonderfl.net/user/clockmaker )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/uvXF
*/
/**
* ネットで投稿されたメッセージが
* クリスマスの夜空を飾ります
*
* クリスマスメッセージは
* 「Twitter」と「はてなブックマーク」で送ることができます
* ※追記:11/5(木)、Twitter仕様変更によりTwitter投稿はできなくなりました
*
* ※投稿はFlash内のボタンを押して、
* Twitterの場合は[]内にコメントを
* はてなブックマークの場合は、ブックマークコメントを記入下さい
*
* このFlashはwonderfl上の様々なテクニックを元に作られました
*
* Twitter連動
* http://wonderfl.net/code/04d6d1ae7e943b7faab88623ed83404e7a40c5f7
*
* キラキラ3D
* http://wonderfl.net/code/c0f513f31389ba07e375331256c2bfa3a8b9006c
*
* 文字のパーティクル化
* http://wonderfl.net/code/08ec1c2c8da8e9e487867a8e2ab8cddeb75f8986
*
* 木を作る再帰関数
* http://wonderfl.net/code/00cbe3ecbf0db2e865bc410a8ea54677c19ba4c6
*
* 配列のシャッフル
* http://wonderfl.net/code/8cfa39c9e91a268791142fe605c010dc30338588
*
* BetweenAS3の時間調整
* http://wonderfl.net/code/59af9d8a3cc45e4c2e6fcf12a9bc9ea55b1576e7
*
*/
package {
import com.bit101.components.Label;
import flash.display.*;
import flash.events.*;
import flash.geom.Matrix;
import flash.net.*;
import flash.system.*;
import flash.text.*;
import flash.utils.*;
import jp.progression.casts.*;
import jp.progression.commands.lists.LoaderList;
import jp.progression.commands.lists.SerialList;
import jp.progression.commands.net.*;
import jp.progression.data.getResourceById;
import org.libspark.betweenas3.BetweenAS3;
import org.libspark.betweenas3.easing.*;
import org.libspark.betweenas3.tweens.ITween;
import org.papervision3d.cameras.Camera3D;
import org.papervision3d.core.effects.*;
import org.papervision3d.core.geom.Pixels;
import org.papervision3d.core.geom.renderables.Pixel3D;
import org.papervision3d.core.math.Number3D;
import org.papervision3d.materials.*;
import org.papervision3d.objects.DisplayObject3D;
import org.papervision3d.objects.primitives.*;
import org.papervision3d.render.BasicRenderEngine;
import org.papervision3d.scenes.Scene3D;
import org.papervision3d.view.Viewport3D;
import org.papervision3d.view.layer.BitmapEffectLayer;
[SWF(width=465, height=465, frameRate=30, backgroundColor=0)]
public class Main extends CastDocument {
static public const URL_HATENA:String = "http://pipes.yahooapis.com/pipes/pipe.run?_id=a9fa16f111d33689315b50bd196f691e&_render=rss";
//static public const URL_TWITTER:String = "http://search.twitter.com/search.atom?q=%23XmasMessage&rpp=50";
static public const URL_TWITTER:String = "http://clockmaker.jp/labs/091022_pv3d_xmas/search.atom";
static public const URL_IMG_BG:String = "http://clockmaker.jp/labs/091021_checkmate/assets/bg.png";
static public const URL_IMG_TITLE:String = "http://clockmaker.jp/labs/091021_checkmate/assets/title.png";
static public const URL_IMG_BTN_HATENA:String = "http://clockmaker.jp/labs/091021_checkmate/assets/btn-hatena.png";
static public const URL_IMG_BTN_TWITTER:String = "http://clockmaker.jp/labs/091021_checkmate/assets/btn-twitter.png";
static private const MAX_PARTICLES:int = 2500;
// color
public static const WHITE:uint = 0xFFF0F0F0;
public static const GREEN:uint = 0xFF008800;
public static const RED:uint = 0xFFCC0000;
public static const GOLD:uint = 0xFFFFCC66;
public static const SILVER:uint = 0xFFCCCCCC;
public static const COLORS:Array = [WHITE, GREEN, RED, GOLD, SILVER];
// 3d
private var scene:Scene3D = new Scene3D();
private var renderer:BasicRenderEngine = new BasicRenderEngine();
private var viewport:Viewport3D = new Viewport3D(465, 465);
private var camera:Camera3D = new Camera3D()
// 3d objects
private const MAX_RADIUS:int = 10000;
private const PIXCEL_MARGIN:Number = 1.25;
private var textPixels:Pixels;
private var treePixels:Pixels;
private var pixelArr:Array = [];
private var bfx:BitmapEffectLayer;
private var dmyObjs:Array = [];
private var tweens:Array = [];
// 3d camera
public var cameraDistance:Number = 1000;
public var cameraYaw:Number = 0;
public var cameraPitch:Number = 0;
// 2d
private var canvas:BitmapData;
private var mtx:Matrix;
private var progress:ProgressBar;
private var loading:Label;
private var loadingTween:ITween;
// array of objs
private var data:Array = [];
// fractal
private var level:int = 0;
// data
private var index:int = 0;
private const RESET_TIMER:Timer = new Timer(11 * 1000);
/**
* Progression 4 では atReady から処理が始まります
*/
override protected function atReady() : void{
stage.quality = StageQuality.MEDIUM;
// 3D
addChild(viewport);
// progress bar
progress = new ProgressBar();
progress.value = 0;
this.addChild(progress)
progress.x = int(stage.stageWidth / 2 - progress.width/2);
progress.y = int(stage.stageHeight / 2 + 10);
// loading
loading = new Label(this, stage.stageWidth / 2 - 33, stage.stageHeight / 2 - 10, "NOW LOADING");
loadingTween = BetweenAS3.tween(loading, { alpha:1 }, { alpha:0.25 }, 0.05);
loadingTween = BetweenAS3.serial(loadingTween, BetweenAS3.reverse(loadingTween));
loadingTween.stopOnComplete = false;
loadingTween.play();
var context:LoaderContext = new LoaderContext(true);
var loaderList:LoaderList = new LoaderList(null,
new LoadBitmapData(new URLRequest(URL_IMG_BG), {context:context}),
new LoadBitmapData(new URLRequest(URL_IMG_TITLE), {context:context}),
new LoadURL(new URLRequest(URL_TWITTER), {context:context}),
new LoadURL(new URLRequest(URL_HATENA), {context:context}),
new LoadBitmapData(new URLRequest(URL_IMG_BTN_HATENA), {context:context}),
new LoadBitmapData(new URLRequest(URL_IMG_BTN_TWITTER), {context:context})
);
loaderList.onProgress = function():void{
progress.value = 0.5 * (loaderList.loaded / loaderList.numCommands)
}
new SerialList(null,
loaderList,
function():void{
// Twitter連携 by http://wonderfl.net/code/04d6d1ae7e943b7faab88623ed83404e7a40c5f7 (coppieee)
default xml namespace = new Namespace("http://www.w3.org/2005/Atom");
var xml:XML = XML(getResourceById(URL_TWITTER).data);
for (var i:int = 0; i < xml.entry.length(); i++) {
// コメントを抽出
var comment:String = scan(xml.entry[i].title,/\[(.+)\]/);
// RT は除外
if(xml.entry[i].title.indexOf("RT") > -1) continue;
// コメントなしは除外
if(comment == "") continue;
// データに追加
data.push({
img : xml.entry[i].link.(@type == "image/png").@href,
name : xml.entry[i].author.name.split(" (")[0],
comment : comment
});
}
xml = XML(getResourceById(URL_HATENA).data);
for (i = 0; i < xml.channel.item.length(); i++) {
if(xml.channel.item[i].description != undefined){
comment = xml.channel.item[i].description;
var name:String = xml.channel.item[i].title;
var imgurl:String = "http://www.st-hatena.com/users/" + name.substr(0,2) + "/" + name + "/profile.gif";
data.push({
img : imgurl,
name : name,
comment : comment
});
}
}
data = shuffle(data);
data = data.slice(0, 8)
},
init3d
).execute();
}
/**
* Papervision3D関係のごにょごにょ
*/
private function init3d():void {
// カメラの設定
camera.target = DisplayObject3D.ZERO;
// ビットマップエフェクトレイヤー
bfx = new BitmapEffectLayer(viewport, stage.stageWidth, stage.stageHeight);
bfx.addEffect(new BitmapColorEffect(1, 1, 1, 0.75));
viewport.containerSprite.addLayer(bfx);
// ピクセル3D
textPixels = new Pixels(bfx);
scene.addChild(textPixels);
treePixels = new Pixels(bfx);
scene.addChild(treePixels);
var i:int;
var arr:Array = [];
// snow
var star:Array = [0xFFFFFFFF, 0xFFcccccc, 0xFF666666, 0xFF333333]
for (i = 0; i < 1500; i++) {
treePixels.addPixel3D(new Pixel3D(star[star.length * Math.random() | 0],
10000 * (Math.random() - 0.5),
10000 * (Math.random()) - 1000,
10000 * (Math.random() - 0.5)));
}
for (i = 0; i < 255; i++) {
treePixels.addPixel3D(new Pixel3D(0xFF0066CC,
100 * (Math.floor(i / 15) - 7),
-1000,
100 * (i % 15 - 7)));
}
// ダミーオブジェクトを作成
for (var k:int = 0; k < data.length; k++) {
dmyObjs[k] = new DisplayObject3D();
while (true) {
var angle:Number = 360 * Math.random();
dmyObjs[k].x = 2000 * Math.sin(angle * Number3D.toRADIANS);
dmyObjs[k].y = 2000 * (Math.random() - 0.5);
dmyObjs[k].z = 2000 * Math.cos(angle * Number3D.toRADIANS);
if (k == 0)
break;
else {
if (DisplayObject3D(dmyObjs[k]).distanceTo(dmyObjs[k - 1]) >= 500)
break;
}
}
dmyObjs[k].lookAt(DisplayObject3D.ZERO);
scene.addChild(dmyObjs[k]);
}
for (i = 0; i < MAX_PARTICLES; i++) {
var p:Pixel3D = new Pixel3D(COLORS[COLORS.length * Math.random() | 0]);
textPixels.addPixel3D(p);
}
drawTree(0, -1000, 0, 1000, 90, GOLD);
loading.text = "NOW RENDERING"
loading.x = stage.stageWidth / 2 - 39
var cmd:SerialList = new SerialList();
var renderIndex:int=0;
for (i = 0; i < data.length; i++) {
cmd.addCommand(
function():void{
preRenderText(renderIndex);
createText(renderIndex);
renderIndex ++;
progress.value = 0.5 + 0.5 * (renderIndex / data.length);
},
0.05
);
}
cmd.addCommand(
function():void{
// remove loading
removeChild(loading);
removeChild(progress);
loadingTween.stop();
loadingTween = null;
// ui
addChildAt(new CastBitmap(getResourceById(URL_IMG_BG).data), 0);
addChild(new CastBitmap(getResourceById(URL_IMG_TITLE).data, "auto", false, {x:20, y:376}));
var hatenaBtn:CastButton = new CastButton({x:20, y:433});
hatenaBtn.addChild(new Bitmap(getResourceById(URL_IMG_BTN_HATENA).data));
hatenaBtn.onCastMouseUp = function():void{
navigateToURL(new URLRequest("http://b.hatena.ne.jp/append?http://wonderfl.net/code/d5de28ad1f65364ee33b964101222be41a7320af"));
}
hatenaBtn.buttonMode = true;
addChild(hatenaBtn);
var tweetBtn:CastButton = new CastButton({x:240, y:433});
tweetBtn.addChild(new Bitmap(getResourceById(URL_IMG_BTN_TWITTER).data));
tweetBtn.onCastMouseUp = function():void{
navigateToURL(new URLRequest("http://twitter.com/home/?status=" + escapeMultiByte("#XmasMessage [] http://j.mp/xmaswon")));
};
tweetBtn.buttonMode = true;
addChild(tweetBtn);
// キラキラロジック by http://wonderfl.net/code/c0f513f31389ba07e375331256c2bfa3a8b9006c (sake)
canvas = new BitmapData(720 / 4, 485 / 4, false, 0x000000);
var bmp:Bitmap = new Bitmap(canvas, PixelSnapping.NEVER, false);
bmp.scaleX = bmp.scaleY = 4;
bmp.smoothing = true;
bmp.blendMode = BlendMode.ADD;
addChildAt(bmp,2);
mtx = new Matrix();
mtx.scale(0.25, 0.25);
// tree
drawTree(0, -1000, 0, 1000, 90, GOLD);
// タイマーを作る
RESET_TIMER.addEventListener(TimerEvent.TIMER, timerHandler);
RESET_TIMER.start();
// エンターフレーム
addEventListener(Event.ENTER_FRAME, loop);
},
timerHandler
);
cmd.execute();
}
private function timerHandler(e:TimerEvent = null):void {
var i:int;
var arr:Array = [];
for (i = 0; i < textPixels.pixels.length; i++) {
arr[i] = BetweenAS3.serial(
BetweenAS3.delay(
BetweenAS3.to(textPixels.pixels[i], getRandomPos(), 3, Expo.easeInOut),
Math.random()));
}
BetweenAS3.serial(
BetweenAS3.parallelTweens(arr),
BetweenAS3.func(function():void {
setTimeout(motionText, 2 * 1000);
})
).play();
//
index++;
if (index == data.length - 1)
index = 0;
//
var cameraTarget:DisplayObject3D = new DisplayObject3D();
cameraTarget.copyTransform(dmyObjs[index]);
cameraTarget.moveBackward(250);
var dis:Number = cameraTarget.distanceTo(DisplayObject3D.ZERO);
var rot:Number = Math.atan2(cameraTarget.x, cameraTarget.z);
BetweenAS3.parallel(
BetweenAS3.delay(
BetweenAS3.bezier(this, {
cameraYaw: rot,
cameraDistance: dis,
cameraPitch: cameraTarget.y
}, null, getRandomPos(), 10.5, Expo.easeInOut),
1.0) ,
BetweenAS3.serial(
BetweenAS3.to(camera, {
fov: 60
}, 4, Sine.easeInOut),
BetweenAS3.to(camera, {
fov: 50
}, 8, Sine.easeInOut)
)
).play();
}
private var preRenderedTests:Array = [];
private function preRenderText(itemIndex:int):void {
preRenderedTests[itemIndex] = [];
var text:TextField = new TextField();
text.multiline = true;
text.autoSize = "left";
text.htmlText = "<font face='Arial' size='24'>WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW</font>";
trace(data[itemIndex].comment);
var size:int = 24;
while(text.textWidth * text.textHeight > MAX_PARTICLES){
text.htmlText = "<font face='Arial' size='" + size +"'>" + data[itemIndex].comment + "</font>"
// +"<br><font face='Arial' size='" + size / 2 + "'>" + String(data[itemIndex].name).toUpperCase() + "</font>";
size--;
}
var cap:BitmapData = new BitmapData(text.textWidth + 10, text.textHeight, true, 0xFFFFFFFF);
cap.lock();
cap.draw(text);
// particle motion
var cnt:int = 0;
for (var i:int = 0; i < text.textWidth + 10; i++) {
for (var j:int = 0; j < text.textHeight; j++) {
if (cap.getPixel(i, j) == 0xFFFFFF)
continue;
if(preRenderedTests[itemIndex].length >= MAX_PARTICLES) break;
preRenderedTests[itemIndex].push({x: i, y: j, textWidth: text.textWidth, textHeight: text.textHeight});
}
}
cap.unlock();
cap.dispose();
}
/**
* テキストをBitmapData化してトゥイーンとして作成
* @param itemIndex
*/
private function createText(itemIndex:int):void {
var t:DisplayObject3D = dmyObjs[itemIndex];
// particle motion
var cnt:int = 0;
var arr:Array = [];
for (var i:int = 0; i < preRenderedTests[itemIndex].length; i++) {
var pix:Object = preRenderedTests[itemIndex][i];
var p:Pixel3D = textPixels.pixels[i];
var vec:DisplayObject3D = new DisplayObject3D();
vec.x = (pix.x - pix.textWidth / 2) * PIXCEL_MARGIN;
vec.y = (pix.textHeight / 2 - pix.y) * PIXCEL_MARGIN;
vec.z = 0;
vec.transform.calculateMultiply(t.transform, vec.transform);
arr[i] = BetweenAS3.bezier(p, {
x: vec.x,
y: vec.y,
z: vec.z
}, null, getRandomPos(), 1.2 + Math.random() * 0.25 + i * 0.002, Expo.easeOut);
}
tweens.push(arr);
}
private function motionText():void {
var tw:ITween = BetweenAS3.parallelTweens(tweens[index]);
// トゥイーンのデュレーションを調整
tw = BetweenAS3.scale(tw, 3.5 / tw.duration);
tw.play();
}
/**
* エンターフレームイベント
*/
private function loop(e:Event = null):void {
// カメラ
camera.x = cameraDistance * Math.sin(cameraYaw);
camera.y = cameraPitch;
camera.z = cameraDistance * Math.cos(cameraYaw);
// レンダリング
renderer.renderScene(scene, camera, viewport);
// キラキラ
canvas.fillRect(canvas.rect, 0x000000);
canvas.draw(viewport, mtx);
}
private function getRandomPos():Object {
return {
x: MAX_RADIUS * (Math.random() - 0.5),
y: MAX_RADIUS * (Math.random() - 0.5),
z: MAX_RADIUS * (Math.random() - 0.5)
};
}
/**
* 木を作る再帰関数
* http://wonderfl.net/code/00cbe3ecbf0db2e865bc410a8ea54677c19ba4c6
* @param x
* @param y
* @param z
* @param length
* @param angle
* @param cf
*
*/
private function drawTree(x:Number, y:Number, z:Number, length:Number, angle:Number, cf:int):void {
level += 1;
var destx:Number = x + length * Math.cos(angle * (Math.PI / 180));
var desty:Number = y + length * Math.sin(angle * (Math.PI / 180));
var destz:Number = z + length * (Math.random() - 0.5) * 2;
if (Math.random() < 0.5)
cf = COLORS[COLORS.length * Math.random() | 0];
var max:int = 10 - level / 1.5;
for (var i:int = 0; i < max; i++) {
treePixels.addPixel3D(new Pixel3D(cf,
destx * i / max + x * (max - i) / max,
desty * i / max + y * (max - i) / max,
destz * i / max + z * (max - i) / max
));
}
if (level < 6) {
drawTree(destx, desty, destz, length * (1 + 3 * Math.random()) * 0.25, angle + 60 * (Math.random() - Math.random()), cf);
drawTree(destx, desty, destz, length * (1 + 3 * Math.random()) * 0.25, angle + 60 * (Math.random() - Math.random()), cf);
drawTree(destx, desty, destz, length * (1 + 3 * Math.random()) * 0.25, angle + 60 * (Math.random() - Math.random()), cf);
drawTree(destx, desty, destz, length * (1 + 3 * Math.random()) * 0.25, angle + 60 * (Math.random() - Math.random()), cf);
}
level -= 1;
}
/**
* 1行でArrayをシャッフルする by nemu90kWw
* http://wonderfl.net/code/8cfa39c9e91a268791142fe605c010dc30338588
*/
public function shuffle(array:Array):Array{
return array.sort(function():int{return int(Math.random()*3)-1});
}
/**
* @see http://takumakei.blogspot.com/2009/05/actionscriptrubystringscan.html
*/
//package com.blogspot.takumakei.utils
//{
//public
public function scan(str:String, re:RegExp):Array
{
if(!re.global){
var flags:String = 'g';
if(re.dotall)
flags += 's';
if(re.multiline)
flags += 'm';
if(re.ignoreCase)
flags += 'i';
if(re.extended)
flags += 'x';
re = new RegExp(re.source, flags);
}
var r:Array = [];
var m:Array = re.exec(str);
while(null != m){
if(1 == m.length)
r.push(m[0]);
else
r.push(m.slice(1, m.length));
m = re.exec(str);
}
return r;
}
//}
}
}
import flash.display.*;
class ProgressBar extends Sprite {
private var edge:Sprite = new Sprite;
private var cont:Sprite = new Sprite;
private var _value:Number = 0;
public function set value(v:Number):void {
_value = v;
cont.scaleX = _value;
}
public function get value():Number {
return _value;
}
public function ProgressBar() {
edge.graphics.lineStyle(1, 0xFFFFFF);
edge.graphics.drawRect(0.5, 0.5, 100, 10);
cont.graphics.beginFill(0x555555);
cont.graphics.drawRect(0, 0, 100, 10);
addChild(cont);
addChild(edge);
value = 0;
}
}