Broken Pieces
お好きなBGMを流しながらどうぞ。
マウスオーバーでサムネイル
クリックで画像ページに飛ぶよ
@see http://www.nicovideo.jp/watch/sm11464969
@see http://www.youtube.com/watch?v=x08R89_zRSs
/**
* Copyright uwi ( http://wonderfl.net/user/uwi )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/w3PE
*/
// お好きなBGMを流しながらどうぞ。
// マウスオーバーでサムネイル
// クリックで画像ページに飛ぶよ
//
// @see http://www.nicovideo.jp/watch/sm11464969
// @see http://www.youtube.com/watch?v=x08R89_zRSs
package {
import flash.display.*;
import flash.events.*;
import flash.net.*;
import flash.geom.*;
import flash.ui.*;
import flash.system.LoaderContext;
import flash.text.*;
import flash.filters.*;
import flash.utils.*;
import org.libspark.betweenas3.*;
import org.libspark.betweenas3.easing.*;
import org.libspark.betweenas3.tweens.*;
import com.bit101.components.*;
[SWF(backgroundColor="#f3f3f3")]
public class BrokenPieces extends Sprite {
private const FEEDURL : String = "http://api.flickr.com/services/feeds/photos_public.gne?format=rss_200&tags=";
private const media : Namespace = new Namespace("http://search.yahoo.com/mrss/");
// stageの大きさ情報
private var SW : Number;
private var SH : Number;
private const N : uint = 7; // 断片の個数
private var _msg : Label; // メッセージボックス
private var _searchBox : Text; // タグクエリ入力
private var _searchBtn : PushButton; // タグ検索ボタン
private var _thumb : Bitmap; // サムネイル
private var _ul : URLLoader; // フィードローダー
private var _loadingQueue : Array; // 未ロードのpicture <String>
private var _loadingTween : ITween; // ローディング用tween
private var _urlInfoMap : Object = {}; // ロード中の、URLとpictureの情報を対応
private var _nValid : uint; // フィード内にある有効なpictureの個数
private var _blurs : Array; // ピンぼけ用blur <Array<BlurFilter>>
private var _parts : Array; // 断片のリスト <ExSprite>
private var _step : Number = -9999999; // 時間
private var _useBlur : Boolean; // ブラー使用フラグ
private var _selectSmall : Boolean; // 小サイズ限定フラグ
private var _tf : TextField; // デバッグ用
private var _jobs : Array; // 負荷分散用ジョブ配列
public function BrokenPieces() {
Wonderfl.capture_delay(20);
_tf = new TextField();
_tf.width = 465;
_tf.height = 465;
_tf.y = 250;
// addChild(_tf);
// stage.scaleMode = "noScale";
SW = stage.stageWidth;
SH = stage.stageHeight;
_parts = [];
_loadingQueue = [];
_jobs = [];
// ブラー格納
_blurs = [[]];
for(var i : uint = 1;i < 100;i++){
_blurs.push([new BlurFilter(i, i)]);
}
_ul = null;
addEventListener(Event.ENTER_FRAME, onEnterFrame);
_searchBox = new Text(this, 10, SH - 23);
_searchBox.height = 21;
_searchBox.editable = true;
// 日本語対応
_searchBox.textField.embedFonts = false;
_searchBox.textField.defaultTextFormat = new TextFormat(null, 9, Style.INPUT_TEXT);
// エンター決定対応
var flagEnter : Boolean = false;
_searchBox.textField.addEventListener(KeyboardEvent.KEY_DOWN, function(e:KeyboardEvent) : void {if(e.keyCode == Keyboard.ENTER){flagEnter = true; }});
_searchBox.textField.addEventListener(TextEvent.TEXT_INPUT, function(e:TextEvent) : void {if(flagEnter){e.preventDefault();}});
_searchBox.textField.addEventListener(KeyboardEvent.KEY_UP, function(e:KeyboardEvent) : void {if(e.keyCode == Keyboard.ENTER && flagEnter){onTagSearch(null);} flagEnter = false; });
_searchBtn = new PushButton(this, 220, SH - 23, "TagSearch!", onTagSearch);
_searchBtn.setSize(70, 22);
var checkBlur : CheckBox = new CheckBox(this, 300, SH - 30, "Blur", function(e:MouseEvent) : void {_useBlur = e.currentTarget.selected; });
checkBlur.selected = true;
_useBlur = true;
var checkSmall : CheckBox = new CheckBox(this, 300, SH - 15, "Small pictures only", function(e:MouseEvent) : void {_selectSmall = e.currentTarget.selected; });
checkSmall.selected = true;
_selectSmall = true;
_thumb = new Bitmap();
_thumb.x = 400;
// _thumb.y = SH - 90;
addChild(_thumb);
_msg = new Label(this, 10, SH - 43);
_msg.autoSize = true;
_loadingTween = null;
_nValid = 999;
}
// タグ検索
private function onTagSearch(e : MouseEvent) : void
{
if(_ul != null){
_ul.removeEventListener(Event.COMPLETE, onFeedLoadComplete);
_ul.close();
}
_ul = new URLLoader();
_ul.addEventListener(Event.COMPLETE, onFeedLoadComplete);
_ul.load(new URLRequest(FEEDURL + encodeURIComponent(_searchBox.text)));
_msg.text = "Loading feed..";
_loadingQueue = [];
if(_loadingTween != null){
_loadingTween.stop();
_loadingTween = BetweenAS3.to(_msg, {x:10}, 1.0);
_loadingTween.play();
}
}
private function onEnterFrame(e : Event) : void
{
if(_loadingQueue.length == 0){
// ロード中のフィードがなく、フィード内の有効なpictureが存在するとき、フィードをロード
if(_ul == null && _nValid > 0){
_ul = new URLLoader();
_ul.addEventListener(Event.COMPLETE, onFeedLoadComplete);
_ul.load(new URLRequest(FEEDURL + encodeURIComponent(_searchBox.text)));
_msg.text = "Loading feed..";
}
}else{
// 6秒ごとに、断片の数が20個以下のときloadingQueueにあるpictureをひとつロード
if(_step % 180 == 0 && _parts.length <= 20){
var l : Loader = new Loader();
l.load(new URLRequest(_loadingQueue.pop()), new LoaderContext(true));
l.contentLoaderInfo.addEventListener(Event.COMPLETE, onPictureLoadComplete);
}
}
stepJobs();
stepParts();
_step++;
}
// 断片を動かす
private function stepParts() : void
{
for(var i : int = 0;i < _parts.length;i++){
var sp : ExSprite = _parts[i];
if(sp.y >= SH){
// 断片消去処理
removeChild(sp);
sp.removeEventListener(MouseEvent.CLICK, onPartClick);
sp.removeEventListener(MouseEvent.MOUSE_OVER, onPartOver);
sp.removeEventListener(MouseEvent.MOUSE_OUT, onPartOut);
sp.bmp.bitmapData.dispose();
sp.thumb.dispose();
if(i == _parts.length - 1){
_parts.pop();
}else{
_parts[i] = _parts.pop();
i--;
}
continue;
}
sp.x += sp.v[0];
sp.y += sp.v[1];
if(_useBlur){
var oaz : int = Math.abs(sp.z) / 10;
sp.z += sp.v[2];
var az : int = Math.abs(sp.z) / 10;
if(oaz != az || sp.alpha == 0){
sp.bmp.filters = _blurs[az]; // 必要なときだけフィルタを変更
}
}else{
sp.z += sp.v[2];
sp.bmp.filters = null;
}
sp.rotationX += sp.omega[0];
sp.rotationY += sp.omega[1];
sp.rotationZ += sp.omega[2];
if(sp.alpha < 1)sp.alpha += 0.05;
}
}
// pictureを断片に分割
// pictureに断片の種をばらまいて成長させていく。
// 色差が激しい時は乗り越えるのに時間がかかるようにする。
public function stepJobs() : void
{
if(_jobs.length == 0)return;
var n : uint = 10000 / _jobs.length;
for(var i : int = 0;i < _jobs.length;i++){
var job : Object = _jobs[i];
var p : uint = job.p;
var map : BitmapData = job.map;
var q : Vector.<uint> = job.q;
var base : BitmapData = job.base;
var xsums : Vector.<Number> = job.xsums;
var ysums : Vector.<Number> = job.ysums;
var nums : Vector.<uint> = job.nums;
var step : uint = 0;
for(;p < q.length && step <= n;step++){
var x : uint = q[p++];
var y : uint = q[p++];
var left : uint = q[p++];
if(left >= 1){
q.push(x, y, left - 1);
}else{
var c : uint = map.getPixel(x, y);
xsums[c-1] += x;
ysums[c-1] += y;
nums[c-1]++;
var oc : uint = base.getPixel(x, y);
for(var u : int = -1;u <= 1;u++){
for(var v : int = -1;v <= 1;v++){
if(u == 0 && v == 0)continue;
var nx : int = x + u;
var ny : int = y + v;
if(nx >= 0 && nx < map.width && ny >= 0 && ny < map.height && map.getPixel(nx, ny) == 0){
var dc : Number = deltaColor(base.getPixel(nx, ny), oc);
map.setPixel(nx, ny, c);
q.push(nx, ny, Math.min(dc / 7, 20));
}
}
}
}
}
if(p >= q.length){
makeParts(map, base, job.thumb, job.info, job.xsums, job.ysums, job.nums);
if(i == _jobs.length - 1){
_jobs.pop();
}else{
_jobs[i] = _jobs.pop();
i--;
}
}else{
job.p = p;
}
}
}
// フィード読み込み完了時
public function onFeedLoadComplete(e : Event) : void
{
_ul.removeEventListener(Event.COMPLETE, onFeedLoadComplete);
var xl : XMLList = XML(_ul.data)..media::content;
var len : uint = xl.length();
_nValid = 0;
for(var i : uint = 0;i < len;i++){
if(!_selectSmall || xl[i].@height * xl[i].@width <= 1500 * 1500){
_nValid++;
_urlInfoMap[xl[i].@url.toXMLString()] = {
link : XML(_ul.data)..link[i+2].text().toXMLString(),
thumbnail : XML(_ul.data)..media::thumbnail[i].@url.toXMLString()
}
// tr(XML(_ul.data)..link[i+1].text().toXMLString());
// tr(XML(_ul.data)..media::thumbnail[i].@url.toXMLString());
_loadingQueue.push(xl[i].@url.toXMLString());
}
}
_msg.text = len == 0 ? "Contents not found." : (_nValid == 0 ? "Every content is too large!" : "Loading contents...");
if(_nValid > 0){
if(_loadingTween != null)_loadingTween.stop();
_loadingTween = BetweenAS3.repeat(
BetweenAS3.serial(
BetweenAS3.to(_msg, {x:200}, 0.5, Cubic.easeInOut),
BetweenAS3.to(_msg, {x:10}, 0.5, Cubic.easeInOut)
), 9999);
_loadingTween.play();
}
_ul = null;
}
// picture読み込み完了時
public function onPictureLoadComplete(e : Event) : void
{
e.currentTarget.removeEventListener(Event.COMPLETE, onPictureLoadComplete);
_msg.text = "Enjoy!";
if(_loadingTween != null){
_loadingTween.stop();
_loadingTween = BetweenAS3.to(_msg, {x:10}, 1.0);
_loadingTween.play();
}
var l : Loader = e.currentTarget.loader as Loader;
// 縮小
var shrink : Number = 200 / Math.sqrt(l.width * l.height);
if(shrink > 1.0)shrink = 1.0;
var base : BitmapData = new BitmapData(l.width * shrink, l.height * shrink, false, 0x000000);
base.lock();
base.draw(l.content, new Matrix(shrink, 0, 0, shrink), null, null, null, true);
// tr(l.width, l.height, shrink);
// サムネイル用に縮小
var shrink2 : Number = 80 / Math.sqrt(l.width * l.height);
if(shrink2 > 1.0)shrink2 = 1.0;
var thumb : BitmapData = new BitmapData(l.width * shrink2, l.height * shrink2, false, 0x000000);
thumb.lock();
thumb.draw(l.content, new Matrix(shrink2, 0, 0, shrink2), null, null, null, true);
var i : uint;
// 断片の重心計算用
var xsums : Vector.<Number> = new Vector.<Number>(N);
var ysums : Vector.<Number> = new Vector.<Number>(N);
var nums : Vector.<uint> = new Vector.<uint>(N);
for(i = 0;i < N;i++){
xsums[i] = 0;
ysums[i] = 0;
nums[i] = 0;
}
// 断片情報格納用
var map : BitmapData = new BitmapData(base.width, base.height, false, 0);
map.lock();
var q : Vector.<uint> = new Vector.<uint>();
for(i = 0;i < N;i++){
var x : uint = Math.random() * base.width;
var y : uint = Math.random() * base.height;
map.setPixel(x, y, i + 1);
q.push(x, y, 0);
}
_jobs.push({
map : map,
p : 0,
q : q,
base : base,
thumb : thumb,
info : _urlInfoMap[l.contentLoaderInfo.url],
xsums : xsums,
ysums : ysums,
nums : nums
});
_urlInfoMap[l.contentLoaderInfo.url] = null;
l.unload();
}
private function makeParts(map : BitmapData, base : BitmapData, thumb : BitmapData, info : Object, xsums : Vector.<Number>, ysums : Vector.<Number>, nums : Vector.<uint>) : void
{
// 断片実体の作成
var targX : Number = SW / 2 + Math.random() * SW / 2.5 - SW / 5; // 合体予定座標
var targY : Number = SH / 2 + Math.random() * SH / 2.5 - SH / 5;
var T : Number = targY + SW / 3; // 合体予定時刻
var i : uint;
for(i = 0;i < N;i++){
var bb : BitmapData = new BitmapData(base.width, base.height, true, 0);
bb.copyPixels(base, base.rect, new Point());
bb.threshold(map, map.rect, new Point(), "!=", i+1, 0, 0xffffff);
var sp : ExSprite = new ExSprite();
var g : Array = [xsums[i] / nums[i], ysums[i] / nums[i]];
var bmp : Bitmap = new Bitmap(bb);
bmp.x = -g[0];
bmp.y = -g[1];
sp.addChild(bmp);
_parts.push(sp);
addChildAt(sp, 0);
// sp.mouseEnabled = false;
sp.mouseChildren = false;
sp.v = [
(Math.random() - 0.5) * 1.5,
(Math.random() + 0.5) * 1,
(Math.random() + 0.5) * -1
]; // 並進速度
sp.omega = [
(Math.random() - 0.5) * 2,
(Math.random() - 0.5) * 2,
(Math.random() - 0.5) * 0
]; // 回転速度
sp.bmp = bmp;
sp.alpha = 0.0;
sp.x = targX - base.width / 2 + g[0] - sp.v[0] * T;
sp.y = targY - base.height / 2 + g[1] - sp.v[1] * T;
sp.z = 0 - sp.v[2] * T;
sp.rotationX = -sp.omega[0] * T;
sp.rotationY = -sp.omega[1] * T;
sp.rotationZ = -sp.omega[2] * T;
sp.thumb = thumb.clone();
sp.info = info;
sp.addEventListener(MouseEvent.CLICK, onPartClick);
sp.addEventListener(MouseEvent.MOUSE_OVER, onPartOver);
sp.addEventListener(MouseEvent.MOUSE_OUT, onPartOut);
}
thumb.dispose();
base.dispose();
var gg : Number = getTimer();
}
// 断片をクリックしたら画像を開く
private function onPartClick(e : MouseEvent) : void
{
navigateToURL(new URLRequest(e.currentTarget.info.link), "_blank");
}
// 断片にマウスオーバーしたらサムネイルを表示
private function onPartOver(e : MouseEvent) : void
{
_thumb.bitmapData = e.currentTarget.thumb;
_thumb.y = SH - 5 - _thumb.bitmapData.height;
}
// 断片からマウスアウトしたらサムネイルを消す
private function onPartOut(e : MouseEvent) : void
{
_thumb.bitmapData = null;
}
// 色差を計算
public function deltaColor(a : uint, b : uint) : Number
{
var ra : uint = (a >> 16) & 0xff;
var ga : uint = (a >> 8) & 0xff;
var ba : uint = (a >> 0) & 0xff;
var rb : uint = (b >> 16) & 0xff;
var gb : uint = (b >> 8) & 0xff;
var bb : uint = (b >> 0) & 0xff;
return Math.abs(ra - rb) + Math.abs(ga - gb) + Math.abs(ba - bb);
}
private function tr(...o : Array) : void
{
_tf.appendText(o + "\n");
_tf.scrollV = _tf.maxScrollV;
}
}
}
import flash.display.*;
class ExSprite extends Sprite
{
public var v : Array;
public var omega : Array;
public var bmp : Bitmap;
public var thumb : BitmapData;
public var info : Object;
}