Elsaの技術日記(徒然なるままに)

主に自分で作ったアプリとかの報告・日記を記載

MENU

Webアプリでの注意点と画像タッチでスライドショーを再生させる

今回は半年前に作成したスライドショーに音楽を付与し、
画像をクリックすると音楽とスライドショーが開始するようにしてみたいと思います。
作成したスライドショーはこちらにまとめております。
elsammit-beginnerblg.hatenablog.com

合わせてhtmlで音声出力する際の注意点もまとめておきたいと思います。



■音声出力時の注意点

実は当初、webアプリを開いたらスライドショーと音声を自動実行しようとしておりました。
が、、、
現状、ブラウザのポリシーにより音声の自動実行は禁止されており、自動実行することは不可能になっております。
確かに、youtubeとかも再生画面で必ず再生ボタン押さなければならないですもんね。。
参考:
developer.mozilla.org


ただ、再生ボタンを用意するのもちょっとカッコ悪いな。
と思い、スライドショーの画面をタッチやクリックすることで音声とスライドショーが開始されるように変更することにしました。

■画像クリックで音楽とスライドショーが開始するように修正

前回のスライドショーのコードを抜粋するとこちらになります。
【html】

<div id="photo">
            <ul class="slideshow">
                <li class="current"><img src="image/Photo1.JPG" alt="A" class="photoImg" id="photoNum1"></li>
                <li><img src="image/Photo2.JPG" alt="B" class="photoImg" id="photoNum2"></li>
                <li><img src="image/Photo3.JPG" alt="C" class="photoImg"  id="photoNum3"></li>
                <li><img src="image/Photo4.JPG" alt="D" class="photoImg" id="photoNum4"></li>
                <li><img src="image/Photo5.JPG" alt="E" class="photoImg"  id="photoNum5"></li>
            </ul>
</div>

css

.slideshow > li {
    opacity: 0;
    transition: opacity 1s;
}
  
.slideshow > li.current {
    opacity: 1;
}

javascript

(function($){
    $(document).ready(function(){
      var slides = $(".slideshow > li");    // slideshowクラス配下のliをセット.
      
      // 画像切り替え関数.
      function toggle_slide(){
        count = (count + 1) % 5;
        slides.removeClass("current").eq(count).addClass("current");    // 現在セットされているcurrentクラスを削除し次に表示させるimgタグにcurrentタグを付与.
        selectImgcircle(count);                                         // ●印付与関数コール.
      }
      setInterval(toggle_slide, 5000);                                  // 定期処理実行.

    });  
})(jQuery);

htmlにてリストで画像を表示するようにし、
cssでクラス名がcurrentのみを表示、
javascriptで定期時間毎にクラス名currentを順番に付与・削除を繰り返すことで、
スライドショーを実現しています。

このコードに対して、画像画面をクリックされたイベントが拾えれば
audioタグに対して音楽再生、一時停止関数を発行すれば機能としては実現できます。

クリックイベントはjQueryをこちらのようなコードを実装すればOK。

