いつからか、大体毎週金曜日の朝、@cocoponさんにJSのあれこれを教わる勉強会を開催しています。
これまでは、クラスやthisについてなどを学び、
雰囲気で書いてきてしまっていたJavascriptを基礎からきちんと教えて頂いてきました。
今回、EaselJSを使用したマウスインタラクションコンテンツができたので
成果物とそれを作るまでの経緯を学習メモとしてまとめました。
今回の題材
気になるインタラクションデザインを集めたギャラリーサイト、
Interaction Design Galleryというのをこじんまりと運営しているのですが、
Namaleというサイトにある下記のような動き:
・ドットが円形に配置
・マウスを近づけるとマウスから逃げる
・マウスから離れると元の位置に戻ろうとする
・元の位置と離れすぎない位置でランダムに動く
をEaselJSの練習がてら、実装してみたいと思いました。
STEP1: マウスに円を追随させる
まずは、マウスの位置と円の位置を連動させて、
マウスに円がついてくるようなものを作りました。
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 |
function Circle(opt_options){ var defaults = { color: 'white', defaultX: 100, defaultY: 100 }; var options = $.extend(defaults,opt_options); this.defaultX = options.defaultX; this.defaultX = options.defaultX; this.shape = new createjs.Shape(); this.shape.graphics.beginFill(options.color).drawCircle(0, 0, 30); } Circle.prototype.update = function(mouseX,mouseY){ this.shape.x = mouseX; this.shape.y = mouseY; }; function init() { var stage = new createjs.Stage("studyCanvas"); var circle0 = new Circle({ color: 'orange', defaultX: 100, defaultY: 100 }); stage.addChild(circle0.shape); createjs.Ticker.addEventListener("tick", handleTick); function handleTick(event) { circle0.update(stage.mouseX,stage.mouseY); stage.update(); } } |
Circle
というクラスを生成して、
オプションとして、色と最初の位置を渡しています。
STEP2: マウスから円が逃げる
次に、マウスから円が逃げるようにしました。
マウスの位置と自分の位置を認識して、
マウスとは反対の方向に動く=逃げる、という動きです。
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 |
function Circle(opt_options){ var defaults = { color: 'white', defaultX: 0, defaultY: 0 }; var options = $.extend(defaults,opt_options); this.defaultX = options.defaultX; this.defaultY = options.defaultY; this.shape = new createjs.Shape(); this.shape.graphics.beginFill(options.color).drawCircle(0, 0, 50); 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); this.shape.x += Math.cos(angle)*3; this.shape.y += Math.sin(angle)*3; }; function init() { var stage = new createjs.Stage("studyCanvas"); var circle0 = new Circle({ color: 'Orange', defaultX: 100, defaultY: 100 }); stage.addChild(circle0.shape); createjs.Ticker.addEventListener("tick", handleTick); function handleTick(event) { circle0.update(stage.mouseX,stage.mouseY); stage.update(); } } |
ベクトルの話を思い出して楽しくなってきました。
STEP3: マウスから近づくほど円が逃げる
次に、マウスからただ逃げてしまうのではなく、
マウスが近づくほど円の逃げる力が大きくなるようにしてみました。
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 |
function Circle(opt_options){ var defaults = { color: 'white', defaultX: 0, defaultY: 0 }; var options = $.extend(defaults,opt_options); this.defaultX = options.defaultX; this.defaultY = options.defaultY; this.shape = new createjs.Shape(); this.shape.graphics.beginFill(options.color).drawCircle(0, 0, 30); 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.975; 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); }; function init() { var stage = new createjs.Stage("studyCanvas"); var circle0 = new Circle({ color: 'Orange', defaultX: 100, defaultY: 100 }); stage.addChild(circle0.shape); createjs.Ticker.addEventListener("tick", handleTick); function handleTick(event) { circle0.update(stage.mouseX,stage.mouseY); stage.update(); } } |
距離に応じた値を現在の位置に足すことで、
近づくほど激しく逃げる動きを作っています。
また、0.975と35というのは、ざっくりと調整した際のパラメータです。
STEP4: マウスから近づくほど円が逃げつつも、元の位置に戻ろうとする
次に、マウスからただ逃げてしまうのではなく、
元の位置に戻ろうとするようにしました。
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 |
function Circle(opt_options){ var defaults = { color: 'white', defaultX: 0, defaultY: 0 }; var options = $.extend(defaults,opt_options); this.defaultX = options.defaultX; this.defaultY = options.defaultY; this.shape = new createjs.Shape(); this.shape.graphics.beginFill(options.color).drawCircle(0, 0, 30); 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.975; 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 circle0 = new Circle({ color: 'Orange', defaultX: 100, defaultY: 100 }); stage.addChild(circle0.shape); createjs.Ticker.addEventListener("tick", handleTick); function handleTick(event) { circle0.update(stage.mouseX,stage.mouseY); stage.update(); } } |
マウスから逃げるだけでなく、もともといた位置との距離に応じて、
戻るように位置を引き算しています。
とても長くなってしまったので、
一旦ここまででvol.1を終了としたいと思います。
次回は今回作成したクラスを使用して、
一個一個の円を円形に並べて、参考にしているサイトに近づけていきます。
ただ逃げるだけでは単調な動きだった円が、
・マウスとの距離に応じた力
・元の位置に戻ろうとする力
を考慮するだけで、急に生命感を帯びてくるのはとても勉強になりました。
汎用性のあるクラス設計や、命名規則といったエンジニアのルール的なところから、
細部の動きのチューニング、命を吹き込むポイントまでいつも丁寧に教えてくださる@cocoponさん、本当にありがとうございます。