- 投稿日:2020-04-05T23:18:39+09:00
【async/await】JavaScriptで非同期処理プログラムを作成する【標準入力】
はじめに
「解きながら学ぶJava」(その名の通り「〜〜するプログラムを作成せよ。」といった問題を解きながらJavaを学んでいく書籍)に書かれている問題を"JavaScriptで書く"ということをやっている中で見事に詰まりました。
JavaScriptは、JavaやRubyのようにクラスを用いて綺麗に定義、みたいなことがしづらいですし、標準入力も若干面倒なので結構苦戦しました
(今回はreadlineモジュールを使用)ちなみに私は、上記の本を持っておきながら、Javaを触ったことはありません。笑
どんな問題?
キーボードから次々と整数値を読み込んで、合計と平均を求めるプログラムを作成せよ。加算する整数の個数は最初に読み込むこと。
という問題です。
実際に書いたプログラム
4_26.jsconst readline = require('readline'); const log = console.log; const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); let num_int; let input_int; let sum = 0; async function readInt() { const rl = readline.createInterface({input: process.stdin}); const ait = rl[Symbol.asyncIterator](); log('整数:'); input_int = parseInt((await ait.next()).value); }; function checkInt() { rl.question('何個加算しますか: ', async function (answer) { num_int = parseInt(answer); for(let i = 0; i < num_int; i++) { await readInt(); sum += input_int; } rl.close(); log(`合計は${sum}です。`); log(`平均は${sum / num_int}です。`); }); }; log('整数を加算します。'); checkInt();コードの中身は深くは追いませんが、ざっくりポイントだけ記載します。
大元の処理を行うcheckInt関数に、「何個加算しますか?」の問に対して入力された数の回数分だけfor文で処理をループさせている
checkInt関数とは別に、readInt関数を定義し、加算対象の数を入力する処理を書いている
標準入力は、readlineモジュールを使用し、繰り返し入力が必要になる部分にasync/awaitを使用
async/awaitを使用することで非同期処理が可能となり、コンソールへの繰り返し入力が可能となります。
実行するとこんな感じになります(↓)
整数を加算します。 何個加算しますか: 3 整数: 48 整数: 67 整数: 89 合計は204です。 平均は68です。上記のコードはあくまで一例ですので、ご参考までに。
- 投稿日:2020-04-05T23:13:21+09:00
[Rails]画像選択時にプレビュー表示
本記事投稿のいきさつ
アプリの中で画像投稿機能を実装したとき、ただフォームを作成するだけでは画像を選択しても表示がされず何を選んだのか確認をすることが出来ません。
そこで、画像を選択した時点でプレビュー表示することができればいいなと思い、実際に機プレビュー機能を作成したため、記録として残したいと思います。
今回はユーザーのプロフィール画像の編集画面を想定します。
そのため、既に登録されている画像は最初からプレビューさせた状態で表示をさせます。前提
- 変種画面はprofile_edit.html.haml
- 画像の保存先は Usersテーブル: imageカラム
- file_field本体は隠し、画像が選択されていない時はiconを、 選択されている時はプレビュー画像をクリックすることで画像選択できるようにします。
- jQueryを使用しますが、必要なGemのインストール等は既に出来ているとします。
- ユーザー機能はDeviseを使用しています。
- cssは今回の内容に含まれません。
フォーム作成
まず、今回はhamlでビューのフォームを作成します。
profile_edit.html.haml= form_for current_user, url: {action: 'profile_update'} do |f| .form-group .image_form .image_form__contents -# ラベルでfile_fieldとicon、プレビュー画像を紐付けます = f.label :image, class: 'image_label' do .prev-contents -# 既に登録されている画像があれば表示をさせます - if current_user.image.present? .prev-content = image_tag current_user.image.url, alt: "preview", class: "prev-image" -# 既に登録されている画像がなければiconを表示させます - else = icon('fas', 'image', class: 'photo-icon') -# file_fieldはdisplay: none;で隠します = f.file_field :image, class: 'image_form__contents__field hidden_file'FileReader
今回はFileReaderを使用します。
FileReaderとはHTML5世代の機能でユーザーのPC内にあるファイルやバッファ上の生データに対して、読み取りアクセスを行えるオブジェクトです。jsファイルの編集
今回はimage_preview.jsを作成して、そこに記述していきます。
image_preview.js$(document).on('turbolinks:load', function () { $(function () { // 画像をプレビュー表示させる.prev-contentを作成 function buildHTML(image) { var html = ` <div class="prev-content"> <img src="${image}", alt="preview" class="prev-image"> </div> ` return html; } // 画像が選択された時に発火します $(document).on('change', '.hidden_file', function () { // .file_filedからデータを取得して変数fileに代入します var file = this.files[0]; // FileReaderオブジェクトを作成します var reader = new FileReader(); // DataURIScheme文字列を取得します reader.readAsDataURL(file); // 読み込みが完了したら処理が実行されます reader.onload = function () { // 読み込んだファイルの内容を取得して変数imageに代入します var image = this.result; // プレビュー画像がなければ処理を実行します if ($('.prev-content').length == 0) { // 読み込んだ画像ファイルをbuildHTMLに渡します var html = buildHTML(image) // 作成した.prev-contentをiconの代わりに表示させます $('.prev-contents').prepend(html); // 画像が表示されるのでiconを隠します $('.photo-icon').hide(); } else { // もし既に画像がプレビューされていれば画像データのみを入れ替えます $('.prev-content .prev-image').attr({ src: image }); } } }); }); });上記で出てくるDataURISchemeとは、
簡単にいうと、画像やらJavascriptやらそういったHTMLのコンテンツを文字列として定義出来るものです。
以上でで、画像が表示されます。終わり
最後まで見ていただきありがとうございました。
- 投稿日:2020-04-05T19:32:08+09:00
GitHub の「Fork」ボタンを「パクる」に置き換える UserScript
// ==UserScript== // @name Replace Fork with パクる for GitHub // @namespace https://foooomio.net/ // @version 0.1 // @description だから、そういうことじゃなくて? // @author foooomio // @license MIT License // @match https://github.com/* // @run-at document-idle // @grant none // ==/UserScript== // 元ネタ: https://twitter.com/IiToshihide/status/1246487047545556992 (() => { 'use strict'; const $ = document.querySelector.bind(document); function main() { $('.pagehead-actions .octicon-repo-forked').nextSibling.textContent = 'パクる'; } new MutationObserver(main).observe( $('#js-repo-pjax-container'), { childList: true } ); main(); })();
- 投稿日:2020-04-05T19:29:09+09:00
【プログラミング学習4日目】JavaScriptをかじってみた
はじめに
独学プログラミング学習の4日目
忘れっぽいので備忘録代わりに残しておこうと思う
※人に見せる前提ではないよ目標
- 簡単なページをサクっと作れるようになる
- Web上に自分のポートフォリオを公開
- アウトプットのスキルを身につける
JavaScriptってなに?
こちらを見てみると,動的にコンテンツを更新したり,マルチメディアを管理したり,その他多くのことができるスクリプト言語と書いてある
つまりどういうことだってばよ?となっているのだが,
おそらくページ内のものを動かしたり,なにかのアクションをトリガーとして動作するプログラムを作れるということだと思っておく
使ってみればわかるよね実際に使ってみた
今回作ったのは,こちら
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>JavaScriptのお勉強</title> </head> <body> <input type="text" id="message" placeholder="ここに文字を入力"> <select id="count"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> <option value="4">4</option> <option value="5">5</option> <option value="10">10</option> </select> <button class="submit">送信</button> <script src="./jslearning.js"></script> </body> </html>'use strict'; const displayText = () => { const texts = document.getElementById("message").value; const counts = document.getElementById("count").value; for(let i = 0; i < counts; i++){ let p = document.createElement('p'); p.textContent = texts; document.body.appendChild(p); } } const buttons = document.querySelectorAll('button'); for(let i = 0; i < buttons.length ; i++) { buttons[i].addEventListener('click', displayText); }テキストボックスに入力した文字列を,セレクトボックスで選択した数値の分だけ表示させるプログラムですね.
今回,これを作るにあたって調べた項目は,
- inputとselectのvalueを取得する方法
- for以外の繰り返し処理
この2つだけ!
HTMLから値を取得する
HTMLから値を取得する方法はいくつかあるけど,今回はdocumentオブジェクトを使ったよ
理由はなんとなく,それが一番基本になんだと思い込んでいたから.Documentオブジェクトとは,DOMツリー(HTMLを木構造にした状態のこと)を取得して,ドキュメントの操作ができるようになるすごいやつだよ
難しい事は考えないで,HTMLの操作ができるって思ってくれればいいよさて,こいつ↓
<input type="text" id="message" placeholder="ここに文字を入力">
の値を取得してるのが,
JavaScriptにかかれているこいつ↓
const texts = document.getElementById("message").value;
だよ.
今回はidをキーにして値を取得したけど,classとかnameとかで探すこともできるよ.セレクトボックス↓
<select id="count">
~~~
</select>
から値を取得してるのが,こいつ↓
const counts = document.getElementById("count").value;
値が取れればあとはforで表示させるだけだよ
for以外の繰り返し処理
JavaScriptの繰り返し処理にはたくさんの種類があるよ
細かい特徴とかは調べてないけど,
name 説明 for 基本 while 無限ループ楽しい foreach 配列くるくる回せる for in オブジェクトを回せるっぽい for of オブジェクトを回せるっぽい map 戻り値がある filter 配列に入ってる値を計算して返してくれるらしい こんな感じかな?
いっぱいあるからどの場面で何を使うかとかわからないんだけど,いい見分け方とかないかな?おわり
いろいろ調べたけど,考えが散らばりすぎたので今日は一旦ここまで
JavaScriptはHTMLを操作して,なにかのイベント(クリックとか)後に表示させたい時とかに使うってことはわかったよ
テトリスとかお絵かきの森が作れるんだよ!って聞いたけど,どうやるんだろう・・・・明日はcallback関数について調べてみようかな
- 投稿日:2020-04-05T19:18:36+09:00
【node.js】validationを新しいバージョンに対応コードに変更する
解決したい問題
パッケージvalidationのバージョンが古いので新しいバージョンの書き方に変更したい。
環境
OS: macOS
express: ^4.17.1
ejs: ^2.6.2
express-validator: ^6.4.0変更前のコード(古いコード)
router.post('/', (req, res, next) => { var request = req; var response = res; req.check('name', 'NAMEは必ず入力して下さい。').notEmpty(); req.check('password', 'PASSWORDは必ず入力して下さい。').notEmpty(); req.getValidationResult().then((result) => { if (!result.isEmpty()) { var content = '<ul class="error">'; var result_arr = result.array(); for(var n in result_arr) { content += '<li>' + reault_arr[n].msg + '</li>' } content += '</ul>'; var data = { title: 'Login', content: content, form: req.body } response.render('login', data); } else { var nm = req.body.name; var pw = req.body.password; User.query({where: {name: nm}, andWhre: {password: pw}}) .fetch() .then((model) => { if (model == null) { var data = { title: '再入力', content: '<p class="error">名前またはパスワードが違います</p>', form: req.body }; respose.render('login', data); } else { request.session.login = model.attributes; var data = { title: 'Login', content: '<p>ログインしました!<br>トップページに戻ってメッセージを送信下さい。</p>', form: req.body } respose.render('login', data); } }); } }) });このreq.checkがエラーになる。
変更後
上に↓のコードを書き足す。
const { check, validationResult } = require('express-validator');router.post('/', [ check('name', 'NAMEは必ず入力して下さい。').notEmpty(), check('password', 'PASSWORDは必ず入力して下さい。').notEmpty() ], (req, res, next) => { var request = req; var response = res; const errors = validationResult(req); if (!errors.isEmpty()) { var content = '<ul class="error">'; var result_arr = errors.array(); for(var n in result_arr) { content += '<li>' + result_arr[n].msg + '</li>' } content += '</ul>'; var data = { title: 'Login', content: content, form: req.body } response.render('login', data); } else { var nm = req.body.name; var pw = req.body.password; User.query({where: {name: nm}, andWhere: {password: pw}}) .fetch() .then((model) => { if (model == null) { var data = { title: '再入力', content: '<p class="error">名前またはパスワードが違います</p>', form: req.body }; response.render('login', data); } else { request.session.login = model.attributes; var data = { title: 'Login', content: '<p>ログインしました!<br>トップページに戻ってメッセージを送信下さい。</p>', form: req.body } response.render('login', data); } }).catch((error) => { var data = { title: '再入力', content: '<p class="error">名前またはパスワードが違います。</p>', form: req.body }; res.render('login', data); console.log(error); }); } });大きく変わったのはcheckの位置です。
参考
↓この本の学習中に、バージョンが古くてエラーになった。
https://www.amazon.co.jp/Node-js%E8%B6%85%E5%85%A5%E9%96%80-%E7%AC%AC2%E7%89%88-%E6%8E%8C%E7%94%B0-%E6%B4%A5%E8%80%B6%E4%B9%83/dp/4798055220↓最新バージョンの参考
https://express-validator.github.io/docs/
- 投稿日:2020-04-05T18:59:01+09:00
【React + Material-UI】数字当てゲームを作ろう!【2020年4月版】
はじめに
この記事は「Reactのチュートリアルを終えて、何かを作ってみたい」という読者を想定しています。
分からない部分が出た場合はReactの公式ドキュメントに立ち返りましょう。material-uiの公式ドキュメントも要チェック
前にReact+Material-UIで作ったもの
今回作ったもの
React Number Guess
ランダムに生成された1~100の数字を推測するゲームです。「60より上ですか?」などの質問をしていき、数字が絞れたら「〇〇ですか?」で正解を出します。右側に予想履歴が表示されます。
戦略
ReactコンポーネントにはMaterial-UIを使い、保持するstateをReactのuseStateで実装します
また、今回はuseEffectを使い、読み込み時に1~100のランダムな数字を生成します。component, containerの洗い出し
component
- 数字ボタン: 0~9の数字を入力できるボタンコンポーネント。色はピンクで固定。
- 大きいボタン: 「より下ですか?」「クリア」「予想する」ボタンなど、数字ボタン以外に使うコンポーネント。色をpropsで指定できる。
- ディスプレイ: 現在の質問を表示するコンポーネント
- 予想履歴: いままでの予想の一覧を表示するコンポーネント
- 正解モーダル: 正解したときに表示されるモーダルコンポーネント
その他、タイトルと説明はコンポーネントにせず、containerにそのまま書きます。
container
- NumberGuess: すべてのコンポーネントを集めて表示するコンテナ
stateの洗い出し
- inputNumber: 予想に使う数字(int)
- questionContent: 質問の内容。"より下ですか?"、"より下ですか?"、"ですか?"の3種類(string)
- history: 質問の履歴が格納された配列(array)。
[[34,"より下ですか?", "はい"],[17,"より下ですか?", "はい"],[7,"より上ですか?", "はい"]]
のような2次元配列- answer: 答えの数字。はじめにuseEffectでランダムに設定される(int)
- open: 正解したときにmodalを開くために必要なboolean
- guessTimes: 質問した回数(int)
インストール
create react app
npx create-react-app react-calculator cd react-calculator@material-ui/core
// npm を使う場合 npm install @material-ui/core // yarn を使う場合 yarn add @material-ui/coreバージョン情報
- react: 16.13.1
- @material-ui/core: 4.9.9
ファイル構成
コンポーネントの実装
Display
Display.jsimport React from 'react'; import { makeStyles } from '@material-ui/core/styles'; import Box from '@material-ui/core/Box'; import Typography from '@material-ui/core/Typography'; const useStyles = makeStyles((theme) => ({ box: { width: "100%", margin: "10px auto", }, })); export default function Display(props) { const classes = useStyles(); return ( <div> <Box className={classes.box}> <Typography variant="h3"> {props.children} </Typography> </Box> </div> ); }NumberButton
NumberButton.jsimport React from 'react'; import { makeStyles } from '@material-ui/core/styles'; import Button from '@material-ui/core/Button'; export default function NumberButton(props) { const useStyles = makeStyles((theme) => ({ margin: { margin: theme.spacing(1), backgroundColor: "#f48fb1", }, })); const classes = useStyles(); return ( <div> <Button className={classes.margin} onClick={props.onClick}> {props.children} </Button> </div> ); }ポイント
- propsでonClickイベントを受け取って、ButtonのonClickに接続しています
LongButton
LongButton.jsimport React from 'react'; import { makeStyles } from '@material-ui/core/styles'; import Button from '@material-ui/core/Button'; export default function LongButton(props) { const useStyles = makeStyles((theme) => ({ margin: { margin: theme.spacing(2), padding: theme.spacing(1), backgroundColor: props.backgroundColor, }, })); const classes = useStyles(); return ( <div> <Button className={classes.margin} onClick={props.onClick}> {props.children} </Button> </div> ); }ポイント
- 数字ボタンと他のボタンでスタイルを変えようと思いましたが、同じような見た目になってしまいました。こちらは背景色をpropsに渡すことができます。
GuessHistory
GuessHistory.jsimport React from 'react'; import { makeStyles } from '@material-ui/core/styles'; import Typography from '@material-ui/core/Typography'; import Box from '@material-ui/core/Box'; const useStyles = makeStyles((theme) => ({ box: { textAlign: "left", margin:"20px 0", padding:"0 20px" }, })); export default function GuessHistory(props) { const classes = useStyles(); const history = props.history; return ( <div> <Typography>予想履歴</Typography> <Box className={classes.box}> { history.map((guess, index) => <Box key={index} marginBottom="10px"> <Typography variant="h5"> {index+1}. {guess[0]}{guess[1]}: {guess[2]} </Typography> </Box> ) } </Box> </div> ); }ポイント
history
をprops
で受け渡しています。history.map
で単一のguess
を受け取り、Box
内で表示しています。map
を使うときはコンポーネントにkey
をつけるのを忘れずに。正解モーダル
SuccessModal.jsimport React from 'react'; import { makeStyles } from '@material-ui/core/styles'; import Modal from '@material-ui/core/Modal'; import Backdrop from '@material-ui/core/Backdrop'; import Fade from '@material-ui/core/Fade'; import Box from '@material-ui/core/Box'; import Typography from '@material-ui/core/Typography'; const useStyles = makeStyles((theme) => ({ modal: { display: 'flex', alignItems: 'center', justifyContent: 'center', }, paper: { backgroundColor: theme.palette.background.paper, border: '2px solid #000', boxShadow: theme.shadows[5], padding: theme.spacing(2, 4, 3), }, })); export default function TransitionsModal(props) { const classes = useStyles(); const guessTimes = props.guessTimes; const answer = props.answer; const open = props.open return ( <div> <Modal aria-labelledby="transition-modal-title" aria-describedby="transition-modal-description" className={classes.modal} open={open} closeAfterTransition BackdropComponent={Backdrop} BackdropProps={{ timeout: 500, }} > <Fade in={open}> <div className={classes.paper} > <Box marginBottom="30px" id="transition-modal-title"> <Typography variant="h4">正解です!</Typography> </Box> <Box id="transition-modal-description"> <Typography variant="h5">正解の数字: {answer}</Typography> <Typography variant="h5">正解までの質問数: {guessTimes}</Typography> </Box> </div> </Fade> </Modal> </div> ); }ポイント
- 元となるコンポーネントはmaterial-uiの公式ドキュメント、モーダルから引っ張ってきました
props
でopen
を受け取ることで、コンテナのopen
stateがtrue
になったときに子のopenも更新されます。コンテナの実装
NumberGuess.jsimport React, {useState, useEffect} from 'react'; //components import NumberButton from '../components/NumberButton'; import LongButton from '../components/LongButton'; import Display from '../components/Display'; import GuessHistory from '../components/GuessHistory'; import SuccessModal from '../components/SuccessModal'; //material-ui import Box from '@material-ui/core/Box'; import Grid from '@material-ui/core/Grid'; import Typography from '@material-ui/core/Typography'; export default function NumberGuess() { //states const [inputNumber, setInputNumber] = useState(0); const [questionContent, setQuestionContent] = useState(""); const [history, setHistory] = useState([]); const [answer,setAnswer] = useState(0); const [open, setOpen] = useState(false); const [guessTimes, setGuessTimes] = useState(0); //ページが読み込まれたときに一度だけ実行させる useEffect(() => { setAnswer(Math.floor( Math.random() * (100) ) + 1); }, []) //数字ボタンをクリックしたとき const handleNumberClick = (num) => { let newInputNumber = inputNumber*10 + num; if(newInputNumber >= 1 && newInputNumber <= 100){ setInputNumber(newInputNumber); } } //予想の内容ボタンをクリックしたとき const handlequiestionContentClick = (content) => { if(content === "biggerThan"){ setQuestionContent("より上ですか?"); } else if(content === "smallerThan"){ setQuestionContent("より下ですか?"); } else if(content === "exact"){ setQuestionContent("ですか?"); } } //クリアボタンをクリックしたとき const handleClearClick = () => { setInputNumber(0); setQuestionContent(""); } //予想ボタンをクリックしたとき const handleSubmitClick = () => { //予想の内容が入力されていない場合はすぐに返す if(questionContent === "") return; //予想した回数を1増やす setGuessTimes(guessTimes + 1); if(questionContent === "より上ですか?"){ if(inputNumber < answer){ setHistory([ ...history, [inputNumber, "より上ですか?", "はい"] ]); } else { setHistory([ ...history, [inputNumber, "より上ですか?", "いいえ"] ]); } } else if(questionContent === "より下ですか?"){ if(inputNumber > answer){ setHistory([ ...history, [inputNumber, "より下ですか?", "はい"] ]); } else { setHistory([ ...history, [inputNumber, "より下ですか?", "いいえ"] ]); } } else if(questionContent === "ですか?"){ if(inputNumber === answer){ setHistory([ ...history, [inputNumber, "ですか?", "はい"] ]); //正解の場合にモーダルを開く setOpen(true); } else { setHistory([ ...history, [inputNumber, "ですか?", "いいえ"] ]); } } setInputNumber(0); setQuestionContent(""); } return ( <div> <Box marginBottom="20px"> <Typography variant="h4">React Number Guess</Typography> <Typography variant="h6">1~100の数字を予想しよう</Typography> </Box> <Grid container spacing={1}> <Grid item sm={6} xs={12}> <Display>{inputNumber} {questionContent}</Display> <Box display="flex" flexDirection="column"> <Box display="flex" justifyContent="center"> <NumberButton onClick={() => handleNumberClick(0)}>0</NumberButton> <NumberButton onClick={() => handleNumberClick(1)}>1</NumberButton> <NumberButton onClick={() => handleNumberClick(2)}>2</NumberButton> <NumberButton onClick={() => handleNumberClick(3)}>3</NumberButton> <NumberButton onClick={() => handleNumberClick(4)}>4</NumberButton> </Box> <Box display="flex" justifyContent="center"> <NumberButton onClick={() => handleNumberClick(5)}>5</NumberButton> <NumberButton onClick={() => handleNumberClick(6)}>6</NumberButton> <NumberButton onClick={() => handleNumberClick(7)}>7</NumberButton> <NumberButton onClick={() => handleNumberClick(8)}>8</NumberButton> <NumberButton onClick={() => handleNumberClick(9)}>9</NumberButton> </Box> <Box display="flex" justifyContent="center"> <LongButton onClick={() => handlequiestionContentClick("biggerThan")} backgroundColor="#f48fb1">より上ですか?</LongButton> <LongButton onClick={() => handlequiestionContentClick("smallerThan")} backgroundColor="#f48fb1">より下ですか?</LongButton> <LongButton onClick={() => handlequiestionContentClick("exact")} backgroundColor="#f48fb1">ですか?</LongButton> </Box> <Box display="flex" justifyContent="center"> <LongButton onClick={handleClearClick} backgroundColor="#bdbdbd">クリア</LongButton> <LongButton onClick={handleSubmitClick} backgroundColor="#b2ff59">予想する</LongButton> </Box> </Box> </Grid> <Grid item sm={6} xs={12}> <GuessHistory history={history}/> </Grid> </Grid> <SuccessModal open={open} guessTimes={guessTimes} answer={answer}/> </div> ); }ポイント
- useEffectを使って、マウント時に一度だけanswerを設定するようにしました。単純に
const answer = ...
としていると、ボタンを押すごとに更新されてしまいます。handleSubmitClick
のsetHistory
内では元のhistory
を展開して、最後に新たな要素を追加しています。App.js
App.jsimport React from 'react'; import './App.css'; import NumberGuess from './containers/NumberGuess' function App() { return ( <div className="App"> <div className="container"> <NumberGuess /> </div> </div> ); } export default App;App.css
App.css.App { text-align: center; } .container{ margin: 80px auto 80px auto; max-width: 900px; background-color: #80deea; }以上で完成です!
おまけ
こちらの記事を参考に、gh-pagesでサイトを公開しました。
https://shintaro-hirose.github.io/react-number-guess/今後もReactで簡単なアプリを作っていきます!
- 投稿日:2020-04-05T18:10:49+09:00
sakuraエディタマクロ(json整形、マークダウン形式変換、改行統一、デコード)
サクラのマクロ
サクラエディタのマクロについて
サクラエディタでは、下記のファイルをマクロとして実行することができる。
https://sakura-editor.github.io/help/HLP000204.html
拡張子 種類 mac キーマクロ ppa PPAマクロ js WSH:Jscriptマクロ vbs WSH:VBScriptマクロ その他 WSH:他にWSHが認識できるものであれば使える 設定方法・利用方法
https://qiita.com/t_ookubo/items/6ad844ab04fc0170b4eb
利用しているマクロ
(あくまで支援目的なので、細かい精査とかエラーハンドリングとかはしてないです。)
jsonファイル整形
jsonFormat.js// 行数を取得 var cnt = GetLineCount(0); // 改行を除いた行文字列を取得 var textAll = ""; for (var i = 1; i <= cnt; i++) { var str = GetLineStr(i).replace(/\r\n/,"").replace(/\n/,""); textAll += str; } var editJson = ""; var innerFlg = false; var intentLv = 0; var quoteType = []; var blockType = []; var preChr = ""; for (var i = 0; i < textAll.length; i++) { var prefix = ""; var suffix = ""; var c = textAll.substring(i, i + 1); if (!innerFlg) { if (c == " ") { continue; } if (c == "\"" || c == "'"){ innerFlg = true; quoteType[quoteType.length] = c; // JSON または 配列の1つ目の要素の時はスペース1個を追加 var chkText = editJson.replace(/( |\t|\r\n|\n)/g, ""); if (chkText.length > 0) { var preChar = chkText.substring(chkText.length - 1, chkText.length); if (preChar == "[" || preChar == "{") { c = " " + c; } } } else { if (c == "{" || c == "[") { suffix = "\r\n"; intentLv = intentLv + 1; blockType[blockType.length] = c; for (var j = 0; j < intentLv; j++) { suffix = suffix + "\t"; } } if (c == "}" || c == "]") { prefix = "\r\n"; suffix = "\r\n"; intentLv = intentLv - 1; blockType.pop(1); } if (c == ",") { prefix = "\r\n"; } } } else { if (quoteType.length > 0 && c == quoteType[quoteType.length - 1]){ innerFlg = false; } } if (prefix != "") { for (var j = 0; j < intentLv; j++) { prefix = prefix + "\t"; } } var line = "" + prefix + c + suffix; if (preChr == "\r\n" && line.substring(0, 1) == "\r\n") { line = line.substring(1); } editJson = editJson + "" + line; preChr = line.substring(line.length - 1, line.length); } SelectAll(0); InsText(editJson + "\r\n");CRLF改行統一
CRLF.macS_SelectAll(0); // すべて選択 S_CopyCRLF(0); // CRLF改行でコピー S_Paste(0); // 貼り付けタブ区切りをMarkdown形式のテーブルに変換
toMarkdownTable.jsvar selStRow; var selEdRow; var result = ''; // 行非選択状態の場合 if(IsTextSelected === 0 ) { selStRow = 0; selEdRow = GetLineCount(0); SelectAll(0); } else { selStRow = GetSelectLineFrom; // 選択開始行取得 selEdRow = GetSelectLineTo; // 選択終了行取得 } // 項目数を取得 var colCount = GetLineStr(selStRow).split(/\t/).length; for (var i = selStRow; i <= selEdRow; i++) { // i行目を取得し、先頭・終了・タブをパイプラインで置き換える result += '| ' + GetLineStr(i).split(/\t/).join(' | ').replace( /\r?\n/g , ' |') + '\r\n'; // 選択1行目の場合は、header行を追加する if (i === selStRow) { result += '|'; for (var j = 1; j <= colCount ; j++) { result += '--------|'; } result += '\r\n'; } } InsText(result);「¥uXXXX」形式ユニコードをアンエスケープ
unicodeUnescape.js// 行数を取得 var cnt = GetLineCount(0); var result = ''; for (var i = 1; i <= cnt; i++) { // i行目を取得 var str = GetLineStr(i); // \u.... の文字列の配列で取得、大文字小文字は問わない uniArray = str.match(/\\u.{4}/ig); if (uniArray) { for (var j = 0, len = uniArray.length; j < len; j++) { str = str.replace(uniArray[j], String.fromCharCode(uniArray[j].replace('\\u', '0x'))) } } result += str; } SelectAll(0); InsText(result);URLデコード
decodePercent.js// 行数を取得 var cnt = GetLineCount(0); var result = ''; for (var i = 1; i <= cnt; i++) { // i行目を取得 var str = GetLineStr(i); str = decodeURI(str); result += str; } SelectAll(0); InsText(result);
- 投稿日:2020-04-05T17:21:42+09:00
ワイ「なに!?型のズレを吸収できるやと!?」
前回の記事、
4歳娘「パパ、20歳以上のユーザーを抽出して?」
の続きやで!休日ワイ
ワイ「最近、コロナウィルスの影響でリモートワークがメインになってしもうたから」
ワイ「家でも快適にお仕事ができるように、42.5インチの特大モニタを買ったで!」
ワイ「やっぱデッカいモニタはええな〜」
ワイ「インデントも思いっ切り入れれるわ」よめ太郎「さすがに入れすぎやろ・・・」
ワイ「私のインデントは53ですいうてな」
よめ太郎「いやフリーザ様どこで勝負してんねん」
よめ太郎「しかもインデント奇数なんかい」娘(4歳)「ねえ、パパ?」
ワイ「ん?」
ワイ「なんや、娘ちゃん」娘「その大きいモニタを使って、早速やってほしい作業があるの」
ワイ「おお、ええでええで!」
娘ちゃんの依頼内容
娘「実は、こないだ山で拾ってきた家族風オブジェクトに、子供が増えてて」
娘「オブジェクトの内容が変わっちゃったの」JavaScriptconst family = { mother: { name: "よめ太郎", age: 35 }, father: { name: "やめ太郎", age: 37 }, children: [ { name: "娘ちゃん", age: 4 }, { name: "ボブ", age: 39 } ] };娘「
daughter
がchildren
になっちゃったの」ワイ「ん?どういうことや?」
娘「前回は娘が1人だけだったから
daughter
っていうオブジェクトだったけど」
娘「子供が1人増えて、children
という配列になっちゃったの」
娘「だから、20歳以上のユーザーを上手く抽出できなくなっちゃったの」ワイ「そ、そうなんか」
ワイ「でも、子供たちはどうせ20歳未満なんやから」
ワイ「mother
とfather
だけ取り出せばええんちゃうの?」娘「それが・・・弟のボブは養子で39歳なの」
ワイ「Oh...ボブ...」
ワイ「ワイの知らん間に子供が増えてるから、少しおかしいなって思ったけど」
ワイ「そういう事情やったんか」
ワイ「これは、ワイだけでは無理や」
ワイ「ハスケル子ちゃんを呼ぼう」ハスケル子もTV会議で参戦
ワイ「もしもし、ハスケル子ちゃん?」
ハスケル子「はい」
ハスケル子「私を呼ぶってことは、ボブの件ですね」ワイ「さすがハスケル子ちゃん、話が早いわ」
よめ太郎「(いや早すぎやろ)」
オブジェクトと配列が混ざっている件
ワイ「
mother
とfather
はオブジェクトなのに」
ワイ「children
は配列やねん」
ワイ「オブジェクトと配列が混ざってんねん」娘「そうなの」
娘「そこから、20歳以上のユーザーを抽出して」
娘「以下のようなotonaArray
っていう配列を作りたいの」JavaScript[ { name: "よめ太郎", age: 35 }, { name: "やめ太郎", age: 37 }, { name: "ボブ", age: 39 } ]ハスケル子「うーん」
ハスケル子「オブジェクトと配列が混じってて扱いづらいなんて・・・」
ハスケル子「そもそも仕様とか設計の問題のような・・・」
ハスケル子「もっと、サーバサイドでデータを整形してから返したほうが・・・」ワイ「いや、仕様とか設計とかサーバサイドとかじゃないねん」
ワイ「山で拾ったオブジェクトなんやから」
ワイ「もうボブは家族なんやから」ハスケル子「やむを得ないですね」
ハスケル子「分かりました」
ハスケル子「大人だけを抽出したotonaArray
という配列を作ればいいんですね」まずは
Object.values()
とmap()
ハスケル子「
family
というオブジェクトを元に配列を作るので」
ハスケル子「まずはObject.values(family)
ですね」JavaScriptconst familyArray = Object.values(family)ハスケル子「↑こうですね」
ハスケル子「オブジェクトのkey名は使わずにvalueだけ取り出して、配列を作ってくれるメソッドです」
ハスケル子「つまり、familyArray
の中身は・・・」JavaScript// familyArrayの中身 [ { name: "よめ太郎", age: 35 }, { name: "やめ太郎", age: 37 }, [ { name: "娘ちゃん", age: 4 }, { name: "ボブ", age: 39 } ] ]ハスケル子「↑こんな風になります」
ワイ「おお、あとはこの中から20歳以上のユーザーを抽出すんねやな」
ワイ「でも、オブジェクトと配列が混じってるけど大丈夫なん?」ハスケル子「余裕です」
ハスケル子「配列のflat()
メソッドを使って」
ハスケル子「ネストした配列から1段階フラットな配列を作ります」JavaScriptconst flatFamilyArray = familyArray.flat()ハスケル子「↑こうですね」
ハスケル子「すると・・・」JavaScript// flatFamilyArrayの中身 [ { name: "よめ太郎", age: 35 }, { name: "やめ太郎", age: 37 }, { name: "娘ちゃん", age: 4 }, { name: "ボブ", age: 39 } ]ハスケル子「↑こんな感じの
flatFamilyArray
が出来上がります」ワイ「おお」
ワイ「子供の配列が1段階フラットになって、単なるフラットな配列になってフラットやな!」よめ太郎「(語彙どうなってんねん)」
ワイ「あとは
filter()
で20歳以上のユーザーを抽出するだけやな・・・!」ハスケル子「はい」
ハスケル子「なので、いちいち変数に入れずにまとめて書くと・・・」JavaScriptconst otonaArray = Object.values(family) .flat() .filter(person => person.age >= 20);ハスケル子「↑こうですね」
ハスケル子「すると・・・」JavaScript// otonaArrayの中身 [ { name: "よめ太郎", age: 35 }, { name: "やめ太郎", age: 37 }, { name: "ボブ", age: 39 } ]ハスケル子「↑こんな感じの、大人だけを集めた
otonaArray
が出来あがります」ワイ「まじか〜」
ワイ「flat()
メソッド便利やな」ハスケル子「はい」
ワイ「今までのワイやったら
flat()
メソッドなんて知らんかったから」
ワイ「Array.isArray()
メソッドを使って」
ワイ「配列ならこう、配列じゃなければこう・・・みたいな感じで」
ワイ「if文とかで条件分岐しながら」
ワイ「配列の詰め直しをしとったところやわ」ハスケル子「なるほど」
ハスケル子「つまり・・・」JavaScript// まず空配列を生成。 const flatFamilyArray = []; familyArray.forEach(personOrArray => { // if文で配列かどうか判断しながら詰め直していく。 if (Array.isArray(personOrArray)) { // 配列の場合はスプレッドで展開してpush。 flatFamilyArray.push(...personOrArray); } else { // 配列じゃない場合はそのままpush。 flatFamilyArray.push(personOrArray); } });ハスケル子「↑こういう感じで処理するってことですよね」
ワイ「せやせや」
ハスケル子「空配列を作って、そこにどんどん詰め直していく・・・」
ハスケル子「そういった、副作用と手続きの連続で書くのもまあ」
ハスケル子「分かりやすいといえば分かりやすいので良いんですけど」
ハスケル子「私はやっぱり」JavaScriptconst otonaArray = Object.values(family) .flat() .filter(person => person.age >= 20);ハスケル子「↑この、メソッドの連続で書ける関数型な感じが好きですね」
ワイ「なるほどなぁ・・・」
ワイ「flat()
はなんか、if文を内包してるようで」
ワイ「魔法みたいやな」ハスケル子「ちょっとそんな感じですよね」
ハスケル子「たとえば・・・」JavaScript[3].flat() // -> [3] [[3]].flat() // -> [3]ハスケル子「↑こんな感じで」
ハスケル子「元の値が3
であろうと[3]
であろうと」
ハスケル子「一度[]
で包んでflat()
してやれば」
ハスケル子「同じ[3]
になる・・・」
ハスケル子「こういう型ハックみたいなの楽しいですよね」ワイ「なるほどな」
ワイ「[3]
をflat()
しても、ただの3
にはならないんやな」
ワイ「せやから[3].flat()
してやることで型を合わせられる・・・」ハスケル子「そうなんです」
ワイ「なるほどなぁ・・・」
ワイ「オブジェクトから配列に変換しつつ、フラットにして、フィルタリングする・・・」
ワイ「って一つの流れの中でできるから」
ワイ「なんかカッコええね」ハスケル子「はい!」
まとめ
flat()
はネストした配列を1段階フラットにしてくれる。
(引数を与えることで2段階、3段階フラットにすることも可能)ワイ「↑こんな感じやね!」
ハスケル子「はい」
ハスケル子「今回みたく、オブジェクトと配列を同列に扱わないといけない、なんてのは」
ハスケル子「そもそも設計が微妙な感じもしますけど」
ハスケル子「例えば、スクレイピングで取得したデータとかの場合は」
ハスケル子「データの形式を自分で決められないので」
ハスケル子「こういうハックが役に立つこともあるかもしれませんね」
ハスケル子「もし思い出したら、使ってみてください」
ハスケル子「たまに便利ですよ!」ワイ「なるほどな」
ハスケル子「もっと楽しい、
flatMap()
なんていうメソッドもありますよ」
ハスケル子「map()
してflat()
してくれるやつです」
ハスケル子「まるで関数が変身するみたいですよ」ワイ「関数が変身する・・・?」
ワイ「どういうこと・・・?」ハスケル子「それはまた次回お話しますね」
ワイ「おお、なんか楽しみやな・・・!」
〜つづく〜
参考文献
注意
もちろんIEでは使えへんで!!!
- 投稿日:2020-04-05T16:56:04+09:00
jQueryで特定セルをクリックした時にそのセルの内容で検索する
djangoでjQueryをいじっていたら結構難しかったのでメモ。
よく調べたらcellのdataを受け、searchに突っ込むだけですね。Reference様様。スクリプト
sample.html<script> // jQueryの関数書くところ。 $(document).ready(function () { // id="dataTable"と付くtableを取得する。 var table = $('#dataTable').DataTable(); // クリック時のイベントを定義 $('#dataTable').on('click', 'td', function () { // どの列がクリックされたか、列番号を取得(0から開始) var row_number = table.column(this).index(); // 例えば3列目がクリックされたときに限定 if (row_number == 2 ) { // クリックされたセルの内容を得る var data = table.cell(this).data(); // セルの内容が空の時には処理しない if( data != undefined ){ // 検索バーに文字列を突っ込み(search(data))、それで検索(draw())する。 table.search(data).draw(); } } }); }); </script>refs
- 投稿日:2020-04-05T15:49:08+09:00
C3.js で折れ線グラフを描画する
概要
- D3.js のラッパーライブラリ C3.js で折れ線グラフを描画する
- 環境: C3.js 0.7.15 + D3.js 5.15.1
C3.js と D3.js のダウンロード
Release v0.7.15 · c3js/c3 · GitHub から v0.7.15.zip をダウンロードして展開する。c3.css と c3.min.js を取り出す。
Release v5.15.1 · d3/d3 · GitHub から d3.zip をダウンロードして展開する。d3.min.js を取り出す。
シンプルな折れ線グラフを描画するサンプル
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>C3.js で折れ線グラフを描画する</title> <link href="c3.css" rel="stylesheet"><!-- c3.css を読み込む --> </head> <body> <div id="my-container" style="width: 640px; height: 480px; border: solid 1px red"> <div id="my-chart"></div> </div> <script src="d3.min.js"></script><!-- D3.js を読み込む --> <script src="c3.min.js"></script><!-- C3.js を読み込む --> <script> let chart = c3.generate({ bindto: '#my-chart', size: { width: 640, height: 480 }, // グラフ描画領域のサイズ data: { columns: [ ['データ1', 30, 200, 100, 400, 150, 250], ['データ2', 50, 20, 10, 40, 15, 25] ], labels: true // それぞれの点に数値を表示 }, axis: { x: { label: { text: 'X軸', position: 'outer-middle' } }, y: { label: { text: 'Y軸', position: 'outer-middle' } } } }); </script> </body> </html>時系列データの折れ線グラフを描画するサンプル
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>都道府県別のPCR検査陽性者累積数</title> <link href="c3.css" rel="stylesheet"><!-- c3.css を読み込む --> </head> <body> <div id="my-chart"></div> <script src="d3.min.js"></script><!-- D3.js を読み込む --> <script src="c3.min.js"></script><!-- C3.js を読み込む --> <script> let chart = c3.generate({ bindto: '#my-chart', size: { width: 800, height: 600 }, data: { x: 'mydate', // 時系列インデックスの項目名 xFormat: '%m%d', // 時系列インデックスの日付フォーマット columns: [ ['mydate', '0325', '0326', '0327', '0328', '0329', '0330', '0331', '0401', '0402', '0403', '0404'], ['東京都', 160, 201, 227, 227, 368, 436, 449, 527, 527, 690, 779], ['大阪府', 123, 129, 136, 154, 192, 209, 217, 245, 245, 312, 347], ['愛知県', 139, 144, 147, 150, 164, 164, 170, 176, 176, 186, 198], ['千葉県', 46, 51, 54, 60, 126, 160, 160, 164, 164, 181, 192], ['北海道', 162, 166, 167, 168, 172, 176, 177, 177, 177, 185, 190], ['神奈川県', 73, 77, 82, 88, 104, 107, 115, 120, 120, 153, 181], ['兵庫県', 87, 87, 87, 90, 125, 132, 136, 147, 147, 167, 172], ] }, axis: { x: { type: 'timeseries', tick: { format: '%m月%d日' }, label: { text: '日', position: 'outer-middle' } }, y: { label: { text: 'PCR検査陽性者の累積', position: 'outer-middle' } } } }); </script> </body> </html>参考資料
- 投稿日:2020-04-05T15:40:19+09:00
BlocklyでWebページをデザイン
BlocklyでWebページをデザインして表示するサイトを作成しました。JavaScript命令のブロックで動きをプログラミングします。
「ねそプロ」ウェブサイトを作ろう
この教材は、3つのWebページで構成しています。
○ ウェブサイトを作ろう ← サイト作成
○ ウェブサイトに画像登録 ← 画像の登録
○ ウェブサイトを見よう ← 作成したサイト表示
↓ 教材はこちら
http://iwate-manabi-net.sakura.ne.jp/nesopuro/ビジュアル → テキストプログラミングへ
2020年から小学校ではプログラミング教育が始まります。多くの学校で教材としてScratch等のビジュアルプログラミング使って指導を行うと思われます。小学校で体験したビジュアルプログラミングからテキストプログラミングへ橋渡しをするための教材として開発しました。HTMLタグ命令のブロックを組み合わせてWebページをデザインします。JavaScript命令のブロックでインタラクティブなページを作成できます。作成したプログラムをテキストとして表示します。ブロックで作成したビジュアルプログラムとテキストを見比べることでWebページの基本を理解させることができます。このテキストをメモ帳等に貼り付け、拡張子を.htmlとして保存するとWebページになります。ブラウザから起動するとボタン等も作動します。
双方向性のあるコンテンツ
2021年から中学校で実施される学習指導要領 技術・家庭 解説において、「コンテンツのプログラミングによる問題を解決する学習活動としては、例えば、学校紹介の Web ページにQ&A方式のクイズといった双方向性のあるコンテンツを追加」と示されています。そこで、次のサンプルを作成しました。
○ 2択クイズ
○ 英単語クイズ(テキスト入力)
○ APIを使って「天気」「地図」を表示
命令ブロックの組み合わせで様々なデザイン、作動を刷るページの作成ができます。さらに、APIを使って他のWebで公開されているデータの活用も体験できます。「命令ブロック」について
HTMLとJavaScript命令のブロック、外部Webサイトの画像やAPI等を読み込むブロックを用意しています。
HTML命令ブロック
見出し(特大) <h1>
見出し(大) <h2>
改行 <br>
段落 <p>
文字色 <font color='red'>
文字の大きさ <font size='4'>
センタリング <div align='center'>
グループ <div id='Group'>
ボタン <button id='B'>
文字入力 <input type='text'>
画像表示 <img src='sample.jpg'>
※ 「ウェブサイトに画像登録」からアップロードした画像を表示します。
※ 登録した当日の画像のみ表示します(過去の画像は表示しません)。JavaScript命令ブロック
ボタンが押された addEventListener('click', function(){})
グループを非表示 style.display ='none'
グループを表示 style.display ='block'
グループ内容を削除 textContent = null
グループに追加 insertAdjacentHTML('beforeend','')
条件分岐 if(A=='こたえ'){}else{}
くり返し for (var count=0; count<5; count++){}外部Webサイトの画像やAPI等のブロック
天気予報(気象庁) <img src='http://www.jma.go.jp/jp/yoho/'>
天気図(気象庁) <img src='http://www.jma.go.jp/jp/g3/'>
降水量(気象庁) <img src='http://www.jma.go.jp/jp/amedas/'>
Googleマップ <iframe src='https://www.google.com/maps/'>これらの命令ブロックを組み合わせて、Webページを作成します。この実習を通して、生活の課題をネットワークを活用したプログラミングによって解決する方法を考えさせます。
- 投稿日:2020-04-05T15:18:57+09:00
obnizとブザーで兄弟配管工ゲームのコイン音を出してみた
作ろうと思ったきっかけ
- ProtoOutoStudio 3回目授業でobnizに触れる
- ブザーが面白かった
兄弟配管工ゲームでは、ブロックを叩くとコインが出てきますよね。その時の効果音を再現してみようと思いました。
”あの”効果音の音階を調べてみた
が、楽譜が読めないぞ....。オクターブもよく分からん....。
よし、トライ&エラーか???(音痴です)まずはベースとなる”ド”を鳴らす
”ド”の音が何Hzか調べました。今回はこちらのサイトを参考に周波数を設定していきます。
【鳴らしてみた様子】
obnizにブザーをつけてみた。まずはドの音を実装!#obniz pic.twitter.com/4pDRjS9z9N
— まえぷー@出窓菜園 BWG (@kmaepu) April 5, 2020音を鳴らすだけなら、obnizのJavascriptパーツライブラリから試すと爆速で確認できます。
obniz IDを入力し、Test Runをクリックすると、指定した音が鳴ります。デフォルトは1000Hzになっているので、鳴らしたい音に合わせるといいかなと思います。
今回はobnizのボタンを押すと、指定の音が鳴るようなプログラムをNode.jsで動かしています。ソースコードはこちら。
const Obniz = require('obniz'); var obniz = new Obniz("OBNIZ-ID"); // OBNIZ-IDに自分のIDを入れます obniz.onconnect = async function () { // スピーカーを呼び出す var speaker = obniz.wired("Speaker", {signal:0, gnd:1}); // ディスプレイ処理 obniz.display.clear(); // 一旦クリアする obniz.display.print("Hello obniz!"); // Hello obniz!という文字を出す // スイッチの反応を常時監視 obniz.switch.onchange = function(state) { if (state === "push") { // 押されたとき console.log("pushed"); // ディスプレイ処理 obniz.display.clear(); // 一旦クリアする obniz.display.print("pushed"); // pushed という文字を出す // 音を鳴らす speaker.play(523.25); // ドの音 } else if (state === "none") { // none で押してないとき obniz.display.clear(); // 一旦クリアする // スピーカーで音を鳴らさない stop speaker.stop(); } } }上述のプログラムを使う際は、obniz.jsが必要となります。次のコードでインストールしておきましょう。
npm i obnizあの効果音に使われている音を鳴らす
あの効果音は”シ”と”ミ”の2音で構成されているようです。といっても”ミ”は1つ上のオクターブになっているようです。
では、音階表を参考にしてみると....。
”シ”は987.76Hz、”ミ”は1318.51Hzでつくります。
音を鳴らしてみるとこのようになりました。
ちょっと発展。#obniz pic.twitter.com/SDfbhotxgO
— まえぷー@出窓菜園 BWG (@kmaepu) April 5, 2020よさそうですね!あとは自動化してタイミング調整だな....
組み合わせる
音を順番に出力するのは簡単ですが、タイミングがわからない!!!!!!!!
楽譜を見ると次のようになっています。こちらのサイトを参考にしてみると、200bpsのテンポであると読み解きました。200bpsとは、1分間に200拍の速度です。つまり、
1分間(60秒) ÷ 200 = 0.3秒
となります。楽譜をみると、どうやら”シ”と”ミ”の間は何も記号がないので1拍だとして、”ミ”の後は2拍ありそうなので0.6秒として作りました。
結果....。
あの効果音に少し近づいたか?
— まえぷー@出窓菜園 BWG (@kmaepu) April 5, 2020
オクターブを1つ上げれば良さそう(^^)#obniz #protoout pic.twitter.com/TiLEEtjqCd
思っていたより音が低そうなので、1オクターブあげてみましょう。1オクターブあげてみた。
— まえぷー@出窓菜園 BWG (@kmaepu) April 5, 2020
こっちの方が良さそう。でも、なにか物足りなさが…。#obniz #protoout pic.twitter.com/3abNA7yBQHこちらの方が近そうですね!でも何か足りない気がしてくる....
何度も同じ音を聞いていると分からなくなってしまうなぁ。でもこれで良しとしますか。余談
圧電スピーカーで効果音作るの楽しそう....沼に片足いれはじめてしまうぅ!
最終的なソースコード
const Obniz = require('obniz'); var obniz = new Obniz("Obniz_ID"); // Obniz_IDに自分のIDを入れます obniz.onconnect = async function () { // スピーカーを呼び出す var speaker = obniz.wired("Speaker", {signal:0, gnd:1}); // ディスプレイ処理 obniz.display.clear(); // 一旦クリアする obniz.display.print("Hello obniz!"); // Hello obniz!という文字を出す // スイッチの反応を常時監視 obniz.switch.onchange = async function(state) { if (state === "push") { // 押されたとき console.log("pushed"); // ディスプレイ処理 obniz.display.clear(); // 一旦クリアする obniz.display.print("pushed"); // pushed という文字を出す // 音を鳴らす speaker.play(1975.53); // シ await obniz.wait(300); // Wait speaker.play(2637.02); // ミ await obniz.wait(600); // Wait speaker.stop(); } else if (state === "none") { // none で押してないとき obniz.display.clear(); // 一旦クリアする // スピーカーで音を鳴らさない stop speaker.stop(); } else if (state === "left") { speaker.play(1318.51); // ミ } } }
- 投稿日:2020-04-05T15:11:54+09:00
JavaScriptの浮動小数点数を符号なし64ビット整数で表現したい
はじめに
JavaScriptにおける数値は、倍精度浮動小数点数しかないわけですが、倍精度浮動小数点数を構成するビット列を符号なし64ビット整数で表現した値が欲しいという場面がありました。このやり方について調べたので、記事にしてみました。
変換方法
function DoubleToBigInt(f) { var buf = new ArrayBuffer(8); (new Float64Array(buf))[0] = f; lower = BigInt((new Uint32Array(buf))[0]); upper = BigInt((new Uint32Array(buf))[1]); return (upper << BigInt(32)) | lower; }上記のコードでは、
ArrayBuffer
を経由して、倍精度浮動小数点数から2つの符号なし32ビット整数に変換しています。upper
は倍精度浮動小数点数の上位32ビット、lower
は倍精度浮動小数点数の下位32ビットです。残念ながら、Uint64Array
なんてものはありませんから、こうするしかないですね…2つの符号なし32ビット整数に変換した後は、これらの値を
BitInt
に変換して符号なし64ビット整数の値を表現しています。この関数の戻り値を確認してみましょう。
var f = 0.1234567; console.log(DoubleToBigInt(f)); // 4593560413433027186n
4593560413433027186
という値になりましたが、これが正しいのかC言語のコードで検証してみます。#include <stdio.h> union { double d; unsigned long l; } val; int main(void) { val.d = 0.1234567; printf("%ld\n", val.l); return 0; }上記のコードをコンパイルして、実行してみます。
$ gcc test.c $ ./a.out 4593560413433027186JavaScriptコード側の出力と一致しました。
おわりに
これをやろうと思ったのは、ももテクの記事を参考にJavaScriptの
Math.random
関数(XorShift128+)の内部状態を復元しようとしたためです。浮動小数点数表現で出力されるよりは、整数値で出力された方が解析しやすいかなと思いました。なにはともあれ、上記のコードで浮動小数点数 -> 符号なし64ビット整数への変換ができたので、一件落着です。
追記 (2020/04/05 15:38)
@uhyo さんから
BigUint64Array
の存在を教えていただきました。ありがとうございます!BigUint64Array
を使えば、DoubleToBigInt
関数を以下のようにもっと簡潔に書けます。function DoubleToBigInt(f) { var buf = new ArrayBuffer(8); (new Float64Array(buf))[0] = f; return (new BigUint64Array(buf))[0]; }参考サイト
https://stackoverflow.com/questions/2003493/javascript-float-from-to-bits
https://qiita.com/uhyo/items/f9abb94bcc0374d7ed23
- 投稿日:2020-04-05T13:05:48+09:00
(小ネタ)Node.jsのWebアプリでclusterを使いながら定期的に子プロセスを再起動させる
Node.jsでサーバサイドWebアプリを開発中、なぜかメモリリークがあるライブラリに遭遇してしまったので、ワークアラウンドとして、定期的にプロセスを再起動させて、メモリリークの問題を緩和したいと思いました。
- コード: https://github.com/knjname/2020-04-05_restartClusterChild
- 参考: https://nodejs.org/api/cluster.html
サーバサイド
http://0.0.0.0:10080 をリスンするプロセスが4つ立ち上がり、5秒未満で子プロセスを停止させ、その後に再起動します。(実用上は、もっと長い時間でプロセスを殺すべきです。)
src/index.jsconst cluster = require("cluster"); const http = require("http"); const sleep = time => new Promise(done => setTimeout(done, time)); const clusterCount = 4; const portNumber = 10080; if (cluster.isMaster) { const spawnProcess = () => { // プロセスを終了させるまでの時間: 0 〜 5000 msec const ttl = ~~(5000 * Math.random()); const child = cluster.fork(); let timeout; child.on("listening", () => { // 指定時間で終了(Graceful kill)させる console.log(`誕生! 死まで ${ttl} msec.`); timeout = setTimeout(() => { console.log(`死: ${child.id}`); child.kill(); }, ttl); }); child.on("disconnect", () => { // 別の理由で死んだ場合はkillをキャンセル if (timeout) { clearTimeout(timeout); } }); child.on("exit", () => { // 子プロセスが終了したら代わりのものを1つ起動する spawnProcess(); }); }; // 子プロセスを複数起動する for (let i = 0; i < clusterCount; i++) { spawnProcess(); } } if (cluster.isWorker) { // Express や Koa など好きに使いましょう http .createServer(async (req, res) => { // リクエスト終了までやや時間がかかる設定 await sleep(1000); res.writeHead(200); res.end("Request done\n"); }) .listen(portNumber); }起動すると、下記のようなログを吐きながら、プロセスの終了と生成を延々と繰り返します。
誕生! 死まで 2712 msec. 誕生! 死まで 3984 msec. 誕生! 死まで 4297 msec. 誕生! 死まで 1547 msec. 死: 4 誕生! 死まで 4276 msec. 死: 2 : :テスト
ab
コマンドできちんとリクエストが中断されずにいるか、テストしてみます、$ ab -c 20 -n 500 http://localhost:10080/ This is ApacheBench, Version 2.3 <$Revision: 1826891 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking localhost (be patient) Completed 100 requests Completed 200 requests Completed 300 requests Completed 400 requests Completed 500 requests Finished 500 requests Server Software: Server Hostname: localhost Server Port: 10080 Document Path: / Document Length: 13 bytes Concurrency Level: 20 Time taken for tests: 26.226 seconds Complete requests: 500 Failed requests: 0 Total transferred: 44000 bytes HTML transferred: 6500 bytes Requests per second: 19.07 [#/sec] (mean) Time per request: 1049.029 [ms] (mean) Time per request: 52.451 [ms] (mean, across all concurrent requests) Transfer rate: 1.64 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 1 1.0 0 7 Processing: 1000 1008 5.4 1007 1025 Waiting: 1000 1007 4.7 1006 1023 Total: 1000 1008 5.5 1007 1025 Percentage of the requests served within a certain time (ms) 50% 1007 66% 1010 75% 1012 80% 1013 90% 1016 95% 1019 98% 1020 99% 1022 100% 1025 (longest request)特に何も問題なくリクエスト処理は完了しているようです。
Complete requests: 500 Failed requests: 0まとめ
- cluster でマルチプロセス化できるし、定期的にプロセスを再起動して、健全性を保つことができるはず
- こういうゴミ掃除はマルチスレッドモデルだとできなさそう
- 投稿日:2020-04-05T13:00:44+09:00
puppeteer 画像保存 (fs, path)
puppeteerで画像を保存する
結論
app.jsconst puppeteer = require('puppeteer'); const fs = require('fs'); const path = require('path'); (async () => { const browser = await puppeteer.launch({headless: false}); const page = await browser.newPage(); await page.goto('目的の画像サイトのURL'); await page.waitFor(5000); let images = await page.evaluate(() => Array.from(document.querySelectorAll('.pict')).map(a => a.src) ) //↓これ絶対冗長だからなおしたい let imagesPop = await page.evaluate(() => Array.from(document.querySelectorAll('.pict')).map(a => a.src.split('/').pop()) ) for(let i = 0; i < images.length; i ++){ let localfilefullpath = path.join(__dirname, imagesPop[i]); const viewSource = await page.goto(images[i]); fs.writeFile(localfilefullpath, await viewSource.buffer(), (error) => { if (error) { console.log(error); return; } console.log('保存できたよ!'); }); await page.waitFor(1000); } await browser.close(); })();概略
evaluateで要素を2種類取得
①images = https~から始まるやつ
②imagesPop = 一番最後の/から~.jpgとかで終わるやつ画像の保存
①ローカルファイルのフルパスとimagesPopを連結する
②ローカルファイルに直接保存させちゃう(本当はfs.mkdirSyncでフォルダ作った方がいいと思う)誤解があったらご指摘ください
解説
インストールするnpmは
puppeteer
fs
path
の3つブラウザで確認できるように
const browser = await puppeteer.launch({headless: false});
に目的のページにたどり着いてからローディングのぐるぐるが一旦止まるくらい待つ
await page.waitFor(5000);
セレクタで待つ方法、Xpathで待つ方法、ページ遷移で待つ方法、など様々あるが、簡単なのでこの方法。バンされちゃったらいやなので。安全に。複数の要素を取得
let images = await page.evaluate(() =>
Array.from(document.querySelectorAll('.pict')).map(a => a.src)
)
このevaluate, Array.from, querrySelectorAll, mapの流れはめっちゃ使う
ほかにも$xで指定する方法もあるがいつも混乱してしまうから個人的にはあまり好まないローカルファイルのフルパスと画像URLの最後の部分を連結
let localfilefullpath = path.join(__dirname, imagesPop[i]);
バイナリデータを扱うときは
buffer()
を使うんかなおそらく
fs.writeFile(localfilefullpath, await viewSource.buffer())
第一引数で保存場所、第二で保存したい画像URLに.buffer()
を追加参考文献
https://qiita.com/Ancient_Scapes/items/5f63bcfc2b5108313f77
https://qiita.com/KawamotoShuji/items/878ae659a5c6e540343e
http://info-i.net/fs-writefile
https://wa3.i-3-i.info/word1146.html
- 投稿日:2020-04-05T11:16:40+09:00
FIDOデバイスエミュレータを作成してみた(これが最後)
何回か続けてきたFIDOデバイスエミュレータはこれが最後です。
・WebAuthnを使ったFIDOサーバを立ててみた
本物のFIDOデバイスをお持ちであれば、この投稿だけでも参考になります。
・FIDOデバイスエミュレータを作成してみた。。。が
仮想的なFIDOデバイスをサーバに配置しようという野望です。
・FIDOデバイスエミュレータを作成してみた(だけどもうちょっと。)
Arduinoを使って、ブラウザとFIDOデバイスエミュレータの橋渡しをしました。上記の投稿ですでに編集済みですが、結局のところ、x509証明書が、FIDO Allianceに登録されていないためと、最終的に判断しました。
試行錯誤しながらも、ある程度の完成度まで仕上げられました。
FIDOのプロトコルだけでなく、PIN認証付きBLEペリフェラル、x509証明書など、多岐にわたるノウハウを獲得できました。今回の主要な目的の一つでしたので良かったと思っています。最後の最後で行った修正を記録しておきます。
x509v3証明書にする
x509証明書の作成には以下を使っていました。
・Junkurihara/js-x509-utils
https://github.com/junkurihara/js-x509-utilsしかしながら、v3には対応していないため、急遽以下のnpmモジュールに変更しました。
・jsrsasign
https://kjur.github.io/jsrsasign/これでv3にはなりました。fido2-libのコメントアウトも解除できました。
ですが、Chromeはエラーを吐いていましたので、さらに、る本物のFIDOデバイスから抜き出したx509証明書との違いを探してみました。以下の2つで差分がありました。
・サブジェクトキー識別子
・FIDO U2F certificate transports extensionサブジェクトキー識別子が何なのかがわかりませんでした。一般にHashらしく、それっぽく入れてみたのですがだめでした。コード上ではコメントアウトしています。
FIDO U2F certificate transports extension は、以下の仕様書に記載がありました。
FIDO U2F Authenticator Transports Extension結局x509証明書作成の部分は、以下のようなコードになりました。
(有効期限が固定値だったりと、力尽きてる感がありますが。。。)index.js// X.509証明書の作成 var tbsc = new rs.KJUR.asn1.x509.TBSCertificate(); tbsc.setSerialNumberByParam({'int': 1234}); tbsc.setSignatureAlgByParam({'name': 'SHA256withECDSA'}); tbsc.setIssuerByParam({'str': "/CN=FT FIDO 0200"}); tbsc.setNotBeforeByParam({'str': "190511235959Z"}); tbsc.setNotAfterByParam({'str': "340511235959Z"}); tbsc.setSubjectByParam({'str': "/CN=FT FIDO P2000000000000"}); tbsc.setSubjectPublicKey(kp.pubKeyObj); /* //サブジェクトキー識別子 var extSKI = new rs.KJUR.asn1.x509.Extension(); extSKI.oid = '2.5.29.14'; const ski = rs.KJUR.crypto.Util.hashHex(kp_cert.pubKeyObj.pubKeyHex, 'sha1'); const derSKI = new rs.KJUR.asn1.DEROctetString({ hex: ski }); extSKI.getExtnValueHex = () => {return derSKI.getEncodedHex() }; tbsc.appendExtension(extSKI); */ // FIDO U2F certificate transports extension var extSKI2 = new rs.KJUR.asn1.x509.Extension(); extSKI2.oid = '1.3.6.1.4.1.45724.2.1.1'; extSKI2.getExtnValueHex = () => { return "03020640" }; tbsc.appendExtension(extSKI2); var cert = new rs.KJUR.asn1.x509.Certificate({'tbscertobj': tbsc, 'prvkeyobj': kp_cert.prvKeyObj }); cert.sign(); var attestationCert = Buffer.from(cert.hTLV, 'hex');FIDO仮想デバイスの通信バッファサイズを変更
X509証明書をv3にしたからなのか、通信サイズが少し大きくなって、バッファオーバーフローが発生しました。
Arduinoのソースコードで、通信バッファサイズを2倍にしました。結果
だめでした。状況変わらずです。
以下のブラウザ画面です。何回見させられたことか。。。。最後に
ここまでご参照いただきありがとうございました。
- 投稿日:2020-04-05T09:51:07+09:00
Vue.js 画像URLから幅と高さを取得しようとしてはまった
こちらの記事どうり簡単に取得できるものだと思っていたけど、
var img = new Image(); img.src = 'http://image.src.com/image.jpg'; var width = img.width; // 幅 var height = img.height; // 高さvue.jsでやってみた時には
img.width
やimg.height
が0だった。調べているとstackoverflowなどでreturnが0だ!なんで!みたいな記事を見つけたがそもそもjavascriptの知識が乏しいのとjQueryを使ってどうにかしようとしている解決法をvue.jsでどうかくかわからなかった
知人に聞きこちらの記事を発見
https://www.raymondcamden.com/2019/06/13/reading-image-sizes-and-dimensions-with-vuejs
img.onload
という関数を使ってvar img = new Image();
でできたimgを読み込まなきゃ画像の幅、高さを取得できないそうだ。かつ、スコープの関係で
onload
内で使った変数を他で使うことはできないため、vue.jsのdataにプロパティを設定して、そこにセットする形になるそう。vue.jsexport default { data () { return { new_image: [] } }, methods: { getImageSize (obj) { var img = new Image(); img.onload = () => { this.new_image.push({width: img.width, height: img.height}); } img.src = obj; } } } // objには画像urlが入ります。
- 投稿日:2020-04-05T04:51:40+09:00
[rails,payjp]カード登録の際に登録画面をリロードしないとトークンを作成できない時の解決例
1.原因
今回の筆者の問題はturbolinksが効いていたからでした。
2.なぜ起こった?
理由は、turbolinksの機能が影響範囲ページの遷移を非同期で行う(影響範囲の全てのviewページが合わさって1ページとして扱う)ためでした。
他画面を経由し複数で情報を持った状態でクレジットカードの登録をしようとすると、悪意のある情報を持っている危険性があり安全性を保てないと見なされてしまいpayjpからの返答を受けることができなくなっていました。3.解決方法
クレジットカード登録画面のみturbolinks(非同期通信)を切ってページ遷移するように記述することで筆者は解決しました。
具体的には
app/views/layouts/apprication.html.haml(全体のbodyの記述部分)
とapp/views/cards/index(ページ遷移元)
を下記のように修正しましたapp/views/layouts/apprication.html.haml〜前略(header部分の記述は省略します)〜 %body - if content_for?(:body_attributes) = yield(:body_attributes) = render 'layouts/notification' = yieldapp/views/cards/index〜前略〜 = link_to "/cards/new", class: 'method-of-payment__body__button__icon',"data-turbolinks": false do #app/views/cards/newに遷移する記述の部分に "data-turbolinks": false をつけました 〜後略〜4.参考記事
https://gist.github.com/saboyutaka/8727377
https://qiita.com/Cheekyfunkymonkey/items/216bf7426493e6213927
https://qiita.com/keitah/items/05f02efe4e11ab2473e8
- 投稿日:2020-04-05T03:59:42+09:00
JavaScript【よく使う表現集】
概要
JavaScriptでよく個人的に使用頻度の高い表現をまとめた備忘録。
随時更新予定。1から99の数値間で整数の乱数を生成する
Math.floor(Math.random() * (100 - 1)) + 1範囲を変更したい場合は最大値(上記の場合だと100)と最小値(上記の場合だと1)の値を変更する。
数値を任意の倍数に整える
Math.ceil(num / trim) * trim) //切り上げ Math.floor(num / trim) * trim) //切り下げnumには元の数値、trimには整えたい倍数の数値をそれぞれに入力。
下記は配列で渡された数値を5の倍数(切り上げ)で整えた例。function multipleTrim(num) { return num.map(a => Math.ceil(a / 5) * 5) } console.log(multipleTrim([73, 67, 38, 33])); //=> [ 75, 70, 40, 35 ]
- 投稿日:2020-04-05T03:37:23+09:00
Chromium系ブラウザのdevコンソールで画像やリンクのURL一覧を一気にコピーする
クイックスタート
F12->consoleに以下をコピペして実行。
selectorとattrの中身は好きなものに書き換えるvar selector = "a"; var attr = "href"; copy(Array.from(document.querySelectorAll(selector)).map(x => x.getAttribute(attr)).join("\n"));クリップボードの中にURL(などのattrで指定した要素)の一覧がコピーされる
やっていること
- Array.from()の中にquerySelectorAllを入れることでエレメントを全て配列化
- 配列をmapしてその中でgetAttribute(attr)することで"href"など指定したプロパティ一覧の配列にする
- join("\n")で改行コードを挟んで結合した文字列にする
- copy()はコンソール限定のビルトイン関数で、その引数がクリップボードに入る
- 投稿日:2020-04-05T03:29:36+09:00
常に動くLINEBOTにお引っ越し(now編)(未解決版)
前回つまって諦めたnowに再挑戦した記録。
ngrokで作った時の記事:
WikipediaのAPIを使ってLINEbotに調べてもらうはじめに
前回同様、課題感とテーマとしては「ngrokだと使いたいときに使えない!だから常時使えるようにしたい!」です。
目次としては、
- now でだいぶハマる
- さらに now にハマる
- 対応したこと
です。
nowでだいぶハマる
nowを正しく動かすところでまずハマりました。
いくつかハマった気がして、だいたいはいじったりnow自体を消してやり直したりしたらできた印象だが、うっかりでハマったのがこの記事参照。
https://qiita.com/shima-07/items/64c051c9982ac0899b21さらにnowにハマる
無事、ngrokを卒業して、nowを使ってLINE botができました。
が、、
なんか挙動がおかしい。ソースコードはngrokの時と何も変えてないのに。
botなのになんだか頭が悪い人間っぽさが出てきた・・・。具体的には、
1. 初めて検索するキーワードにはwikipediaからの返り値を返してくれない。 (「初めてなので・・・」とかウブさはbotに求めてない)
2. 前回の回答を返してくる。(質問をちゃんと聞いて)
今回は「カラス?」と聞いているのに、「くま」について説明し始めた、、
3. 自動応答したいreplayMessageとWikipedia APIのpushMessageの順番がバラバラ(落ち着いてほしい…)
本当は「カラスの説明:」が上にきて、その下にwikipediaからの返答をのせたいのだけど、ずれる時がある。
nowにしたことで、何やら処理の順番とかが変わるようだ。。
対応したこと
https://qiita.com/n0bisuke/items/fb19ed3cd0138135ae69
この記事を見て、asyncとawaitを使い、replyとpushの場所も変えてみた
変更前↓
before.js. 省略 . . . function handleEvent(event) { if (event.type !== 'message' || event.message.type !== 'text') { return Promise.resolve(null); } let mes = '' // console.log(event.message.text); if(event.message.text.indexOf('?') > -1){ // ?を含んでいる場合にはwikiで検索したものを出して、含んでない場合はurlを返す var str = event.message.text; var result = str.split( '?' ).join( '' ); //?を取り除く処理 mes = result + 'の説明:'; //wikiのbodyの前の一言 getBody(event.source.userId,result); //wiki APIで取得できたらプッシュメッセージ }else{ var result = event.message.text; mes = result + 'のURL:'; //wikiのurlの前の一言 getUrl(event.source.userId,result); //wiki APIで取得できたらプッシュメッセージ } return client.replyMessage(event.replyToken, { type: 'text', // text: event.message.text //実際に返信の言葉を入れる箇所 text : mes }); } const getBody = async (userId,word) => { const res = await axios.get('http://wikipedia.simpleapi.net/api?keyword='+ encodeURIComponent(word) + '&output=json'); const item = res.data; // console.log(item); await client.pushMessage(userId, { type: 'text', text: item[0].body, }); } const getUrl = async (userId,word) => { const res = await axios.get('http://wikipedia.simpleapi.net/api?keyword='+ encodeURIComponent(word) + '&output=json'); const item = res.data; // console.log(item); await client.pushMessage(userId, { type: 'text', text: item[0].url, }); } (process.env.NOW_REGION) ? module.exports = app : app.listen(PORT); console.log(`Server running at ${PORT}`);変更後↓(replyMessageをasyncとawaitを使って書き換え、場所も変えてみた。)
※?がある場合の分岐の抜粋。after1.jsasync function handleEvent(event) { if (event.type !== 'message' || event.message.type !== 'text') { return Promise.resolve(null); } let mes = '' // console.log(event.message.text); if(event.message.text.indexOf('?') > -1){ // ?を含んでいる場合にはwikiで検索したものを出して、含んでない場合はurlを返す var str = event.message.text; var result = str.split( '?' ).join( '' ); //?を取り除く処理 mes = result + 'の説明:'; //wikiのbodyの前の一言 await client.replyMessage(event.replyToken, { type: 'text', // text: event.message.text //実際に返信の言葉を入れる箇所 text : mes }); getBody(event.source.userId,result,mes); //wiki APIで取得できたらプッシュメッセージ }こういう対応ではダメだった?
これにしても直らず。
上記3つのおとぼけbotさんのままでした。。次に、
いっそwikiの内容をとってくる関数の中にreplyMessage入れたらいいんじゃない?
と思い、やってみた。(完全に素人の悪あがき感。。)
getBody.jsconst getBody = async (userId,word,message) => { await client.replyMessage(event.replyToken, { type: 'text', // text: event.message.text //実際に返信の言葉を入れる箇所 text : message }); const res = await axios.get('http://wikipedia.simpleapi.net/api?keyword='+ encodeURIComponent(word) + '&output=json'); const item = res.data; console.log(item); await client.pushMessage(userId, { type: 'text', text: item[0].body, }); }これでも、相変わらずおとぼけbotは治らず。。
今回も失敗…
前回の諦めポイントのnow自体からは進んだのですが、結果botがおとぼけさんになってしまった。
(もっと人間的な振る舞いをしたら可愛げがあって多少のミスも許せるが、、まだ十分な人間味もない。)時間的にもきついので、しばらくはこれ以上深掘らず、いったんここまで。
- 投稿日:2020-04-05T03:27:21+09:00
【CSS】最低限の見た目にするためにコンテンツを画面の中心にしたいだけです。
1. 記事の目的
わたしはCSSが書けません。
しかし、htmlやjavascriptで作成した成果物やコンテンツを、最低限の見た目にしたいという思いはあります。
「最低限の見た目」=「画面サイズに依存せずに画面の中心に配置する」と考えた私は、「なら、成果物を画面の中心にもってくるCSSをいつでも使えるように用意しておこう。」と決意しQiitaに投稿しました。それだけです。2. コード(コピペ用)
bodyタグに対して、以下のようにCSSを設定することで、コンテンツを中心に配置できます。
body { margin:0; display:flex; min-height:100vh; justify-content:center; align-items:center; /* flex-direction: column; */ }一般公開するので、一通り解説をします。
詳細は参考リンクに譲ります。3. CSS の解説
3-1. 解説用html
解説用のhtmlサンプルは以下の通りです。今回はbodyタグの下に見た目用のCSSを追記しています。あくまで解説用のため、本来は必要ないです。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body { /* ここを個々に解説をします */ /* margin: 0; */ /* display: flex; */ /* justify-content: center; */ /* min-height: 100vh; */ /* align-items: center; */ /* flex-direction: column; */ } </style> </head> <body> <div class="item item1">アイテム1</div> <div class="item item2">アイテム2</div> <div class="item item3">アイテム3</div> </body> <style> /* 枠線や背景色などの見た目用CSS */ body { box-sizing:border-box; border: black 10px solid; padding: 10px; background-color: gainsboro; } .item{ border: black 2px solid; padding: 3px; margin: 3px; background-color: #fff; } </style> </html>こんな感じの見た目になります。(通常なら見た目用CSSがないため、もっと味気ない感じになりますよね。)
図1. 何もない状態3-2.
margin:0;
まず、
margin
と呼ばれる枠(border
)の外の設定です。(図1の黒太枠の外の余白です。)デフォルトでは
margin:8px
となっていました。margin:0
に設定することで枠の外の余白をなくします。
※margin:0
のときはpxのような単位がなくても構いません。もちろん、単位があっても動作します。body { margin: 0px; /* display: flex; */ /* justify-content: center; */ /* min-height: 100vh; */ /* align-items: center; */ /* flex-direction: column; */ }黒太枠(bodyのborder)の外側の余白(margin)がなくなりました。
3-3.
display: flex;
flexコンテナと呼ばれるものを設定します。
以下のjustify-content
やalign-items
などを使用するために必要です。body { margin: 0; display: flex; /* justify-content: center; */ /* min-height: 100vh; */ /* align-items: center; */ /* flex-direction: column; */ }
display:flex
を適応するとこんな感じです。黒太枠内がflexコンテナです。
flexコンテナを適用したタグの子要素(アイテムたちのことです!)が左に寄せられています。
ブロックレベル要素(タグごとに改行される要素)のアイテムたちが横並びになっています。(図2だと縦に並んでました)3-4.
justify-content:center;
flexコンテナ内の要素を、水平方向のどの位置に配置するかを指定します。
デフォルト(指定していない場合)はjustify-content:flex-start
。左寄せになります。(図3 参照)
今回はjustify-content:center
を指定して、中央にします。body { margin: 0px; display: flex; justify-content: center; /* min-height: 100vh; */ /* align-items: center; */ /* flex-direction: column; */ }
図4. justify-content: center の適用左に寄っていたアイテムたちが、真ん中に集合しました!
3.5.
min-height:100vh;
flexコンテナの領域の最低の高さを100 vhに設定します。
vhはviewport heightと呼ばれる、viewport(表示領域)の高さに対する割合の単位です。
100vhはviewportに対して100%という意味です。
つまりmin-height:100vh
=「bodyの高さを少なくとも、表示領域と同じにする」になります。body { margin: 0px; display: flex; justify-content: center; min-height: 100vh; /* align-items: center; */ /* flex-direction: column; */ }黒太枠線(
body
のborder
)が画面いっぱいに広がりました。
それにともない、アイテムたちが縦にみょーんとなっています。3-6.
align-items:center;
図5でアイテムたちが縦にみょーんとなってしまっているのは、
align-items
のためです。
align-items
はflexコンテナ内の要素を垂直方向のどの位置に配置するかを指定します。
デフォルト(指定していない場合)はalign-items:stretch
が指定されている状態です。親要素の高さになります。(図5参照)
今回は、中央揃えにしたいのでalign-items:center
を指定します。body { margin: 0px; display: flex; justify-content: center; min-height: 100vh; align-items: center; /* flex-direction: column; */ }3-7. (追記)
flex-direction: column;
3-3.
display: flex;
でアイテムが横配置になりました。
これはflex-direction: row;
がデフォルトで反映されているからです。
flex-direction
はflexコンテナ内のアイテムの向きを指定するプロパティです。
flex-direction: column;
とすることで、縦配置のまま画面中心にコンテンツを移動することができます。4. さいごに
コンテンツを画面の中心に持ってくることができました!
これで最低限の見た目は保証がされるはずです!(私は信じています!)
あとはいいコンテンツを作るだけですね!…参考リンク
CSS フレックスボックスレイアウト
flexboxをちゃんと理解したい場合はこちら。日本語対応!CSS Flexboxのチートシートを作ったので配布します
使い方がわかりやすく参考にしています。CSS には vw, vh, vmin, vmax という単位がある
vhについてはこちら。
- 投稿日:2020-04-05T01:34:08+09:00
nowでアプリケーションエラーが出たとき対応したこと
nowでハマったメモ
状況
@n0bisuke のこの記事を参考にやっていたつもりがこんなエラーと遭遇。
An error occurred with this application.
This is an error with the application itself, not the platform.「nowは問題ないよ。お前のアプリケーション(ソースコード)が問題なんだよ。」とのこと。
対応
このnow.json内で指定しているjsファイルの一部を直したら直った。
(記事の例でいくと、server.jsってファイルの文末)直した場所
自分にとってわかりやすいと思って書き換えてた最後のserver running のあたり。
エラー出てたとき
エラー出てたとき.jsif(process.env.NOW_REGION){ module.export = app; }else{ app.listen(PORT); console.log(`Server running at ${PORT}`); }これ、記事の通り下記に直したら直った。
(初めから記事の通りやっておけばよかった話)エラー出なくなった.js(process.env.NOW_REGION) ? module.exports = app : app.listen(PORT); console.log(`Server running at ${PORT}`);結論
自分でアレンジしたところが悪かった。記事の通りにやっておけばよかった。
でもなんでこの書き方だとダメだったんだろう。。。
あと、nowを起動させるときにこのjsファイルも見ているんだなあ。
- 投稿日:2020-04-05T01:17:01+09:00
typescriptを使ってjavascriptのシンタックスシュガーを理解する
背景
typescriptを勉強していて、アロー演算子構文の読み方がわからない。ドキュメントを読んでもよくわからなかった、ということがありました。
そんな時、tscコマンドを使うことで難解なシンタックスシュガーへの理解のヒントになることに気が付きました。
それについて紹介します。https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/Arrow_functions
たとえば下記アロー演算子の構文がわかりませんでした。
['bbbbb', '3'].map(({ length }) => length)
仮引数に{}
使とは、、、?みたいな感じです。
出力内容から見ると、length
プロパティを返していることを察することができましたが、いまいち腑に落ちませんでした。tscコマンドの出番です
typescriptをjavascritに変更するコマンドが
tsc
です。arrow.ts['bbbbb', '3'].map(({ length }) => length)上記のようにをファイルに書き出して、tscコマンドを実行します。
tsc arrow.ts
すると、arrow.jsというファイルが生成されます。中身を見るとシンタックスシュガーを使わずに実装されたjavascriptができています。arrow.js['bbbbb', '3'].map(function (_a) { var length = _a.length; return length; });
arrow.js
を読むと、['bbbbb', '3'].map(({ length }) => length)
がどのように実行されているのかがわかりました。
ローカル変数にlengthを格納しておいて、そのローカル変数をreturnしていたんですね。
すごいです。これは理解できます。他の「初見殺し構文」を読んでみる
難解.tsvar f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c;難解.jsvar f = function (_a, _b) { var _c = _a === void 0 ? [1, 2] : _a, a = _c[0], b = _c[1]; var c = (_b === void 0 ? { x: a + b } : _b).x; return a + b + c; };javascriptに変換したところで意味はわかりませんでしたが、頑張れば読めるような気がしていきました。
- 投稿日:2020-04-05T01:05:24+09:00
点と(直線 or 線分)の距離の2乗 JavaScriptでの実装
いろいろやり方はありますが、この記事では一次式の連立方程式を使って考えます。
点と直線の場合
o と p を通る直線に q からおろした垂線の距離の2乗を求めます。
o を p と q からあらかじめ引いておいてやると、計算が楽です。
以下のように求められます。
const _Dist2 = ( px, py, qx, qy ) => { const num = px * qy - py * qx return num * num / ( px * px + py * py ) } const Dist2 = ( ox, oy, px, py, qx, qy ) => _Dist2( px - ox, py - oy, qx - ox, qy - oy )注)o と p が同じ場合 0 / 0 になるので NaN を返します。
参考:点と直線の距離公式の3通りの証明@高校数学の美しい物語
点と線分の場合
交点(X,Y)を求めてそれが線分の内に入っているかどうかチェックします。
X,Y は以下のように求められます。const den = px * px + py * py const _ = ( py * qy + px * qx ) / den const X = px * _ const Y = py * _これの説明は文末につけておきます。
これの X が 0 から px, Y が 0 から py の中に入っていればよさそうです。
ただ、符号の判断が面倒になるので、計算の最初に p が第一象限にあるように鏡像を作ってから考えます。const _SegDist2 = ( px, py, qx, qy ) => { const den = px * px + py * py if ( den == 0 ) return NaN if ( px < 0 ) { px = -px qx = -qx } if ( py < 0 ) { py = -py qy = -qy } const _ = ( py * qy + px * qx ) / den const X = px * _ if ( X < 0 || px < X ) return NaN const Y = py * _ if ( Y < 0 || py < Y ) return NaN const num = px * qy - py * qx return num * num / den } const SegDist2 = ( ox, oy, px, py, qx, qy ) => _SegDist2( px - ox, py - oy, qx - ox, qy - oy )q から op に降ろせる点がない場合 NaN を返しているので、Number.isNaN() で判定してください。
交点の求め方
プログラムっぽく書いてみました。
直線 py Y = -- X px に直行する直線は px Y = - -- X + c py これが、qx, qy を通るから px qy = - -- qx + c py すなわち px px Y = - -- X + qy + -- qx py py なので交点は py px px -- X = - -- X + qy + -- qx px py py px * ( px * qx + py * qy ) X = -------------------------- px * px + py * py py * ( px * qx + py * qy ) Y = -------------------------- px * px + py * py
- 投稿日:2020-04-05T00:26:35+09:00
【React + Material-UI】電卓アプリを作ろう!【2020年4月版】
はじめに
この記事は「Reactのチュートリアルを終えて、何かを作ってみたい」という読者を想定しています。
分からない部分が出た場合はReactの公式ドキュメントに立ち返りましょう。material-uiの公式ドキュメントも要チェック!
作ったもの
iPhoneに入っている電卓アプリのようなものを作りました。
Githubにソースコードを公開しています。
戦略
ReactコンポーネントにはMaterial-UIを使い、保持するstateをReactのuseStateで実装します。
component、containerの洗い出し
component
container
- calculator: ボタンとディスプレイを集結させたもの。今回はこれ自体がアプリとなる。
stateの洗い出し
iPhoneの電卓アプリをいじってみて、どんなstateが必要かを考えます。はじめにざっくりと書き出して、実際には実装しながら追加していくカタチになります。
- value1: 演算対象の数字の文字列
- value2: value1に変化を与える数字の文字列
operator: add(加算), subtract(減算), divide(除算), multiply(乗算)。operatorが指定されていないときはfalseにする。
isAllClear: clearボタンがAC / Cどちらかを判別するboolean。
isDicimalInput: .ボタンが押されて小数点以下入力になっているか判別するboolean。
isAnswerDisplay: =ボタンが押された直後には特殊な挙動をするので、判別するbooleanを用意しておく。
たとえば、 5 * 7 =
と入力した場合、value1は5, value2は7, operatorは"multiply"である。インストール
create react app
npx create-react-app react-calculator cd react-calculator@material-ui/core
// npm を使う場合 npm install @material-ui/core // yarn を使う場合 yarn add @material-ui/coreバージョン情報
- react: 16.13.1
- @material-ui/core: 4.9.8
ファイル構成
コンポーネントの実装
Display
Display.jsimport React from 'react'; import { makeStyles } from '@material-ui/core/styles'; import Box from '@material-ui/core/Box'; const useStyles = makeStyles((theme) => ({ box: { fontSize: "60px", width: "100%", margin: "0 auto", }, })); export default function Display(props) { const classes = useStyles(); return ( <div> <Box className={classes.box}> {props.children} </Box> </div> ); }ポイント
props.children
とすることで、<Display></Display>
ではさんだものを表示できます。MyButton
MyButton.jsimport React from 'react'; import { makeStyles } from '@material-ui/core/styles'; import Button from '@material-ui/core/Button'; export default function MyButton(props) { const useStyles = makeStyles((theme) => ({ margin: { margin: theme.spacing(1), backgroundColor: props.backgroundColor }, })); const classes = useStyles(); return ( <div> <Button className={classes.margin} onClick={props.onClick}> {props.children} </Button> </div> ); }ポイント
ボタンコンポーネントは親から1. 背景色, 2. クリックしたときに実行する関数, 3. ボタンの文字を受け取ります。クリックしたときに実行する関数について、親コンポーネントで
<MyButton onClick={somthing}>
としても機能しないので、子のButtonのonClickに接続する必要があるんですね。コンテナの実装
長いコードなのでコメントアウトで説明していきます。
Calculator.jsimport React, {useState} from 'react'; //components import MyButton from '../components/MyButton'; import Display from '../components/Display'; //material-ui import Box from '@material-ui/core/Box'; import Typography from '@material-ui/core/Typography'; function Calculator(){ //保持したいstates const [value1, setValue1] = useState("0"); const [value2, setValue2] = useState("0"); const [operator, setOperator] = useState(false); const [isAllClear, setIsAllClear] = useState(true); const [isDicimalInput, setIsDicimalInput] = useState(false); const [isAnswerDisplay, setIsAnswerDisplay] = useState(false); //数字ボタンを押したとき const handleNumberClick = (num) => { //=ボタンを押した直後に数字を押した場合 if(isAnswerDisplay){ setValue1(num); setValue2("0"); setOperator(false); setIsAnswerDisplay(false); setIsDicimalInput(false); } else { //演算子が押されていないときはvalue1を更新する if(!operator){ if(isDicimalInput){ setValue1(String(value1) + String(num)) } else { setValue1(String(Number(value1)*10+num)); } //演算子が押されているときはvalue2を更新する } else { if(isDicimalInput){ setValue2(String(value2) + String(num)) } else { setValue2(String(Number(value2)*10+num)); } } } //0以外が押されたときはACをCに変える if(num !== 0){ setIsAllClear(false); } } //00ボタンを押したとき const handleDoubleZeroClick = () => { if(!operator){ if(isDicimalInput){ setValue1(String(value1) + "00") } else { setValue1(String(Number(value1)*100)); } } else { if(isDicimalInput){ setValue2(String(value2) + "00") } else { setValue2(String(Number(value2)*100)); } } } //AC/Cボタンを押したとき const handleClearClick = () => { setIsDicimalInput(false); //ACのときはすべてリセット if(isAllClear){ setValue1("0"); setOperator(false); setIsAnswerDisplay(false) //Cのときはvalue2を"0"にする } else { setIsAllClear(true); if(isAnswerDisplay){ setValue1("0") } else{ if(!operator){ setValue1("0"); } else { setValue2("0"); } } } } // -/+ボタンを押したとき const handleMinusPlusClick = () => { if(isAnswerDisplay){ setValue1(-1 * Number(value1)); } else { if(!operator){ setValue1(-1 * Number(value1)); } else { setValue2(-1 * Number(value2)); } } } // %ボタンを押したとき const handlePercentageClick = () => { if(isAnswerDisplay){ setValue1(Number(value1) * 0.01); } else { if(!operator){ setValue1(Number(value1) * 0.01); } else { setValue2(Number(value2) * 0.01); } } } //演算子を押したとき //引数はadd subtract divide multiplyのどれか const handleOperatorClick = (a) => { if(isAnswerDisplay){ setIsAnswerDisplay(false); } else { if(value2 !== "0"){ if(operator === "add"){ setValue1(Number(value1) + Number(value2)) } else if(operator === "subtract"){ setValue1(Number(value1) - Number(value2)) } else if(operator === "divide"){ setValue1(Number(value1) / Number(value2)) } else if(operator === "multiply"){ setValue1(Number(value1) * Number(value2)) } } } setIsDicimalInput(false); setOperator(a); setValue2("0") } // .ボタンを押したとき const handleDicimalPointClick = () => { if(isDicimalInput) return; setIsDicimalInput(true); if(!operator){ setValue1(String(value1) + '.') } else { setValue2(String(value2) + '.') } } // =ボタンを押したとき const handleAnswerClick = () => { setIsDicimalInput(false); setIsAnswerDisplay(true); if(operator === "add") { if(value2 === "0"){ //演算子が指定されたあと、value2が入力されずに=が押された場合 //例えば、5 + = と入力した場合、10と表示する。 setValue2(value1) setValue1(Number(value1) + Number(value1)) } else { setValue1(Number(value1) + Number(value2)) } } else if(operator === "subtract") { if(value2 === "0"){ setValue2(Number(value1)) setValue1(Number(value1) - Number(value1)) } else { setValue1(Number(value1) - Number(value2)) } } else if(operator === "divide") { if(value2 === "0"){ setValue2(Number(value1)) setValue1(Number(value1) / Number(value1)) } else { setValue1(Number(value1) / Number(value2)) } } else if(operator === "multiply"){ if(value2 === "0"){ setValue2(Number(value1)) setValue1(Number(value1) * Number(value1)) } else { setValue1(Number(value1) * Number(value2)) } } } //ディスプレイに表示する内容 const displayMarkup = isAnswerDisplay ? ( value1 ) : ( operator ? ( value2 ) : ( value1 ) ); // AC/Cボタンの表示 const clearButtonMarkup = isAllClear ? ( "AC" ) : ( "C" ) return( <div> <Typography variant="h5"> React Calculator </Typography> <Display> {displayMarkup} </Display> //material-uiのBoxはflexを使って好きなように並べられる。公式ドキュメント参照。 <Box display="flex" flexDirection="column"> <Box display="flex" justifyContent="center"> //ボタンの中に表示したいものをMyButtonではさむ //MyButtonのpropsに背景色、onClick関数を渡す <MyButton backgroundColor="gray" onClick={handleClearClick}> <Typography variant="h5">{clearButtonMarkup}</Typography> </MyButton> <MyButton backgroundColor="gray" onClick={handleMinusPlusClick}> <Typography variant="h5">-/+</Typography> </MyButton> <MyButton backgroundColor="gray" onClick={handlePercentageClick}> <Typography variant="h5">%</Typography> </MyButton> //onClickに引数を渡したい場合はこのように記述する <MyButton backgroundColor="orange" onClick={() => handleOperatorClick("divide")}> <Typography variant="h5" >÷</Typography> </MyButton> </Box> <Box display="flex" justifyContent="center" > <MyButton onClick={() => handleNumberClick(7)} backgroundColor="#e0e0e0"> <Typography variant="h5">7</Typography> </MyButton> <MyButton onClick={() => handleNumberClick(8)} backgroundColor="#e0e0e0"> <Typography variant="h5">8</Typography> </MyButton> <MyButton onClick={() => handleNumberClick(9)} backgroundColor="#e0e0e0"> <Typography variant="h5">9</Typography> </MyButton> <MyButton backgroundColor="orange" onClick={() => handleOperatorClick("multiply")}> <Typography variant="h5" >×</Typography> </MyButton> </Box> <Box display="flex" justifyContent="center"> <MyButton onClick={() => handleNumberClick(4)} backgroundColor="#e0e0e0"> <Typography variant="h5">4</Typography> </MyButton> <MyButton onClick={() => handleNumberClick(5)} backgroundColor="#e0e0e0"> <Typography variant="h5">5</Typography> </MyButton> <MyButton onClick={() => handleNumberClick(6)} backgroundColor="#e0e0e0"> <Typography variant="h5">6</Typography> </MyButton> <MyButton backgroundColor="orange" onClick={() => handleOperatorClick("subtract")}> <Typography variant="h5" >-</Typography> </MyButton> </Box> <Box display="flex" justifyContent="center"> <MyButton onClick={() => handleNumberClick(1)} backgroundColor="#e0e0e0"> <Typography variant="h5">1</Typography> </MyButton> <MyButton onClick={() => handleNumberClick(2)} backgroundColor="#e0e0e0"> <Typography variant="h5">2</Typography> </MyButton> <MyButton onClick={() => handleNumberClick(3)} backgroundColor="#e0e0e0"> <Typography variant="h5">3</Typography> </MyButton> <MyButton backgroundColor="orange" onClick={() => handleOperatorClick("add")}> <Typography variant="h5" >+</Typography> </MyButton> </Box> <Box display="flex" justifyContent="center"> <MyButton onClick={() => handleNumberClick(0)} backgroundColor="#e0e0e0"> <Typography variant="h5">0</Typography> </MyButton> <MyButton onClick={handleDoubleZeroClick} backgroundColor="#e0e0e0"> <Typography variant="h5">00</Typography> </MyButton> <MyButton onClick={handleDicimalPointClick} backgroundColor="#e0e0e0"> <Typography variant="h5" >.</Typography> </MyButton> <MyButton backgroundColor="orange" onClick={handleAnswerClick}> <Typography variant="h5" >=</Typography> </MyButton> </Box> </Box> </div> ) }; export default Calculator;ポイント
handleNumberClick
などの引数がある関数は、onClick={() => somthing(引数)}
のように記述します- stateはひとつにまとめても良いが、どこで何を使うかが決まってないうちは独立したstateにしておいて後からまとめよう。
App.js
App.jsimport React from 'react'; import './App.css'; import Calculator from './containers/Calculator' function App() { return ( <div className="App"> <div className="container"> <Calculator /> </div> </div> ); } export default App;App.css
App.css.App { text-align: center; } .container { margin: 80px auto 80px auto; padding: 20px; max-width: 300px; background-color: #80deea; }以上で完成です!
おまけ
こちらの記事を参考に、gh-pagesでサイトを公開しました。
https://shintaro-hirose.github.io/react-calculator/今後もReactで簡単なアプリを作っていきます!