20200207のJavaScriptに関する記事は15件です。

ショートコーディングで階乗計算機を作る

ショートコーディングで階乗計算機を作る

動作サンプル https://www.o--o.cf/

<script>for(a=prompt(),i=1,k=a;k--;)i*=k+1;document.write(i)</script>

たったこれだけ。

メモ帳に貼り付けてやってみてください。

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

#twitch の配信中のチャットログを #discord でロギングするBOT

githubと改行ルールが違って二重管理になるため、詳細は下記のgithubのREADME.mdを参照のこと。
https://github.com/github895439/twitch_chat_logging_bot

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Next.jsで環境変数を利用するのにdotenvは必要ない

tl;dr

next.config.jsenvに任意の値を設定するだけ
dotenvを使いたい場合にはnext.config.jsdotenvを呼び出せばよい

解説

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.js

next.config.js
require('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
Update dotenv example by francismarcus · Pull Request #9372 · zeit/next.js

Next.jsは内部でwebpackを用いてビルドを行っています。それをカスタマイズする方法です。
筆者はNext.jsにおいてwebpackのカスタマイズは最小限にするべきだと考えています。
今回のようなケースでは使う必要はないかと思われます。

Next.js 8以降でdotenv-webpackを利用するケースが思いつかないのでよかったらコメントください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

(JavaScript)イベント駆動について

今日はJavaScriptとjQueryについて学習しました。

JavaScriptは「イベント駆動」という概念に基づいて設計されているそうです。

Javaスクリプトにおけるイベントとは、HTMLの要素に対して行われた処理要求のことで

例えば、「ある要素の上にマウスカーソルを乗せる」、「ブラウザ上のボタンをクリックする」なども

イベントに当たります。

そのイベントが発生したらそれをきっかけにコードが実行される仕組みが「イベント駆動」です。

Webページを見ていてカッコいい動作は大体JavaScriptのおかげなのだなと思いました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Web Speech API で読み上げ位置を取得

ブラウザでテキストを読み上げる Web Speech API で読み上げ位置を取得します。

Web Speech API

語学学習に音声合成が使えないかとクラウドサービスを調べましたが、契約が必要で料金が気になりました。ですが最近のブラウザでは API として実装されていることを知りました。

概要は次の記事に詳しいです。

詳細はこれらに譲り、要点をサンプルで示します。

読み上げ

主に 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 と組み合わせて使用する例です。

自分の勉強を兼ねてブログで語学記事を執筆することを計画しており、テンプレートを作成中です。対訳を記述しやすくするなどの利便性に注力しています。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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アプリの作成

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初心者によるプログラミング学習ログ 232日目

100日チャレンジの232日目

twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。

100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。

232日目は

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【備忘録】コンソール君「ん?そのプロパティ未定義やぞ?」 俺「何言うとんねんワレェ!」【まぬけ注意】

※ご覧いただいてる方々に諸注意です。投稿主はプログラミング・環境構築含め独学のため、誤解している点や間違っている点が多くある場合があります。もし、私が間違った知識・情報をドヤ顔で記載していた場合、優しくご指摘いただくようよろしくお願いします。

Reactの技術本のソースコード模写中の話

模写終了後ターミナルでサーバー起動を確認。
動かそうとするとlocalhostの接続はできるものの画面は何も表示されず…↓
64dfa314cc10be07c7d04c18f766ef8f.png
ハテ・・・困りましたわ・・・
とりあえずchrome側のコンソールでエラーを確認しまっせ。↓
3cb60ac6d22960ca67f4e1e8b02f851e.png
俺「うーんと、キャノットリードプロパティゲットスコアオブアンディフィネッドね。うんうんなるほど。」
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()

ささっと修正して、動作確認。無事動きました。
77d9628aaa3192774f1ee403158c029f.png

本日の学び

大文字と小文字はきちんと分けて使いましょう。
いつになったらコーダー初心者抜けれるのでしょうか。
現場からは以上です~~~(^_^)ノシ(激萎えしながら)
        

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

任意の 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 を使用するので馴染みのない方は雰囲気だけでも掴んでいただけると幸いです。

まずはチェックボックスのコンポーネントです。
親コンポーネントで状態の管理をしているのを想定して、propschecked (チェックされているか) と onChange (チェックを切り替える関数) を受け取っています。
Wrapper に注目すると tabIndex 属性をしていしているのがわかります。(React だと tabindextabIndex と記述しなければならない)

Checkbox.tsx
import 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 Checkbox

CSS は省略していますが親のコンポーネントです。

Form.tsx
import 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>
  )
}