$(".クラス名").click(function(event) {
    //クリックイベント検知時に実行したいコード
}

ということで、、、
画像を表示しているリストに対して、クリックイベントを検知させるためにこちらのコードを作成。

$(".slideshow").click(function(event) {
    console.log("Clicked!!");
}

こちらのコードをjavascriptに追記して動作確認してみたのですが、、、
画像上のクリックイベントを検知出来ていない。
仕方ないのでリストの上位階層である、id名photoにクリックイベントを仕込んで実行。

$(".slideshow").click(function(event) {
    console.log("Clicked!!");
}
||< 
今度はクリックイベント検知できたのですが、、、
枠だけで画像の中心部ではやはり反応なし。。

う~~ん。。
悩んだ末たどり着いた方法、、それは、
「画像と同じ領域に重ねる形で別の透明なタグを配置し、追加したタグでクリックイベントを検知」
です。

ちょっと安直すぎる気もしたのですが、、
一旦この方針で実装!!
変更箇所はこちらになります。

【html】
>|html|
<audio loop id="music">
            <source  src="music/hoge.mp3" type="audio/mp3">
        </audio>
<div id="photo">
            <p class="musicStpse"></p> 
            <ul class="slideshow">
                <li class="current"><img src="image/Photo1.JPG" alt="A" class="photoImg" id="photoNum1"></li>
                <li><img src="image/Photo2.JPG" alt="B" class="photoImg" id="photoNum2"></li>
                <li><img src="image/Photo3.JPG" alt="C" class="photoImg"  id="photoNum3"></li>
                <li><img src="image/Photo4.JPG" alt="D" class="photoImg" id="photoNum4"></li>
                <li><img src="image/Photo5.JPG" alt="E" class="photoImg"  id="photoNum5"></li>
            </ul>
            <div id="selectedImg">
                <div id="select1" onclick="ImgSelect(1)"></div>
                <div id="select2" onclick="ImgSelect(2)"></div>
                <div id="select3" onclick="ImgSelect(3)"></div>
                <div id="select4" onclick="ImgSelect(4)"></div>
                <div id="select5" onclick="ImgSelect(5)"></div>
            </div>
</div>

javascript

$(".musicStpse").click(function(event) {
    let music = document.getElementById("music");
    if(!music.paused){
      music.pause();
    }else{
      music.play();
    }
  });

(function($){
    $(document).ready(function(){
      var slides = $(".slideshow > li");    // slideshowクラス配下のliをセット.
      
      // 画像切り替え関数.
      function toggle_slide(){
        let music = document.getElementById("music");
        if(!music.paused){    // 動画が停止している間はスライド遷移させない
            count = (count + 1) % 5;
            slides.removeClass("current").eq(count).addClass("current");    // 現在セットされているcurrentクラスを削除し次に表示させるimgタグにcurrentタグを付与.
            selectImgcircle(count);                                         // ●印付与関数コール.
        }
      }
      setInterval(toggle_slide, 5000);                                  // 定期処理実行.

    });  
})(jQuery);

先ほど記載しました通り、透明なタグとして、

<p class="musicStpse"></p> 

を新規で追加。
musicStpseのcssはクラス名photoImgと同様の定義としております。
異なる点は、z-indexを用いてmusicStpseを全面に配置している点。

後はクリックイベントして、

$(".musicStpse").click(function(event) {
    let music = document.getElementById("music");
    if(!music.paused){
      music.pause();
    }else{
      music.play();
    }
  });

を定義することで、
透明なタグがクリックされたことを検知して音楽のスタート・ストップを切り替えております。

こちらを実行してみたところ、、、
問題なく動作することを確認!!

■追加でクリック時にスタート、ストップマークを一瞬表示させてみる

先ほどのコードで目的は達成できたのですが、、
どうせなら、画像をタッチした時にスタート・ストップのアイコンを一瞬画面上に表示させてみたいと考え、
追加実装することにしました。

追加コードはこちらになります。

<div id="photo">
            <p class="musicStpse"></p>
            <img src='image/StartIcon.png' alt="" id="on_fade">
            <img src='image/Pause.png' alt="" id="on_fade_Pause">
            <ul class="slideshow">
                <li class="current"><img src="image/Photo1.JPG" alt="A" class="photoImg" id="photoNum1"></li>
                <li><img src="image/Photo2.JPG" alt="B" class="photoImg" id="photoNum2"></li>
                <li><img src="image/Photo3.JPG" alt="C" class="photoImg"  id="photoNum3"></li>
                <li><img src="image/Photo4.JPG" alt="D" class="photoImg" id="photoNum4"></li>
                <li><img src="image/Photo5.JPG" alt="E" class="photoImg"  id="photoNum5"></li>
            </ul>
</div>

javascript

$(".musicStpse").click(function(event) {
    let music = document.getElementById("music");
    if(!music.paused){
      music.pause();
      $('#on_fade').removeClass('on_fade');
      $('#on_fade_Pause').removeClass('on_hidden');
      $('#on_fade_Pause').addClass('on_fade');
      $('#on_fade').addClass('on_hidden');
    }else{
      $('#on_fade').removeClass('on_hidden');
      $('#on_fade').addClass('on_fade');
      
      $('#on_fade_Pause').removeClass('on_fade');
      $('#on_fade_Pause').addClass('on_hidden');
      music.play();
    }
  });

css

#on_fade, #on_fade_Pause{
    position: absolute;
    width: 100px;
    height: 100px;
    top:300px;
    left:900px;
}

.on_fade{
    z-index:6500;
    opacity:0;
    transition: opacity 1s;
}

.on_hidden{
    z-index:1000;
    opacity:1;
}

実施していることですが、
それぞれ、一時停止用のタグ(on_fade_Pause)とスタート用のタグ(on_fade)を用意して、
javascriptのコードにて、
アイコンをフェードアウトするように実装しております。
ただフェードアウトさせるだけですと、2回目以降透過率が0のままで表示がされないため、
使用していない側のアイコンにはクラス名on_hiddenを付与し、
表示しているのですが、スライドショーの画面領域よりも後ろに表示するようにz-indexを用いて調整。

こちらの最終コード実行結果はこちらに掲載しましたので気になる方はご覧ください。
(注)本当はスライドショースタートと一緒に音声が流れ、一時停止すると音声も止まるのですが、
  動画掲載の関係で音声は常に流れた状態になっております。
youtu.be

■最後に

今回は画像クリックして音声とスライドショーが開始するようなコードを作成してみました。
まだまだWebアプリの実装は慣れてないです。
後、ブラウザの仕様も全く理解していなかった。。Webページ開いたときに自動再生が不可とは。。
もう少し勉強進めていきたいと思います!!



pythonでテキストや文字列を読み上げる

今回はpythonでテキストや文字列を読み上げる方法についてまとめていきます。



■準備

今回読み上げを行うにあたり、pyttsx3を用います。

【pyttsx3とは】
pyttsx3は、pythonのテキスト読み上げ変換ライブラリです。
代替ライブラリとは異なり、オフラインで動作し、Python2と3の両方と互換性があります。

pyttsx3は下記でインストールが可能です。

pip install pyttsx3

or 

pip3 install pyttsx3

自分は発生しなかったのですが、、
もしこちらのようなエラーメッセージが出た場合、pypiwin32のインストールが必要なようです。

No module named win32com.client

or 

No module named win32

or

No module named win32api

pypiwin32をインストールするためには、

pip install pypiwin32

or 

pip3 install pypiwin32

でOK。

■簡単な文字列を読み上げてみる

では早速、簡単な文字列の読み上げのためのコードです。
コードはこちら。

import pyttsx3

engine = pyttsx3.init()
engine.say("こんにちは。こんばんは。")
engine.runAndWait()

こちらを実行すると、

こんにちは。こんばんは。

と音声が出力されるかと思います。

■英語の文章を読み上げてみる。

次に英語をしゃべらせてみます。
デフォルトだと日本語になっているので、

engine.say("Hello world")

としても、日本語の発音で英文を読むだけになってしまい、
ネイティブが発音したようにはいきません。

そこで、発話するidを切り替える必要があります。
現在登録されているidを確認するコードはこちら。

import pyttsx3

engine = pyttsx3.init()
voices = engine.getProperty('voices')
for voice in voices:
    print("Voice: %s" % voice.name)
    print(" - ID: %s" % voice.id)

こちらを実行すると、各種IDや言語が一覧で出力されます。
今回はついでに人物名も出力しています。

自分の環境では、

Voice: Microsoft Haruka Desktop - Japanese
 - ID: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\TTS_MS_JA-JP_HARUKA_11.0

Voice: Microsoft Zira Desktop - English (United States)
 - ID: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\TTS_MS_EN-US_ZIRA_11.0

となります。

コードを見ると分かるように、voicesが配列になっております。
2番目に出力された、

Microsoft Zira Desktop - English (United States)

が英語のようですので、idを指定して出力させてみたいと思います。
コードはこちら。

import pyttsx3

engine = pyttsx3.init()
voices = engine.getProperty('voices')
engine.setProperty("voice", voices[1].id)
engine.say("Hello world")
engine.runAndWait()

こちらを実行することでネイティブな発音で、"Hello world"が出力されます。

■音量や発話速度を変更

デフォルトのままですと、個人的に少し読み上げるのが早い気がします。
そこで、速度を落としたり音量を変化させたりしてみたいと思います。

まずは、発話速度です。
速度の設定は、

engine = pyttsx3.init()
rate = engine.getProperty('rate')
engine.setProperty('rate', 発話速度)

で可能です。
このrateですが、
1分あたりの単語数で表した整数の発話速度。
とのこと。

自分は、

engine = pyttsx3.init()
rate = engine.getProperty('rate')
engine.setProperty('rate', rate-50)

とすることにしました。

次に音量ですが、

engine = pyttsx3.init()
rate = engine.getProperty('rate')
engine.setProperty('volume', 音量)

でプロパティの設定が可能です。
音量ですが、0.0 ~ 1.0までの範囲で設定が可能なようです。

■読み上げた音声を録音する

音声を録音するためには、

engine.save_to_file(文字列 , '出力ファイル名')

で実行可能です。
例えば、

import pyttsx3

engine = pyttsx3.init()
voices = engine.getProperty('voices')
engine.save_to_file("Hello world" , 'hello.mp3')
engine.runAndWait()

といったコードを作成し、実行すると
hello.mp3が生成され、再生すると
Hello world
と出力されるかと思います。

■テキストを読み上げてみる

最後に、これらの上記を組み合わせてテキストを読み上げてみたいと思います。
コードはこちら。

import pyttsx3

engine = pyttsx3.init()
rate = engine.getProperty('rate')
engine.setProperty('rate', rate-50)

with open("file.txt",encoding="utf-8") as f:
    s = f.read()
    engine.save_to_file(s , 'japan.mp3')
    engine.say(s)
    engine.runAndWait()

file.txtには下記文言を格納しました。

文がいくつか集まり、かつ、まとまった内容を表すもの。内容のうえで前の文と密接な関係をもつと考えられる文は、そのまま続いて書き継がれ、前の文と隔たりが意識されたとき、次の文は行を改めて書かれる。すなわち、段落がつけられるということであり、これは、書き手がまとまった内容を段落ごとにまとめようとするからである。この、一つの段落にまとめられる、いくつかの文の集まりを一文章というが、よりあいまいに、いくつかの文をまとめて取り上げるときにそれを文章と称したり、文と同意義としたりすることもあるなど文章はことばの単位として厳密なものでないことが多い。これに対して、時枝誠記(ときえだもとき)は、文章を語・文と並ぶ文法上の単位として考えるべきことを主張し、表現者が一つの統一体ととらえた、完結した言語表現を文章と定義した。これによれば、一編の小説は一つの文章であり、のちに続編が書き継がれた場合には、この続編をもあわせたものが一つの文章ということになる。俳句、和歌の一句・一首は、いずれも一つの文章であり、これをまとめた句集・歌集は、編纂(へんさん)者の完結した思想があることにおいて、それぞれ一つの文章ということになる。

こちらのテキストを元にコードを実行するとテキストが読み上げられるかと思います。
また、japan.mp3が生成されるかと思います。

次に英語です。
英語の場合には、idの切り替えが必要になりますので、

import pyttsx3

engine = pyttsx3.init()
voices = engine.getProperty('voices')
engine.setProperty("voice", voices[1].id)
rate = engine.getProperty('rate')
engine.setProperty('rate', rate-50)

with open("file2.txt",encoding="utf-8") as f:
    s = f.read()
    engine.save_to_file(s , 'us.mp3')
    engine.say(s)
    engine.runAndWait()

これでOKです。

file2.txtにこちらを書き込んで実行してみると、

Kishida calls election for end of October
Fumio Kishida became Japan’s prime minister on Oct. 4. He was elected by lawmakers in the Diet’s two houses.
Later the same day, Kishida told reporters that he will call a general election on Oct. 31.
The 64-year-old takes office as Japan continues to struggle with the pandemic. He said he wants to fight the coronavirus and make drastic changes to the economy.

英語でテキスト読み上げが実行されます。
また、us.mp3が生成されるかと思います。

■最後に

今回は日本語や英語テキストの読み上げ方法に関してまとめてみました。



Reactでポップアップメッセージとのデータのやり取り

先日Reactでポップアップメッセージを自作する方法をご紹介しました。
elsammit-beginnerblg.hatenablog.com

今回はポップアップメッセージとメインページとのデータのやり取り方法に関してまとめておこうと思います。



■前提

先日と同様にソースコードは下記に保存しております。
https://github.com/Elsammit/Moguratataki

また今回の方法に関しては先日の記事のコードを元に記載します。
前回の記事が気になる方はこちらをご参照ください。
elsammit-beginnerblg.hatenablog.com

■ポップアップメッセージからメインページへのデータ送信

ではまずはポップアップメッセージからメインページへのデータ送信に関してまとめていきます。
と言っても、こちらは先日のポップアップを閉じる際にご紹介した通り、

メインページを、

   // 追加.
    updateState = (state:boolean)=>{
        this.setState({popupstate:state});
    }

    OpenDialog = () =>{
        const {popupstate} = this.state;
        const{result} = this.state;
        let Msg:string = "score is " + result;
        if(popupstate === true){
            return <MadeDialog closeState={this.updateState.bind(this)} showMsg = {Msg}></MadeDialog> //←closeState追加
        }else{
            return <p></p>
        }
    }

とし、ポップアップメッセージを

import React from 'react';
import "./dialog.css";

 //追加
export interface Props { 
    closeState?:any;
}

export default class MadeDialog extends React.Component<Props> {
    CloseDialog = () => {
        this.props.closeState(false); //←追加
    }

    render() {
        return (
        <div>
            <div className="overlay">   
                <div className="content">
                    <p className='finsMsg'>"Game Finish!!"</p>
                    <p><button className='closeBtn' onClick={this.CloseDialog}>close</button></p>
                </div>
            </div>
        </div>
    )}
}

とします。
このコードにより、ポップアップメッセージ側でボタン押下時に、
propsで定義したcloseStateにfalseが代入されます。
メインぺージにてcloseStateが変更されたことによりupdateStateがコールバックされ、closeStateでセットしたfalseを入手できます。

今回はboolですが、数値や文字列の変数をpropsで定義し、
定義した変数でコールバック関数がコールされるようにすればポップアップメッセージで定義した数値や文字列を入手することが可能です。

■メインページからポップアップメッセージへのデータ送信

今度は逆にメインページからポップアップメッセージ側へデータを送信してみたいと思います。
今回はメインページで計算していたスコアを前回のポップアップメッセージ側に通知する方法を例にまとめていきます。

先ほどのコードから追記した点はそれぞれこちらになります。
ポップアップメッセージ(dialog.tsx)

import React from 'react';
import "./dialog.css";

export interface Props {
    closeState?:any;
    showMsg?:string; // ←追加.
}

export default class MadeDialog extends React.Component<Props> {
    CloseDialog = () => {
        this.props.closeState(false);
    }

    render() {
        return (
        <div>
            <div className="overlay">   
                <div className="content">
                    <p className='finsMsg'>"Game Finish!!"</p>
                    <p className='finsMsg'>{this.props.showMsg}</p> // ←追加.
                    <p><button className='closeBtn' onClick={this.CloseDialog}>close</button></p>
                </div>
            </div>
        </div>
    )}
}

メインページ(mogura.tsx)

    updateState = (state:boolean)=>{
        this.setState({popupstate:state});
    }

    OpenDialog = () =>{
        const {popupstate} = this.state;
        const{result} = this.state;
        let Msg:string = "score is " + result; ← メッセージ追加.
        if(popupstate === true){
            return <MadeDialog closeState={this.updateState.bind(this)} showMsg = {Msg}></MadeDialog> //←showMsg 追加
        }else{
            return <p></p>
        }
    }

追加した点ですが、
 ・ポップアップメッセージ側のpropsにshowMsgを追加
 ・メインページ側のMadeDialog にポップアップメッセージ側で定義したshowMsgにスコアメッセージ(Msg)を代入
です。
要するに、propsに変数を定義し、親となるメインページ側で定義された変数に数値もしくは文字列を代入することで、
親であるメインページから子であるポップアップメッセージにデータの送信が行えます。

■作成結果

作成した結果こちらのような動作になります。
モグラたたきのページで表示したスコアと同じ値がポップアップメッセージに表示されています。
f:id:Elsammit:20211205180222g:plain

■最後に

今回は前回のポップアップメッセージ表示を改変し、データのやり取りが行えるようにしました。
まだまだReact知らないことが多いので勉強していきます!!
学んだことは備忘録としてこちらの記事にまとめていきたいと思います!!



Reactでポップアップメニューを自作する

今回はReactでポップアップメニューを自作する方法に関してまとめていきます。

先日から活用しているモグラたたきアプリに関して、終了時に終了メッセージを表示させていたのですが、
当初alertを用いて実装していました。

ただ、、
alert表示場所が中央に表示されないので分かりにくい!!
ということで中央に表示させるべく、ポップアップメッセージを自作することに。



■最終系

今回作成したポップアップメッセージはこちら。
f:id:Elsammit:20211204215733g:plain

このように、終了したらポップアップメッセージを中央に表示させることが出来ました。

ソースコードに関して

ソースコードは下記に保存しております。
GitHub - Elsammit/Moguratataki

■自作ポップアップメッセージを作成

まずは表示させるポップアップメッセージを作成していきます。
コードはこちら。
[dialog.tsx]

import React from 'react';
import "./dialog.css";

export default class MadeDialog extends React.Component<Props> {
    CloseDialog = () => {
    }

    render() {
        return (
        <div>
            <div className="overlay">   
                <div className="content">
                    <p className='finsMsg'>"Game Finish!!"</p>
                    <p><button className='closeBtn' onClick={this.CloseDialog}>close</button></p>
                </div>
            </div>
        </div>
    )}
}

メッセージ表示とボタンが配置されたwindowを1つ用意しているのみになります。
ボタンをクリックするとCloseDialog関数がコールされます。

モグラたたき終了時にポップアップメニューを表示

では終了時に先ほど作成したポップアップメニューを表示させてみます。

まず先ほど作成したポップアップメッセージを表示させるためには、

import MadeDialog from './dialog'
<MadeDialog></MadeDialog>

というように作成したクラスをimportし、付与した名前でタグを生成すればOKです。

今回のケースはモグラたたき終了時にポップアップメッセージですので、

    OpenDialog = () =>{
        const {popupstate} = this.state;
        const{result} = this.state;
        if(popupstate === true){
            return <MadeDialog></MadeDialog>
        }else{
            return <p></p>
        }
    }

といったOpenDialog関数を作成し、
popupstateがtrueの時にMadeDialogがコールされポップアップメッセージが表示。
falseの時に別タグがコールされポップアップメッセージ非表示としました。
今回は割愛しますが、モグラたたき終了時にpopupstateをtrueにするようにしています。

■ポップアップメッセージを閉じる処理を追加

先ほどのコードでモグラたたきが終了すればpopupstateがtrueになり、
自作ポップアップメッセージが表示されます。

しかし、現在のコードではポップアップメッセージを閉じることが出来ません。
そこで次にポップアップを閉じるための処理を追加していきます。

まずはポップアップメッセージclass側を改造し、ボタンをクリックした時にcloseするための処理を追加していきます。
コードはこちら。

import React from 'react';
import "./dialog.css";

 //追加
export interface Props { 
    closeState?:any;
}

export default class MadeDialog extends React.Component<Props> {
    CloseDialog = () => {
        this.props.closeState(false); //←追加
    }

    render() {
        return (
        <div>
            <div className="overlay">   
                <div className="content">
                    <p className='finsMsg'>"Game Finish!!"</p>
                    <p><button className='closeBtn' onClick={this.CloseDialog}>close</button></p>
                </div>
            </div>
        </div>
    )}
}

追加したのは、

export interface Props { 
    closeState?:any;
}

    CloseDialog = () => {
        this.props.closeState(false); //←追加
    }

です。
closeStateを定義してボタンがクリックされた時に、closeStateにfalseをセットしています。

次にモグラたたき側のコードを変更していきます。
変更箇所はこちら。

   // 追加.
    updateState = (state:boolean)=>{
        this.setState({popupstate:state});
    }

    OpenDialog = () =>{
        const {popupstate} = this.state;
        const{result} = this.state;
        if(popupstate === true){
            return <MadeDialog closeState={this.updateState.bind(this)}></MadeDialog> //←closeState追加
        }else{
            return <p></p>
        }
    }

MadeDialogタグですが、

<MadeDialog closeState={this.updateState.bind(this)}}></MadeDialog>

の通り、closeState={this.updateState.bind(this)}}を付与しております。
こちらはcloseStateが変化した時にupdateState関数がコールバックされるコードになります。
そして、updateState関数ですが

    updateState = (state:boolean)=>{
        this.setState({popupstate:state});
    }

