2019年5月5日 星期日

Scratch : TTS

LLK 的 scratch-vm 專案中看到 Scratch 的語音合成參數:



兜了一個可以念出中文的網址:



解析一下參數:

  • locale : 用什麼語言發音。中文用「cmn-CN」,英文用「en-US」。
  • gender : 發音的性別。男聲用「male」,女聲用「female」。不過,目前中文似乎只能用女聲。
  • text:要發音的字串。



2019年4月29日 星期一

HTML5 : 筆順編輯工具

教育部「常用國字標準字體筆順學習網」(以下簡稱「筆順網」) 中收錄了 4808 個國字的筆順,而那些不在這 4808 個字內的國字,如果想要練習筆順怎麼辦呢?

最近因為國語課本中有個生字「粄」不在筆順網的收錄範圍內,為了它,我開發了一個小工具,可以利用它來下載有筆順的其它字,再將「粄」所需要的筆畫拼接起來,就這樣無中生有造出和筆順網相同格式「粄」的筆順資料了。

姑且就叫這個「html5_strokeeditor」 小工具為「筆順編輯工具」。這個工具的網址如下:



當然囉!因為教育部並沒有開放我們將造好的新字上載到主機儲存,所以造出的筆順還是無法使用筆順網來練習。不過,可以利用我開發的新版筆順練習工具來載入自己造的新單字。新版的筆順練習工具,網址如下:

如何利用「筆順編輯工具」造出「粄」這個字呢?

分析部件

由於我們是利用筆順網已有的字來「重組」成新的字,所以在開始「造字」前,得先來分析一下新單字需要哪些筆畫,哪些字中帶有那些筆畫。

以「粄」來說,它可拆成左側的「米」和右側的「反」兩個部件,因為要放兩部件,所以「米」可能需要稍小一點點,才不會和右偏旁的「反」重疊。

綜合上述的需求,「米」我們可以取結構上分左、中、右三部件的「糊」,再結合結構上分為左、右兩部件的「版」。「版」的「反」這個部件會稍小一點點的。這樣應該就有機會造出一個比例適當的「粄」囉!

下載筆順

根據分析部件後的結果,想要造出「粄」這個字,我們需要下載「糊」和「版」兩個字的筆順。

打開「筆順編輯工具」後,在下載的對話框中,輸入想下載筆順的字後,按「開始下載」的按鈕。

之後如果還想再下載別的字,可以按一下右下角的工具箱開關,打開工具箱以後,按一下「下載筆順」的圖示,即可出現下載筆順的對話框。


選擇筆畫

筆畫是有順序的,「筆順編輯工具」怎麼知道哪個筆畫先寫,哪個筆畫後寫呢?

因為筆順網的筆畫有其習慣上的書寫順序,而我們在造字時,其實是依習慣切分的部件,一個筆畫、一個筆畫來選取,所以一個字中,同一個部件的,筆畫順序應該會按照原有的順序。

那不同字的,哪一個要優先呢?以造「粄」這個字需要「糊」的「米」,和「版」的「反」,取出的「米」和「反」,哪一個會先寫呢?

為了解決筆順的順序,「筆順編輯工具」是以著色的方式來選取所需要的筆畫(原本白色或灰色的筆畫,按一下會被塗色,表示已選擇該筆畫;反之,已有塗色的則會被取消),同一顏色的會被當作同一組(同一部件)。此外,因為顏色是有其優先順序的(請注意「色盤」上的編號),所以最後會依顏色的順序來結合成成新單字。

底下以「粄」這個字為例,說明它是如何「造」出來的:
  1. 選取「編號 1 」的顏色。
  2. 點選「糊」中屬部件「米」的所有筆畫。
  3. 選取「編號 2 」的顏色。
  4. 點選「版」中屬部件「反」的所有筆畫。



照上述的順序來換色並點選所需要的筆畫,最後就可以讓新單字「粄」,擁有正確的全字筆順了。不但如此,連部件的設定,也一併設定完畢了。

匯出單字

在「筆順編輯工具」造好的字,可以在右下角的工具列中,找到「匯出單字」的工具。打開後,輸入新筆順是哪個單字,即可匯出一個 .json 的檔案。匯出的檔案,日後可以重新匯入,這樣就不用再重新造字了。





因為目前教育部的筆順網並無法讓我們自行擴充筆順字庫,所以目前只能將造好的筆順,用個人開發的筆順工具來展示並練習。



進階使用

