2024年6月17日 星期一

JavaScript : 增加 SVG path 的觸控感應範圍

2024年6月17日 星期一

最近很認真的研究 SVG 的圖形,原本是在做筆順練習改版的前置作業,結果寫了以 SVG 圖形元件為主的小遊戲 HTML5 FUN PARTDLE。初版的 PARTDLE 遇到在平板上拖曳 SVG path 繪製的國字部件,有時移動會很遲鈍的情形,也可以說是點不到部件,尤其是面積較小的部件;另外像「口」這種「中空」的形狀,有的人習慣按著中央後開始拖曳,但是因為中央空白的部分並是不屬於「口」的 path,當然就「感應」不到了。

「太小」和「中空」的 SVG 圖形元件,怎麼增加它們的「感應區」呢?

一開始我是以國字的「部件」為單位,依部件的大小,在底層加一個填滿且透明的矩形,這樣子,隨便點,部件都會被感應到。不過,針對部件大小來加入的矩形背景也有新的問題產生,像「返」的「辶」部,看起來是在字的左下角而已,如果取部件的矩形範圍,幾乎是整個字的大小了,如果又剛好是先被點選了,它右上角的小部件,可能因位於下層,就會被遮掩而無法點選。又比如像「回」,中央的小部件「口」,也可能被大的遮蔽。

[圖1] 是將字以「部件」為單位,加上「矩形」背景的結果(暫時以黃色顯示):

[圖1] 在部件底層加上矩形背景

為了減少範圍較大的 path 去遮蔽較小的,第一個想法是不要使用矩形來畫背景,改用橢圓形;另外,PARTDLE 的字是用教育部筆順網的筆順資料,圖形的基本單位是「筆畫」,所以如果加背景時,改以「筆畫」為基本單位,這樣子,像「辶」部,只會在字的左下角加上背景,對字右上方其它部位的影響,就會減到最小。

[圖2] 是將字以「筆畫」為單位,加上「橢圓」背景的結果(暫時以黃色顯示):

[圖2] 在筆畫的底層加上橢圓形背景

如果看靜態的圖片及前述的說明不太能理解,底下利用「啊」這個字的實際操作來體會一下差異。測試時可以試著在「口」的中央拖曳看看,加了背景,再試著拖曳看看。

請按底下四個功能鈕的其中一個,它會先載入 SVG 圖檔,接著就可以拖曳「啊」的各部件來測試感應區到底有什麼差別。


[請選擇上方按鈕載入字]

相關連結

2024年6月15日 星期六

HTML5 : 112-2 國語 PARTDLE 遊戲集

2024年6月15日 星期六

底下是以112學年度下學期國小國語生詞當題庫的的 HTML5 FUN PARTDLE 遊戲。設定「版本」或「年級」會出現遊戲的網址跟 QR code,按一下網址可以將網址複製到剪貼簿中,按一下 QR code 的圖案則可以在新的分頁進入遊戲。

PARTDLE 支援多點觸控,如果在平板或是有觸控功能的螢幕上操作,可以多人合作,一起破關哦!比較一下,或許可以「雙手互搏」,具有「左右開弓」的能人異士,答題的速度會飛快。

底下是選單的截圖:


如果想設計自己的 PARTDLE 遊戲,可以參考遊戲主頁「雄:HTML5 FUN : PARTDLE 兜一兜」中的說明。

題庫檔案

本頁面中的 PARTDLE 題庫內容集中在一個 Google 試算表中,可以按底下的連結看原始內容:

相關連結


2024年6月13日 星期四

HTML5 FUN : PARTDLE 兜一兜

2024年6月13日 星期四

HTML5 FUN 的筆順練習使用了教育部「國字標準字體筆順學習網」的筆順資料,再結合 HTML5 FUN 的部件設定,除了可以練習筆順,也能利用部件來增進學習的效能。2024年5月參照數位發展部的「CNS11643中文標準交換碼全字庫」,重新整理了 HTML5 FUN 的部件設定,除了讓部件規準化,也發展了一個新的遊戲叫「PARTDLE」,它載入語詞的題庫以後,會將字拆解為部件,玩家的目標就是在看到被打亂的部件以後,將它們「兜一兜」,還原為原來的詞語,送出答案核對,答對了,就繼續下一題。除了使用拖曳的方式來進行字詞的「兜一兜」,也可以使用「語音輸入」的方式來說出答案。答對了,也提供教部雲教育百科詞條查詢的連結,方便查看詞語的用法。