と定義しております。
updateState関数の引数であるstateですが、今回はcloseStateの値が格納されます。
先ほどのポップアップメッセージ側でボタンをクリックした際にfalseがセットされるので、
popupstateにはfalseがセットされます。

これでボタンをクリックした際に、
closeStateにfalse ⇒ updateState関数コール ⇒ popupstateにfalseがセット ⇒ ポップアップメッセージが閉じる
が実現出来ました。

■最後に

これでモグラたたき終了時に、
 ・popupstateがtrueになりポップアップメッセージが表示
 ・ポップアップメッセージのボタンクリックでpopupstateがfalseになりポップアップメッセージが閉じる
が実現出来ました。

長々と書いてしまいましたが、
案外簡単に実装できるので試してみてください。



CMakeで複数の実行ファイルを生成するあれこれ

今回はCMakeを用いて複数の実行ファイルを生成したり、生成する実行ファイルを切り替える方法についてまとめておきたいと思います。
以前より気にはなっていたのですが、調べるの億劫で出来ていませんでした。。
ちょっと用事があったのでこの機に調べてみることに!!



■経緯

今回ちょうどC++言語でいくつかのアプリを作成しているのですが、
まぁ同じような処理を多々やっている。
どうせなら、共通処理は同じコードを使いまわしたいと考えたのですが、、、
ライブラリ作って管理するのは面倒だし、、
ということでこちらのフォルダ構成で別々にコンパイルすれば?とも考えたのですが、、
CMakeファイルが2種類必要。。
f:id:Elsammit:20211128182626p:plain

