フリーセルクリア後のシャーってなるやつ作ってみた
懐かしのフリーセルについて
こんにちは、犬を飼いたい社内ニートの Castoroides です!皆さんは昔フリーセル(またの名をソリティア)やっていましたか?
私はというと、小学生時代はいつもお世話になっていました!
(どちらかというと、ピンボールの方をやっていましたが…)
今のフリーセルはストアからゲームをインストールする必要があるみたいですね。
www.microsoft.com
これですこれ!良かった、まだ健在みたい!
と、思ったら別のアニメーションも用意されているみたい😊
ということで、今のフリーセルのクリア後アニメーションはいくつかパターン化されているみたいですね!
画像撮影中、フリーセルを思ったより楽しんでしまいましたが、
ここからが本題です。
このアニメーションJavaScriptで組めないかな?
このトランプがシャーってなるやつをいつでも見れるようにJavaScriptで組んでいきたいと思います。
・
・
2日後
・
・
微調整している間にとんでもなく時間がかかってしまった…
とりあえずできたのでスクリプト全体を以下に記載します!
■ 今回作成したコード全体
<script> // 画像読み込み let imageWidth = 409 let imageHeight = 600 data = new Array( "https://3.bp.blogspot.com/-x1YD4kp-X9E/WQBAA3RKDbI/AAAAAAABD9M/5K8vEH6kYSo5M9r2jzxheGlnup2k-El5gCLcB/s800/card_heart_02.png", "https://2.bp.blogspot.com/-S-31SRfkS1o/WQBAEtCo1nI/AAAAAAABD98/fzjjCIIibhMkeoKx9NfyTo3i1TjRaONPQCLcB/s800/card_spade_01.png", "https://4.bp.blogspot.com/-hIKME4x_kbQ/WQA_3IHzulI/AAAAAAABD7o/Ft4JOq2ydaEHteja59Zzv_i3ieXnmONwwCLcB/s800/card_club_03.png", "https://4.bp.blogspot.com/-WNnDDDnVkrs/WQA_9vUTTJI/AAAAAAABD8g/PSyYSizOnVkaC37L7bLn82Ex83Tvm0BKACLcB/s800/card_diamond_04.png", "https://4.bp.blogspot.com/-bvSGYORb6Xg/WQBAF61kUOI/AAAAAAABD-Q/SalVkKKYYt85TNJJsOjmgNwfT0ao3foewCLcB/s800/card_spade_05.png", "https://3.bp.blogspot.com/-w5tuOxBszlU/WQBABseF49I/AAAAAAABD9c/9yF8y-6012MKF5UlSz-dxORnZU_YhEuxgCLcB/s800/card_heart_06.png", "https://2.bp.blogspot.com/-LXJw0prlZp0/WQA_6wZyEVI/AAAAAAABD74/eFWVHzCtgLMj_C-JPzjOd7YfhiveHytlQCLcB/s800/card_club_07.png", "https://1.bp.blogspot.com/-FZGbdtedARU/WQA__nBH5iI/AAAAAAABD84/X9BsjJbBLZcuGBr5VT2EeisGjlp0wtJpwCLcB/s800/card_diamond_08.png", "https://1.bp.blogspot.com/-lYcdLoEoqhk/WQBAGkz_02I/AAAAAAABD-Y/BzeTL07VRhc9viqmclMywil_LqOdQGL5QCLcB/s800/card_spade_09.png", "https://4.bp.blogspot.com/-Dj2Yzi1XR-E/WQBADWUni-I/AAAAAAABD9s/2Cy3plnNZGwt9sV3vDO4eCvJSSg9DGx-wCLcB/s800/card_heart_10.png" ) imag= new Array(); for (i=0; i<data.length; i++){ imag[i] = new Image(); imag[i].src = data[i]; } // getContextメソッドで描画機能を有効にする var canvas = document.querySelector("canvas"); var context = canvas.getContext('2d'); // canvas の幅と高さの指定 var canvas = document.getElementById("canvas"); canvas.width = canvas.clientWidth; canvas.height = canvas.clientHeight; // トランプのクラス var Card = function(scale, color, vx, vxs, vy, gv) { this.scale = scale; // 縮尺 this.dstWidth = dstWidth; this.dstHeight = dstHeight; this.kind = kind; // トランプの種類 this.vx = vx; // X速度 this.vxs = vxs; // 退場時の速度 this.vy = vy; // Y速度 this.gv = gv; // 重力 this.position = { // 位置 x: 0, y: 0 }; }; // 変数宣言 var Cards = []; // トランプをまとめる配列 var kind; var scale; var dstWidth; var dstHeight; var x; var xs; var y; var g; var indx; var startDate = new Date(); var interval = 2000; // ループ処理 function animloop(){ requestAnimFrame(animloop); // カードの追加 if(new Date() - startDate > interval){ startDate = new Date(); // 詳細値の作成 //Math.random() * (max - min) + min; kind = Math.random() * 10; scale = Math.random() * (0.25 - 0.1) + 0.1; dstWidth = imageWidth * scale; dstHeight = imageHeight * scale; x = Math.random() * (10) -5; xs = x / 10; y = Math.random()* 9 + 4; g = Math.random()* 0.2 + 0.3; indx = Cards.length; Cards[indx] = new Card(scale, kind, x, xs, -y, g); Cards[indx].position.x = Math.random() * (canvas.width); Cards[indx].position.y = 100; } // カードの更新 for (var i in Cards) { Cards[i].update(); } } // コピペ from https://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ window.requestAnimFrame = (function(){ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function( callback ){ window.setTimeout(callback, 1000 / 60); }; })(); // 座標の更新 Card.prototype.update = function() { this.vy += this.gv; this.position.x += this.vx; this.position.y += this.vy; // 地面の衝突判定 if (this.position.y > canvas.height - this.dstHeight) { this.vy *= -0.8; this.vx *= 0.85; this.position.y = canvas.height - this.dstHeight; if(Math.abs(this.vx) < 0.8){ this.position.x += this.vxs; } } this.draw(); }; // canvasに描画 Card.prototype.draw = function() { context.beginPath(); context.arc(this.position.x, this.position.y, this.scale, 0, 2*Math.PI, false); context.fillStyle = this.color; //context.fill(); console.log(Math.floor(this.kind)) context.drawImage(imag[Math.floor(this.kind)], this.position.x, this.position.y, this.dstWidth, this.dstHeight); }; // アニメーションの起点 animloop(); </script>
■ 解説:画像読み込み
まず、冒頭で画像の読み込み等をしてます。(いらすとやのお借りしてます)
// 画像読み込み let imageWidth = 409 let imageHeight = 600 data = new Array( "https://3.bp.blogspot.com/-x1YD4kp-X9E/WQBAA3RKDbI/AAAAAAABD9M/5K8vEH6kYSo5M9r2jzxheGlnup2k-El5gCLcB/s800/card_heart_02.png", "https://2.bp.blogspot.com/-S-31SRfkS1o/WQBAEtCo1nI/AAAAAAABD98/fzjjCIIibhMkeoKx9NfyTo3i1TjRaONPQCLcB/s800/card_spade_01.png", "https://4.bp.blogspot.com/-hIKME4x_kbQ/WQA_3IHzulI/AAAAAAABD7o/Ft4JOq2ydaEHteja59Zzv_i3ieXnmONwwCLcB/s800/card_club_03.png", "https://4.bp.blogspot.com/-WNnDDDnVkrs/WQA_9vUTTJI/AAAAAAABD8g/PSyYSizOnVkaC37L7bLn82Ex83Tvm0BKACLcB/s800/card_diamond_04.png", "https://4.bp.blogspot.com/-bvSGYORb6Xg/WQBAF61kUOI/AAAAAAABD-Q/SalVkKKYYt85TNJJsOjmgNwfT0ao3foewCLcB/s800/card_spade_05.png", "https://3.bp.blogspot.com/-w5tuOxBszlU/WQBABseF49I/AAAAAAABD9c/9yF8y-6012MKF5UlSz-dxORnZU_YhEuxgCLcB/s800/card_heart_06.png", "https://2.bp.blogspot.com/-LXJw0prlZp0/WQA_6wZyEVI/AAAAAAABD74/eFWVHzCtgLMj_C-JPzjOd7YfhiveHytlQCLcB/s800/card_club_07.png", "https://1.bp.blogspot.com/-FZGbdtedARU/WQA__nBH5iI/AAAAAAABD84/X9BsjJbBLZcuGBr5VT2EeisGjlp0wtJpwCLcB/s800/card_diamond_08.png", "https://1.bp.blogspot.com/-lYcdLoEoqhk/WQBAGkz_02I/AAAAAAABD-Y/BzeTL07VRhc9viqmclMywil_LqOdQGL5QCLcB/s800/card_spade_09.png", "https://4.bp.blogspot.com/-Dj2Yzi1XR-E/WQBADWUni-I/AAAAAAABD9s/2Cy3plnNZGwt9sV3vDO4eCvJSSg9DGx-wCLcB/s800/card_heart_10.png" ) imag= new Array(); for (i=0; i<data.length; i++){ imag[i] = new Image(); imag[i].src = data[i]; } // getContextメソッドで描画機能を有効にする var canvas = document.querySelector("canvas"); var context = canvas.getContext('2d');
■ 解説:クラスの定義
ここはクラスを定義しています。// トランプのクラス var Card = function(scale, color, vx, vxs, vy, gv) { this.scale = scale; // 縮尺 this.dstWidth = dstWidth; this.dstHeight = dstHeight; this.kind = kind; // トランプの種類 this.vx = vx; // X速度 this.vxs = vxs; // 退場時の速度 this.vy = vy; // Y速度 this.gv = gv; // 重力 this.position = { // 位置 x: 0, y: 0 }; };
■ 解説:メインのループ処理
ここはメインのループ処理です。// ループ処理 function animloop(){ requestAnimFrame(animloop); // カードの追加 if(new Date() - startDate > interval){ startDate = new Date(); // 詳細値の作成 kind = Math.random() * 10; scale = Math.random() * (0.25 - 0.1) + 0.1; dstWidth = imageWidth * scale; dstHeight = imageHeight * scale; x = Math.random() * (10) -5; xs = x / 10; y = Math.random()* 9 + 4; g = Math.random()* 0.2 + 0.3; indx = Cards.length; Cards[indx] = new Card(scale, kind, x, xs, -y, g); Cards[indx].position.x = Math.random() * (canvas.width); Cards[indx].position.y = 100; } // カードの更新 for (var i in Cards) { Cards[i].update(); } }
この部分でクラス追加の制御をしてます。
// カードの追加 if(new Date() - startDate > interval){ startDate = new Date();</code>
今回はvar interval = 2000;
に設定しているので2秒間隔ですね。
■ 解説:クラスの更新
Cards[i].update();
で以下の処理を呼び出してます。
// 座標の更新 Card.prototype.update = function() { this.vy += this.gv; this.position.x += this.vx; this.position.y += this.vy; // 地面の衝突判定 if (this.position.y > canvas.height - this.dstHeight) { this.vy *= -0.8; this.vx *= 0.85; this.position.y = canvas.height - this.dstHeight; if(Math.abs(this.vx) < 0.8){ this.position.x += this.vxs; } } this.draw(); };
地面に衝突する(y座標がcanvasの大きさに達する)と、y軸の速度[vy]をマイナスにすることでトランプを上に描画しています。
x軸の速度が絶対値で0.8以下となった際は、これ以上バウンドしないと判断し、生成時の速度×0.1で等速に動かしています。
動かしてみた!
動かしてみると、こんな感じです!
youtu.be
ぜひ、以下リンクよりブラウザでお試しくださいね!
https://castoroides.github.io/others/Freecell_fall.html