実際には次のように動きます。

check.gif

これで tabindex 属性を使ってインタラクティブかつフォーカスができる要素を作ることが出来ました。

おわりに

今回はアクセシビリティを向上させるための手段として tabindex 属性を紹介しました。普段アクセシビリティを気にしている方は Light House の結果を気にすることが多いと思いますが、Light House ではフォーカスに関するスコアは出ないので tabindex 属性は目新しい情報だったかもしれません。これを機に Web のアクセシビリティについて検討しようという方が増えると嬉しいです。

もし記事の内容に間違いや誤解を招く箇所があればコメント欄か Twitter (@saitoeku3) で指摘していただけると助かります。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ビット演算による商(切り捨て)・余り計算

ビット演算は低級言語に近いのでどんな言語においても速い.
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));
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[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は利用できる幅がもっと広いはずです。
他にも便利なものがあったら教えてください!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【サンプルコード】JavaScriptでルーレットゲームをプログラミング

どうも、UT(@ut_1029)です。ブログ(UTの日常)の紹介です。

JavaScriptでルーレットゲームをプログラミングしたサンプルコードを紹介します。
ルーレットゲームは、JavaScriptのアニメーション勉強に最適でした。

ルーレット.jpeg

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の日常)で!

【サンプルコード】JavaScriptでルーレットゲームをプログラミング | UTの日常

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【サンプルコード】JavaScriptでスロットゲームをプログラミング

どうも、UT(@ut_1029)です。ブログ(UTの日常)の紹介です。

JavaScriptでスロットゲームをプログラミングしたサンプルコードを紹介します。

スロットゲームは、JavaScriptのアニメーション勉強に最適でした。

スロットのリールの表示位置をJavaScriptで制御して、回転のアニメーション制御処理がポイントです。

スロット.jpeg

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の日常)で!

【サンプルコード】JavaScriptでスロットゲームをプログラミング | UTの日常

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Vue.js] どうしてVue.set()で配列の更新が検知できるの?

[Vue.js]配列の変更が検知できない理由の「JavaScriptの制限」って何よ! - Qiita

こちらの記事で配列の更新検知にはVue.setを使うと記載したのですが、
どうして検知できるのかな?って思って調べました。

結論

内部ではsplice()をしているだけだった。

実装を見てみる

公式GitHubを見てみましょう! :eye: :eye:

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)をしていることが確認できました!
だから検知できるんですね!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[超簡単] Reactからfirestoreにアクセスするデモ!

firebase初心者のアクマちゃん(@akumachanit)デモ!

firebaseめちゃくちゃ簡単で楽しいからみんなもやってみるデモ!
今日はReactからfirestoreにアクセスする手順を説明するデモ〜〜〜!

今回やること

  1. firebaseをReactアプリにインストールする
  2. firebaseプロジェクトを作成する
  3. データベースを作成する
  4. 接続用コンポーネントを作成する
  5. データを出し入れしてみる

1と2はすでにやってある人は飛ばしていいデモよ!

firebaseをReactアプリにインストールする

Reactアプリのディレクトリでnpm installするデモ

npm install firebase@latest

firebaseプロジェクトを作成する

説明しなくてもわかると思うけど、コンソール入ってプロジェクトを追加!
プロジェクト作成
あとは適当に名前をつけて続行すれば大丈夫デモ(適当)

データベースを作成する

サイドバーからDatabaseを選択するとデータベースの作成ボタンが現れるのでクリックデモ!
データベース作成

「本番環境で開始」「テスト環境で開始」があるデモ。
テスト環境で開始

「本番環境で開始」を選ぶとルールで設定したクライアントからしかアクセスできなくなるデモ。
「テスト環境で開始」を選ぶとどこからでもアクセスできるデモ。

今回はテスト環境を選択するデモ。
これはリリースするまでに本番環境に設定し直す必要があるんだけど、30日後に自動的に本番環境に移行してくれるみたいデモ。
開発中にいきなりアクセスできなくなるので、はやめに本番環境用に設定しておいた方が良いデモね。(今回はやらないけど)

リージョンはasia-northeast1(東京)にとりあえず設定。
リージョン
アジア
アジアリージョンのロケーションはこんな感じ

接続用コンポーネントを作成する

サイドバーからOverview</>アイコン(わかりづらい)
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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む