ということで1つのCMakeファイルで、
複数の実行ファイルを生成したり、生成するファイルを切り替えたり出来ないか?と考えたのが始まりです。

■使用環境

今回のフォルダ構成はこちらとします。
f:id:Elsammit:20211128182640p:plain

今回使用するコードはこちらになります。

ファイル名:calc.h

#include <stdio.h>

int add(int a,int b);
int sub(int a,int b);

ファイル名:calc.cpp

#include "calc.hpp"

int add(int a,int b){
    return a+b;
}

int sub(int a,int b){
    return a-b;
}


ファイル名:main1.cpp

#include <stdio.h>
#include "common/calc.hpp"

int main(){
    printf("%d \n", add(1,2));
}


ファイル名:main1.cpp

#include <stdio.h>
#include "common/calc.hpp"

int main(){
    printf("%d \n", add(1,2));
}

ファイル名:main2.cpp

#include <stdio.h>
#include "common/calc.hpp"

int main(){
    printf("%d \n", sub(1,2));
}

■CMakeファイルで複数の実行ファイルを生成

まずは1つのCMakeファイルで複数のコードを実行する方法についてまとめます。
今回のケースの場合、CMakeLists.txtはこちらのようになります。

cmake_minimum_required(VERSION 3.5.1)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++11")