開始玩

想要來玩玩 HTML5 FUN 的「PARTDLE」,就按一下底下的紅色啟動鈕。


試試其它題目

使用自訂的題庫

PARTDLE 除了玩預設的題庫,也可以使用自己 Google 試算表中的題庫。方法如下:

  1. 在 Google 試算表的第一個工作表的「A」欄中,依序往下方填入題目,一格一個中文詞語。
  2. 將試算表「共用」為「任何知道這個連結的網際網路使用者都能查看」。
  3. 複製共用網址。

完成前面三個準備程序,接下來,將複製好的試算表共用網址,貼到底下的遊戲網址產生器,即可測試自訂的題庫。

參數設定

雲端硬碟試算表 [共用連結網址]
【送出參數並製作網址】

回報問題

由於部件設定有六千多筆,都是我一個個手動輸入的,難免有錯誤,所以如果遇到「兜一兜」完,明明答案應該是對的,卻無法過關,請在本篇文章最下方的留言區輸入無法過關的語詞,有空我再來檢查是哪裡有問題。

版權聲明

本程式所使用的筆順資料來自:

語詞查詢連結連至:

相關連結

更新記錄

  • 2024.06.16 載入筆順時,以打亂順序的方式載入,避免可以由出現的順序看出答案;題目先以語詞部件總數排序,部件少的先玩,跨越不同總數時,出現升級的訊息,並加1000點。
  • 2024.06.15 如果下載不到筆順資料的字,會以全字一個部件的方式呈現。
  • 2024.06.14 在部件中心加一個幾乎全透明的小圓當背景,提高被按到的機率;支援多點觸控,可以多人一起玩;自訂題庫提供資料預覽畫面,可以選取想要使用的欄位。
  • 2024.06.13 發布。


2024年6月11日 星期二

JavaScript : 讀取 Google 試算表的內容

2024年6月11日 星期二

這幾天在寫一個新的遊戲,自訂的題庫打算直接讀取 Google 試算表的內容,所以重新研究了一下如何利用 JavaScript 擷取試算表的資料。時間投入了,又有新的發現,去年玩「雄: HTML5 FUN:自製遊戲啟動器」用過的東西,感覺好像忘了不少,還是把一些關鍵的東西記錄一下比較保險。

擷取試算表資料的網址

Google 試算表只要在共用的設定中,將「一般存取權」中,「知道連結的任何人」設為「檢視者」,就會變成「任何知道這個連結的網際網路使用者都能查看」的權限。以此共用方式,我們就可以利用 JavaScript 來擷取試算表中的某一個工作表。用類似這樣的網址來擷取:

https://docs.google.com/spreadsheets/d/{SPREADSHEET_ID}/gviz/tq?tqx=out:json&tq

網址中,{SPREADSHEET_ID} 是 Google 試算表的 ID,例如我月來當成語題庫的試算表,它的共用網址為:

https://docs.google.com/spreadsheets/d/1kBueULlojPOH9E3EZYEUcUAv1HfJm_wULQT1hT2m1nM/edit?usp=sharing

{SPREADSHEET_ID} 就是在「/d/」和「/edit?」之間那一長串藍色粗體的字串。

想要直接擷取試算表的資料,就組合為這樣的網址:

https://docs.google.com/spreadsheets/d/1kBueULlojPOH9E3EZYEUcUAv1HfJm_wULQT1hT2m1nM/gviz/tq?tqx=out:json&tq

試算表擷取的資料分析

如果我們以前面所述的網址,可以下載試算表中最左側,第一個工作表的資料。

內容會類似這樣:

/*O_o*/
google.visualization.Query.setResponse({..........});

觀察一下,第一行的 /*O_o*/ ,看起來是不是就是 JavaScript 的註解;

而 google.visualization.Query.setResponse( ) ,是不是就是一個 function ?

我們要的資料是在小括號中的 {..........}

是的!它是 JSON 的格式,也就是試算表的資料會被當成一個參數,傳給名為 google.visualization.Query.setResponse 的 function。

以字串處理來取得資料