筆順編輯工具除了直接將現有的筆畫或部件拼接以後,也有提供將筆畫移動、縮小和放大的功能。由於只要新增或減少新字的筆畫,移動和縮放的設定都會重置,所以建議想移動或是縮放前,得先確認所需要的筆畫都已增、刪好了,以免「前功盡棄」。

無論搬移或是縮放的功能,操作的程序都是先按一下想進行的功能鈕,然後拖曳想調整的筆畫或是按一下調整的筆畫。以縮放的功能來說,每按一下想調整大小的筆畫,都會放大或縮小為原來的 5%。

有了搬移和縮放的功能,相信大家可以創造出更多字的筆順了。

下面是製作「闁」這個字的簡單示範影片:


相關網站


更新記錄

  • 2019.05.04 加入由儲存在 localStorage 的資料載入的功能。
  • 2019.04.28 加入將造的字移動、縮放的功能。
  • 2019.04.23 可依填入的顏色區分筆畫的順序。
  • 2019.04.20 初版


HTML5 : Unscramble 重組小遊戲

Unscamble 重組小遊戲可以依字串分割設定,出題時,將句子或字詞分成好幾個部份,再以亂數把順序打亂,最後讓玩家將它們依正確的順序排好。

此外,為了避免玩家卡關,出題時也可以在題庫中加入提示用的文字、圖片或是語音。語音的部份如果不想自己錄語音檔,目前可以使用 Google 的 TTS 替代。

試玩與下載

Unscamble 可以在我的 HTML5 FUN 網站中找到,

試玩:

下載:

《範例一》是英文句子重組的遊戲

本範例會先自動播放提示的語音,以 TTS 或是MP3聲音檔來唸出全句。

操作時,先點一下要換位置的字卡,再點一下另一張字卡,兩張字卡就會交換位置。如此兩兩交換,直到正確為止。


《範例二》是英文單字重組的遊戲

本範例不會先自動播放提示的語音,玩家想聽單字的語音時,可以自己按左下角的語音播放鈕。單字提供中文的文字提示,有些甚至還有或圖片的提示。

操作時,先按住要換位置的字母卡不放,將卡片拖曳到要放的位置後,再放開卡片。如此一張張拖、放,直到正確為止。

《範例三》是中文句子重組的遊戲

本範例會先自動以 TTS 來播放全句的提示語音。

操作時,和《範例二》相同,是以拖、放卡片的方式來換位置。

選單設定檔

 「Unscramble 重組小遊戲」在啟動以後,會先載入主設定檔「unscramble_set.js」,接著會載入預設的選單設定檔「unscramble_menu_set.js」,然後依選單設定檔製作選單供玩家選擇,玩家選好後,最後載入題庫設定檔。

選單設定檔裡面的格式很簡單
  • 每一個題庫檔會對應到一行,設定會用一對引號(單引號或雙引號均可)括住。
  • 每一行會以一個半形逗號當分隔符號,將它分為兩個欄位。
  • 每一行的第一個欄位是「項目名稱」,第二個欄位為「題庫設定檔的檔名」。

menu_items = new Array(
//------------------------------------下一行開始為選單增加項目
 '設定範例(英文句子),question_set_example-1.js'
,'設定範例(英文單字),question_set_example-2.js'
,'設定範例(中文句子),question_set_example-3.js'
//------------------------------------選單選項結束,以下請勿修改
);
以上面的設定來說,選單總共會出現三個選項:
  1. 設定範例(英文句子)
  2. 設定範例(英文單字)
  3. 設定範例(中文句子)

按下第一個選項時,程式會打開檔名為「question_set_example-1.js」的題庫設定檔,並出題給玩家玩。同樣的道理,按下第二個選項時,程式會打開檔名為「question_set_example-2.js」;按下第三個選項時,程式會打開檔名為「question_set_example-3.js」。

另外,如果選單項目很多時,可以將它們分類,並建立「次選單」,此部份的機置和我另一個作品 BINGO 是一樣的,在 Blog 中有詳細的說明,可以參考這篇:



題庫設定檔

前面提到的例子,當我們按下範例選單的第一個選單後,「Unscramble 重組小遊戲」會幫我們載入題庫檔案「question_set_example-1.js」,這個檔案是個純文字檔,建議使用純文字的編輯工具來開啟,並修改內容(個人在 Windows 上習慣使用 Notepad++,在 Mac OS X 上使用 Textwrangler ) 。

底下簡單介紹一下 Unscramble 可以自訂的參數。