file(GLOB SOURCES "common/*.cpp")

set(PROJECT_NAME1 main1)
project(${PROJECT_NAME1})

set(PROJECT_NAME2 main2)
project(${PROJECT_NAME2})

add_executable(${PROJECT_NAME1} main1.cpp ${SOURCES}) 
add_executable(${PROJECT_NAME2} main2.cpp ${SOURCES}) 

実施していることは、

set(PROJECT_NAME1 main1)
project(${PROJECT_NAME1})

set(PROJECT_NAME2 main2)
project(${PROJECT_NAME2})

にて実行ファイル名を各アプリ毎に指定し、

add_executable(${PROJECT_NAME1} main1.cpp ${SOURCES}) 
add_executable(${PROJECT_NAME2} main2.cpp ${SOURCES}) 

にて実行ファイルを生成する。
になります。
ここで、${SOURCES}は、

file(GLOB SOURCES "common/*.cpp")

にて定義している通り、共通コード部分であるcommonフォルダを指しております。

こちらを実行するとそれぞれのアプリに対してコンパイルが実行され2種類の生成物(実行ファイル)が生成されます。
※今回のコードですとmain1, main2が生成。

生成されたコードを実行すると正しく実行出来ることが確認できるかと思います。

■CMake実行時に生成する実行ファイルを切り替える

