大体毎週金曜日の朝、@cocoponさんにJSのあれこれを教わる勉強会を開催しています。
今回、EaselJSを使用したマウスインタラクションコンテンツができたので
成果物とそれを作るまでの経緯を学習メモとしてまとめた記事vol.1の続きの記事となります。
前回は一つの円に対して、マウスが近づくと逃げつつも、元の位置に戻ろうとする動きまでを実装しました。
今回はそれにさらに手を加えて、より参考としているサイトの動きに
近づけていきたいと思います。
STEP5: 円周上にランダムに並べる
前回一つの円だけだったものを、ドーナツのような形になるように並べてみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
function Circle(opt_options){ var defaults = { color: 'white', defaultX: 0, defaultY: 0, circleRadius: 30 }; var options = $.extend(defaults,opt_options); this.defaultX = options.defaultX; this.defaultY = options.defaultY; this.circleRadius = options.circleRadius; this.shape = new createjs.Shape(); this.shape.graphics.beginFill(options.color).drawCircle(0, 0, this.circleRadius); this.shape.x = this.defaultX; this.shape.y = this.defaultY; } Circle.prototype.update = function(mouseX,mouseY){ var angle = Math.atan2(this.shape.y - mouseY, this.shape.x - mouseX); // ボールから逃げる var para = 0.97; var d = Math.sqrt(Math.pow((this.shape.x - mouseX),2) + Math.pow((this.shape.y - mouseY),2)); this.shape.x += Math.cos(angle)*35*Math.pow(para,d); this.shape.y += Math.sin(angle)*35*Math.pow(para,d); // 元の位置に戻ろうとする var distant = Math.sqrt(Math.pow((this.shape.x - this.defaultX),2) + Math.pow((this.shape.y - this.defaultY),2)); this.shape.x -= (this.shape.x - this.defaultX)*distant*0.01 this.shape.y -= (this.shape.y - this.defaultY)*distant*0.01 }; function init() { var stage = new createjs.Stage("studyCanvas"); var circles = []; var circle; var gridCircleR = 100; var gridCircleX = stage.canvas.width / 2; var gridCircleY = stage.canvas.height / 2; for (var i=0; i<100; i++){ circle = new Circle({ color: 'Orange', defaultX: gridCircleX + Math.cos(Math.PI*2/100*i)*(gridCircleR*(Math.random()/3+1)), defaultY: gridCircleY + Math.sin(Math.PI*2/100*i)*(gridCircleR*(Math.random()/3+1)), circleRadius: Math.random()*6+1 }); circles.push(circle); stage.addChild(circle.shape); } createjs.Ticker.addEventListener("tick", handleTick); function handleTick(event) { for (var i=0; i<100; i++){ circles[i].update(stage.mouseX,stage.mouseY); } stage.update(); } } |
これまで1つだった円が、100個の円をランダムにドーナツ状に並べただけで、
より質感が出てスクリーン上の円の集合体に質量が生まれてきたような感覚がありました。
STEP6: 円周上にランダムに並べた円それぞれがランダムに動く(動かない編)
今回参考にさせていただいているNamaleでは、円それぞれがマウスの位置とは無関係にランダムに動いていました。
そこで、それを実装してみました。(実装したかったです。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
function Circle(opt_options){ var defaults = { color: 'white', defaultX: 0, defaultY: 0, circleRadius: 30 }; var options = $.extend(defaults,opt_options); this.defaultX = options.defaultX; this.defaultY = options.defaultY; this.circleRadius = options.circleRadius; this.shape = new createjs.Shape(); this.shape.graphics.beginFill(options.color).drawCircle(0, 0, this.circleRadius); this.shape.x = this.defaultX; this.shape.y = this.defaultY; } Circle.prototype.update = function(mouseX,mouseY){ var angle = Math.atan2(this.shape.y - mouseY, this.shape.x - mouseX); // ボールから逃げる var para = 0.97; var d = Math.sqrt(Math.pow((this.shape.x - mouseX),2) + Math.pow((this.shape.y - mouseY),2)); this.shape.x += Math.cos(angle)*35*Math.pow(para,d); this.shape.y += Math.sin(angle)*35*Math.pow(para,d); // 元の位置に戻ろうとする var distant = Math.sqrt(Math.pow((this.shape.x - this.defaultX),2) + Math.pow((this.shape.y - this.defaultY),2)); this.shape.x += distant*0.1 this.shape.y += distant*0.1 }; function init() { var stage = new createjs.Stage("studyCanvas"); var circles = []; var circle; var gridCircleR = 100; var gridCircleX = stage.canvas.width / 2; var gridCircleY = stage.canvas.height / 2; for (var i=0; i<100; i++){ circle = new Circle({ color: 'Orange', defaultX: gridCircleX + Math.cos(Math.PI*2/100*i)*(gridCircleR*(Math.random()/3+1)), defaultY: gridCircleY + Math.sin(Math.PI*2/100*i)*(gridCircleR*(Math.random()/3+1)), circleRadius: Math.random()*6+1 }); circles.push(circle); stage.addChild(circle.shape); } createjs.Ticker.addEventListener("tick", handleTick); function handleTick(event) { for (var i=0; i<100; i++){ circles[i].update(stage.mouseX,stage.mouseY); } stage.update(); } } |
どんどん円が逃げていってしまいました。。
STEP6(2): 円周上にランダムに並べた円それぞれがランダムに動く(動いた!編)
今度はちゃんと動きました!
>> 別ウインドウで見る
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
function GridCircle(opt_options){ var defaults = { radius: 100, x: 100, y: 100 }; var options = $.extend(defaults,opt_options); this.radius = options.radius; this.x = options.x; this.y = options.y; this.childrenCount = this.radius*0.75; this.stage = options.stage; this.circles = []; this.createChildren(); } GridCircle.prototype.createChildren = function(){ var circle; for (var i=0; i<this.childrenCount; i++){ circle = new Circle({ color: 'Orange', defaultX: this.x + Math.cos(Math.PI*2/this.childrenCount*i)*(this.radius*(Math.random()/3+1)), defaultY: this.y + Math.sin(Math.PI*2/this.childrenCount*i)*(this.radius*(Math.random()/3+1)), circleRadius: Math.random()*Math.random()*9+1 }); this.circles.push(circle); this.stage.addChild(circle.shape); } } GridCircle.prototype.updateChildren = function(){ for (var i=0; i<this.childrenCount; i++){ this.circles[i].update(this.stage.mouseX,this.stage.mouseY); } } function Circle(opt_options){ var defaults = { color: 'white', defaultX: 0, defaultY: 0, circleRadius: 30 }; var options = $.extend(defaults,opt_options); this.defaultX = options.defaultX; this.defaultY = options.defaultY; this.circleRadius = options.circleRadius; this.saveDefaultX = options.defaultX; this.saveDefaultY = options.defaultY; this.shape = new createjs.Shape(); this.shape.graphics.beginFill(options.color).drawCircle(0, 0, this.circleRadius); this.shape.x = this.defaultX; this.shape.y = this.defaultY; } Circle.prototype.update = function(mouseX,mouseY){ var moveAngle = Math.random()*Math.PI*2; var moveDistance = Math.random()*3; this.defaultX += Math.cos(moveAngle)*moveDistance; this.defaultY += Math.sin(moveAngle)*moveDistance; // デフォルトの基準位置が、本来の位置に戻ろうとする var distantDefault = Math.sqrt(Math.pow((this.defaultX - this.saveDefaultX),2) + Math.pow((this.defaultY - this.saveDefaultY),2)); this.defaultX -= (this.defaultX - this.saveDefaultX)*distantDefault*0.01; this.defaultY -= (this.defaultY - this.saveDefaultY)*distantDefault*0.01; // マウスとの角度 var angle = Math.atan2(this.shape.y - mouseY, this.shape.x - mouseX); // マウスから逃げる var para = 0.97; var d = Math.sqrt(Math.pow((this.shape.x - mouseX),2) + Math.pow((this.shape.y - mouseY),2)); this.shape.x += Math.cos(angle)*35*Math.pow(para,d); this.shape.y += Math.sin(angle)*35*Math.pow(para,d); // 各円が元の位置に戻ろうとする var distant = Math.sqrt(Math.pow((this.shape.x - this.defaultX),2) + Math.pow((this.shape.y - this.defaultY),2)); this.shape.x -= (this.shape.x - this.defaultX)*distant*0.01; this.shape.y -= (this.shape.y - this.defaultY)*distant*0.01; }; function init() { var stage = new createjs.Stage("studyCanvas"); var gridcircle = new GridCircle({ stage: stage, radius: 100, x: stage.canvas.width / 2, y: stage.canvas.height / 2 }); createjs.Ticker.addEventListener("tick", handleTick); function handleTick(event) { gridcircle.updateChildren(); stage.update(); } } |
マウスが来た時に戻るべき位置this.defaultX
,this.defaultY
を
var distantDefault = Math.sqrt(Math.pow((this.defaultX - this.saveDefaultX),2) + Math.pow((this.defaultY - this.saveDefaultY),2));
this.defaultX -= (this.defaultX - this.saveDefaultX)*distantDefault*0.01;
this.defaultY -= (this.defaultY - this.saveDefaultY)*distantDefault*0.01;
として動かしているものに、さらにマウスとの動きを加える事で、
・もともとランダムに動きつつもドーナツ状を崩しすぎない
・マウスが近づいたら逃げつつも元に戻りたがる
という動きとなっています。
また、@fladdictさんによりGUILDで開催されている「インタラクティブ勉強会」で学んだことを活かし、
ただのMath.random()
ではなく、2回かけることで簡易的に小さい円を多く、
大きな円ほど少なくなるように
circleRadius: Math.random()*Math.random()*9+1
としています。
まとめ
少し色など調整してみました。
このようにただ見ていたものを実際に作ってみることで、
正確な物理演算をするのではなく、擬似的に表現するための手法や、
ディスプレイ上のものに質量を生み出すための工夫、
randomを扱いつつもただのrandomにはしない、など
多くのことに気づくことができました。
それぞれの円の色を写真から抽出した色に合わせてみたり、
円の大きさを小さめのものだけにしてぎっしり並べてみたり、
文字のように並べてインタラクティブなロゴとしてみたり…
とたくさん妄想が膨らんだので、
今回のクラスを使用しながらスタディを重ねていきたいと思います。
また、こういったゴリゴリなアート作品ではないけれど、
ページトップへ戻るボタンや、ナビゲーション、フォーカス時など
少しインタラクティブな要素が必要なWebサイトを作るお仕事お待ちしております笑
@cocoponさん、いつも本当にありがとうございます。