在開始出題前,遊戲會出現說明畫面,底下是說明對話框相關的設定參數:
  • help_title : 對話框的標題。
  • help_description : 對話框中的說明文字。
  • help_button_caption : 對話框的按鈕。

前面提過,玩家作答時,可以直接將卡片拖曳到要放的位置;或是用按下要選的卡片,再按一下另一張卡,讓兩張卡交換位置的方式。作答的操作方式是由底下的參數所控制的:
  • card_swap_mode : 字卡交換方式。
    0 為用拖曳方式換卡片的位置。
    1 為用按一下指定要交換位置的兩張卡片。

有關對答案的方式,提供一個 enableSubmitButton 的參數,可以用來控制是否出現「送出答案」的按鈕:

  • enableSubmitButton
    true : 玩家按「送出答案」才核對答案。
    false : 卡片換位置後就自動對答案。


有關聲音播放的參數:
  • sound_enabled : 是否播放音效及語音。
    true 播放音效及語音。
    false 不播放任何聲音。
  • sound_autoPlay : 是否自動播放語音。
    true : 出題後自動播放語音。
    false : 使用者按播放鈕後才播放語音。
  • sound_autoPlayLoop : 自動播放的次數。 
  • sound_autoPlayDelay : 隔多久自動重播(單位秒)。
  • tts_enabled : 是否使用 TTS 的語音
    true : 語音採用 TTS 來發音。
    fasle : 不使用 TTS 的語音。
  • tts_is_at_index : 如果使用 TTS ,將題庫中的哪一個欄位的文字轉語音。如果值為 0 ,表示使用左側題目中的文字當要發聲的字串;如果為 1,則是以右側提示欄位中的文字來發聲。
  • tts_language : TTS 選用的語言代碼。
    en : 英文。
    zh_tw : 中文。
  • tts_base_url : TTS 的呼叫網址。

有關題庫的參數:

Unscramble 在出題時,要知道
  • 完整的題目字串是什麼?
  • 怎麼切割字串?
  • 提示是什麼?(可包括文字、圖片、語音)
照這樣看來,每一題題目,我們要:
  • 先切割成題目(第一欄位)和提示(第二欄位)兩個欄位。
  • 第一欄位中的題目再切成多個字串,變成一張張的卡片。
  • 第二欄位中的提示也切成多個項目,變成文字、圖片檔檔名或是聲音檔檔名。
照這樣分析,我們只要使用兩個「分隔符號」分別當「第一層」和「第二層」的分割器,就可以符合我們的需求了。先以 「data_seperator」指定的符號分割,再以「split_seperator 」指定的符號進行第二層的分割。

資料分割符號設定:
  • data_seperator : 用來切割題目和提示的分隔符號(第一層)。
  • split_seperator : 用來切割題目子字串,或是將提示分割為文字、圖片和聲音檔的分隔符號(第二層)。
以《範例二》「question_set_example-2.js」中的設定來說明:

它的資料分割符號設定為:
data_seperator = "###";
split_seperator = "==";
所以「題目」和「提示」之間是以三個井字號「###」來切割。
這個題庫是用來進行英文單字重組的,字母和字母之間是以兩個等號「==」來切割;而提示用的中文或提示用的圖片之間也是以兩個等號「==」來切割。
question_lines=new Array(
//------------------------------------下一行開始增加題目
  'h==a==p==p==y###快樂==images/happy.png'
, 'a==n==g==r==y###生氣'
, 'c==r==y###哭泣'
//------------------------------------題庫結束,以下請勿修改
);
看看第一題是測驗「happy」這個英文單字



三個井字號將第一題分為題目:

h==a==p==p==y

和提示:

快樂==images/happy.png

題目又以兩個等號分為:「h」、「a」、「p」、「p」和「y」五個字母。
提示也以兩個等號分為:中文意思的「快樂」,和提示圖片檔名的「images/happy.png」,兩部份。

觀察一下,後面兩題的「angry」和「cry」則都只有中譯的文字提示,沒有圖片提示。

範例二的解說影片



進階設定

如果有需要調整版面的配置,讓題目、提示文字、提示圖片和語音播放器的位置能自訂,就打開主設定檔「unscramble_set.js」依需要調整底下的座標:
  • answer_block_y : 作答區的 y 座標
  • audioPlayer_x : 語音播放器的 x 座標
  • audioPlayer_y : 語音播放器的 y 座標
  • text_hint_x : 提示文字區的 x 座標
  • text_hint_y : 提示文字區的 y 座標
  • image_hint_x : 圖片的 x 座標
  • image_hint_y : 圖片的 y 座標