では次にcmake実行時に生成する実行ファイルを切り替える場合についてまとめていきます。

CMakeでは引数を与えることが出来ます。
例えば、

cmake . -DHOGE=1

とすると、HOGEという変数に1を代入してcmakeを実行する意味になります。
また、CMakeではif文も使用できるので、今回のケースですと、

cmake_minimum_required(VERSION 3.5.1)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++11")

file(GLOB SOURCES "common/*.cpp")

if("${HOGE}" EQUAL 1)
    set(PROJECT_NAME1 main1)
    project(${PROJECT_NAME1})
    add_executable(${PROJECT_NAME1} main1.cpp ${SOURCES}) 
elseif("${HOGE}" EQUAL 2)
    set(PROJECT_NAME2 main2)
    project(${PROJECT_NAME2})
    add_executable(${PROJECT_NAME2} main2.cpp ${SOURCES}) 
endif()

とし、main1を生成させたい場合には、

cmake . -DHOGE=1
make

main2を生成させたい場合には、

cmake . -DHOGE=2
make

とすればOK。

実行してみると確かに、生成されるコードが切り替わることが確認できるかと思います。

■最後に

今回はCMakeファイルで複数の実行ファイルを生成したり、生成される実行ファイルを切り替えたりしてみました。
まだチャレンジ出来ていないけど、共通コードをあまり変更しないならオブジェクトファイルを利用すればコンパイルがもっと早く・楽にできそうだな。
後で時間がある時に試してみます。



