- 投稿日:2020-02-07T23:31:01+09:00
ショートコーディングで階乗計算機を作る
ショートコーディングで階乗計算機を作る
動作サンプル https://www.o--o.cf/
<script>for(a=prompt(),i=1,k=a;k--;)i*=k+1;document.write(i)</script>たったこれだけ。
メモ帳に貼り付けてやってみてください。
以上
- 投稿日:2020-02-07T23:11:20+09:00
#twitch の配信中のチャットログを #discord でロギングするBOT
githubと改行ルールが違って二重管理になるため、詳細は下記のgithubのREADME.mdを参照のこと。
https://github.com/github895439/twitch_chat_logging_bot
- 投稿日:2020-02-07T22:10:27+09:00
Next.jsで環境変数を利用するのにdotenvは必要ない
tl;dr
next.config.js
のenv
に任意の値を設定するだけ
dotenv
を使いたい場合にはnext.config.js
でdotenv
を呼び出せばよい解説
Next.jsはVersion 8からbuild-inで環境変数(Environment Variables)に対応しています。
Blog - Next.js 8 | Next.js
下記リンクを参考にすれば容易に設定できるでしょう。
Environment Variables - Documentation | Next.jsただし既存のdotenvシステムを利用したいケースもあるかと思います。
本体レポジトリにdotenvを使用したexampleも存在しているので参照してみましょう。
next.js/examples/with-dotenv at v9.2.1 · zeit/next.jsnext.config.jsrequire('dotenv').config() module.exports = { env: { // Reference a variable that was defined in the .env file and make it available at Build Time TEST_VAR: process.env.TEST_VAR, }, }特に難しいことはなく
dotenv
が公式で推奨しているようにconfigを実行するだけです。
motdotla/dotenv: Loads environment variables from .env for nodejs projects.As early as possible in your application, require and configure dotenv.
require('dotenv').config()
dotenv-webpack
は(通常)必要ない昔の記事では
dotenv-webpack
の使用が推奨されていることがあります。
これはbuild-inで対応していなかったVesion 8以前にexampleにて推奨されていた方法です。
現在でも動作はしますが推奨されていません。以下が該当のIssueとPRです。
The with-dotenv example does not seem to follow current Next.js conventions · Issue #9371 · zeit/next.js
Updatedotenv
example by francismarcus · Pull Request #9372 · zeit/next.jsNext.jsは内部でwebpackを用いてビルドを行っています。それをカスタマイズする方法です。
筆者はNext.jsにおいてwebpackのカスタマイズは最小限にするべきだと考えています。
今回のようなケースでは使う必要はないかと思われます。Next.js 8以降で
dotenv-webpack
を利用するケースが思いつかないのでよかったらコメントください。
- 投稿日:2020-02-07T21:48:38+09:00
(JavaScript)イベント駆動について
今日はJavaScriptとjQueryについて学習しました。
JavaScriptは「イベント駆動」という概念に基づいて設計されているそうです。
Javaスクリプトにおけるイベントとは、HTMLの要素に対して行われた処理要求のことで
例えば、「ある要素の上にマウスカーソルを乗せる」、「ブラウザ上のボタンをクリックする」なども
イベントに当たります。
そのイベントが発生したらそれをきっかけにコードが実行される仕組みが「イベント駆動」です。
Webページを見ていてカッコいい動作は大体JavaScriptのおかげなのだなと思いました。
- 投稿日:2020-02-07T21:19:27+09:00
Web Speech API で読み上げ位置を取得
ブラウザでテキストを読み上げる Web Speech API で読み上げ位置を取得します。
Web Speech API
語学学習に音声合成が使えないかとクラウドサービスを調べましたが、契約が必要で料金が気になりました。ですが最近のブラウザでは API として実装されていることを知りました。
概要は次の記事に詳しいです。
- Webページでブラウザの音声合成機能を使おう - Web Speech API Speech Synthesis
- Web Speech APIの実装 - Speech Synthesis API | CodeGrid
詳細はこれらに譲り、要点をサンプルで示します。
読み上げ
主に 2 つのインターフェイスを使います。
API がサポートされているブラウザでは
speechSynthesis
というインスタンスがデフォルトで存在します。読み上げはSpeechSynthesisUtterance
のインスタンスで言語を指定してspeechSynthesis.speak()
に渡します。読み上げの中止はspeechSynthesis.cancel()
です。実装例を示します。API が使える環境かどうかは
speechSynthesis
の存在をチェックします。See the Pen Web Speech API のテスト (1) by 七誌 (@7shi) on CodePen.
音声の指定
使用できる音声が複数ある場合、それらを選択できるようにします。音声をローカルにインストールしていなくても、Chromium系のブラウザではオンラインのエンジンが使えるものもあります。
音声一覧は
speechSynthesis.getVoices()
で取得できます。注意点として初回の呼び出しで空の配列が返って来る環境があります。その場合は裏で準備が進んでいるため、しばらくして再度呼べば取得できます。取得のタイミングはonvoiceschanged
イベントで通知されます。その仕様を踏まえた実装例です。取得に失敗した場合、コールバックで再度取得します。
See the Pen Web Speech API のテスト (2) by 七誌 (@7shi) on CodePen.
読み上げ位置を取得
今どこを読み上げているかという情報は
onboundary
イベントで通知されます。実装例を示します。最後に
onend
イベントで選択を解除します。See the Pen Web Speech API のテスト (3) by 七誌 (@7shi) on CodePen.
読み上げの際に形態素解析が行われているのが垣間見えて面白いです。
SSML
仕様には
onmark
イベントがあります。これが使えれば細かい状況が把握できそうです。発話された utterance が SSML (音声合成マークアップ言語) の "mark" タグに達した時に発火します。
しかし現状では SSML はサポートされていない環境が多いようです。
仕様上、text にプレーンテキストのほかに SSML を指定できるのですが、現在、一部の Voice でしか対応していないようです。
実際に試してみましたが、確かにそのような挙動になりました。SSML の記事はあまり見当たりませんが、まだ安定して使える段階ではないようです。
関連記事
Promise や async/await と組み合わせて使用する例です。
自分の勉強を兼ねてブログで語学記事を執筆することを計画しており、テンプレートを作成中です。対訳を記述しやすくするなどの利便性に注力しています。
- 投稿日:2020-02-07T21:07:16+09:00
Tabulatorを使ってみた日
Tabulatorとは
html5の表を簡単に作成できるJavaScriptライブラリー
手順
・jqueryをここからCDNで読み込んだ.
・GitHubリポジトリからTabulatorライブラリーをコピーして、tabulator.cssとtabulator.jsファイルをプロジェクトに読み込む。hoge.html<! DOCTYPE html> <link rel="stylesheet" href="tabulator.css"> <script type="text/javascript" src="tabulator.js"> <div id="example-table"></div>hoge.js$("#example-table").tabulator({ columns:[ {title:"Name", field:"name", sortable:true, width:200}, {title:"Progress", field:"progress", sortable:true, sorter:"number"}, {title:"Gender", field:"gender", sortable:true}, {title:"Favourite Color", field:"col", sortable:false}, {title:"Date Of Birth", field:"dob"}, {title:"Cheese Preference", field:"cheese"}, ], }); var sampleData = [ {id:1, name:"Oli Bob", progress:12, gender:"male", rating:1, col:"red", dob:"", car:1, lucky_no:5, cheese:"Cheader"}, {id:2, name:"Mary May", progress:1, gender:"female", rating:2, col:"blue", dob:"14/05/1982", car:true, lucky_no:10, cheese:"Gouda"}, {id:3, name:"Christine Lobowski", progress:42, gender:"female", rating:0, col:"green", dob:"22/05/1982", car:"true", lucky_no:12, cheese:"Manchego"}, {id:4, name:"Brendon Philips", progress:100, gender:"male", rating:1, col:"orange", dob:"01/08/1980", lucky_no:18, cheese:"Brie"}, {id:5, name:"Margret Marmajuke", progress:16, gender:"female", rating:5, col:"yellow", dob:"31/01/1999", lucky_no:33, cheese:"Cheader"}, ];参考
HTMLのテーブルをJSONで動的に更新できる「Tabulator」がスゴい!
Tabulatorを使った見やすい表形式のWebアプリの作成
- 投稿日:2020-02-07T20:42:12+09:00
初心者によるプログラミング学習ログ 232日目
100日チャレンジの232日目
twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。
232日目は
おはようございます
— ぱぺまぺ@webエンジニアを目指したい社畜 (@yudapinokio) February 6, 2020
232日目
・MySQLを自分のパソコンに導入
・udemyでcss+javascript講座#早起きチャレンジ#駆け出しエンジニアと繋がりたい#100DaysOfCode
- 投稿日:2020-02-07T18:29:42+09:00
【備忘録】コンソール君「ん?そのプロパティ未定義やぞ?」 俺「何言うとんねんワレェ!」【まぬけ注意】
※ご覧いただいてる方々に諸注意です。投稿主はプログラミング・環境構築含め独学のため、誤解している点や間違っている点が多くある場合があります。もし、私が間違った知識・情報をドヤ顔で記載していた場合、優しくご指摘いただくようよろしくお願いします。
Reactの技術本のソースコード模写中の話
模写終了後ターミナルでサーバー起動を確認。
動かそうとするとlocalhostの接続はできるものの画面は何も表示されず…↓
ハテ・・・困りましたわ・・・
とりあえずchrome側のコンソールでエラーを確認しまっせ。↓
俺「うーんと、キャノットリードプロパティゲットスコアオブアンディフィネッドね。うんうんなるほど。」
getScoresプロパティが定義されてないよーということらしい。
今回のソースは、
Material-uiを使ったジャンケンアプリ作成
↓
ジャンケンの判定や記録をするコードはJyanken.jsへ
メインのコードはindex.jsへという形で全体のソースは構成されている。つまりスコアを記録する役割を持つgetScoresプロパティはJyanken.jsに書かれていて、
index.jsにimportする形でアプリが動作するというわけなんですが、このJyanken.jsで定義されたgetScoresプロパティがなぜか未定義扱いになっているということらしい。やったこととしては↓
・import部分でミスってる?import周辺の確認。→問題なし
・定義部分と使用部分の誤字?確認するも…→問題なし
と凡ミスを探していると、ここで本日のMVPワイ、とんでもないことに気づく。
他にJyanken.jsのJyankenクラス自体が使えていないのを発見、getScoreプロパティどころかJyankenクラスが持っているプロパティ全部使えていませんでした。
index.jsのコンストラクター部分を確認。やっぱり・・・
クラスの機能を使うためのコンストラクターに設定してたのはコンストラクター部分this.Jyanken = new Jyanken()使用部分this.setState({scores: this.jyanken.getScores()})また沼プレイしてしまいましたわ・・・
コード触り始めてから随分と立ってるのにこんなことばっかしとるんです私。コンストラクター部分修正this.jyanken = new Jyanken()本日の学び
大文字と小文字はきちんと分けて使いましょう。
いつになったらコーダー初心者抜けれるのでしょうか。
現場からは以上です~~~(^_^)ノシ(激萎えしながら)
- 投稿日:2020-02-07T16:34:23+09:00
任意の HTML 要素をフォーカス可能にする tabindex 属性
はじめに
フロントエンド開発をしている皆さんは普段からアクセシビリティを考慮した実装をしていますか?
近年の SPA を中心としてフロントエンド開発の手法が体系化されてきた流れで、アプリケーションの設計や状態管理等の議論は増えましたが、(特に日本では) アクセシビリティについての議論はまだ少ないように思われます。
そこで今回は、HTML のtabindex
属性と具体的な事例を紹介します。結論
- アクセシビリティのために Web ページは tab キーのみで操作できるのが理想
tabindex
属性を使えば tab キーで任意の要素にフォーカスさせたり、フォーカスされる順番を変更したりできる- インタラクティブではない要素に使用すると逆にアクセシビリティが悪化するので注意が必要
tabindex
属性を指定することでインタラクティブな要素を作ることができるなぜ重要なのか
Web は今や世界中の誰もが利用できる場所となっているため公共施設と言っても過言ではありません。そこでは何の不自由もなく利用できる人もいれば 支援技術の力を借りて利用している人 もいます。ビジネスで開発している場合はそのようなユーザーを最初から考慮することは難しいかもしれませんが、彼らを無視して自らターゲットを狭めていること は否めないでしょう。
また、一般のユーザーにとってもアクセシビリティが高い Web サイトはコンテンツを楽しんだり、使いこなしたりする上で重宝されます。特に tab キーによる移動 は一般のユーザーにも広く使われています。
tabindex 属性について
任意の HTML 要素に
tabindex
属性を指定すると、その要素をキーボードによる順次ナビゲーション (一般的には tab キーによる移動) に追加するか、どの順番にするかを決めることができます。指定できる値は整数値ですが、与える値で挙動が変化します。
負の値
: 指定された要素はフォーカス可能になるが、順次ナビゲーションでは無視される0
: 指定された要素はフォーカス可能になり、階層順で順次ナビゲーションされる正の値
: 指定された要素はフォーカス可能になり、値の順番でナビゲーションされる※
負の値
はfucus()
メソッドを使うことでフォーカスできます採用事例
世界で広く使われている Web サイトでは既に
tabindex
属性が活用されています。今回紹介するのは Airbnb の事例です。トップページで tab キーを押すと、最初にコンテンツにスキップするか問われます。
再び tab キーを押すとヘッダーへの順次ナビゲーションが続き、enter キーを押すとコンテンツへの順次ナビゲーションにスキップされます。
同様の手法は Google の検索画面 や mozilla の MDN でも採用されており、世界では一般的なのかもしれません。
注意事項
便利な
tabindex
属性ですが、多用すると不必要な要素もフォーカスされるため逆にアクセシビリティを悪くしてしまいます。そのため
tabindex
属性を使用する要素はインタラクティブな要素のみにすることが強く推奨されています。ここでいうインタラクティブな要素は以下のものです。
a
(href
属性が存在する場合)audio
(controls
属性が存在する場合)button
details
embed
iframe
img
(usemap
属性が存在する場合)input
(type
属性がhidden
ではない場合)label
object
(usemap
属性が存在する場合)select
textarea
video
(controls
属性が存在する場合)他にも
tabindex
属性を指定することで他の要素もインタラクティブな要素にすることができます。また、
1
以上の値を指定すると利用者が意図しない場所にフォーカスされ、特に支援技術に頼って Web を閲覧するユーザーは不便なのでなるべく負の値
か0
を使うようにしましょう。tabindex 属性でインタラクティブな要素を作る
今回作るのは
span
要素で作るチェックボックスです。
React と TypeScript、styled-components を使用するので馴染みのない方は雰囲気だけでも掴んでいただけると幸いです。まずはチェックボックスのコンポーネントです。
親コンポーネントで状態の管理をしているのを想定して、props
でchecked
(チェックされているか) とonChange
(チェックを切り替える関数) を受け取っています。
Wrapper
に注目するとtabIndex
属性をしていしているのがわかります。(React だとtabindex
はtabIndex
と記述しなければならない)Checkbox.tsximport React from 'react' import styled from 'styled-components' import CheckIcon from './CheckIcon' type Props = { onChange: () => void checked: boolean } const Checkbox: React.FC<Props> = ({ checked, onChange }) => { const toggleChecked = (event: React.KeyboardEvent<HTMLSpanElement>) => { if (event.key !== 'Enter' || !onChange) return onChange() } return ( <Wrapper tabIndex={0} onKeyPress={toggleChecked}> <FakeCheckbox checked={checked}> {checked && <CheckIcon />} </FakeCheckbox> </Wrapper> ) } const Wrapper = styled.span` border: 1px solid #00000020; border-radius: 4px; cursor: pointer; width: 24px; height: 24px; &:focus { border: 1px solid #1976d2; outline: none; } ` const FakeCheckbox = styled.span<{ checked: boolean }>` background-color: ${({ checked }) => checked && '#1976d2'}; border-radius: 3px; display: flex; width: 100%; height: 100%; ` export default CheckboxCSS は省略していますが親のコンポーネントです。
Form.tsximport React from 'react' import Checkbox from './Checkbox' const Form: React.FC = () => { const [isChecked, setIsChecked] = useState(false) const toggleChecked = () => { setIsChecked(!isChecked) } return ( <form> <label> <Checkbox checked={isChecked} onChange={toggleChecked} /> 利用規約に同意 </label> </form> ) }実際には次のように動きます。
これで
tabindex
属性を使ってインタラクティブかつフォーカスができる要素を作ることが出来ました。おわりに
今回はアクセシビリティを向上させるための手段として
tabindex
属性を紹介しました。普段アクセシビリティを気にしている方は Light House の結果を気にすることが多いと思いますが、Light House ではフォーカスに関するスコアは出ないのでtabindex
属性は目新しい情報だったかもしれません。これを機に Web のアクセシビリティについて検討しようという方が増えると嬉しいです。もし記事の内容に間違いや誤解を招く箇所があればコメント欄か Twitter (@saitoeku3) で指摘していただけると助かります。
参考
- 投稿日:2020-02-07T15:27:46+09:00
ビット演算による商(切り捨て)・余り計算
ビット演算は低級言語に近いのでどんな言語においても速い.
2 の n 乗での割り算で特に便利.一般
商 : 割り算の解(小数)に対して 0 を OR 演算
(余 : % 演算子を使用, ビット演算じゃない)ex.17÷6の商と余りq = (17/6)|0; // 商 = 2 r = 17%6 // 余り = 5結局途中で小数が発生するからそんなに速くはないが, Math.floor よりは軽い.
2 の n 乗 (1, 2, 4, 8, 16...) での割り算の場合
商 : 左へ n ビットシフト
余 : (2^n)-1 で AND 演算ex.17÷8の商と余りq = 17>>3; // 商 = 2 r = 17&7; // 余り = 1ちなみに
バイナリ周り// N bit のデータを格納するのに必要な byte 数は byteLength = 1 + ((N-1)>>3); // Uint8Array の先頭から N ビット目を取り出すには bitN = uint8array[N>>3] & (1<<(N&7));
- 投稿日:2020-02-07T14:31:23+09:00
[js] filter,reject,map,reduceの基本を押さえる
高階関数(Higher Order Functions)を使いこなしてこそのjsだと偉い人が言っていました。
ここではその代表格である、filter, reject, map, reduceの基本的な使い方を纏めておきます。
ちなみに、これはとあるYoutubeの講座を見て、自分用にまとめておこうと思い立っただけですので悪しからず。高階関数とは
高階関数とは、引数に値の替わりに関数を受け取る関数のことです。
JavaやPHPなどと違い、jsは関数も値であると考え、変数に代入できたりするのが特徴ですよね。
そうした特徴をふんだんに使っていこうぜってことみたいです。
(高階関数はjsに限った話ではありませんが)filterとrejectの使い方
filterとrejectは配列に条件を渡して、
その条件を通ったもの要素だけを纏めた新しい配列を作る関数です。
当然要素数は元の配列より少なくなります。早速中身を見てみましょう。
まずは、比較したいので高階関数を使わない例から見てみます。//動物の名前と種類を格納したデータを用意 let animals = [ {name: 'Tom', species: 'rabbit'}, {name: 'Caro', species: 'dog'}, {name: 'Bob', species: 'dog'}, {name: 'Ken', species: 'cat'}, {name: 'Jimmy', species: 'fish'} ] // forを使ってdogだけを取り出す let dogs = [] for (let i = 0; i < animals.length; i++) { if (animals[i].species === 'dog') { dogs.push(animals[i]) } }こんな感じでforのループを使って、
配列から欲しい情報(犬だけに絞った配列)を取得しました。
では、高階関数のfilterを使ってみます。filter_sample.js//動物の名前と種類を格納したデータを用意 let animals = [ {name: 'Tom', species: 'rabbit'}, {name: 'Caro', species: 'dog'}, {name: 'Bob', species: 'dog'}, {name: 'Ken', species: 'cat'}, {name: 'Jimmy', species: 'fish'} ] // filterでdogを取り出す const dogs = animals.filter((animal) => { return animal.species === 'dog' })filterは引数にbooleanを返す関数を渡して、trueになった場合のみ
新しい配列に追加していきます。なにこれ便利!!
記述量や読みやすさもforより分かりやすいですね。でも良さはそこだけじゃないんですよ。
真のすばらしさは機能を分離できるところにあります。
つまり、引数に渡した関数は完全に独立した機能として動くことができます。
例を見たほうが早いですねfilter_sample.js//動物の名前と種類を格納したデータを用意 const animals = [ {name: 'Tom', species: 'rabbit'}, {name: 'Caro', species: 'dog'}, {name: 'Bob', species: 'dog'}, {name: 'Ken', species: 'cat'}, {name: 'Jimmy', species: 'fish'} ] // dogであるか判定する関数を切り出す const isDog = (animal) => { return animal.species === 'dog' } // dogだけを取得する const dogs = animals.filter(isDog) // dog以外だけを取得する const otherAnimals = animals.reject(isDog)こんな感じで、渡す関数をdogであるか判定する独立した関数にしました。
それを使いまわしてさらっとrejectも使ってみました。
rejectはfilterと反対の動きをする関数で、falseになった値のみを配列に詰めます。
これにより、犬だけの配列と、犬以外だけの配列を簡単に作ることが出来ました。なるほど、確かにこりゃ便利だ!!
mapの使い方
mapも配列の一つ一つの要素を見ていきながら、
引数で渡した関数を実行するという点では先ほどと同様です。
ただ、filterやrejectと違って、新しい配列の要素数は変わらず、
元の配列の要素を"変形"させて新しく詰めなおすようなイメージです。では先ほどと同じく、普通の例から見てみます。
//動物の名前と種類を格納したデータを用意 const animals = [ {name: 'Tom', species: 'rabbit'}, {name: 'Caro', species: 'dog'}, {name: 'Bob', species: 'dog'}, {name: 'Ken', species: 'cat'}, {name: 'Jimmy', species: 'fish'} ] // for文を使って、'Tomはrabbitです'のような文章を詰める let names = [] for (let i = 0; i < animals.length; i++) { names.push(animals[i].name + 'は' + animals[i].species + 'です') }まあ普通ですね笑
mapを使うとこうなりますconst animals = [ {name: 'Tom', species: 'rabbit'}, {name: 'Caro', species: 'dog'}, {name: 'Bob', species: 'dog'}, {name: 'Ken', species: 'cat'}, {name: 'Jimmy', species: 'fish'} ] // mapは引数の関数で変形させた値を新しい配列に詰める let names = animals.map((animal) => { return animal.name + 'は' + animal.species + 'です' })forを使った例より記述量が減りました。
個人的にはmapの方が断然読みやすいですね。reactとかやるときは、コンポーネントやJSXをmapの中でreturnしたり超頻出の関数です
では、最後にreduce
reduceの使い方
配列内の値を全部合計した値を取る例で考えます。
まずはforの書き方で。let orders = [ {amount: 250}, {amount: 500}, {amount: 750}, {amount: 900}, ] let totalAmount = 0 for (let i = 0; i < orders.length; i++){ totalAmount += orders[i].amount }て感じになりますね。
ではreduceを使ってみましょう。
reduceは色々と書き方あって大変なので、一番基本的なものだけ紹介します。let orders = [ {amount: 250}, {amount: 500}, {amount: 750}, {amount: 900}, ] let totalAmount = orders.reduce((sum, order) => { return sum + order.amount }, 0)reduce第一引数には関数、第二引数には初期値を渡します。
今回は、
第一引数:(sum, order) => { return sum + order.amount }
第二引数:0
となっていますね!さらに第一引数に渡した関数が、sumとorderという2つの引数を持っています。
orderは言わずもがなorders配列のそれぞれの要素が入ってきます。試しにconsole.logして値の遷移を見てみましょう
let totalAmount = orders.reduce((sum, order) => { //途中経過のlogを出してみる console.log('sum:' , sum) console.log('order:' , order) return sum + order.amount }, 0) /** console.logの結果 **/ sum: 0 order: { amount: 250 } sum: 250 order: { amount: 500 } sum: 750 order: { amount: 750 } sum: 1500 order: { amount: 900 }sumの最初は、第二引数で渡した初期値である0が入っていますね。
そこからsumに対してorder.amountを足し合わせているので
sumがどんどん増加して合計値になっています。終わりに
特にmapやreduceは利用できる幅がもっと広いはずです。
他にも便利なものがあったら教えてください!
- 投稿日:2020-02-07T13:34:53+09:00
【サンプルコード】JavaScriptでルーレットゲームをプログラミング
どうも、UT(@ut_1029)です。ブログ(UTの日常)の紹介です。
JavaScriptでルーレットゲームをプログラミングしたサンプルコードを紹介します。
ルーレットゲームは、JavaScriptのアニメーション勉強に最適でした。JavaScriptで作成したルーレットゲームのサンプルコード
JavaScriptでプログラミングしたルーレットゲームのサンプルコードを紹介します。
※HTML + CSS + JavaScriptを1つのファイルに記述しています。<html> <head> <meta charset="UTF-8"> <title>JavaScriptでルーレットゲームを作成</title> <style> body { text-align: center; padding: 0; margin: 0; } ul { list-style: none; } .roulette { width: 90%; height: 500px; margin: auto; position: relative; border : solid 1px #333 ; } .panels { position: relative; margin: 0 auto; width: 400px; height: 400px; } .panel { width: 200px; position: absolute; } .panel img { vertical-align: bottom; } </style> </head> <body> <h1>JavaScriptでルーレットゲームを作成</h1> <div class="roulette"> <div class="panels"> <img src="/ut/roulette/img/panel.png" class="panel"> <img src="/ut/roulette/img/panel.png" class="panel"> <img src="/ut/roulette/img/panel.png" class="panel"> <img src="/ut/roulette/img/panel.png" class="panel"> <img src="/ut/roulette/img/panel.png" class="panel"> <img src="/ut/roulette/img/panel.png" class="panel"> <img src="/ut/roulette/img/panel.png" class="panel"> <img src="/ut/roulette/img/panel.png" class="panel"> <img src="/ut/roulette/img/panel.png" class="panel"> <img src="/ut/roulette/img/panel.png" class="panel"> <img src="/ut/roulette/img/panel.png" class="panel"> <img src="/ut/roulette/img/panel.png" class="panel"> <img src="/ut/roulette/img/panel.png" class="panel"> <img src="/ut/roulette/img/panel.png" class="panel"> <img src="/ut/roulette/img/panel.png" class="panel"> <img src="/ut/roulette/img/panel.png" class="panel"> <img src="/ut/roulette/img/panel.png" class="panel"> <img src="/ut/roulette/img/panel.png" class="panel"> <img src="/ut/roulette/img/panel.png" class="panel"> <img src="/ut/roulette/img/panel.png" class="panel"> </div> </div> <div> <button type="button" class="btn-start">start</button> <button type="button" class="btn-stop" disabled="true">stop</button> </div> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script> (function (global) { "use strict"; /* * ルーレットの回転速度(実行毎秒数) */ var sec = 100; /* * ルーレットの停止フラグ */ var stopFlag = false; /* * ・ルーレットのパネル数 * ・ルーレットの回転数 */ var panelNum = 0, loopCount = 0; /** * ルーレット */ var Roulette = { /** * 初期化処理 */ init: function () { // ルーレットのパネル配置を調整 var $panels = $('.panel'); var deg = 360.0 / $panels.length; var red = (deg * Math.PI / 180.0); var r = $panels.width() / 2; var adjustY = ($panels.width() / 2) - ($panels.height() / 2); $panels.each(function (i, elem) { var tmp = i - ($panels.length / 4); var x = Math.cos(red * tmp) * r + r; var y = Math.sin(red * tmp) * r + r + adjustY; var t = tmp * deg; $(elem).css({ 'left': x, 'top' : y, 'transform': 'rotate(' + t + 'deg)' }); }); // ルーレットのパネル数を代入 panelNum = $panels.length; }, /** * ルーレットの回転開始 */ start: function () { stopFlag = false; Roulette.animation(); }, /** * ルーレットの回転停止 */ stop: function () { stopFlag = true; }, /** * ルーレットの回転アニメーション */ animation: function () { $('.panels').animate({ deg: -((360 / panelNum) * loopCount) },{ duration: sec, step: function (now) { $('.panels').css({ transform: 'rotate(' + now + 'deg)' }); }, complete: function () { if (stopFlag) { return ; } loopCount++; Roulette.animation(); } }); } }; global.Roulette = Roulette; })((this || 0).self || global); $(document).ready(function () { // 初期化処理を実行 Roulette.init(); /** * スタートボタンのクリックイベント */ $('.btn-start').click(function () { $(this).attr('disabled', true); Roulette.start(); $('.btn-stop').attr('disabled', false); }); /** * ストップボタンのクリックイベント */ $('.btn-stop').click(function () { $(this).attr('disabled', true); Roulette.stop(); $('.btn-start').attr('disabled', false); }); }); </script> </body> </html>See the Pen [sample code] javascript roulette by UTのCodePen (@ut_1029) on CodePen.
解説は、ブログ(UTの日常)で!
- 投稿日:2020-02-07T13:29:16+09:00
【サンプルコード】JavaScriptでスロットゲームをプログラミング
どうも、UT(@ut_1029)です。ブログ(UTの日常)の紹介です。
JavaScriptでスロットゲームをプログラミングしたサンプルコードを紹介します。
スロットゲームは、JavaScriptのアニメーション勉強に最適でした。
スロットのリールの表示位置をJavaScriptで制御して、回転のアニメーション制御処理がポイントです。
JavaScriptで作成したスロットゲームのサンプルコード
JavaScriptでプログラミングしたスロットゲームのサンプルコードを紹介します。
※HTML + CSS + JavaScriptを1つのファイルに記述しています。<html> <head> <meta charset="UTF-8"> <title>JavaScriptでスロットゲームを作成</title> <style> body { text-align: center; padding: 0; margin: 0; } ul { list-style: none; } .slot { width: 90%; height: 500px; overflow: hidden; margin: auto; border : solid 1px #333 ; } .slot-frame { height: 500px; position: relative; overflow: hidden; border : solid 1px #333 ; } .reels { width: 31%; position: absolute; } .reels:nth-child(1) { left: 0; } .reels:nth-child(2) { left: 33%; } .reels:nth-child(3) { right: 0; } .reel { height: 270px; } .reel img { display: block; width: 89%; margin: auto; } </style> </head> <body> <h1>JavaScriptでスロットゲームを作成</h1> <div class="slot"> <div class="slot-frame"> <ul class="reels"> <li class="reel"><img src="/youtube/slot/img/slot1.jpg"></li> <li class="reel"><img src="/youtube/slot/img/slot2.jpg"></li> <li class="reel"><img src="/youtube/slot/img/slot1.jpg"></li> <li class="reel"><img src="/youtube/slot/img/slot3.jpg"></li> <li class="reel"><img src="/youtube/slot/img/slot4.jpg"></li> <li class="reel"><img src="/youtube/slot/img/slot5.jpg"></li> <li class="reel"><img src="/youtube/slot/img/slot1.jpg"></li> <li class="reel"><img src="/youtube/slot/img/slot2.jpg"></li> </ul> <ul class="reels"> <li class="reel"><img src="/youtube/slot/img/slot5.jpg"></li> <li class="reel"><img src="/youtube/slot/img/slot2.jpg"></li> <li class="reel"><img src="/youtube/slot/img/slot4.jpg"></li> <li class="reel"><img src="/youtube/slot/img/slot1.jpg"></li> <li class="reel"><img src="/youtube/slot/img/slot3.jpg"></li> <li class="reel"><img src="/youtube/slot/img/slot4.jpg"></li> <li class="reel"><img src="/youtube/slot/img/slot5.jpg"></li> <li class="reel"><img src="/youtube/slot/img/slot2.jpg"></li> </ul> <ul class="reels"> <li class="reel"><img src="/youtube/slot/img/slot3.jpg"></li> <li class="reel"><img src="/youtube/slot/img/slot2.jpg"></li> <li class="reel"><img src="/youtube/slot/img/slot5.jpg"></li> <li class="reel"><img src="/youtube/slot/img/slot1.jpg"></li> <li class="reel"><img src="/youtube/slot/img/slot4.jpg"></li> <li class="reel"><img src="/youtube/slot/img/slot5.jpg"></li> <li class="reel"><img src="/youtube/slot/img/slot3.jpg"></li> <li class="reel"><img src="/youtube/slot/img/slot2.jpg"></li> </ul> </div> </div> <div> <button type="button" class="btn-start">start</button> <button type="button" class="btn-reset" disabled="true">reset</button> </div> <div> <button type="button" class="btn-stop" data-val="0" disabled="true">stop 0</button> <button type="button" class="btn-stop" data-val="1" disabled="true">stop 1</button> <button type="button" class="btn-stop" data-val="2" disabled="true">stop 2</button> </div> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script> (function (global) { "use strict"; /* * スロットのリール回転速度(実行毎秒数) */ var sec = 100; /* * スロットのリール情報 * ・スロットのリールelement * ・スロットのリール停止フラグ * ・スロットのリール回転数 */ var $reels = [], stopReelFlag = [], reelCounts = []; /* * 位置情報 */ var slotFrameHeight = 0, slotReelsHeight = 0, slotReelItemHeight = 0, slotReelStart = 0, slotReelStartHeight = 0; /** * スロット */ var Slot = { /** * 初期化処理 */ init: function init() { $reels[0] = $reels[1] = $reels[2] = null; stopReelFlag[0] = stopReelFlag[1] = stopReelFlag[2] = false; reelCounts[0] = reelCounts[1] = reelCounts[2] = 0; }, /** * スタートボタンのクリックイベント */ start: function () { for (var index = 0; index<3; index++) { Slot.animation(index); } }, /** * ストップボタンのクリックイベント */ stop: function (index) { stopReelFlag[index] = true; if (stopReelFlag[0] && stopReelFlag[1] && stopReelFlag[2]) { // 全リール停止したらリセットボタンを押下できるようにする $('.btn-reset').attr('disabled', false); } }, /** * 位置情報の初期化処理 */ resetLocationInfo: function () { slotFrameHeight = $('.slot-frame').outerHeight(); slotReelsHeight = $('.reels').eq(0).outerHeight(); slotReelItemHeight = $('.reel').eq(0).outerHeight(); slotReelStart = 5 - 2; // リールの上下は、半分だけ表示させるための位置調整 slotReelStartHeight = -slotReelsHeight; slotReelStartHeight = slotReelStartHeight + slotFrameHeight + ((slotReelItemHeight * 3 / 2) - (slotFrameHeight / 2)); $('.reels').css({ 'top':slotReelStartHeight }); }, /** * スロットの回転アニメーション */ animation: function (index) { console.log('アニメーション', '開始', index); if (reelCounts[index] >= 5) { reelCounts[index] = 0; } console.log('slotReelStartHeight', slotReelStartHeight); console.log('reelCounts[index]', reelCounts[index]); console.log('slotReelsHeight', slotReelsHeight); console.log('top', slotReelStartHeight + (reelCounts[index] * slotReelItemHeight)); $('.reels').eq(index).animate({ 'top': slotReelStartHeight + (reelCounts[index] * slotReelItemHeight) }, { duration: sec, easing: 'linear', complete: function () { console.log('アニメーション', '完了', index, reelCounts[index]); if (stopReelFlag[index]) { console.log('アニメーション', 'ストップ', index, reelCounts[index]); return ; } // 移動階数をカウント reelCounts[index]++; // スロット回転のアニメーションを実行する Slot.animation(index); } }); }, }; global.Slot = Slot; })((this || 0).self || global); /** * 読み込み後 */ $(document).ready(function () { /* * スロットの初期化処理を実行 */ Slot.init(); Slot.resetLocationInfo(); /** * スタートボタンのクリックイベント */ $('.btn-start').click(function () { // スタートボタンを押せないようにする $(this).attr('disabled', true); // スロットの回転を開始 Slot.start(); // ストップボタンを押せるようにする $('.btn-stop').attr('disabled', false); }); /** * リセットボタンのクリックイベント */ $('.btn-reset').click(function () { // リセットボタンを押せないようにする $(this).attr('disabled', true); // スタートボタンを押せるようにする $('.btn-start').attr('disabled', false); // ストップボタンを押せないようにする $('.btn-stop').attr('disabled', true); // スロットのリール情報を初期化 Slot.init(); }); /** * ストップボタンのクリックイベント */ $('.btn-stop').click(function () { // ストップボタンを押せないようにする $(this).attr('disabled', true); // レールの回転を停止 Slot.stop($(this).attr('data-val')); }); }); </script> </body> </html>See the Pen [sample code]javascript slot by UTのCodePen (@ut_1029) on CodePen.
解説は、ブログ(UTの日常)で!
- 投稿日:2020-02-07T10:29:55+09:00
[Vue.js] どうしてVue.set()で配列の更新が検知できるの?
[Vue.js]配列の変更が検知できない理由の「JavaScriptの制限」って何よ! - Qiita
こちらの記事で配列の更新検知には
Vue.set
を使うと記載したのですが、
どうして検知できるのかな?って思って調べました。結論
内部では
splice()
をしているだけだった。実装を見てみる
公式GitHubを見てみましょう!
export function set (target: Array<any> | Object, key: any, val: any): any { // 中略 if (Array.isArray(target) && isValidArrayIndex(key)) { target.length = Math.max(target.length, key) target.splice(key, 1, val) //ここだ! return val } ...ありました!
内部でtarget.splice(key, 1, val)
をしていることが確認できました!
だから検知できるんですね!
- 投稿日:2020-02-07T07:13:37+09:00
[超簡単] Reactからfirestoreにアクセスするデモ!
firebase初心者のアクマちゃん(@akumachanit)デモ!
firebaseめちゃくちゃ簡単で楽しいからみんなもやってみるデモ!
今日はReactからfirestoreにアクセスする手順を説明するデモ〜〜〜!今回やること
- firebaseをReactアプリにインストールする
- firebaseプロジェクトを作成する
- データベースを作成する
- 接続用コンポーネントを作成する
- データを出し入れしてみる
1と2はすでにやってある人は飛ばしていいデモよ!
firebaseをReactアプリにインストールする
Reactアプリのディレクトリで
npm install
するデモnpm install firebase@latest
firebaseプロジェクトを作成する
説明しなくてもわかると思うけど、コンソール入ってプロジェクトを追加!
あとは適当に名前をつけて続行すれば大丈夫デモ(適当)データベースを作成する
サイドバーからDatabaseを選択するとデータベースの作成ボタンが現れるのでクリックデモ!
「本番環境で開始」を選ぶとルールで設定したクライアントからしかアクセスできなくなるデモ。
「テスト環境で開始」を選ぶとどこからでもアクセスできるデモ。今回はテスト環境を選択するデモ。
これはリリースするまでに本番環境に設定し直す必要があるんだけど、30日後に自動的に本番環境に移行してくれるみたいデモ。
開発中にいきなりアクセスできなくなるので、はやめに本番環境用に設定しておいた方が良いデモね。(今回はやらないけど)リージョンはasia-northeast1(東京)にとりあえず設定。
アジアリージョンのロケーションはこんな感じ接続用コンポーネントを作成する
サイドバーから
Overview
→</>
アイコン(わかりづらい)
アプリ名を登録(プロジェクト名じゃなくてアプリ名デモ。複数のクライアントから呼ぶ予定がある場合はReactAppとかWebAppとか適当につけたらいいと思うデモ)したら次に埋め込むためのコードが表示されるデモ。
設定があらかじめ埋め込んであるデモ。<!-- The core Firebase JS SDK is always required and must be listed first --> <script src="https://www.gstatic.com/firebasejs/7.8.0/firebase-app.js"></script> <!-- TODO: Add SDKs for Firebase products that you want to use https://firebase.google.com/docs/web/setup#available-libraries --> <script src="https://www.gstatic.com/firebasejs/7.8.0/firebase-analytics.js"></script> <script> // Your web app's Firebase configuration var firebaseConfig = { apiKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", authDomain: "xxxxxxxxxxxxxxx.firebaseapp.com", databaseURL: "https://xxxxxxxxxxxxxxx.firebaseio.com", projectId: "xxxxxxxxxxxxxxx", storageBucket: "xxxxxxxxxxxxxxx.appspot.com", messagingSenderId: "xxxxxxxxxxxxxxx", appId: "?????????????????????????????????????", measurementId: "xxxxxxxxxxxxxxx" }; // Initialize Firebase firebase.initializeApp(firebaseConfig); firebase.analytics(); </script>このうち今回使うのは
// Your web app's Firebase configuration
以下だけデモ。適当な名前(firebase.jsとか)でこんな感じでコンポーネントを作成するデモ。
import firebase from 'firebase'; // Your web app's Firebase configuration var firebaseConfig = { apiKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", authDomain: "xxxxxxxxxxxxxxx.firebaseapp.com", databaseURL: "https://xxxxxxxxxxxxxxx.firebaseio.com", projectId: "xxxxxxxxxxxxxxx", storageBucket: "xxxxxxxxxxxxxxx.appspot.com", messagingSenderId: "xxxxxxxxxxxxxxx", appId: "?????????????????????????????????????", measurementId: "xxxxxxxxxxxxxxx" }; // Initialize Firebase firebase.initializeApp(firebaseConfig); firebase.analytics(); export default firebase;
export default firebase
を忘れずにデータを出し入れしてみる
入れ
import './さっき作ったfirebase.jsのパス' firebase.firestore().collection('test').add({id: 'testid', name: 'test'}) .then(function (docRef) { console.log("Document written with ID: ", docRef.id); }) .catch(function (error) { console.error("Error adding document: ", error); });出し
firebase.firestore().collection('test').get().then((querySnapshot) => { querySnapshot.forEach((doc) => { console.log(doc.data()); }); });;出し入れっていうけど入れてから出すもんだよね...
まとめ
簡単!!!!
Twitterやってるデモ!仲良くしてくださいデモ!@akumachanit