調整時要注意的是,(0, 0) 的位置是在左上角。而整個版面是最大範圍:
  • X : 1024
  • Y : 768
以上是有關 Unscramble 重組小遊戲的簡單說明。

相關文章


更新記錄

  • 2019.05.21 加入 split_by_length、split_by_length_substring_length:split_by_length 用來設定是否以字串長度來切割子字串,如果設為 true, 題庫第一欄位會以長度來切割;如果設為 false (預設值), 題庫第一欄位會以 split_seperator 的分隔符號來切割。split_by_length_substring_length 則是當 split_by_length 為 true 時, 每個子字串要多長。
  • 2019.05.18 加入 select_questions_in_random、number_of_questions。
    select_questions_in_random:用來控制如何由題庫選題的方式( true:以亂數選題  false:按題庫順序)。
    number_of_questions:設定在亂數選題時,最多選幾題。
  • 2019.05.15 加入 sound_autoPlayLoop、sound_autoPlayDelay ,用來控制語音自動重播的次數。
  • 2019.04.30 加入 enableSubmitButton 的選項。是否使用「送出答案」的按鈕, true : 玩家按「送出答案」才核對答案; false : 卡片換位置後就自動對答案
  • 2019.04.27 釋出。
  • 2019.04.24 明勳建議加入計時器,可在結束時知道花多少時間作答。
  • 2019.01.06 明勳建議不要自動播放語音,可以在需要時再手動播放語音;版面位置調整改為可以設定檔中自行設定。
  • 2019.01.03 測試版。








2019年4月12日 星期五

Python : 用 SimpleHTTPServer 模組建立一個臨時的網頁伺服器

電腦中有安裝 Python ,臨時需要一個 web server 來測試,怎麼辦?

Python 有個  SimpleHTTPServer 模組,用它就可以馬上建立一個臨時的網頁伺服器。哈!要是以前,我一定是去下載 Appache ,然後再重新「複習」一堆設定的設定檔。但為了一個簡單的需求,玩這麼大,還是用最簡單的方法解決就好:

底下以在 Windows 中的操作來說明建立的流程

步驟一:開啟「命令提示字元」(終端機)


步驟一:切換到要當網頁伺服器根目錄的資料夾 (ex. D:\limejs )

cd \limejs


步驟二:啟動  SimpleHTTPServer

python -m SimpleHTTPServer

如果成功,應該可以在畫面中看到底下類似的訊息

Serving HTTP on 0.0.0.0 port 8000 ...

在網頁瀏覽器的網址列中輸入底下的網址:

http://127.0.0.1:8000/

應該就可以看到該目錄中的內容了。


 SimpleHTTPServer 預設是在 8000 port (埠號),如果想更改網頁伺服器監聽的 port,例如,改在 port 80 監聽,就將啟動指令改為:

python -m SimpleHTTPServer 80

如果想關閉伺服器,就按鍵盤的 Ctrl + C 組合鍵。

這個架站皂方法實在簡單,比以前弄半天的 Appache 容易多了!





2019年4月7日 星期日

JavaScript : 讓行動載具文件不要因拖曳動作亂滑動

忘了從哪一個版本起,無論在 iOS 或 Android 上的瀏覽器玩我寫的 HTML5 小工具,按下並移動位置時,整份文件會跟著滑動,這在玩遊戲進行拖曳或畫圖時真是一大困擾。為了解決我的筆順練習工具在寫字或塗鴨時,畫面一直滑動的問題,忘了是在哪裡找到解決的方法是加入底下的程式碼:


document.addEventListener('touchstart', function(e){ return; }, { passive : false });
document.addEventListener('touchmove', function(e){ e.preventDefault(); }, { passive : false });

主要是改變網頁主體的 document 在監聽到 touchmove 事件時,可以不要執行原來滑動的行為,這樣子,使用工具時就不會滑來滑去,不勝其擾。

筆順練習工具在加了前述的語法後,畫面果然就不會亂滑動了;不過,之前想說如法泡製,來解決其它遊戲的滑動問題,將語法複製、貼上,很容易吧!哈~事情總是沒想的簡單。加了語法的遊戲在測試時可以將畫面固定得牢牢的,在利用 LimeJS 的 build -a 指令,把 JavaScript 的原始碼編譯簡化後,滑動的問題還是出現了。當時百思不得其解,試了各種方法,問題仍然存在,最後只好放棄。