如果我們是用檔案下載的方式來取得,回傳的會是字串(假設給了 data 變數),這樣子我們可以用這樣的指令來取得文字內容:

data.substring(47, data.length - 2)

從第 47 的大括號「{」,取子字串到倒數第 3 字的大括號「}」,因為結果是文字型態,所以還得再利用 JSON.parse 來將字串型轉換為物件,整合起來的指令為:

JSON.parse( data.substring(47, data.length - 2) )

以 JSONP 來取得資料

前面分析過,我們擷取的資料,其實內容就是 JavaScript 的指令,如果將內容指定給一個網頁面頁中的 script ,接收完資料,瀏覽器會呼叫「google.visualization.Query.setResponse」,並將試算表的資料傳給它,因此我是利用 JSONP 呼叫 callback 來取得並處理資料,先寫了個 callback ,再利用下面幾行來讓它們建立關係:

if(typeof(google)=='undefined') google = {};
if(typeof(google['visualization'])=='undefined') google['visualization'] = {};
if(typeof(google['visualization']['Query'])=='undefined') google['visualization']['Query'] = {};
google['visualization']['Query']['setResponse'] = callback;

牛刀小試

底下來做個小實驗,可以在 Google 雲端硬碟中建立一個試算表,輸入一些資料以後,將該檔案設定為「任何知道這個連結的網際網路使用者都能查看」的共用方式,並複製連結,貼到底下的輸入區後按「擷取」鈕,看看能不能抓到資料。


試算表共用網址:

轉換後的網址:

試算表的資料:    縮排:

 

擷取指定的工作表

一個試算表中,可以有多個工作表,如果照前面格式,只修改了 {SPREADSHEET_ID} 而成的網址,它是抓取試算表最左側的工作表,想要擷取別的工作表,就需要利用 sheet 或是 gid 的參數來指定。這兩個參數如果同時放到網址中,我的操作經驗,是看哪一個放在前面,就優先使用該參數。

使用 sheet 參數來擷取指定名稱 {SHEET_NAME} 的工作表資料:

https://docs.google.com/spreadsheets/d/{SPREADSHEET_ID}/gviz/tq?tqx=out:json&tq&sheet={SHEET_NAME}

例如我前面的成語題庫試算表有「工作表2」,我可以用這樣的網址來擷取資料:

https://docs.google.com/spreadsheets/d/1kBueULlojPOH9E3EZYEUcUAv1HfJm_wULQT1hT2m1nM/gviz/tq?tqx=out:json&tq&sheet=工作表2

使用 gid 參數來擷取指定代號{SHEET_ID} 的工作表資料:

https://docs.google.com/spreadsheets/d/{SPREADSHEET_ID}/gviz/tq?tqx=out:json&tq&gid={SHEET_ID}

例如當我在編輯前面成語題庫試算表「工作表2」時,它的網址是:

https://docs.google.com/spreadsheets/d/1kBueULlojPOH9E3EZYEUcUAv1HfJm_wULQT1hT2m1nM/edit#gid=543013940

網址中有 gid=543013940 ,所以 543013940 就是{SHEET_ID},我可以用這樣的網址來指定要擷取資料的工作表:

https://docs.google.com/spreadsheets/d/1kBueULlojPOH9E3EZYEUcUAv1HfJm_wULQT1hT2m1nM/gviz/tq?tqx=out:json&tq&gid=543013940

以這個試算表來說,因為「sheet=工作表2」和「gid=543013940」指的都是同一個工作表,所以抓到的資料都是一樣的。

擷取 SQL 指令篩選過的資料

在前面擷取資料的網址中,是不是都有一個參數「tq」?

如果想要先以 SQL 語篩選試算表中的資料,再擷取,可以用 tq={SQL} 指令的方式來進行篩選。預設不指定。例如我只想找出 B 欄成語有「一」這個字的,可以用這樣的語法:

select * where B like '%一%'

例如我只想要回傳 C 欄的資料就好,就用這樣的語法:

select B

例如只想回傳前 10 筆資料,就用這樣的語法:

select * limit 10

此部份有興趣可以再研究。

手動設定「標題」有幾列

在 Google 試算表回傳的資料中有一個 parsedNumHeaders ,它會指出有多少列資料是「標題」,資料會被放到 cols 各欄的 label 中,而 rows 中就少了那幾列。