Reactでドラッグを無効化するために

去年作成したモグラたたきゲームを使ってみたのですが、、
白熱するとクリックがドラッグ判定になってモグラが叩けないことが判明。

そういえば、Reactでドラッグを無効にする方法を知らないな。と思い調べてみました!!
今後困らないように備忘録として残しておこうと思います。



■Reactでドラッグを無効化するには

まずドラッグ操作を実行された際のイベント検知には、
ondragstartを用いることが出来ます。

例えば今回のモグラたたきの場合、

MakeMap = () =>{
    let List = [];
    for(let i=1;i<=5;i++){
        let buf = [];
        for(let j = 1;j<=5;j++){
            let num = j + (i-1)*5;
            let str = "Mas"+num;
            // 列追加
            buf.push(
                <td><img id={str} src={shibafu} alt="green" onClick={this.onClick.bind(this,str)} /></td>
            );

            let element = document.getElementById(str);
            element.ondragstart = function (){
                    return false;
            };
        }
        // 行追加
        List.push(<tr>{buf}</tr>);
    }
    return List;        
}

というように、特定のID名に対して、

let element = document.getElementById(str);

にてElementを取得した後、

element.ondragstart = function (){
}

にてドラッグ時の処理をコントロールできます。
今回はドラッグを無効化したいので、

element.ondragstart = function (){
         return false;
};

とし、falseが返るようにして無効化としました。

■TypeScriptのケース