昨天在 build 新版的筆順練習工具時,因為加了「-a」參數,想讓程式碼更精簡,有部份功能運作不正常;而不加「-a」參數時又可以使用。讓我聯想到為什麼舊版的筆順練習工具滑動問題得以解決,而新版的在加「-a」去 build 時,又產生滑動問題的原因了。

檢視一下前面兩行程式碼的最後一個參數:

{ passive : false }

這是一個物件,並將「passive」設為「false」,這麼簡單有什麼問題會產生呢?

關鍵就在 LimeJS 是利用 goog Closure optimization 來簡化 JavaScript 的程式碼,所謂的簡化,主要就是將變數、函數名稱 ...... 等變得愈短愈簡單。參考之前這一篇中寫到的:
想要達到程式碼的大小最小化,使用「ADVANCED_OPTIMIZATIONS」,如果沒注意物件存取資料的方式,名稱有可能被改變,導致資料傳遞時會發生失誤。

關於這一點,可以由 build 完的、最佳化後的程式碼看得出來。

1-0 原始碼中用「{ passive : false }」:

document.addEventListener('touchmove', function(e){ e.preventDefault();}, { passive : false });


1-1 未使用「ADVANCED_OPTIMIZATIONS」來 build 後變成:

document.addEventListener("touchmove",function(a){a.preventDefault()},{passive:!1});


1-2 使用「-a」(「ADVANCED_OPTIMIZATIONS」) 來 build 後變成:

document.addEventListener("touchmove",function(a){a.preventDefault()},{ef:q});


註:上面的「q」為 0,可以追到 build 完的程式碼中有「q=!1;」

很明顯的,「-a」參數一加上去,build 完的物件參數名稱「passive」已經被改為「ef」,這樣就無法正確的設定給 document 的 touchmove 事件。


再來看看最後一個參數,在名稱加上一對單引號變成:

{ 'passive' : false }

2-0 原始碼中用「{ 'passive' : false }」:

document.addEventListener('touchmove', function(e){ e.preventDefault();}, { 'passive' : false });


2-1 未使用「ADVANCED_OPTIMIZATIONS」來 build 後變成:

document.addEventListener("touchmove",function(a){a.preventDefault()},{passive:!1});


2-2 使用「-a」(「ADVANCED_OPTIMIZATIONS」) 來 build 後變成:

document.addEventListener("touchmove",function(a){a.preventDefault()},{passive:q});


註:上面的「q」為 0,可以追到 build 完的程式碼中有「q=!1;」

1-0 和 2-0 就只在變數名稱差了一對單引號,但使用引號的名稱讓 2-1、2-2 中的「passive」可以不管在什麼情形下都叫「passive」,始終不改其名。關於這一點,以後可牢牢記住才行,不然真的是除蟲除到地老天荒啊!


2019年3月25日 星期一

LimeJS : 改變 CanvasContext 圖形的透明度


LimeJS 的 CanvasContext 和其它 node 物件一樣,有 setOpacity 可以設定透明度,但它並無法改變在 CanvasContext 中繪製圖形的透明度。當然囉!我們可以在繪圖的指令中加入透明度的參數,一道道的去設定,在想,可以無腦的在呼叫 setOpacity 改變 CanvasContext 的透明度時,不用管裡面繪製了什麼,就將圖形按 setOpacity 的參數來直接換透明度?

在上一篇的「雄 : LimeJS : 取得 canvas 中某一點的顏色」中提到可以利用 getImageData() ,讓我們取得某一區塊的圖形資料。資料中每一個點包括了 RGB (紅綠藍)的的資訊和 A (透明度),所以我們只要將第四個 byte 的透明度,依 CanvasContext 的透明度比例去計算後,再利用 putImageData 把資料寫回去即可達到改變圖形透明度的目的了。

下面假設 canvas 是一個 LimeJS 的 CanvasContext 物件,將繪圖區中圖形變更透明度的方法如下:

var ctx = canvas.getDeepestDomElement().getContext('2d');
var width = canvas.getSize().width;
var height = canvas.getSize().height;
var opacity = canvas.getOpacity();
var imageData = ctx.getImageData(0, 0, width, height);
for(var i=0;i<height;i++) {
for(var j=0;j<width;j++) {
var pixelAlphaIndex = (i*(width*4)) + (j*4) + 3;
var alpha = imageData.data[pixelAlphaIndex];
if( alpha > 0 ) {
imageData.data[pixelAlphaIndex] = Math.floor(alpha*opacity);
}
}
}
ctx.putImageData(imageData,0,0);