我們可以在請求資料的網址中利用 headers 的參數來設定有多少資料要當「標題」。

所以如果有使用 gid 參數,又使用 headers 參數,就會變成類似這樣網址:

https://docs.google.com/spreadsheets/d/{SPREADSHEET_ID}/gviz/tq?tqx=out:json&tq&gid={SHEET_ID}&header={HEADERS} 

 上面的 {HEADERS} 置換成想設定的數字即可。

進階測試版


試算表共用網址:

工作表名稱:

工作表識別碼(gid):

SQL:

轉換後的網址:

試算表的資料:    縮排:

 

資料內容

試算表傳回的資料,幾個重要的項目:

  • status : 如果內容是 "ok",表示有擷取到資料。
  • table
    • cols
      • id : 欄位的代碼 A、B、C...。
      • label : 如果 parsedNumHeaders = 1,內容就是最上面一列的內容;如果 parsedNumHeaders = 0,label 會是空字串。
    • rows : 一列列儲存格的資料,要注意 parsedNumHeaders 的值,不然會有少幾列的情形。
    • parsedNumHeaders : 是否有取某幾列當欄位的「標題」,當它不等於 0 時,rows 會少那幾列內容,因為拿去放到 cols 中了,這一部份要注意。

相關連結


2024年6月7日 星期五

Hiking : 樟山寺

2024年6月7日 星期五

前天去走樟山寺,雖然打著傘,鞋襪還是溼了;今天鞋乾了,雨也停了,特訓一下,不綁髕骨帶,再走一回樟山寺。

幾天前在市場遇到同事,說到她不敢一個人去爬山,我倒是滿享受一個人去騎車或是爬山的。和朋友騎車或是爬山,人多熱鬧;一人獨行則多了幾分自在與逍遙。再說,我的成長背景中,有一大段時間沒有大人在身邊,為了照顧弟弟、妹妹,經常要一個人獨斷獨決,早就習慣一個人了。一個人並不見得就代表孤獨,山林裡,有大自然為伍,何來的孤獨?哈!再說,又不是就這樣子一輩子遠離塵囂了。


前天遠方的 101 大樓完全被水氣給遮掩了,今天又出現了。一隻鳯蝶就停在我面前,不但讓我看個夠,連拿出手機對著牠,也毫不驚慌。

週間又近午的飛龍步道幾乎沒人,這樣走起來才輕鬆,才能舒壓,跟假日像市場,兩樣情!


2024年6月6日 星期四

Android : 清理 USB 插槽

2024年6月6日 星期四

最近手機插上 USB 線,準備充電時,常常會跳出「檢查充電器連線」的訊息:

因為我的手機保護殼完全不會擋到 USB 端子,所以一開始,遇到這個訊息,就是將 USB 線重拔插,一次不行,就再試一次。不過警示出現的頻率愈來愈高,就耐著性子,好好地端詳一番,看看這 USB 插槽倒底是怎麼一會事。

平時出門時,我的手機都是切換到靜音模式,然後放到褲子的口袋中。口袋中難免會有棉絮,如果有衛生紙,那就更精彩了。

摘了眼鏡,仔細一看,原來我的手機 USB 插槽底部卡著一層扎扎實實的白色棉絮,在猜想,原來可能只有一點點纖維在插槽周圍,每次插 USB 端子,就被帶往底部,並壓實,久了、變厚了,厚到讓 USB 端子無法壓到底,變成接觸不良。

以不會導電的牙線棒尖端,一點一點的慢慢挑,總算把棉絮都清完了。看來以後每隔一段時間,就要清理一下才行。不然,幫它設計一個塞子好了!


2024年6月2日 星期日

JavaScript : 取得網頁的原始碼

2024年6月2日 星期日

 HTML5 FUN 烘焙機輸出語法時,可以選擇將遊戲的啟動網頁,以 Base64 編碼後,當成 iframe src 屬性的內容(勾選「是否要嵌入 ClassroomScreen」),這樣子,iframe 會自動將編碼過的資料解碼後載入,好處是不用先為設計好的 HTML5 FUN 遊戲找網路空間存放,程序上會簡便些。不過,上週發現除了 HTML5 FUN speaking 因為需要使用麥克風被瀏覽器的安全管制限制,無法用以這個方式在 iframe 中使用以外,另一個 HTML5 English1200 則是需要使用到瀏覽器的 localStorage 來暫存資料,因為安全管制無法使用 localStorage,在瀏覽器的控制台中會看到類似這樣的錯誤訊息:

Uncaught DOMException: Failed to read the 'localStorage' property from 'Window': Storage is disabled inside 'data:' URLs.

為了確認是不是 HTML5 FUN 程式的問題,我想用一個簡單的網頁來測試,網頁寫好了,怎樣將文字以 Base64 編碼我會,但是怎麼樣可以利用 JavaScript 來取得網頁的原始碼呢?

找了一下,原來可以用這樣的指令,取得目前頁面的原始碼:

new XMLSerializer().serializeToString(document)

不過,大概會遇到另一個被「過度」編碼的情形,一些特殊符號,像大於( > ) ,會變成「&gt;」,小於( < ) ,會變成「&lt;」。

怎麼辦呢?

我在 HTML5 FUN 烘焙機中是使用 textarea 來將編碼過的特殊符號解碼,使用這個自訂函數:

decodeHTML = function(html) {
  var txt = document.createElement("textarea");
  txt.innerHTML = html;
  return txt.value;
};

好的,那以 base64 編碼過的頁面,能不能在 iframe 中使用 localStorage 呢?看來,可能因為安全問題,無解了!

相關連結




2024年5月27日 星期一

HTML5 FUN : 筆順練習修正有問題的筆順資料

2024年5月27日 星期一

 早上在「雄: 教育部筆順學習網這些字的過關秘訣」中提到教育部筆順網有部份的字,筆順資料有問題,導致筆順動畫的演示不正確,練習時過不了關。在想,為了讓 HTML5 FUN 的筆順練習可以正常使用,到底是通知承辦人,修改筆順資料,還是我自己修改 HTML5 FUN 筆順練習程式就好?看到教育部筆順網的聯絡是使用「部長信箱」就懶得聯絡了,還是我自己改程式比較快。

在 HTML5 FUN 筆順練習的資料處理程序中,加了「懂贐榻叫啊瞞慰」幾個字的特別處理,也沒幾行,輕輕鬆鬆解決問題。看一下處理完的截圖:


工具是自己開發的,就是有這樣的好處,快狠準~效率高多了!

可以按一下 HTML5 FUN 筆順練習的連結測試:

相關文章:


教育部筆順學習網這些字的過關秘訣

上一篇「雄:HTML5 筆順練習部件更新為 CNS 的設定」中提到將 HTML5 FUN 筆順練習的部件改使用數位發展部「CNS11643中文標準交換碼全字庫」(簡稱全字庫)中的部件設定,教育部國字標準字體筆順學習網(簡稱筆順網)目前的 6063 個字,花了我三天的時間一個字、一個字的比對與輸入。過程中,接續我 2022年底這篇舊文「雄:教育部筆順學習網部份字的小臭蟲」,又發現了「筆順網」中,練習時,正常寫會過不了關的字,綜計有以下幾個字,清單附上連結,哈!可以試著按連結後寫寫看:

前兩個字主要是筆順網的筆順資料中,筆畫資料多出來了,第三個字「贐」則是有兩個筆畫資料錯置了,後兩個字則是筆順程序有問題。

我將它們以 SVG 動畫的方式呈現,除了「懂」是因為筆畫16畫,卻有32畫的資料,一個字要寫兩遍,而動畫本就循環播放,所以無法由動畫觀察出問題(可以由筆順的序號看出來);其它的字,仔細看動畫或是部件顏色,應該可以知道問題點。下面是這幾個字的 SVG 圖片:

[圖1] 注意筆順序號到 32

[圖2] 注意黃色的部份有 14、15兩個數字

[圖3] 注意黃色及綠色的部份

[圖4] 注意藍色的部件

[圖5] 注意黃色的部件

印象中,之前在測試筆順編輯器時,還有遇過其它的字,如果再有發現,再補上。

  • 「瞞」16畫,但多了第17筆資料,第15畫要再重寫一次才能過關。
  • 「慰」15畫,但卻有18筆資料,「心」部前三畫要重寫一次,才能過關。

相關文章:



2024年5月21日 星期二

HTML5 筆順練習部件更新為 CNS 的設定

2024年5月21日 星期二

HTML5 FUN 筆順練習的部件是讀取我於 2019 年手動建置的設定,再將字的各筆畫所屬部件,以不同的顏色呈現出來。當初在分割字的部件,純依個人上課會用到的來設定,剛開始很認真的分部件,後來因為有六千多個字要設定,手實在太酸了,只能大概的切分字的部件。

前幾天注意到數位發展部的「全字庫」(https://www.cns11643.gov.tw/)中,其實也有定義各字的部件,而且可以下載目前所有字的部件設定匯出的檔案(在全字庫的「技術支援」「全字庫授權」的頁面中有連結)。它是以 CNS 的代碼來當關鍵欄位查詢,不過,要用 Unicode 來查的話,對照表建立一下,也不是難事。

於是這幾天,啟動了一個 HTML5 FUN 筆順練習部件設定的更新計畫,目前給自己定的進度是每天至少將五百個字的部件設定,對照 CNS 的部件設定,更新到 HTML5 FUN 筆順練習中。哈!總共 6063 個國字,目前已經完成 2700 個字了,手還滿酸的!(註:  2024/05/23 全部完成)

我寫了一個小工具,讓它能呈現現在 HTML5 FUN 的部件、筆順序號,跟 CNS 的部件設定:

[圖1] 部件瀏覽工具

[圖2] 已經修正為 CNS 部件的字

多色的字是目前 HTML5 FUN 筆順練習的部件,而下方黑色的,則是 CNS 定定義的部件。找到有不同的(其實大部份都要改),就將部件的筆順序號重新輸入到設定檔中。

在這個過程中,目前發現教育部筆順網的筆順資料有臭蟲,順便記錄一下:

  • 「懂」的筆畫是 16 畫,但是筆順資料卻是 32 筆;所以無論在教育部的筆順練習,或是 HTML5 FUN 的筆順練習,都要全字寫完一次,還要再寫一次,才能得到答對的回饋。
  • 「榻」的筆畫是 14 畫,但是筆順資料卻是 15 筆,最後一畫多了一筆,最後一筆畫要再寫一次才能過關。
  • 「贐」的第13畫和第12畫的筆順順序顛倒。
  • 「瞞」16畫,但多了第17筆資料,第15畫要再重寫一次才能過關。
  • 「慰」15畫,但卻有18筆資料,「心」部前三畫要重寫一次,才能過關。

而全字庫的部件設定也有部份問題:

  • 慮(多一個心)  372,301,257
  • 搽(多一個一) 220,204,45,186
  • 擄(少一個田) 220,372,301,83
  • 晝(少一個一) 411,209,1
  • 礬(多一個石) 185,54,54,185,105,291
  • 籲(少一個一) 391,47,1,119,119,119,313,1,5,434
  • 颱(多一個石) 60,1,375,88,119
  • 玁(少一個厂) 194,119,119,22,448,221
  • 瞼(調整順序) 298,45,1,119,119,47,47
  • 龢(少一個口) 47,1,119,119,119,313,325

在拆解部件的過程中,又想起之前遇到的一個問題,部件的筆畫,不見得是筆順的順序,如果在練習筆順時,標上部件的顏色,是否反而會誤導學生,增加筆順習寫時的困擾?或許應該要再修改一下部件顏色標註的時機。

相關文章


2024年5月19日 星期日

JavaScript : 偵測圖片的範圍

2024年5月19日 星期日

 前幾天看到一篇「Detecting content boundaries on a HTML5 canvas」:

作者介紹如何在 canvas 中找出有畫圖部份的矩形範圍,基本原理就是取出 canvas 的所有點的資料,然後由上往下,一次取一橫排,一個點、一個點的檢查是否有著色,直到最行一橫排檢查完為止。這部份以前有寫過類似的程式碼,此篇中,多學了幾個有關 JavaScript 陣列的操作:

  • Array.from()
  • Array.prototype.filter()
  • Array.prototype.some()

當我們使用 canvas Context 的 getImageData 取得圖形的資料時,預設每一個點會使用到四筆資料,作者是先使用 filter() ,各點每四筆資料為一個元素,變成新的陣列 pixels。

imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
let counter = 0;
const pixels = Array.from(imgData.data).filter(() => {
  if (counter === 3) {
    counter = 0;
    return true;
  }
  counter++;
  return false;
});

在猜想,可能是為了簡化後面的程序才這樣操作,不過,這樣一來,照說會浪費掉一些運算的時間(稍候再來證明)。

使用「some()」倒是以前沒有注意到的方法,

const row = pixels.slice(y, y + canvas.width);
if (row.some(pixel => pixel > 0)) {
  ......
  ......
}

它是陣列中的元素只要任何一個符合條件,就回傳 true,用來檢查一個點的四筆資料是否有畫上東西(大於 0 ),滿合用的。

我將作者用 filter 取圖點資料的部份去掉,來做一下效率上的比較。在底下的 canvas 上按一下,程式會載入 HTML5 FUN 的小圖示,並畫在亂數取的位置,然後再找出它的邊界。依序使用原作者的演算法,跟改直接由 while 迴圈內取圖形資料,及改直接由 for 迴圈內取圖形資料,看看這三種方法,各使用了多少毫秒。

按這裡開始測試



2024年5月17日 星期五

SVG : path 加不加 Z 差很多

2024年5月17日 星期五

為了解決筆順練習的工具可以在 iOS 中一次下載多個部件或筆畫,認真的研究了一下 SVG 圖形的語法,哈!新手上路,總是有些地方混沌不清(根本亂玩)。不過,遇到問題總是件好事,由一個點出發,挖出了不少盲點與有用的資訊。

筆順練習的筆順資料,基上會使用到的就是 SVG 中的幾個 path 的指令:

  • M : MoveTo
  • L : LineTo
  • Q : QuadTo
  • C : CubicTo

原本以為找出對應關係,就這樣簡單,頂多是加上,部件是由多個筆畫的 path 組合起來這個程序,就能完成一個 SVG 檔案中,放入多個部件的 path,使用者,要運用時,再將部件利用軟體中的「拆解」或「分解」功能來拆開就好。而前兩天在 iOS 中使用 Keynote、Pages 或是 Numbers 也是確實如此。不過,來看看 [圖1] ,當我將我的筆順練習工具製作好的 SVG 檔,插入到 LibreOffice Impress 中時,卻有不同的結果:

[圖1] LibreOffice Impress

「雄」這個字的第一個部件,第一筆畫和第二筆畫交叉處,[圖1] 藍色箭頭和綠色箭頭是不是長得不一樣?

在 SVG 的 path 指令中有一個「Z」可以用來標示 path 結束了,[圖1] 左側的字就是沒有加上「Z」的結果,而右側的字,在每一個筆畫的指令字串最後面加上「Z」以,就正常顯示了。

這讓我想起,在使用 HTML5 的 canvas 繪圖指令時,有沒有加上 closePath 也是有差的,當初好像也卡了好久才發覺。

關於 SVG path 還可以使用哪些指令,可以參考 Mozilla 的這個線上說明:

目前筆順練習工具可以在「部件拆解」的頁面中,下載全字的部件或是筆畫 SVG 格式圖片

[圖2] 筆順練習下載 SVG 圖檔的選單

在 LibreOffice 或 Apple 的 Office 工具中,「部件」的話,一個字可以分解到以部件為基本單位,而「筆畫」則可以拆解到以筆畫為基本單位,依自己的需求下載不同的 SVG 圖檔。

相關文章


2024年5月15日 星期三

JavaScript : 計算中文字的字數與取Unicode

2024年5月15日 星期三

在檢查我的筆順練習工具是否有抓到教育部「國字標準字體學習網」的全部筆順資料時(根據網站說明,目前收錄 6030個國字,但清單中有 6063個),比對完,發現我少以下幾個字(括號內為 Unicode):

  • 嫏(5ACF)、广(5E7F)、桕(6855)、鯝(9BDD)、𥑮(2546E)、硓(7853)

然後這六個字的音檔都改用 .m4a 的格式了,所以分析音檔路徑的程式也得修改。

另外,在教育部國字標準字體學習網的清單檔案(PDF)中,序號1266的字(Unicode 5F5D),筆順資料庫中目前並沒有,只能找到一個跟它同音的字(Unicode 5F5E)

查了一下全字庫的資料,不細看,實在分不太出來:

兩個字的注音都是「ㄧˊ」。

在新增的六個字裡,「𥑮」的 Unicode 是「2546E」,這讓我發現自己用了十幾年的程式有個大臭蟲。

原來我在 JavaScript 中,用來計算多少個字,都是直接用 .length 來取得,例如:

var words = '這是新的字𥑮';
console.log(words.length);

照以前的經驗,應該會顯示結果為「6」;今天遇到「𥑮」就變成「7」了!

原來「𥑮」這個字是 UTF-32 的,而 JavaScript 是 UTF-16 的環境,所以 '𥑮'.length 會是「2」。這下子,我的程式遇到這些 UTF-32 的字就全亂了!

沒關係,總有解決方法的。利用 Array.from 可以將字串的字一個個地切開,並放入陣列中,我只要取陣列有幾個元素就好了,可以利用這個來測試:

var words = '這是新的字𥑮';
console.log( Array.from(words) );

它會顯似這樣的結果:

(6) ['這', '是', '新', '的', '字', '𥑮']

另一個問題是如何取得中文字的 Unicode?

以前我是使用 charCodeAt(0) 來取得 Unicode,像這樣:

'𥑮'.charCodeAt(0).toString(16).toUpperCase()

但是因為「𥑮」是 UTF-32 的,所以得改用 codePointAt(0) 才行:

'𥑮'.codePointAt(0).toString(16).toUpperCase()

哈!有時遇到新的狀況,時間要花很多,但是問題得以解決,更是開心啊!不過,程式改東改西,還得再觀察個幾天再上線的好,不然,掃了使用者的興可不好。

相關文章


2024年5月12日 星期日

Life: the world has moved on and we believe it no longer needs ...

2024年5月12日 星期日

Google 的 Closure 專案發展了十幾年了,看到這則聲明「Closure Library is in Maintenance Mode」:

聲明中有一句:

 In short, the world has moved on and we believe it no longer needs Closure Library.

「the world has moved on and we believe it no longer needs ...」這句型可以套用在很多人、事、物上,時代在進步,隨著時光的推演,沒有什麼是不可取代的;潮起、潮落,後浪推前浪本就是一件很正常的事,許多人老是糾結在哪兒,一直淘醉在過去的光環中,而無法接受現在的自己,只能說是想不開。


2024年5月11日 星期六

Google : 雲端硬碟的 MIME type

2024年5月11日 星期六

之前研究過如何由 Google 雲端硬碟公開分享的資料夾或是檔案連結中,解析出圖片及影音的直接取用網址;這幾天發現用同樣的方法,也可以解析出純文字檔案(例如: .html、.txt、.xml ......)的內容,主要是雲端硬碟判定是純文字檔的,就可以直接預覽內容,而預覽的內容會有一個擷取的網址。這有什麼好處呢?可以不用透過 CORS proxy 就能直接下載內容,不用受制於別旳網站,就是讓人覺得愉悅。

不過,就在開心有新的發現時,也發現很怪的事。像 .gpx 的檔案,內容明明就是 XML 格式的純文字檔,Google 雲端硬碟卻判定它的 MIME type 是「application/gpx+xml」,而不是「text/xml」。在雲端硬碟的檔案清單中就可以看到同樣內容的檔案,只是使用的檔案名稱不同,圖示就不一樣:

觀察一下:

  • 2024-04-30-hiking-gpx.xml 是可預覽的 text/xml 文件。
  • 2024-04-30-hiking.gpx 是無法預覽的 application/gpx+xml 文件。

試著打開檔案 2024-04-30-hiking-gpx.xml  的截圖是:

2024-04-30-hiking.gpx 的截圖是:

實驗了一下,只要在上載之前,將檔案名稱結尾改用 .xml ,上載到雲端硬碟後,它就會被判定為可預覽的純文字檔案了。不過,如果是上載到雲端硬碟以後,在雲端硬碟中改檔案名稱是無效的,可見 MIME type 是在上載時就已儲存在檔案資訊中,並不會因為線上改檔名,就將判定的結果改掉。

當然囉~如果能在不改檔案名稱,就能直接抓出文字內容,就更棒了,繼續研究看看有沒有其它的方法。


 
雄::gsyan © 2009. Design by Pocket