次にTypeScriptのケースです。
といってもTypeScriptの場合もほとんど同様です。
先ほどのMakeMap関数を例にご紹介しますと、

    MakeMap = () =>{
        let List = [];
        for(let i=1;i<=5;i++){
            let buf = [];
            for(let j:number = 1;j<=5;j++){
                let num:number = j + (i-1)*5;
                let str:string = "Mas"+num;
                // 列追加
                buf.push(
                    <td><img id={str} src={shibafu} alt="green" onClick={this.onClick.bind(this,str)} /></td>
                );

                let element: HTMLElement | null = document.getElementById(str);
                if(element != null){
                    element.ondragstart = function (){
                        return false;
                    };
                }
            }
            // 行追加
            List.push(<tr>{buf}</tr>);
        }
        return List;        
    }

といった形になります。
TypeScriptの場合、

let element: HTMLElement | null = document.getElementById(str);

の通り、HTMLElement もしくはnullになる型になります。
element.ondragstartはnullを許さない(コンパイル時にエラーになる)ため、

if(element != null){
       element.ondragstart = function (){
               return false;
       };
}

と、elementがnullでない場合に限り、element.ondragstartをコールするようにしました。

■最後に

今回はドラッグを無効化する方法について記載しました。

まだまだWeb作成の技術は初心者です。。
もっと勉強していきたいと思います!!



Reactで作成したコードをTypeScriptに変換

去年、Reactでいくつかアプリを作成してみました。
こちらのWebアプリケーションをtypescriptに置き換えることでtypescriptの勉強が行えないかと思いたち、
既存のReactで生成したコードをtypescriptに置き換えてみることにしました。

今回は、typescriptに置き換えるにあたり環境構築方法についてまとめておきたいと思います!!



■TypeScript環境をインストール

まずTypeScriptを用いるための環境が必要になるのでこちらをインストールしていきます。
コマンドはこちら。

npm install --save typescript @types/node @types/react @types/react-dom @types/jest

エラー等が発生しなければOK。
これでインストールは完了です。

■TypeScriptコンパイル環境準備

次にTypeScriptで記載されたコードをコンパイルするための設定ファイルを作成していきます。
っと言ってもコンパイル実行に必要なファイルである、
tsconfig.json
は、
npm start

npm build
などを実行すると自動生成されます。
このため、TypeScriptを明示的に使用する旨をconfigファイルに追記するのみでOKになります。
追加・編集が必要なファイルはありませんでした。

追加・編集せずにtypescriptが導入できるのは楽で便利ですね。

■既存ファイルをTypeScriptに置き換える

では実際に既存の.jsファイルをTypeScriptに変換していきます。

変換手順は、
①自分が作成した.jsファイルの拡張子を.tsxに書き換えます。
②TypeScript用に型指定などを行っていく
になります。

②ですが、vscode
TypeScriptの拡張機能を用いるとエラーを教えてもらえるのでとても便利。
自分は、
JavaScript and TypeScript Nightly
をインストールして、エラー検知や修正案を教えてもらっています。

②で型指定等々はTypeScriptの構文・文法に沿って実施してもらえればOK。
documentインターフェースの型指定だけちょっと迷ってしまったので備忘録として残しておきます。

まずdocumentインターフェースの型指定方法ですが、
javascriptにて、

const TurnMessage = document.getElementById("TurnId");

と記載していたとします。
このコードをTypeScriptに置き換える場合には、

const TurnMessage = document.getElementById("TurnId") as HTMLElement;

と型アサーションを付与することで解決できます。

【型アサーションとは?】
型定義の上書き機能とのこと。
TypeScriptでは、推論・分析により得た型を任意の方法で上書きでき、これを型アサーションと呼ばれるメカニズムによって実行しているとのこと。
※参照
typescript-jp.gitbook.io

ただし、型アサーションは実行途中で無理やり型を指定・変換する行為になりますので多用はしない方がよい(出来る限り使用は避けた方がよい)です。
自分も、documentインターフェースにのみ適用してコードを作成してみました。

■TypeScriptへ変換したコードを動かしてみる

コードをTypeScriptに置き換え終わったら、今まで通り、

npm start
もしくは
yarn start

を実行すればTypeScriptのコンパイルが実行され、内部的にjavascriptに変換されます。
後はWebブラウザ上で、

http://ipアドレス:3000

のようにアクセスすれば作成したアプリが表示されるかと思います。

■作品紹介

去年作成した〇×ゲームとモグラたたきをtypescriptに置き換えてみました。
コードはgithubに上げておりますのでよろしかったが参考にしてください。
github.com
github.com

■最後に

今回はReactで作成したコードをTypeScriptに変換してみました。
どうしても型アサーションに頼ってしまっている部分があるので、こちらは見直し!!
解決出来たらまたブログに載せたいと思います!!