這個方法是一個點,一個點的掃描,並變更透明度的值,有空再來看看有沒有效率更高的方法。

LimeJS : 解決 CanvasContext 無法立即繪圖

LimeJS 提供了 lime.CanvasContext 物件來動態的畫圖,但是之前發現有一個問題,如果不是剛建立好 CanvasContext 物件,如果緊接著在 context 中下的繪圖指令,並不會立即看到想畫的東西。這樣一來,就不能動態建立所需要的 CanvasContext 物件了,實在很不方便。

原本就在猜測,可能是因為 CanvasContext 物件還沒來得及在場景中完全建立好,就開始繪圖所產生指令下了,卻看不到圖的問題,今天想到一招,那就讓繪圖指令延遲一下下好了,例如,在百分之一秒後再執行繪圖指令:

如果原來是:

ctx.fillStyle = "#ff0000";
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(300, 0);
ctx.lineTo(300, 300);
ctx.lineTo(0, 300);
ctx.closePath();
ctx.fill();

就利用 setTimeout 來延遲繪圖指令:

setTimeout( function() {
ctx.fillStyle = "#ff0000";
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(300, 0);
ctx.lineTo(300, 300);
ctx.lineTo(0, 300);
ctx.closePath();
ctx.fill();
}, 10);

最後的 10 ,讓 funtion() 中的指令在 10/1000 秒後才執行。

至於要延遲多少時間,可以自行再調整。

先用這樣解決了眼前的問題,再看看有沒有更好的方法可用。



2019年3月2日 星期六

Biking : 自行車道→大稻埕碼頭

天氣狀況 : 晴
TRIP DIST : 41.71
TRIP TIME : 02:18:12
AVG. SPEED : 18.11
MAX. SPEED : 33.45

接連兩天的好天氣,變天前的今天,太陽不大,天空不藍,順著自行車道往大稻埕碼頭前進。因為是假日,一路的小孩特別多,一些路較窄的地方,常會因為未注意安全的小孩擋在路上而必須停下來,讓人騎得不夠痛快。

快到光復橋前的草坪上空,可見「大鳥」在追逐著:



領頭的起飛以後,一個接著一個的尾隨在後面,如同飛機編隊飛行般,真是有趣!看得我原本騎得熱熱的身體都涼掉了。

假日的大稻埕碼頭,車潮、人潮如織,連公廁都大排長龍。出了水門,原本想去迪化街買花生湯的,因為廟會的陣頭擋住去路,只好作罷。看到路邊有賣黑糖地瓜,花六十元,買了一小盒,幸好還滿好吃的。

碼頭的人實在太多了,沒停留就往回騎了。路上看到兩名十來歲的孩子竟騎著車玩起追逐,並互拉對方車尾的遊戲,心想,萬一有個閃失,不只兩輛車卡在一起,一旁不知有多少倒楣鬼要被捲入了。這讓我想起之前參加北海岸自行車比賽,看過的車禍,一輛摔車,後面一輛輛的壓在一起,令人感到驚心動魄的。一開始和玩命的兩個小孩保持距離,之後看時機成熟,趕緊超車而過。




2019年2月28日 星期四

LimeJS : 取得 canvas 中某一點的顏色

LimeJS 提供了 lime.CanvasContext 物件,供動態的畫圖。如果想取得 canvas 的內容,可以用 getDeepestDomElement().getContext('2d') ,例如:

var context = new lime.CanvasContext().setSize(600,600);
var ctx = context.getDeepestDomElement().getContext('2d');

canvas 提供 getImageData() 讓我們取得某一區塊的圖形資料,它的語法如下:

context.getImageData(x,y,width,height);

四個參數分別為:

  • x : 左上角的 x 座標。
  • y: 左上角的 y 座標。
  • width : x 方向取多大。
  • height : y 方向取多大。

所以如果以前面例子來說,想在 ctx 中取  (400, 300) 這一點的顏色值,可以用這樣的語法:

var pixel = ctx.getImageData(400, 300, 1, 1).data;

傳回的前四個元素分別對應到該點顏色的 RGBA 值:

  • pixel[0] : 紅色 (R)。
  • pixel[1] : 綠色 (G)。
  • pixel[2] : 藍色 (B)。
  • pixel[3] : 透明度 (A)。

如果想把取得圖形資料處理後再更新顯示的內容,可以使用 putImageData 寫回原 canvas。





LimeJS : getPosition

