- 投稿日:2021-07-15T21:55:22+09:00
JavaScriptの基礎 Part2
length 配列.lengthとすることで要素の数を取ることができます。 script.js const fruits = ["apple","banana","orange"]; console.log(fruits.length); 出力結果はこちらです。 3 オブジェクト 配列とオブジェクトの違いを理解しなければいけません。 配列は [値,値,値];で表しますが、 オブジェクトはプロパティという名前を使い、管理します。 {プロパティ1:値1,プロパティ2:値2}; ES5では連想配列という名前になっていて、プロパティに代わりに キーという名前になっています。 キーはダブルクォーテーションで囲むようにして下さい。 undefined 存在しない配列の要素や存在しないオブジェクトのプロパティを出力すると undefinedと表示されます。これは定義されていないという意味です。 push ES5ではpushメソッドもあります。 配列.push(値)を入れることで配列の後尾に値を代入することができます。 最後に 初心者ですので、拙い文章や書き方だと思いますが、最後まで見て頂き ありがとうございました。もし、ご指摘等がございましたらよろしくお願い致します。
- 投稿日:2021-07-15T21:28:32+09:00
トイレの花子さんに学ぶ仕様変更
発端 「女子トイレの一番奥の扉を3回ノックして、花子さん遊びましょって3回言うと花子さんが出て来るんだけど、順番や制限時間は定められていないの。2回ノックして3回名前を呼んで放置しておけば、誰かが扉を1回ノックしただけで花子さんが出てくるってわけ」「デバッカーみたいなことをするな」— 真弓創 (@nofun1978) July 9, 2021 この例があまりにも初学者向けに使いやすいと思ったので、投稿してみます。 引用では 一番奥となっていますが例を作るために少し変更しています。 取り急ぎ仕様 三番目の個室を三回ノックする 花子さんの名前を呼ぶ 花子さんが出現する 仕様通りに実装してみる 3回ノック(クリック)して、呼んでみると花子さんが出現されます。 See the Pen call_hanako_san_1 by keima (@matsugena) on CodePen. 仕様を疑え 三番目の個室を三回ノックする 花子さんの名前を呼ぶ 花子さんが出現する 上の3つの仕様書は全く足りていません 1.については、引用ツイートの通り事前にノックされてしまうことで破綻します。 あえて解説のため、グローバル変数として実装しています。 HTMLだと開いた時点で0になっていますが、多数が利用するトイレとして、同じページを複数回操作する再現にしました。 現実ではサーバー側で管理されると思います。 その場合でも、日付でリセットされるのか個人ごとに管理するのか、仕様を詰めるだけ詰める必要があります。 2.については、実装者(私)が呼んだことをトリガーに実装しています。 1.2. 2つの条件が揃った際に花子さんが出現するということなのかどうかを詰める必要があるでしょう。 3.花子さんが複数人出現する。 すでに何人ものハナコさんを召喚されているかたはここを読み飛ばしてください。 花子さんの上限は仕様からは読み取れません。 花子さんはひとりじゃないかもしれませんし、複数かもしれません。 ここも条件を詰めましょう。 仕様変更 さらに、仕様の変更が発生した場合どうなるでしょう。 花子さんは地域によって様々な呼び方ルーティンがあるようです。 例えば、一部の地域(localeId=11110)では、4番目のドアを15回ノックした場合にだけ現れるようにしたいという変更がありました。 function callSomething(){ let name = document.getElementById('name').value; let localeId = document.getElementById('localeId') if(localeId != 11110 && knockCount == 3 && name === '花子さん'){ return appearHanakoSan(); }else if(localeId == 11110 && knockCount == 15 && name === '花子さん'){ return appearHanakoSan(); }else{ alert("しかし花子さんは現れなかった"); } } もちろん、上の実装はやってしまった例なので真似をしてはいけません。 さらに、別の地域でN番めのドアをM回ノックした場合に現れるようにしてほしいといった要望が続々発生し果ては呼び名も違う場合や時間の指定があることも想定しなければいけません。 else ifが地域の数やパラメータの数だけ増え続けます。 というわけで、改善例は以下になります。 function callSomething(){ let form = document.getElemetnById('nameForm'); let name = form.name; // クライアント側での最低限のエラーチェック if(!name){ return; // ユーザにエラーを知らせるべきですが、名前を呼ばれていないという認識として省略します } form.submit(); //送信されたサーバーで花子さんの出現をチェックしましょう } もちろんサーバーに実装できずクライアントで判定ロジックを実装しなければいけないこともありますが少数だと思います。 サーバーにロジックを用意することで、全国の学校(仮に5000として)のトイレで地域別に花子さんを用意する必要が発生した際に5000花子さんを用意する必要がなくなります。 まとめ 仕様変更が無いという言葉は信用するな 補足(アンチパターン:マジックナンバー) 仕様書の件と直接関わりはないですが、 花子さんを出現させるための、以下のif文ですが仕様の通り間違ってはいませんが、やってはいけない例(アンチパターン)になっています。 "3"番の個室 "3"回ノックする "花子さん"という名前 3つの値をif文で直接扱うことで実装した人にしか分からない数字及び文字列になっています。 以下の通り改善できるでしょう。 // 悪い例 if(toiletNumber == 3 && knockNum == 3 && callName === '花子さん'){ return appearHanakoSan(); } // 意味をもたせる例 const correctToiletNumber = 3; const correctKnockNum = 3; const correctCallName = '花子さん'; if(toiletNumber == correctToiletNumber && knockNum == correctKnockNum && name === correctCallName){ return appearHanakoSan(); } ロジックが複雑かつクライアントに実装しなければならない場合であれば、if内の条件式を関数化することも想定できます。
- 投稿日:2021-07-15T19:06:30+09:00
JavaScriptの更新が反映されないときの対処
結論 ブラウザで古いJavaScriptファイルがキャッシュされており、更新後のファイルを読み込めていなかった。 前提知識 Google Chrome、Mozilla Firefox、Internet Explorerなどのウェブブラウザは、インターネット上にある情報を取得・閲覧するソフトウェアアプリケーションです。ブラウザは、私たちが探しているウェブサイトやその他の情報を、URL経由で検索、発見、表示しています。 キャッシュとは、一度アクセスしたサイトのデータ(画像やHTMLなど)をブラウザで一時的に保管しておく仕組みです。こうしてデータを保管しておく目的は、帯域幅の混雑を避けページの表示を高速化することにあります。 つまり、次回同じページを閲覧する際に、キャッシュに保存されたページデータが読み込まれるため、インターネット上から読み込むよりも速く表示することができるのです。 言い換えれば、ページデータがキャッシュに保管されていれば、ブラウザは新しくHTMLリクエストを送信する必要がありません。そのため、古いWebサイトが表示される、という結果になるのです。 参考:ブラウザのキャッシュとは? 「.js」に限らず静的なファイル(.jpgなども)はブラウザにキャッシュされているので、更新した後はサーバにあるファイルを再度読み込む必要がある。 確認 開発者ツールの「Network」タブにて確認する(例はGoogle Chrome)。 1回目のページ読み込み。 「validation.js」という17.3kBのファイルを93msで読み込んでいることがわかる。 2回目のページ読み込み。 ファイルサイズを示す「Size」に(memory cache)が表示されている。通信を行っていないのでTimeも0ms。 「validation.js」はブラウザで保持しているので、jsファイルをとるリクエストが必要なく高速にページを表示できる。 ...のはいいが変更した「validation.js」を再度読み込んでほしいので、これでは困る。 対処 1.ハード再読み込みを使用する Ctrl+Rでページの更新を行うことができるが、Ctrl+Shift+Rでキャッシュを破棄しつつページの更新を行うことができる。 (Macの場合はCmd+Shift+R) 2.開発者ツールの設定を変更する 「Disable cache」にチェックを入れることでブラウザでキャッシュを保持しなくなる。
- 投稿日:2021-07-15T19:03:50+09:00
japan-map-jsを使って日本地図を表示させる
はじめに jQueryのプラグインであるJapan Mapというものを使い、各都道府県ごとにクリックができる日本地図を表示させます。 また、各都道府県をクリックすると指定のページ(都道府県のホームページ)に変遷するようにできたので、その方法も含めてご紹介します。 mapの表示 head内に以下のコードを記述します。 <head> 省略 <script type="text/javascript" src="https://unpkg.com/japan-map-js@1.0.1/dist/jpmap.min.js"></script> //追記 <script type="text/javascript" src="dist/jpmap.min.js"></script> //追記 </head> 次にhtmlに地図を設置するdiv要素を作成します。 <div id="my-map"></div> そして、javascriptで以下のように記述します。 <script> var d = new jpmap.japanMap(document.getElementById("my-map"), { onSelect: function(data){ alert(data.name); } }); </script> すると、下記の画像のような日本地図が表示されます。 都道府県名を表示させる htmlの<script>タグ内を以下のように変更します。 <script> var d = new jpmap.japanMap(document.getElementById("my-map"), { showsPrefectureName: true, //都道府県名を表示させる width: 1000, movesIslands: true, //沖縄地方が地図の左上の分離されたスペースに移動する lang: 'ja', //表示させている都道府県名を日本語にする onSelect: function(data){ alert(data.name); } }); </script> 各都道府県をクリックすると指定のページ(都道府県のホームページ)に変遷させる htmlの<script>タグ内を以下のように変更します。 <script> var areaLinks = { 1:"https://www.pref.hokkaido.lg.jp/", //リンク先URL 2:"https://www.pref.aomori.lg.jp/", ・ ・ 省略 ・ ・ }; var d = new jpmap.japanMap(document.getElementById("my-map"), { areas: [ {code : 1, name: "北海道", color: "#7f7eda", hoverColor: "#b3b2ee"}, {code : 2, name: "青森", color: "#759ef4", hoverColor: "#98b9ff"}, {code : 3, name: "岩手", color: "#759ef4", hoverColor: "#98b9ff"}, {code : 4, name: "宮城", color: "#759ef4", hoverColor: "#98b9ff"}, {code : 5, name: "秋田", color: "#759ef4", hoverColor: "#98b9ff"}, {code : 6, name: "山形", color: "#759ef4", hoverColor: "#98b9ff"}, {code : 7, name: "福島", color: "#759ef4", hoverColor: "#98b9ff"}, {code : 8, name: "茨城", color: "#7ecfea", hoverColor: "#b7e5f4"}, {code : 9, name: "栃木", color: "#7ecfea", hoverColor: "#b7e5f4"}, {code : 10, name: "群馬", color: "#7ecfea", hoverColor: "#b7e5f4"}, {code : 11, name: "埼玉", color: "#7ecfea", hoverColor: "#b7e5f4"}, {code : 12, name: "千葉", color: "#7ecfea", hoverColor: "#b7e5f4"}, {code : 13, name: "東京", color: "#7ecfea", hoverColor: "#b7e5f4"}, {code : 14, name: "神奈川", color: "#7ecfea", hoverColor: "#b7e5f4"}, {code : 15, name: "新潟", color: "#7cdc92", hoverColor: "#aceebb"}, {code : 16, name: "富山", color: "#7cdc92", hoverColor: "#aceebb"}, {code : 17, name: "石川", color: "#7cdc92", hoverColor: "#aceebb"}, {code : 18, name: "福井", color: "#7cdc92", hoverColor: "#aceebb"}, {code : 19, name: "山梨", color: "#7cdc92", hoverColor: "#aceebb"}, {code : 20, name: "長野", color: "#7cdc92", hoverColor: "#aceebb"}, {code : 21, name: "岐阜", color: "#7cdc92", hoverColor: "#aceebb"}, {code : 22, name: "静岡", color: "#7cdc92", hoverColor: "#aceebb"}, {code : 23, name: "愛知", color: "#7cdc92", hoverColor: "#aceebb"}, {code : 24, name: "三重", color: "#ffe966", hoverColor: "#fff19c"}, {code : 25, name: "滋賀", color: "#ffe966", hoverColor: "#fff19c"}, {code : 26, name: "京都", color: "#ffe966", hoverColor: "#fff19c"}, {code : 27, name: "大阪", color: "#ffe966", hoverColor: "#fff19c"}, {code : 28, name: "兵庫", color: "#ffe966", hoverColor: "#fff19c"}, {code : 29, name: "奈良", color: "#ffe966", hoverColor: "#fff19c"}, {code : 30, name: "和歌山", color: "#ffe966", hoverColor: "#fff19c"}, {code : 31, name: "鳥取", color: "#ffcc66", hoverColor: "#ffe0a3"}, {code : 32, name: "島根", color: "#ffcc66", hoverColor: "#ffe0a3"}, {code : 33, name: "岡山", color: "#ffcc66", hoverColor: "#ffe0a3"}, {code : 34, name: "広島", color: "#ffcc66", hoverColor: "#ffe0a3"}, {code : 35, name: "山口", color: "#ffcc66", hoverColor: "#ffe0a3"}, {code : 36, name: "徳島", color: "#fb9466", hoverColor: "#ffbb9c"}, {code : 37, name: "香川", color: "#fb9466", hoverColor: "#ffbb9c"}, {code : 38, name: "愛媛", color: "#fb9466", hoverColor: "#ffbb9c"}, {code : 39, name: "高知", color: "#fb9466", hoverColor: "#ffbb9c"}, {code : 40, name: "福岡", color: "#ff9999", hoverColor: "#ffbdbd"}, {code : 41, name: "佐賀", color: "#ff9999", hoverColor: "#ffbdbd"}, {code : 42, name: "長崎", color: "#ff9999", hoverColor: "#ffbdbd"}, {code : 43, name: "熊本", color: "#ff9999", hoverColor: "#ffbdbd"}, {code : 44, name: "大分", color: "#ff9999", hoverColor: "#ffbdbd"}, {code : 45, name: "宮崎", color: "#ff9999", hoverColor: "#ffbdbd"}, {code : 46, name: "鹿児島", color: "#ff9999", hoverColor: "#ffbdbd"}, {code : 47, name: "沖縄", color: "#eb98ff", hoverColor: "#f5c9ff"}, ], showsPrefectureName: true, width: 1000, movesIslands: true, borderLineColor: "#000000", lang: 'ja', onSelect: function(data){ location.href = areaLinks[data.area.code]; } }); </script> 参考記事 ・japan-map-js公式リファレンス ・JapanMapで描画した日本地図にページリンクを追加する方法 ・JapanMapを使った県名付き日本地図の動的描画を行う方法(地図のカラーコードを参照)
- 投稿日:2021-07-15T17:37:00+09:00
コンポーネントからStoreのStateを変更するまでの流れ
はじめに 前回、react-reduxについて自分なりに記事を書きました。しかし実際にコンポーネントとstoreを繋いでstateを変更するのはどういう流れになっているのかさらに深堀するために今回はコードベースで書いてみようと思いました。 コンポーネント作成 それでは、早速やっていきましょう。 今回はボタンをクリックするとダイアログが発生する処理を書きます。そのダイアログが発生する箇所をstoreに記載します。 まずはstoreを繋ぐコンポーネント用意します。 今回は、material-uiボタンを置くだけの簡単なコンポーネントを作成します。 myComponent.tsx import React from 'react'; import * as patientList from '../../store/MyComponent'; import Fab from '@material-ui/core/Fab'; import AddIcon from '@material-ui/icons/Add'; import Button from '@material-ui/core/Button'; import AddCircleIcon from '@material-ui/icons/AddCircle'; return ( <React.Fragment> <Fab color='primary' style={{ position: 'fixed', zIndex: 3, right: '2em', bottom: '2em', }} onClick={onListClick} > <AddIcon /> </Fab> <Button onClick={() => { history.push('/test') //UDVコンポーネント }} > <AddCircleIcon color="secondary" /> </Button> <div style={{ height: 500, width: 600, }} > <List columns={mockColumn} items={mockItems} /> </div> </React.Fragment> ); export default MyComponent; これでブラウザの右下に以下のようなボタンが表示される storeの作成 次に上記で作成したコンポーネントに接続するstoreを作成します 今回実装したコードを表示します。 myComponent.ts import * as React from 'react'; import { Action, Reducer } from 'redux'; import { AppThunkAction } from '.'; import { push, RouterAction } from 'react-router-redux' export interface MyComponentState { master: Master; test: string; userData: any; }; interface Master { word: string } interface Init { type: 'MYCOMPONENT/INIT'; value: any; } interface GetMaster { type: 'MYCOMPONENT/SET_MASTER'; master: Master; } interface ClearAll { type: 'MYCOMPONENT/CLEAR_ALL'; } interface ListClick { type: 'MYCOMPONENT/LIST_CLICK'; } export type KnownAction = Init | GetMaster | ListClick | ClearAll; const unloadedState: MyComponentState = { master: { word: '' }, test: '', userData: {} }; export interface IActionCreators { onInit: () => AppThunkAction<KnownAction>; onClick: () => AppThunkAction<KnownAction | RouterAction>; setMaster: () => AppThunkAction<KnownAction>; }; export type ActionCreators = IActionCreators; function setInit() { return 'test'; } export const actionCreators: ActionCreators = { onInit: () => (dispatch, getState) => { const userData = setInit(); console.log(userData); dispatch({ type: 'MYCOMPONENT/INIT', value: userData }); }, onClick: () => (dispatch, getState) => { alert('onClick run'); dispatch(push('/case')); dispatch({ type: 'MYCOMPONENT/LIST_CLICK' }); }, setMaster: () => (dispatch, getState) => { dispatch({ type: 'MYCOMPONENT/SET_MASTER', master: { word: 'test' } }); } } export const reducer = (state: MyComponentState, incomingAction: Action) => { const action = incomingAction as KnownAction; switch (action.type) { case 'MYCOMPONENT/INIT': { return { ...state, userData: action.value } } case 'MYCOMPONENT/SET_MASTER': { return { ...state, master: action.master } } case 'MYCOMPONENT/LIST_CLICK': { return { ...state, } } case 'MYCOMPONENT/CLEAR_ALL': { return { ...unloadedState, } } default: const exhaustiveCheck: never = action; } return state || unloadedState; }; 今回は、ボタンをクリックすると、'onClick run'というアラートを表示させるのでまず初めにonClickというActionをAction Creatorに追加しました。 export interface IActionCreators { onInit: () => AppThunkAction<KnownAction>; onClick: () => AppThunkAction<KnownAction | RouterAction>; setMaster: () => AppThunkAction<KnownAction>; }; 次に、stateを変更するActionの中身を実装する。 export const actionCreators: ActionCreators = { onInit: () => (dispatch, getState) => { const userData = setInit(); console.log(userData); dispatch({ type: 'MYCOMPONENT/INIT', value: userData }); }, setMaster: () => (dispatch, getState) => { dispatch({ type: 'MYCOMPONENT/SET_MASTER', master: { word: 'test' } }); } } onClickというAction Creatorの中に処理の内容を実装します。 今回追加する処理はボタンをクリックすると'onClick run'というアラートを表示させるだけなので以下のような処理を追加します。 onClick: () => (dispatch, getState) => { alert('onClick run'); dispatch({ type: 'MYCOMPONENT/ON_CLICK' }); }, このままでは、Actionが発行されてもstateを変更できないので最後にReducerを以下ように定義します。 export const reducer = (state: MyComponentState, incomingAction: Action) => { const action = incomingAction as KnownAction; switch (action.type) { case 'MYCOMPONENT/INIT': { return { ...state, userData: action.value } } case 'MYCOMPONENT/SET_MASTER': { return { ...state, master: action.master } } case 'MYCOMPONENT/LIST_CLICK': { return { ...state, } } case 'MYCOMPONENT/CLEAR_ALL': { return { ...unloadedState, } } default: const exhaustiveCheck: never = action; } return state || unloadedState; }; これでstoreの実装は完成しました。 次に、このstoreをコンポーネントに接続する必要があります。 myComponent.tsx export default MyComponent; この箇所を修正してstoreに接続できるようにします。 connectを用いてstoreと接続します。 myComponent.tsx export default connect( (state: ApplicationState) => (state.patientList), (dispatch) => bindActionCreators({ ...myComponent.actionCreators }, dispatch) )(MyComponent); それでは動作確認を行います。 小さくですが、画面上部に'onClick run'というアラートが表示されたのでコンポーネントとstoreが接続できたことを確認できました。 まとめ コンポーネントとstoreの接続フローを最小単位のコードベースで説明しました。 今回は、ただアラートを表示させるだけの簡単な処理になりました、画面遷移やその他の処理でもこの方法で実装することができます。 この方法を取ることでstateの管理が楽になるのでこれからの開発でも使っていこうと思います!
- 投稿日:2021-07-15T16:50:23+09:00
【React】Identifier 'Hoge' has already been declaredの対処法【javascript】
症状 Reactで画面作成時に以下のエラーが発生しました。 翻訳すると、「識別子 'Hoge'はすでに宣言されています」でした。 シンプルに、変数が二重に宣言されていると言われているようです。 エラー Identifier 'Hoge' has already been declared 以下が該当のソースです。 HogeHome.jsx import React,{useState} from "react"; import Hoge from "../Hoge"; import Hoge from "../Hoge"; export const HogeHome = () => { const [value, setValue] = useState(Hoge); return ( {value} ) } 解決策 imortが2重に書かれていた箇所を1つに直したら、解決しました。 importした値やオブジェクトが2重にimportされているため、既に宣言されているというエラーが出ていたようです。 HogeHome.jsx import React,{useState} from "react"; import Hoge from "../Hoge"; import Hoge from "../Hoge"; export const HogeHome = () => { const [value, setValue] = useState(Hoge); return ( {value} ) } 参考
- 投稿日:2021-07-15T15:10:12+09:00
毎回ログインするの面倒だから自動でログインをできるようにした
はじめに こんにちは。 この前行った美容室、会員カードとか作らない感じのお店で、個人的にいいなと思いました。増えるんすよね、あれって さて、今回は1日経つとログアウトされちゃうような会員サイトで毎度ログインページに行って、ログインボタン押してが面倒に感じたので自動化しました。 日々の煩わしさから来るストレスを減らしていきましょう 使用するもの 以下拡張機能です。 こちらは、任意のJavaScriptを実行できる拡張機能です。 ドメインなどのフィルタもできるがいいところ! 使い方 以下記事が参考になりました 実践 今回はContentfulのログインを自動化します ScriptAutoRunnerにスクリプト追加します。 私の場合はGitHubのアカウントを使用してContentfulにログインします GitHubでログインするボタンのセレクタはDevtoolsから取得しました。 (() => { // Contentfulトップページにアクセスしたらログインページに飛ばす. if (location.href == 'https://www.contentful.com/') { location.href = 'https://be.contentful.com/login' } // ログインページのときは、GitHubでログインするボタンをクリックする. if (location.href == 'https://be.contentful.com/login') { document.querySelector("body > div.row > div > div.account__box.box > div > div.row.row--lessgutter > div:nth-child(1) > a").click() } })() これで、Contentfulにアクセスした際に自動でログインするようになりました! おわりに いやー、Vimiumを使用していつもキーボード操作だけでログインしてまして、どのキーを押して行けばログインできるか、複数のサイトで頭に焼き付いてました それでもいいんですが、こんなのさっさと自動化すべきですね、ほかのサイトにもどんどん追加していこうと思います それでは!
- 投稿日:2021-07-15T15:09:56+09:00
Local Storageを使ってみる
Local Storageとは Local StorageとはHTHL5から導入されたWEB APIの一種で、Javascriptでブラウザにデータを記録する仕組みです。 利点 ブラウザにデータを保存できる cookieのようにWEBサーバーにデータを保存するのではなくブラウザに保存するので、 ブラウザを閉じてもユーザーの入力情報などを保持し続けることができます。 保存容量が大きい 主要なブラウザなら5MBまでデータを保存できます。 注意点 データは永続的に保存される こちらでLocal Storageのデータを削除する処理を書かないと、データは永続的に残り続けるので削除する処理を書きましょう。 Javascriptから自由にアクセスできる セキュリティ上非常に大きな注意点です。 ユーザーの個人情報など、機密性の高い情報には使わないようにしましょう。 構文 保存 localStorage.setItem('key', 'value')あるいはlocalStorage.saveKey = 'value'と書くだけで保存ができます。 test.js localStorage.setItem('key', 'value1'); localStorage.saveKey = 'value2'; 取得 localStorage.getItem('Key')あるいはlocalStorage.saveKeyでvalueを取得することができます。 test.js var value1 = localStorage.getItem('Key') var value2 = localStorage.saveKey; 削除 localStorage.removeItem('key')で指定したキーのデータを削除するか、localStorage.clear()ですべてのデータを削除します。 test.js localStorage.removeItem('key'); localStorage.clear(); 実装 この記事を読んだ日時を記録する機能を作成しました。 この記事を見た日を記録を押すと現在の日時を記録し、この記事を見た日を表示で記事を見た日時を表示します。 クリアを押すことでLocal Storageのデータを消去します。 日時を記録してからブラウザを閉じ、もう一度この記事を開いて日時が記録されているか実験してみてください。 See the Pen by masuda (@sankomasuda) on CodePen.
- 投稿日:2021-07-15T11:25:23+09:00
対戦ゲームを作る。 その6
概要 対戦ゲームを作る。 とりあえずの動作品できました。 動作の確認方法 2つ開いて、最初にクリックした方が白。 交互にクリックします。 サンプルコード var coma = 'koma.png'; var ban = []; var flg = 1; var game = new Game(); game.fps(5); game.preload([coma]); game.start(); var aLabel = new Label("a", 50, 330); aLabel.size(20); aLabel.label('白: 自分<br>黒: 相手'); ban = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 1, 1, 1, 1, 1, 1, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; var field = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; for (var i = 0; i < 64; i++) { field[i] = new Sprite(40, 40, coma, 4, 1); field[i].frame(ban[i]); field[i].x((i % 8) * 40); field[i].y(Math.floor(i / 8) * 40); field[i].jq.click(function() { var n = this.id; n = n.replace("tsGameNode", ""); n = n - 2; test(n); }); } function getUniq() { var strong = 3; return new Date().getTime().toString(16) + Math.floor(strong * Math.random()).toString(16) } var id0 = getUniq(); var state = 0; var iro = 2; var bLabel = new Label("a", 50, 390); bLabel.size(12); var url = "wss://ohiapp0.herokuapp.com/pub"; var ws = new WebSocket(url); ws.onopen = function() { bLabel.label("> CONNECTED"); }; ws.onmessage = function(e) { bLabel.label("> RECV: " + e.data); var json0 = JSON.parse(e.data); //alert(json0); if (json0.id == id0) return; //alert(json0.iro); if (json0.iro == 2) { iro = 0; aLabel.label('白: 相手<br>黒: 自分'); } else { iro = 2; aLabel.label('白: 自分<br>黒: 相手'); } //alert(json0.ban); ban = json0.ban; //alert(json0.te); var res = oku(json0.te, json0.iro); if (res == -1) { var end = -1; for (var i = 0; i < 64; i++) { field[i].frame(ban[i]); if (ban[i] == 1) end = 0; } if (end == -1) alert("game over"); } else { alert("石が置けません。"); } }; ws.onerror = function(e) { bLabel.label("> ERROR: " + e.data); }; function send(id, state, iro, ban, te) { var m = "{"; m += '"id":"' + id + '",'; m += '"state":' + state + ","; m += '"iro":' + iro + ","; m += '"ban": [' + ban + "],"; m += '"te":' + te; m += "}"; bLabel.label("> SEND: " + m ); ws.send(m); } function check(put, d) { var x = put % 8; var y = Math.floor(put / 8); var res = 0; if (x == 0 && (d == -9 || d == -1 || d == 7)) res = 1; if (x == 7 && (d == -7 || d == 1 || d == 9)) res = 1; if (y == 0 && (d == -9 || d == -8 || d == -7)) res = 1; if (y == 7 && (d == 7 || d == 8 || d == 9)) res = 1; return res; } function oku(put, iro) { var res = 0; var turn = 0; if (iro == 0) turn = 2; var dir = new Array(-9, -8, -7, -1, 1, 7, 8, 9); var tugi; var i; var count; if (ban[put] == 1) { for (i = 0; i < 8; i++) { count = 0; tugi = put; do { if (check(tugi, dir[i]) == 1) break; count++; tugi += dir[i]; } while (ban[tugi] == turn); if ((count > 1) && (ban[tugi] == iro)) { res = -1; tugi = put; do { ban[tugi] = iro; tugi += dir[i]; } while (ban[tugi] == turn); } } } return res; } function test(n) { var put; var res; if (flg > 0) { put = n; send(id0, state, iro, ban, put); res = oku(put, iro); if (res == -1) { var end = -1; for (var i = 0; i < 64; i++) { field[i].frame(ban[i]); if (ban[i] == 1) end = 0; } if (end == -1) alert("game over"); } } } //alert(oku(19, 2)); 写真 成果物 以上。
- 投稿日:2021-07-15T09:29:33+09:00
ReactでTodo App 作ってみた2
今日はチェックボックスの選択とtodoの削除! 学んだこと Todoアプリの作成 ・Todoの完了状態を変更できるようにする チェックボックス ・全て完了にするボタンが動作できるようにする チェックボックス ・全て完了にするボタンの一括操作をできるようにする チェックボックス ・完了済みのTodoを削除できるようにする ■用語 every 配列のメソッド 配列全てがtrueならtrue 配列全てがtrueじゃなければfalse filter 配列のメソッド 配列に対してfilterをかける filterの中で呼ばれた関数がtrueを返したやつだけ返す
- 投稿日:2021-07-15T09:22:52+09:00
Wiki(の募金)をぶっ壊す党
はじめに みなさん、Wikipediaは利用してますでしょうか? 私はよく利用させていただいています。 サイトを利用している方なら下記のような、募金を促すお知らせをみたことがありますよね? ↓↓↓ 募金の有無に関わらず、定期的に表示され邪魔に感じているのではないでしょうか? 今回はUserJSを利用してパージしてみようと思います! 準備 UserJSというサイト表示時にJavascriptを挿入してくれるChrome拡張機能を使用します! User Javascript and CSS https://chrome.google.com/webstore/detail/user-javascript-and-css/nbhcbdghjpllgmfilhnhkllmkecfmpld 実装 まず、DOMを調べる このメッセージの箇所を調べます。 <div id="siteNotice"> id="siteNotice"というのがサイト全体のお知らせになっているようですね。 その中の、id="centralNotice"やclass="cn-fundraising", class="frb-xxx"辺りが 募金系のメッセージのようです (fundraising=募金) JSでDOMを削除する 今回は、サイトのお知らせ自体削除してしまいます。 (記事が見れれば問題ないですしね) UserJS window.addEventListener('load', () => { const siteNotice = document.getElementById('siteNotice') if (siteNotice) { siteNotice.remove() } }) ※ UserJSはheadタグ内に追加されるコードを記述できます いざ、実行 $\color{red}{\rm {Now\ it's\ clean!\ ?}}$ さいごに 今回は、UserJSを利用してWikiの募金メッセージを削除してみました。 DOMが変わると対応できなくはなりますが、広告削除などにも応用が効きますので よく利用するサイトであれば、UserJSなどでパージしておくと便利になるかなと思います! ではでは ✈️
- 投稿日:2021-07-15T09:21:47+09:00
DenoでTerminalをアニメーションさせて遊ぶ
はじめに この本を参考に、ターミナルでAtomが動くバチクソかっこいいサンプルをDenoに移植しました。 こんな感じで動きます。 ページをめくるたびに失神するほどかっこいいので、みんなで読んで失神しよう。 実行してみる ソースはここに置いてます。 denoはURLを渡すだけで直接実行できます。 何はともあれ実行してみてください。面倒なことは何もありません。 ファイルをダウンロードしてくる必要すらない、最高! deno run --allow-run=stty https://raw.githubusercontent.com/zakuroishikuro/sharin/main/asobu/atom.ts 画面の大きさを取得するためにsttyコマンドを実行してるから--allow-runのパーミッションが必要です。 画面をデカくすればするほどカッコいいよ。 Ctrl+cで終了してね。 元のソースはこれ。 どうやって動かしてるの? 画面全体に文字列を出力して、消して、また出力してるだけ。 消すってなんだよ!? 普通の文字とは違う特殊な文字(エスケープシーケンス)を出力すると、何も表示されず不思議なことが起きます。 ここで使ってるのは二つ。 "\x1b[2J": 画面全体を消去 "\x1b[1:1H": カーソルを左上に移動 他にもいろいろあります。一覧はこちら。 もっと詳しく知りたい人はこちら。歴史的経緯も分かってめちゃくちゃ面白い。 で、JavaScriptで文字列を出力するといったらconsole.logなんだけど これ使うと最後に常に改行されるので、改行せずに文字列を出力する関数を作っておく。 文字列をUint8Arrayに変換して標準出力(Deno.stdout)にブチ込む。 print(str) const encoder = new TextEncoder(); /** console.logだと毎回改行されて困るので、改行せず文字列を出力する関数 */ async function print(str: string) { const bytes = encoder.encode(str); await Deno.stdout.write(bytes); } これでprint("\x1b[2J")すれば改行せずに画面を消せる。 ちなみにRubyでは改行するのがputs関数で、改行しないのがprint関数なのでそれに倣う。 sleep関数でn秒ディレイする ループ内で待つ処理を挟むために、awaitでn秒待つsleep関数を作りました。 setIntervalでやってもいいんだけど、元のイカすソースに似せたかった。 sleep(n) /** n秒待つ関数 (await忘れずに) */ function sleep(sec: number) { return new Promise((res) => { setTimeout(() => res(null), sec * 1000); }); } これでawait sleep(1)すれば処理が1秒中断されます。 Denoではトップレベルawaitが有効なのでasyncするために関数の中で実行しなくて済む。 ターミナルの行数・列数を取得 Denoにはターミナルのサイズを取得する機能がないからstty sizeコマンドを実行して取得してる。 なので--allow-runの許可が必要。 Denoは許可を得ないと危ないソースを実行できない良い子なんです。 getDisplaySize() /** * ターミナルのタテ・ヨコの大きさを取得する関数 * @returns {Number[]} [height, width] * */ async function getDisplaySize() { const stty = Deno.run({ cmd: ["stty", "size"], // 標準出力に文字列が表示されないようにする stdout: "piped", stderr: "piped", }); const decoder = new TextDecoder(); if ((await stty.status()).code === 0) { return decoder.decode(await stty.output()).trim().split(/\s+/).map((n) => parseInt(n) ); } else { throw "failed: " + decoder.decode(await stty.stderrOutput()); } } サブキャラクターレンダリング 半角文字は縦に長い。 半角1文字をタテに2ドットあると見立ててコンマやコロンなどを駆使し1文字で2ドット分の情報を描画する手法。 こういうとき以外で使うかは知らない。 ドット サブ □ □ [ ] ■ □ ['] □ ■ [,] ■ ■ [;] 点字 使ったらもっとすごそう ソース全体 肝心の処理部分。 複素数が必要だったのでSkypackからmathjsをimportしてる。 // original source code: // https://github.com/mame/trance-book/blob/master/8-1/subcharacter-rendering-demo.rb // // from: // あなたの知らない超絶技巧プログラミングの世界 by 遠藤侑介 // 8-1-2 アスキーアートの生成(1):サブキャラクターレンダリング import { complex, multiply } from "https://cdn.skypack.dev/mathjs"; const [height, _width] = await getDisplaySize(); const S = height; // 描画領域の大きさ const A = S / 2.1; // 長径 const B = S / 8.0; // 短径 print("\x1b[2J"); // 画面クリア let n = 0; while (true) { // バッファ const s = [...Array(S)].map(() => [..." ".repeat(S * 2)]); [...Array(100)].forEach((_, i) => { // 楕円の媒介変数表示 const t = Math.PI * 2 * (i + n) / 100; const e = complex(A * Math.cos(t), B * Math.sin(t)); // 楕円を 3 つ描く [-1, 0, 1].forEach((j) => { // 楕円を傾ける const e2 = multiply( e, complex({ r: 1.0, phi: Math.PI * 2 / 3 * j + n / 500 }), ); // ドットを置く const x = Math.floor(e2.re * 2 + S); const y = Math.floor(e2.im * 2 + S); const row = s[Math.floor(y / 2)]; if (i === 99) { [row[x - 1], row[x]] = "()"; } else if (y % 2 == 0) { row[x] = row[x] == " " || row[x] == "'" ? "'" : ";"; } else { row[x] = row[x] == " " || row[x] == "," ? "," : ";"; } }); }); print("\x1b[1;1H" + s.map((row) => row.join("")).join("\n")); await sleep(0.03); n++; } 移植しただけの自分にはたいして解説できねぇ。すまねぇ。 ソースコードとにらめっこして解読してほしい。ごめんなさい。 おしり。
- 投稿日:2021-07-15T08:12:14+09:00
[Rails]JavaScript(jQuery)でサーっとページトップに戻るボタン(FontAwesome)
ページトップ機能はいろんな実装方法があると思いますが、今回はJavaScript(jQuery)とFontAwesomeを使ったページトップへ戻るボタンの実装をしていきます。 スクロールが多いページだとページトップボタンは必須かなと考えます。 ただスマホの場合は指でスクロールが簡単にできるので、スマホのみのサービスの場合は必須とまではいかないかなと。(スマホではあまりページトップボタンクリックされてないみたいなデータがあったような、、、) 完成イメージはこちらです。(右下のボタン) 実際の動作を確認したい方は、ぼくのポートフォリオを確認してみてください。 ある程度スクロールしたら右下に出てくるので、クリックしてみてください。 サーッと1番上まで戻ります。 それではいってみましょう! 開発環境 ruby 2.6.3 Rails 5.2.6 前提 jQueryが使える状態 FontAwesomeが使える状態 手順 ページトップ機能を実装する手順は3ステップです! ビューでボタンを配置 CSSを追加 動きをつける ビューでボタンを配置 まずは、ビューにFontAwesomeを使ってボタンと要素を配置しています。 ボタンは全ビューで共通したものを使うのでapplication.html.erbのmainタグの中に記述していきます。 そのまま記述するとごちゃごちゃするので、部分テンプレート化しています。 views/layouts/_pagetop.html.erb <div class="container"> <div class="row"> <div class="col-12"> <!-- ページトップへ戻るボタン --> <p class="pagetop" style="display: none;"> <a href="#"> <!--ボタンの表示にはFontAwesomeを使用--> <i class="fas fa-chevron-up"></i> </a> </p> </div> </div> </div> <%= yield %>の下に配置 views/layouts/application.html.erb : <main> <%= yield %> <%= render 'layouts/pagetop' %> </main> : CSSを追加 CSSでは、 ①ボタンの配置場所 ②ボタンのデザイン ③FontAwesomeアイコンのデザイン に関する記述をしていきます。 assets/stylesheets/homes.scss /* ページTOPに戻る */ // ボタンの配置場所 .pagetop{ display: block; position: fixed; right: 18px; bottom: 10px; } // ボタンのデザイン .pagetop a{ display: block; font-size: 0; width: 50px; height: 50px; text-align: center; background: gray; border-radius: 50%; line-height: 50px; } // FontAwesomeアイコンのデザイン .pagetop a i{ font-size: 20px; color:#fff; line-height: 50px; } 動きをつける JavaScriptでは2種類の処理を記述します。 ①ページトップボタンの表示の処理 ②ページの1番上まで戻る処理 別々に見ていきます。 ①ページトップボタンの表示の処理 どれだけスクロールしたかによって、ボタンを表示非表示させます。 解説 // ページトップボタン表示、非表示 // Turbolinks無効化(詳しくは過去記事参照) $(document).on('turbolinks:load', function() { // 画面をスクロールを起点 $(window).scroll(function () { // ページのトップの位置をnowに代入 var now = $(window).scrollTop(); // ページトップから1000pxスクロールしたら以下を実行 if (now > 1000) { // ボタンがふわっと現れる $('.pagetop').fadeIn("slow"); } else { // ボタンがふわっと消える $('.pagetop').fadeOut('slow'); } }); }); ②ページの1番上まで戻る処理 表示されたボタンをクリックするとサーっとページの1番上まで戻っていきます。 解説 // ページトップへ戻るクリックで、スクロールして1番上に戻る // Turbolinks無効化(詳しくは過去記事参照) $(document).on('turbolinks:load', function() { $(function(){ // ボタンクリックで発火 $('.pagetop').click(function(){ // アニメーションで速さを指定してサーっとページトップまで戻ります $('body,html').animate({ scrollTop: 0},500); return false; }); }); }); Turbolinksに関する記事はこちら [Rails]リロードしないとJavaScript(jQuery)が動かない問題 最後にこの処理をまとめて記載しておきます。 assets/javascripts/application.js // ページトップボタン表示 $(document).on('turbolinks:load', function() { $(window).scroll(function () { var now = $(window).scrollTop(); if (now > 2500) { $('.pagetop').fadeIn("slow"); } else { $('.pagetop').fadeOut('slow'); } }); }); // ページトップへ戻るクリックで、スクロールして1番上に戻る $(document).on('turbolinks:load', function() { $(function(){ $('.pagetop').click(function(){ $('body,html').animate({ scrollTop: 0},500); return false; }); }); }); これで、ページトップ機能が実装できました。 まとめ ページトップ機能を実装する手順は3ステップです! ビューでボタンを配置 CSSを追加 動きをつける 今回は、かなりかんたんにページトップへ戻る機能を実装しました。 基本的なページトップ機能の実装方法が理解できたと思いますので、デザインを変更してみたり、スクロールする範囲を変えてみたり、1番上まで戻るスピードを変えてみたりと、いろいろ試してみてください。 自分で考えて試してみることで、もっと理解が深まって考えて実装できるようになるはずです! 頑張ってください! 最後まで見ていただきありがとうございました。
- 投稿日:2021-07-15T05:58:06+09:00
対戦ゲームを作る。 その5
概要 対戦ゲームを作る。 盤を描画して、クリックイベントを取るUIの実装。 考え方 ライブラリーは、jqueryとtatenoを使う。 盤は、8マス×8マス。 石は、画像から切り出す。 64個のスプライトに、クリックイベントリステナーを設置する。 画像 サンプルコード var coma = 'koma.png'; var pl0 = 'anta'; var pl1 = 'ore'; var ban = []; var flg = 1; var game = new Game(); game.fps(5); game.preload([coma]); game.start(); var aLabel = new Label("a", 50, 330); aLabel.size(20); aLabel.label('白: ' + pl0 + '<br>黒: ' + pl1); ban = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 1, 1, 1, 1, 1, 1, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; var field = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; for (var i = 0; i < 64; i++) { field[i] = new Sprite(40, 40, coma, 4, 1); field[i].frame(ban[i]); field[i].x((i % 8) * 40); field[i].y(Math.floor(i / 8) * 40); field[i].jq.click(function() { var n = this.id; n = n.replace("tsGameNode", ""); n = n - 2; test(n); }); } 以上。
- 投稿日:2021-07-15T02:30:04+09:00
herokuでrails + nodejsでnodeのバージョン指定する方法
前置き railsアプリを構築し、webpacker用にnodejsを入れたアプリを作った際、 nodeのバージョンが12系なのが気になった。 現時点でのLTSは14系なので開発環境と差異が発生しており環境差異でハマるのが嫌なのでheroku側のnodeバージョンを上げる方法を追求してみた。 軽く検索するとよく引っかかるのが heroku Buildpacks を使うというもの。 公式ドキュメント だがしかし、上記を見てもバージョンを指定する具体的な方法自体は記載がない。 ググって見た感じ、 package.json にバージョンを追記するとバージョンが指定できるとある。 "engines": { "node": "14.17.2", "npm": "6.14.13" } ただし、railsアプリでこれをやってpushしても remote: error app@1.0.0: The engine "node" is incompatible with this module. Expected version "14.17.2". Got "12.16.2" remote: error Found incompatible module. と、出る。 結論 前置きが長くなったが結論。 上記が出るのにはいくつか原因がある。 $ heroku buildpacks === dev-playrooms-free Buildpack URLs 1. heroku/ruby 2. heroku/nodejs heroku buildpacks このコマンドを打って 2にnodeが来ている そもそもnodejsが無い 上記のパターンでデプロイに失敗する。 解決方法 Herokuのアプリ設定を開く Settingsタブを開く Buildpacksの項目で add buildpacks でnodejsを追加する ここの順番がデプロイの順番に影響するのでnodejsを一番上に持ってくる 上記のあと、デプロイすればnodejsのバージョンがpackage.jsonで指定したものにアップグレードされるようだ。 余談 remote: engines.node (package.json): 14.17.2 remote: engines.npm (package.json): 6.14.13 remote: engines.yarn (package.json): unspecified (use default) enginesでnode, npm, yarnのバージョンが指定できるようだ。
- 投稿日:2021-07-15T02:18:06+09:00
これから学習する言語についてまとめた
はじめに これからプログラミングを学習するにあたって、学ぶ言語について以下にまとめました。 (間違いがありましたら、ご指摘を頂けますと幸いです) HTML 言語の正式名称 ハイパーテキスト・マークアップ・ランゲージ(Hyper Text Markup Language)の略 ハイパーテキスト 文書・画像・図表・音声・動画などのリンクを貼ったり、埋め込むことができる仕組み マークアップ サイト内の文書に意味づけをしていく作業。 HTMLでは文書をタグで囲むことで意味をつけていくことで、 コンピューターが正しく認識できるようにする。 ランゲージ 言語 開発された経緯 WEBページを作成するために開発された言語である 主にどのように利用されているのか ほとんどのWEBページにおける基本的な骨組みはHTMLで構成されている メモ SEO「検索エンジン最適化(Search Engine Optimization)」を意識しタグを使うことで、 検索エンジンが、より正確に文書の中身を理解できるようになる。 CSS 言語の正式名称 カスケーティング・スタイル・シート(Cascading Style Sheets) カスケーティング カスケードとは、何段も連なった小さな滝のこと。 転じて、同じものがいくつも数珠つなぎに連結された構造や、 連鎖的あるいは段階的に物事が生じる様子を表す。 スタイルシート 構造化文書などにおける表示形式を制御する仕組み。 ・・・WEBページのデザインやレイアウトなどの見栄えを変更できる 開発された経緯 WEBページの見た目の部分を編集するために開発された 主にどのように利用されているのか 文字のサイズや色、レイアウトなどのWEBページ上で表現される部分を 細かく編集する際に用いられる。 メモ HTMLとセットで使用されることが多い。 HTMLは「ページの構造」cssは「ページの装飾」を指定する言語といえる。 JavaScript(JS) 言語の正式名称 ジャバスクリプト(JavaScript ) 開発された経緯 元々はWEBページに動作をつけるために開発された 主にどのように利用されているのか ・ポップアップウィンドウを出現させる ・ブラウザ上で画像を拡大表示してWebページを見やすくする ・メッセージ送付フォームやパスワードの入力フォームなどを設置する 等 メモ クライアントサイドの言語である。(反対=サーバーサイド言語) プログラミング言語のJAVAとは、全くの別物。メロンとメロンパンくらいの違う。 Ruby 言語の正式名称 ルビー(Ruby) 開発された経緯 まつもとひろゆき氏により開発されたオブジェクト指向言語・スクリプト言語である。 全てのデータがオブジェクトとして扱う純粋なオブジェクト指向スクリプト言語であり 可読性を重視した構文が特徴。 主にどのように利用されているのか Ruby on Railsというフレームワークを使うことで 効率よくWEBサイトやWEBベースの業務システムを開発することができる メモ フリーソフトウェアであるため、無料で利用でき、コピー・変更・再配布が自由である。 Ruby on Rails 言語の正式名称 ルビー・オン・レールズ 開発された経緯 Rubyを使用したフレームワークの一つ。 Rubyを使って簡単なコードでWEBアプリケーションの開発ができるように作られた 主にどのように利用されているのか クックパッド(お料理レシピサイト) グノシー(ニュースアプリ) 食べログ(グルメサイト) 等 メモ Ruby on RailsはMVCアーキテクチャ(アーキテクチャ=構造)で構成されており Model(データを扱う箇所) View(ユーザーが直接目にする箇所) Controller(ModelとViewの連携を行う箇所)の要素に分解し、開発を行う。 調べていて気になった単語 クライアントサイドとサーバーサイド ・クライアントサイド言語 (JavaScript・CSSなど) WEBサーバーから結果がプラウザに返ってきて プラウザ側で処理するときに動作する言語 ・サーバーサイド言語 (Ruby・PHPなど) サーバー内部で動く言語でデータベースの処理を行う仕組みを作る言語 コンパイラ言語とインタプリンタ型言語 前提として ・プログラムを実行するには、 機械で理解するように言語を翻訳(コンパイル)する必要がある ・翻訳の方法の違いにより呼び方が異なる ・コンパイラ言語 記述したプログラムをコンピューターが理解可能な形式に事前に一括でコンパイルする言語 ・インタプリンタ型言語 記述したプログラムを1行ずつコンパイルする言語 *気をつける点 インタプリンタ型言語≠スクリプト言語 スクリプト型言語とは 開発で使用されるコードの内、読みやすく簡易的に記述できるプログラミング言語の汎称
- 投稿日:2021-07-15T01:35:46+09:00
videoをpauseしてもloadは止まらない
概要 htmlのvideoタグをJavaScriptで操作する時の注意点です。 videoをpause()すると動画は停止するのですが、ロードは止まりません。複数の動画があり、一方を止めてもう一方を再生したら、両方ともロードし続ける場合があります。 videoタグの仕様を調べても解決方法は見つからず、ロードを止める方法を探すのに手間取りました。 解決方法 解決方法は以下の通りです。(解決してしまえば、納得) videoをpause()する。 sourceのsrcを空にする。 videoをload()する。 ちなみに、以下の方法はダメでした。ロードが続きます。 srcを空にした後、load()しない。 JavaScriptでvideoタグを削除する。 サンプルコード1 javascript const video = document.querySelector('video'); // 再生 video.play(); // 一時停止 video.pause(); // ロードストップ const source = video.querySelector('source'); source.src = ''; video.load(); サンプルコード2 ロードを止めた後、もう一度再生するためにsrcを扱いやすくしたサンプルです。 html <video loop muted playsinline preload="none"> <source src="" data-src="./mov/sample.mp4" type="video/mp4"> </video> javascript const video = document.querySelector('video'); const source = video.querySelector('source'); // 再生 source.src = source.dataset.src; video.load(); video.addEventListener('canplay', () => { video.play(); }); // 一時停止 video.pause(); // ロードストップ source.src = ''; video.load(); data-srcを使用するとvideoのpreloadが機能しません。もしpreloadを使いたい場合は、srcとdata-srcを両方書くか、JavaScriptでsrcの値を保持しておき、再再生時に使うと良いと思います。 おわり 再生もロードも止まるstop()が欲しいところですが、無いので自分で止めました。 こちらを参考にしました。 https://stackoverflow.com/questions/4071872/html5-video-force-abort-of-buffering
- 投稿日:2021-07-15T00:30:41+09:00
async / await / Promise / then とかがよくわからんので自分なりの感覚的な理解のメモ
前置き JavaScript の async / await / Promise / then(もっというと try, catch とかも)あたり、書けないことはないけど、イメージが掴みづらく自分が何書いてるかよくわからなくなるので、感覚的な・どういう気持ちで書いていけばいいのか、というところを自分なりにメモするものです。 よくわかっていないものを現在進行系で紐解いていくメモなので「〜じゃないですか?」「〜は間違いです」みたいな指摘のされ方をされても、「正直分からないです」「そうですか・・・」とならざるを得ないので、こちらのほうが正しいですとか編集リクエストベースとか、そういう感じでご指摘いただけると大変嬉しいです。 何がわからないか 非同期な処理がしたいんじゃなくて、仕方なく非同期で返ってくる値をうまくこなしたいだけなんだ 別に同期的な処理をしたくて openFileSync を使ってるんじゃなくて、非同期がわからないから openFileSync を使っている、を解決したい Promise がわからん とりあえず async / await だと楽になるらしいけど、説明見ると結局 Promise が出てきてわからん とりあえず非同期で返ってくる値をどうにかしたい とりあえず非同期で返ってくる値を async / await でどうにかしよう (注意:基本的に await は async な関数内でしか使えない、Chrome あたりはトップレベルな await に対応している。) async 関数の宣言(function)の前につけると非同期な関数ですよということになる async function getImageWidth(path) { } こうすれば非同期関数になる。なんか非同期処理をした感が出る。 await 非同期な関数を呼び出すときに使うと、結果が帰ってくるまで次の行を実行せずに待つ 原則として、async な関数内でしか使えない(Chrome などはトップレベルな await に対応している) ので、さっきの async 関数だと、 await をこんな感じに・・・ async function getImageWidth(path) { const image = await loadImage(path); return image.width; // loadImage の値が返ってくるまで実行されない } これは便利!Promise とかコールバックとか考えずに、ひとまず非同期な値を受け取れた! 画像が返ってこなかったら…? 素直に、 try / catch で書けば良い。(これが await の利点) async function getImageWidth(path) { try { const image = await loadImage(path); return image.width; } catch (err) { return false; } } (※ほんとは return は Promise オブジェクトで返す、暗黙的に Promise.resolve でラッピングされる、後述) この getImageWidth は、loadImage が返ってくるまで何も返さないので、非同期関数。 このままでも自動で非同期関数になってくれるから大丈夫だけど、できれば非同期関数ってわかるようにしておきたい。 Promise が顔を出す async は別に非同期な値を扱わなくても非同期関数にできる。 async function sampleAsync() { return "非同期だよ〜"; } 普通の関数と何が違うのかと言うと・・・ async function sample() { console.log(sampleAsync()); // => Promise{<pending>} } でました Promise。これが分からなくて嫌になっちゃうんだよな。 ひとまず回避するには、async な関数なので、await すれば大丈夫。 async function sample() { console.log(await sampleAsync()); // => "非同期だよ〜" } この辺の Promise の気持ちを整理する。 例えば次のコード、 async function sample() { const image = await loadImage(path); console.log(image.width); // => 画像が返った後に実行される } これは、await があるので、console.log は画像が返るまで実行されない では、以下のように、うっかり await を消すと、画像が返ってなくても console.log が実行されてしまう。 const image = loadImage(path); console.log(image.width); // => 画像が返ってなくても実行される この時点で、 image の値は決まっていない。 そういう、非同期処理のときに、(もし実際返ってたとしても、論理的に)まだ返ってるかどうかわからない、という値が Promise と考えるとうまく整理できそう。 Promise って何?って聞かれたら、ざっくりいうと、不確定な値。 最初に書いた async function sampleAsync() { return "非同期だよ〜"; } でも Promise が返ったが、これは return "非同期だよ〜" が暗黙的に Promise に変換されている。 Promise の使い方は以下のように、値を resolve({ここ}) に詰め込めば良い。 先程のをちゃんと書くと、以下のようになる。 async function sampleAsync() { return new Promise((resolve, reject) => { resolve("非同期だよ〜") }) ) こうやって Promise を返す async な関数であれば、await で呼び出せる。 メモ async な関数は2つの形がある Promise を返している await がある await は async な関数に対して使うので、結局これも Promise があるということになる。 async / await が無い頃 async / await が無い頃は、普通の関数で Promise という値を返すことで、非同期関数ということにしていた。 await の代わりに、then を使っていた sampleAsync().then((value) => { console.log(value) // => "非同期だよ〜" }) try / catch の代わりに、catch を使っていた sampleAsync().catch((err) => { console.log(err) }) なんとなく Promise もわかってきた。 async の返り値は Promise で返す 最初の Q. ところで、 Promise もなかった頃はどうしてたの・・・? A. ず〜〜っとコールバックを受け取っていた setTimeout(() => { console.log("1") setTimeout(() => { console.log("2") setTimeout(() => { console.log("3") }, 1000) }, 1000) }, 1000) これが Promise によって // setTimeout を Promise 化する wrapper function sleep (time) { return new Promise(resolve => { setTimeout(resolve, time) }) } sleep(10, console.log("1")) .then(() => { console.log("1") return sleep(1000) }).then(() => { console.log("2") return sleep(1000) }).then(() => { console.log("3") return sleep(1000) }) そしてこれが async / await によって // setTimeout を async 化する wrapper async function sleep (time) { return new Promise(resolve => { setTimeout(resolve, time) }) } (async () => { await sleep(1000) console.log("1") await sleep(1000) console.log("2") await sleep(1000) console.log("3") }) とても見やすくなった! ここまでくれば、ドキュメントを読んでいろいろ解決できるようになるはず async / await https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/async_function 今回説明しなかった、async の成功/失敗や、並列読み込みに注意 Promise https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/all https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/race とりあえず await で受けとけば良いんだけど、 Promise についてもちゃんと知っとくと、もうちょっと柔軟に対応できたりもする。 【注意】async な関数は何でもかんでも await で受ければ良いわけじゃない await をすると、そこで返ってくるまで処理が止まってしまう! const image = await loadImage(imagePath) const sound = await loadSound(soundPath) とすると、画像を取得している間に音楽を並列で取得することができない! そういうときの解決策が上記記事や、async / await の MDN にも書いてある。(Promise.all)