LimeJS 中,可以使用 getPosition() 來取得物件的位置,傳回的是一個「goog.math.Coordinate」。記得之前在寫偷插電的輔具 BattleShips 時,使用 getPosition() 取得物件的座標並設給某個變數後,對該變數的 x、y 做運算以後,例如:

var pos = sprite.getPosition();
pos.x += 300;
pos.y += 300;


居然會改變原始物件的座標值。這種情形也會發生在滑鼠事件中(座標值不是唯讀的嗎?),例如:

var mouse_pos = e.position;
mouse_pos.x += 300;
mouse_pos.y += 300;

之前寫程式遇到的問題,沒紀錄下來的結果就是浪費了我兩天在找問題,幸好最終還是想起舊經驗,不然程式就難產了!

目前是以土法煉鋼的方法,新增了兩個變數來分別取出 x 和 y 的值再作計算。

第一個例子改為:

var pos_x = sprite.getPosition().x;
var pos_y = sprite.getPosition().y;
pos_x += 300;
pos_y += 300;

第二個例子改為:

var mouse_x = e.position.x;
var mouse_y = e.position.y;
mouse_x += 300;
mouse_y += 300;

或許是自己的操作是有問題的,沒用對方法,先這樣把程式完成,找到更好的方法再更新囉!




2019年2月22日 星期五

Biking : 自行車道

天氣狀況 : 陰雨
TRIP DIST : 20.4
TRIP TIME : 01:05:53
AVG. SPEED : 18.64
MAX. SPEED : 32.85

一月下旬,坐在電腦桌前,打了一個噴嚏,舊傷又復發;禍不單行,休養了一陣子,終於稍有起色,坐著的時間可以拉長一點,坐在沙發上吃飯,又來一個噴嚏,這樣嚴重了。因為就快過農曆年,很識趣地去看了中醫,做了這輩子第一次的針灸。其後,每天很安份的熱敷,並且忍痛復健。一個月過去了,生活總算稍微正常了。

可以搬車下樓了,晚上把好久沒騎的登山車搬下樓,沿著自行車道,小騎了一下,伸展伸展。沒帶雨衣,幸好回程只小著小雨。


2019年2月13日 星期三

DIY : 滑順骨溜的電子白板筆

教室的前面裝上大尺寸的觸控電視,取代了原來的單槍投影機和虛擬電子白板。昨天被孩子們笑說手太髒,在螢幕上留下了指痕。手指碰了黑板後使用觸控螢幕,留下痕跡是一定的;另外,使用手指寫字,感覺還是沒有拿筆習慣。為了解決前述的問題,原本是拿了一支免削鉛筆的尾端當畫筆,因為這種筆的尾端是有一點軟的塑膠,在電視的螢幕上書寫時,除了比較不會有聲音外,書寫時的手感也比硬頭的筆較好一些。早上想到教具箱中有好幾支早已乾掉沒水的白板筆,將它們拿來改裝成觸控螢幕的筆,滿不錯的。



這支筆想要擁有的特性是:

  • 筆頭有一點點彈性,書寫時比較不會有聲音。
  • 書寫滑順。


基於上述的需要,準備的材料如下:

  • 白板筆或是其它筆也可以。(筆身)
  • 泡棉。(軟而有彈性)
  • 鐵氟龍膠帶。(滑順)



製作的程序大概如下

1.先將白板筆的筆頭和筆芯拿掉



2.將泡棉由外面塞入筆頭的孔中。


3.將鐵氟龍膠帶貼在泡棉的外面。


泡棉我使用的是「巧拼貼」的一小塊,它軟但又有彈性。泡棉的磨擦力比較大的問題則是利用鐵氟龍膠帶骨溜的特性來彌補。



這樣的一支筆花不到五分鐘就可以完成,在大電視上書寫,不但骨溜流暢,手感滿好的。有興趣可以自己找材料試試。



2019年1月19日 星期六

Android : 找回 Facebook 貼文的「草稿」

昨晚有人在 Facebook 上貼文求救,想找回消失不見的「草稿」。平常沒有使用 Facebook App 的習慣,看到那則才知道,原來如果在行動載具上使用 Facebook App,文章打一半是可以暫時存成「草稿」的,而且最多可以暫存三天。幫忙找了一下救回草稿的方法,為了測試,就在 Android 手機上以 Facebook App 登入。

整理一下測試結果,有兩個重點一定要先具備:
  • 使用 Android 的 Facebook App 。
  • Facebook 的「通知」一定要「開啟」。

底下的截圖是 Sumsung S8 的畫面,其它品牌的可能有部份不同。

1.進入 Android 系統設定,開啟 Facebook 的通知


2.新增一個測試的貼文後,按「返回鍵」


3.出現「儲存為草稿」的選項,按一下該項。


4.我的手機按完「儲存為草稿」以後,畫面會暫時變黑一下下,然後 Android 系統會發出通知;將通知的頁面拉下來後,就可以出現類似底下的「你的草稿已儲存」的畫面。點一下該項目,即可開啟草稿的清單。


5.由草稿清單中點選要繼續編輯的草稿


6.繼續編輯


上面的程序是 Android 的 Facebook App 中測試的,在 iPad 上可以看到草稿儲存的訊息,但卻無法叫出清單,等我有進一步的發現再記錄囉!


2018年12月31日 星期一

JavaScript : URIError: URI malformed

Facebook 的備份檔如果選用 JSON 格式來備份,所有的文字都會以 Unicode 編碼,例如:

"content": "\u00e9\u0099\u008d\u00e9\u009b\u00a8\u00e6\u00a9\u009f\u00e7\u008e\u008790%"

這個如果變成 JavaScript 的變數,內容會被直接解碼,不過,中文字通通變成亂碼。後來想到一招,將每個「\u00」通通換成「%」,就可以利用 JavaScript 的「decodeURIComponent」來解碼了。所以將上面的內容先變成:

"content":
"%e9%99%8d%e9%9b%a8%e6%a9%9f%e7%8e%8790%"

不過,大部份的內容都可以順利的轉換為中文字,遇到上面舉的例子就會出現底下的錯誤的訊息:

URIError: URI malformed

基本上,如果有 decodeURIComponent 不能解碼的字,就會出現前述的錯誤訊息。追了一下「%」編碼的東西,最後面多了一個「%」,導致解碼失敗。因為 URI 的編碼,每個字的前面會加上百分比的符號「%」,照說,如果要用「%」,應該要用「%25」才對,如果沒有將 % 編碼,在解碼時就會出現錯誤。

如果不想改原始內容怎麼辦?駝鳥心態的話,當然是直接跳過不解碼了。哈!我可不想當駝鳥,於是遇到錯誤時,就將錯誤修正過來再重新解碼即可!

底下的程式碼先試著解碼,如果有錯的話,就以 % 將字串分解,然後一一檢查後重組。判斷 % 到底是編碼時的前導符號,還是只是 % 這個字的關鍵如下:

  • 編碼後, % 後面接的字串字數必須為二的倍數

所以如果 % 後面接的字串字數不是二的倍數,甚至是空的,表示它只是一般的百分比符號而已,必須將它變成編碼後的「%25」。

底下是以土法煉鋼的方式寫的程式,先記錄下來 ,以後再進化:


uriDecode = function (str) {
var out = '';
try {
var out = decodeURIComponent(str);
} catch(e) {
if( typeof(console) != 'undefined' ) {
console.log(str);
console.log(e); }
var p = str.split('%'); //以 % 將字串分解
var len = p.length; var out = '';
for(var i=0; i<len; i++) {
//如果是分解完的內容是空的,表示為第一個或是 %
//檢查下一個是否為兩個字元, 如果不是, 表示為%
if(p[i] == '') { if(i<len-1) {
//如果下一個為空的或是字元數不是二的倍數, 則應為%
if( p[i+1]=='' || p[i+1].length % 2 == 1) {
p[i] = '%25'; //轉為 % 的編碼
}
} else { //出現在最後, 一定為 %
p[i] = '%25'; //轉為 % 的編碼
}
out += p[i];
} else {
//如果長度為二的倍數, 就在前面加上 %
if(p[i].length % 2 == 0) {
out += '%'
}
out += p[i];
}
}
try {
out = decodeURIComponent(out);
} catch(e) {
if( typeof(console) != 'undefined' ) {
console.log(str);
console.log(e); } } }
return out; }






2018年12月28日 星期五

PowerPoint : 匯出 pptx 中的聲音檔

想將朋友 PowerPoint (.pptx) 檔案中的聲音檔匯出,查了一下,原來 .pptx 檔案是一個壓縮檔,只要解壓縮就可以取得裡頭的聲音檔了。

如果系統中有安裝類似 7-zip 的工具,只要對準要解壓縮的 .pptx 檔案,按一下滑鼠的右鍵,按照下圖的步驟,選取「解壓縮至...」即可。


再以檔案總管開啟解壓縮後的目錄,依序找到「ppt」→「media」的資料夾,就可以看到想要的聲音檔囉!




 
© 2009. Design by Pocket