顯示具有 JavaScript 標籤的文章。 顯示所有文章
顯示具有 JavaScript 標籤的文章。 顯示所有文章

2026年4月5日 星期日

玩玩自製的單線體中文字

2026年4月5日 星期日

一直想弄一支可以利用現有字型自動變單線體的小工具,玩了好久,都沒有滿意的;最後還是決定用半自動的方式就好(其實還是手工調整居多)。三月份很專心地玩了兩週「造字」,居然將兩個學期一到三年級的生字及注音符號玩完了。哈!不過後來才發現不少字的筆畫離得太開,又進入調整的地嶽。大概是之前玩太累,就這麼丟著一星期都不碰了,換玩別的東西。先來看看部份成果,有空再慢慢完成四到六年級的生字。

[圖1] 單線體字形編輯器的截圖

每個都只取了關鍵的幾個點,用繪圖指令就能畫出完整的字;正因為這樣,可以做很多變化,變點點字、虛線字、實線字,粗細、顏色......就更不用說了。前置工作很無聊,但成果卻很有趣。至於要應用在哪裡呢?除了筆順練習、生字簿產生器,或許也可以拿來設計成遊戲;邊「造字」邊想吧~才弄完不到兩千個字,離六千多個字還差得遠呢!

底下的設定改一下,字的形態就會改變哦~可以玩玩看。


輸出字形自訂參數


2026年3月13日 星期五

Tools : IVS注音字型文字與國字+注音格式轉換

2026年3月13日 星期五

最近 Google 文件中可以加入自帶注音的字型了,只要按一下字型選單旁的「小三角形」▼ ,再點選「更多字型」,就能新增名稱為 Bpmf Huninn /  Bpmf Iansui /  Bpmf Zihi Kai Std 的注音字型。

▋ 注音字型⇦⇨國字+注音

突然有個想法,這些注音字型必須使用支援 IVS 技術的輸入法來調整多音字的注音,再將文字貼到文書處理軟體(例如:Google文件)中指定使用注音字型;如果反向處理,將 Google 文件有加注音的文字貼到無法顯示注音字型的網頁或遊戲中,注音就看不到了;寫個能將「注音字型」文字和「國字+注音」格式間的轉換小工具,應該滿有意思的。這樣子可以將別人在文件中以注音字型顯示的文字,轉成遊戲用的「國字+注音」,或是倒過來處理,將以前國字加注音的內容,直接轉為「注音字型」的文字格式,貼到文件中。

在「ButTaiwan」的「bpmfvs」專案中找到了製作注音IVS字型的國字注音對照表:

IVS格式了解了,對照表有了,簡易的轉換工具就生出來了,先來玩玩看,改天再來增強以前寫的「語文高手題庫&國字加注音產生器」。

▋ 使用方法

✅ 例如將教育部國語小字典中已加注音的解釋轉貼到 Google 文件:

  • 選擇「國字+注音 ⇨ 注音字型」模式 。
  • 在輸入區貼上國語小字典複製的內容。
  • 按「去掉換行」鈕 (去掉國語小字典國字和注音間多的換行)。
  • 按「開始轉換」鈕
  • 按綠色「 複製輸出區 」鈕
  • 貼到 Google 文件,然後套用注音字型。
✅ 例如幫課文加上注音
  • 選擇「注音字型(或國字) ⇨ 國字+注音」模式。
  • 在輸入區貼上課文。
  • 按「開始轉換」鈕。
  • 校正注音後,按「 更新全部 」鈕。
  • 按紅色「 複製 」鈕。
  • 貼到 Google 文件,然後套用注音字型。
說明:按了「更新全部」鈕後,輸入區的國字會加上看不到的 IVS 注音標註符號,使用注音字型時就會出現注音。

▋ 注音IVS字型與國字+注音轉換工具

選擇轉換模式

校正注音 (紅色為多音字, 可按一下調整)



註:2026.07.22 起有另一個新的工具,可以試試


▋ 相關連結

▋ 更新記錄

  • 2026.03.14 參考教育部簡編本字典新增詞庫。
  • 2026.03.13 初版。


2026年2月21日 星期六

以 js-dos 玩 DOS 時期的 CAI

2026年2月21日 星期六

農曆年前心血來潮,接上舊硬碟,挖出了以前放在電腦教室讓學生玩的 CAI。民國85年剛接電腦教室時,資源有限,找了「臺灣省國民學校教師研習會」設計的一些電腦輔助軟體,有多餘的時間會讓學生玩一下。這些「上古時期」的 CAI,相對於現在的軟體,看似陽春,但有不同的樂趣。讓家裡沒經歷過 DOS 時期的小孩玩了一下,他們覺得還滿好玩的呢!

想在瀏覽器中玩那些上古時期的 CAI,可以使用 js-dos 來載入。js-dos 其實是將另一個開源的 DOSBox 包裝成網頁版,在瀏覽器中操作,提高了親和力,也能在平板上玩。js-dos  GitHub 的網址如下:

js-dos 目前的操作界面沒有提供繁中的語言選項,我懶得由原始碼重編,直接把下載回來的 js-dos 加上了由英文翻譯的繁中,讓 js-dos 偵測到瀏覽器語言為 zh-TW ,就設定使用中文的操作界面與訊息。

▋ 臺灣省國民學校教師研習會CAI

以前的臺灣省國民學校教師研習會製作了不少的 CAI

[圖1] 臺灣省國民學校教師研習會 CAI 截圖

這些 CAI 當初大部份是可以自訂題庫的,可以說是小而美。

[圖2] 瀏覽器中的 CAI 可用虛擬按鍵操作

[圖2] 是 DOS 版 CAI 在網頁瀏覽器裡的截圖,以 js-dos 來玩時,比原始的畫面多了左側欄,跟箭頭所指的「虛擬按鍵」;玩的時候要以電腦鍵盤或是最下方的虛擬按鍵來操作。

▋ CAI 選單

以下為30年前蒐集的部份 CAI,想玩哪一個就按一下縮圖。


▋ 筆記

支援中文的 DOSBox 分支 DOSBox-X 

▋相關連結


2025年12月31日 星期三

DD Petals 花瓣字 : 看部件寫形近字

2025年12月31日 星期三

HTML5 FUN 的 DD Petals 花瓣字,在中文字上的應用,除了可以玩「寫字造詞」,也可以看部件「寫部件湊成字」。「寫部件湊成字」出題跟回答都用部件,寫的部件還需要寫對花瓣(結構),才算答對,無論是出題或回答,難度相對高一點。因此最近新增了一個新的玩法:看部件「寫形近字」;這個遊戲模式,出題跟回答都是用完整的國字,題目以完整的國字來出題,程式會自動解析出當問題的部件,答案要寫出帶有題目指定部件的完整形近字。

▋ 中文部件字型的問題

中文的部件有一些在電腦或是行動載具的系統字型中,是沒有造字的,所以用純文字來顯示時,就會變成一個「方框」或是「方框叉叉」(如圖1) ......;看不出題目是什麼,自然無法回答案了!數位發展部的「全字庫」網站,有一個「字形即時顯示」,可以產生字的「圖片」;所以只要我們的程式能夠偵測出一個字是字型「不支援」的字碼,再利用全字庫「字形即時顯示」產生圖片,取代「方框」的位置,這樣子就可以重現字的樣子了。

例如:「鳥」、「島」和「裊」有共同的部件「󶆵」,哈!是不是看不出什麼字形!它的 Unicode 碼是「F61B5」,可以到「全字庫」「字形即時顯示」中,將字或是字碼,填入表單的「Unicode 碼」欄位,試著讓它即時生出圖片來看,網址如下:


[圖1] 中央的方框叉叉是未正確顯示的部件

▋ 偵測未支援字的方法

怎麼樣偵測字是看不出內容的「方框叉叉」「方框..」呢?以下是一個偵測的小程序:

/**
 * 偵測字型是否支援該字
 * @param {string} ch
 * @param {string} font
 * @return {boolean}
 */
function isGlyphSupported(ch, font) {
  let probe = document.getElementById("fontProbe");
  if (!probe) {
    probe = document.createElement('div');
    probe.id = 'fontProbe';
    probe.setAttribute('style', 'font-size:32px; visibility:hidden; position:absolute;');
 document.body.appendChild(probe);
  }
  probe.style.fontFamily = (font ? font + ', ' : '') + "monospace";
  probe.textContent = ch;
  const w1 = probe.offsetWidth;
  probe.style.fontFamily = "monospace";
  probe.textContent = ch;
  const w2 = probe.offsetWidth;
  return (w1 == w2 && w1 != 0 );
}

當我呼叫「isGlyphSupported("雄", "Arial")」它以比較設定字型 Arial 前後的「offsetWidth」是否一樣 (注意:早期的 iOS 10.3.4 中,不支援的字都會為 0),來判斷該字是否被字型支援。「花瓣字」出部件的題型時,預設會在花的中央放部件的文字,如果「isGlyphSupported」回傳為 false,就將原來的文字隱藏,改載入全字庫的「即時顯示圖片」代替。用圖片有什麼缺點呢?載入時間需牽就全字庫網站,較難控制,而且字看起來有鋸齒是難免的。為了怕載入圖片的時間較長時,使用者以為程式當掉了,所以我加上了轉圈圈的載入動畫。底下 [圖2] 是修正完的結果:

[圖2] 正常顯示部件畫面

▋ 哪裡玩花瓣字「看部件,寫形近字」

DD Petals 花瓣字「看部件,寫形近字」和「寫字造詞」都是以完整國字來出題,題目可以共用,花瓣字的側欄選單中 (按遊戲畫面左上角三條線的圖示可以打開側欄選單),有提供切換遊戲模式的選項及按鈕。所以由「雄筆順」的「花瓣字」選單進入 DD Petals 花瓣字的人,一個遊戲會有兩種模式可以玩。

三大出版社(南一、康軒、翰林)國語課本各冊、各課的生字筆順練習可以按這裡:

▋ 卡關了怎麼瓣

個人的經驗,花瓣字一開始玩,造詞會比組字容易很多,組字明明有的字很常在用的,一時之間反而想不出來。當然,花瓣字當初的設計初衷就是希望能好玩、多寫,所以滿建議用 open book 的概念,可以邊查,邊動腦。部件怎麼查呢?全字庫 (https://www.cns11643.gov.tw/) 是一個很好用的資源,它的「字碼查詢」選單中的「IDS查詢」和「部件查詢」,都可以用部件來找字。

另外,花瓣字的題目如果是部件相關的,可以按上方「關卡:?」,會有小驚喜哦!哈~我是覺得這樣子滿好笑的:「卡關」了,就按「關卡」!

▋ 相關連結




2025年12月19日 星期五

修正 iOS 10.3.4 CSS repeat function 的問題

2025年12月19日 星期五

前兩天在測試新遊戲 HTML5 FUN Make 10 時,發現在 iPad 4 的 iOS 10.3.4 居然不能玩!研究半天,找到讓程式卡住的地方是監聽畫面動態的「ResizeObserver」,iOS 10.3.4 不支援,只好多加幾行,讓它改用傳統監聽「resize」事件的方法。以為從此就可以過著幸福快樂的日子了嗎?

來看看 Make 10 正常的畫面應該像 [圖1] 這樣:

[圖1] 正常排列的 Make 10 按鈕

結果在 iOS 10.3.4 中是 [圖2] 這樣,全部疊成一排:

[圖2] iOS 10.3.4 中全部疊在一起

Make 10 中的數字方塊使用了 grid 來配置按鈕棋盤:

.board-grid {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  grid-template-rows: repeat(6, 1fr);
  gap: 1%;
  width: 100%;
  height: 100%;
}

設計的是 8 x 6 的棋盤,在 iOS 10.3.4 中,8欄有正常分配了,6 排卻疊成一排。本來以為 iOS 10.3.4 不支援 repeat function,但實際用 getComputedStyle 取得 grid-template-rows 的值,它有執行 repeat(6, 1fr) ,只是回傳的值是「0px 0px 0px 0px 0px 0px」,分成六份了,但每一份都是 0px。

原來關鍵在 iOS 10.3.4 的 repeat 在計算寬度時,有正確解讀「width: 100%」,而在計算高度時,「height: 100%」使用了百分比,並沒有被正確解讀。如果換成別的單位,例如「height: 100vh」,就不會疊成一排了。

找到原因了,在 resize 的監聽程序中,加入程式計算高度即可,取得棋盤容器的總高,扣掉不能使用的空間,再將「height: 100%」最後用 px 的單位來設定。

不過,怎麼知道哪一個人用了像 iOS 10.3.4 這樣, repeat 有缺陷的瀏覽器呢?

本來是用前面提到的「0px 0px 0px 0px 0px 0px」,以按鈕的高度為 0 來判斷,但是這樣的方法只能在一進入遊戲時用,如果使用者旋轉了裝置或是讓視窗尺寸有所改變時,就無法再重新自動設定格子大小了。後來發現「gap: 1%;」這個也是個線索,因為 iOS 10.3.4 並不支援「gap」,所以用 getComputedStyle 取得的 gap 會是 undefined ,這個特性剛好可以用判斷是否為 iOS 10.3.4。

來看一下修改過後的畫面:

[圖3] 不使用 % 設定高度後的棋盤

套上修正方法後的 [圖3] ,和原始設計的 [圖1] 比較,可以看到 [圖3] 格子與格子沒有間隔,所以格線看不出來;不過修正過後,至少在 iOS 10.3.4 中可以玩 Make 10 了!

    ▋ 完美修正

    還是不能接受「gap: 1%;」沒被 iOS 10.3.4 處理到,那怎麼辦?

    把 1%  到底是多少算出來,然後將所有格子找出來設定它們的 width 和 height 為 calc(100% - gap) 就可以有 1% 的間隔了,底下是實作的程序:

    function gridRepeatFix() {
      const boardGrid = document.querySelector('.board-grid');
      if (boardGrid) {
        if (typeof getComputedStyle(boardGrid)['gap'] == 'undefined') {
          const style = getComputedStyle(boardGrid.parentElement);
          let height = parseFloat(style.height);
          height -= parseFloat(style.paddingTop);
          height -= parseFloat(style.paddingBottom);
          const gap = Math.floor(height * 0.01);
          boardGrid.style.height = Math.floor(height) + 'px';
          Array.from(boardGrid.children).forEach(cell => {
            cell.style.width = `calc(100% - ${gap}px)`;
            cell.style.height = `calc(100% - ${gap}px)`;
          });
        }
      }
    }

    ▋ 補記 DD Car Map Editor 中用的方法

    DD Car Map Editor 中用了另一個方法,當 CSS 遇到不支援的 gap 時, 利用 margin-left 來補救,例如:

    .main-content {
    display: flex;
    flex: 1;
    overflow: hidden;
    gap: 0.5rem;
    }

    就在後面多加判斷及補救

    @supports not (gap: 0.5rem) {
      .main-content > * + * {
        margin-left: 0.5rem; /* LTR 預設 */
      }
    }

    之前花了好多時間在程式裡除錯,原來只是 CSS 的問題,有了這個經驗,以後應該可以縮短卡關的時間了。

    ▋ 相關連結


    2025年12月16日 星期二

    HTML5 FUN : 花朵產生器

    2025年12月16日 星期二

    前兩天重寫了 HTML5 FUN Doodle Petals 花瓣字的花朵模組,完成後,順手弄了個「花朵產生器」,使用者可以自訂控制參數,產生自己的花朵圖案,並匯出為 SVG 或是 PNG 圖形檔。

    ▋ 啟動花朵產生器

    按一下底下的圖示,或是紅色的按鈕,就可以開啟花朵產生器:


    ▋ 功能簡介

    HTML5 FUN 花朵產生器截圖

    上圖是 HTML5 FUN 花朵產生器的截圖。在夠寬的螢幕中,左側是參數設定區,參數改變,右側預覽的花朵也會立即更新;改到滿意了,設定區的最下方有「匯出 SVG」和「匯出 PNG」兩個按鈕,可以將花朵儲存到檔案中。

    參數設定的部份,基本上是在設定區中,由上而下,一項項去設定,玩玩看,應該就可以上手了。幾個特別在此簡單說明:

    • 樣版:每一個樣版會帶入花瓣的基本設定或是層數,而顏色或是其它參數請自行調整。
    • 控制點半徑:改變花瓣的邊緣是圓、是尖,還是平的;是凸、是凹的。
    • 花瓣層數:產生器支援多層花瓣,像上面截圖中的花朵,中央黃色的花心以外,還有兩層的花瓣;可以應用在國語課的「部件」→「字」→「語詞」。
    • 層間擴展距離:控制多不同層花瓣間,外緣的距離,愈大離愈開。
    • 多色:花瓣色彩模式設為「多色」時,會依不同設定,而有不同的效果
      • 「一筆畫」模式時,只有設定為多層的花瓣時,才看得出「多色」的效果,一層用一個顏色,顏色依色盤順序使用。
      • 不是「一筆畫」模式時,不同花瓣會依序套用色盤中的顏色,顏色數不足時,會從頭重覆使用。
    • 全部填白色:如果只想留花朵的輪廓線,就勾選這一個參數,將整朵塗上白色。
    • 一筆畫:這個選項啟用時,畫出的花瓣,無論設定為幾個花瓣,會以一筆畫的方式畫完,所以同層的花瓣無法用多色。未勾選時,則是以一個花瓣為單位輸出,所以每一個花瓣可以有自己的顏色。
    • 連接到花心:當不是「一筆畫」時,花瓣靠中心端,可以選擇只到花心的邊緣,或是到花朵的正中心。由於差異是出現在花心內,所以這個選項要在沒有勾選「顯示中心圓」時,才看得出差異。

    ▋ 相關連結

    ▋ 更新記錄

    • 2025.12.15 v1.0 初版。


    2025年12月11日 星期四

    HTML5 : 提升 Google 試算表查詢效率

    2025年12月11日 星期四

    教育部國語辭典可以在《教育部國語辭典公眾授權網》中下載匯出的文字資料,而且有提供試算表格式,只要上載到 Google 雲端硬碟,另存為 Google 試算格式,就可以使用 JSONP 的方式,將試算表當資料庫查詢。

    在 HTML5 FUN 中,筆順練習、Partdle 和花瓣字均有使用到查詢字典的功能,《國語小字典》和《國語辭典簡編本》1140925版有 45194筆資料, 因為資料筆數較少,查詢的等待時間算可以接受,但是《重編國語辭典修訂本》1140923版有 163907筆資料,從十幾萬筆資料中撈出要用的,會明顯的感受到「等」!如果想將《重編本》的工作表複製到別的試算表,甚至會出現錯誤訊息而無法完成。

    ▋ 輕鬆建立小而美的工作表

    昨天做了一個小實驗,原本在《重編本》的檔案中只有一個名稱為「1140923辭典匯出」的工作表,新增一個工作表,命名為「short」,並利用參照工作表「1140923辭典匯出」的方式來建立資料,把我想查詢的「字詞名」(A 欄)和「注音一式」(I 欄),用以下程序建立:

    • 游標移至「short」的儲存格 A1 
    • 輸入公式:='1140923辭典匯出'!A1
    • 游標移至「short」的儲存格 B1
    • 輸入公式:='1140923辭典匯出'!I1

    公式如果正確,顯示的內容應該就跟「1140923辭典匯出」A1 與 I1 中的一樣了。

    [圖1] Google 試算表表的參照與自動填滿

    因為 Google 試算表的新工作表預設只會有 1000 列,而「1140923辭典匯出」中有的資料遠遠超過 1000 列,所以要先查一下要參照的工作表「1140923辭典匯出」有多少列資料,如前面提過的,這個版本有163907筆資料,所以 163907 - 1000 = 162907,需要在工作表「short」的最底部再新增 16290 列,努力的捲到底部,就可以看到輸入框了 [圖2],在裡面填入需要新增的列數。

    [圖2] Google 試看表在底部新增多列

    新增完所需要的列數,接下來可以準備使用魔法的快速按鍵「Ctrl + Enter」,幫我們自動填入資料了:

    • 游標移至「short」的儲存格 A1
    • 按一下上方的欄名「A」,可以選取整欄。
    • 按鍵盤「Ctrl + Enter」

    成功的話,等一下下,A欄中應該就會將資料都自動填入空格了。

    繼續「自動填滿」B 欄:

    • 游標移至「short」的儲存格 B1
    • 按一下上方的欄名「B」,可以選取整欄。
    • 按鍵盤「Ctrl + Enter」

    成功的話,B 欄的所有空格應該也自動填入資料了。

    ▋ 比比看誰快

    新建立的工作表「short」只有兩個欄位,來和完整版的工作表比較一下查詢所花費的時間,底下在四個不同帳號,分別在兩個工作表查詢 A 欄帶有「山」這個字的,並回傳國字、注音兩欄所花的時間(單位為千分之一秒):

    ✅ 工作表「short」(共2欄, 回傳 2欄, 找到 1038筆)

    時間:  1691 , 筆數:  1038
    時間:  1436 , 筆數:  1038
    時間:  1264 , 筆數:  1038
    時間:  1398 , 筆數:  1038

    ✅ 工作表「1140923辭典匯出」(共18欄, 回傳 2欄, 找到 1038筆)

    時間:  4993 , 筆數:  1038
    時間:  5137 , 筆數:  1038
    時間:  4522 , 筆數:  1038
    時間:  3584 , 筆數:  1038

    只有兩欄的「short」很明顯地快多了。

    來看看資料量較少的《簡編本》如何呢?

    ✅ 工作表「short」(共2欄, 回傳 2欄, 找到 137筆)

    時間:  473 , 筆數:  137
    時間:  624 , 筆數:  137
    時間:  750 , 筆數:  137
    時間:  525 , 筆數:  137

    ✅ 工作表「辭典匯出_1140925」(共15欄, 回傳 2欄, 找到 137筆)

    時間:  1071 , 筆數:  137
    時間:  1297 , 筆數:  137
    時間:  1371 , 筆數:  137
    時間:  1367 , 筆數:  137

    《簡編本》如果查詢只有兩個欄位的「short」工作表,也可以比原來快很多。

    實際應用在 HTML5 FUN 筆順練習和花瓣字中,可以讓程式更靈活了。哈!雖然使用者原來不見得感覺有在等待,畢竟程式在背景載入資料時,大伙兒都忙著操作別的呢!

    ▋ 相關連結


    2025年12月9日 星期二

    DD Petals 花瓣造詞注音手寫

    2025年12月9日 星期二

    HTML5 FUN 的 DD Petals 花瓣字,可以拿來玩造詞、玩部件,或是玩數學 ,但是都要用手寫的方式來輸入答案。原本限制只辨識中文字與數字,注音符號則未完全支援;這幾天認真地研究如何克服注音符號手寫辨的問題,詳見「雄:注音符號手寫輸入提升辨識率」,有了大進展。看一下底下這張截圖 [圖1]:

    [圖1] 花瓣字國字注音混合手寫

    現在花瓣造詞如果是「低年級」課本生字的題目,就會自動啟用手寫注音的辨識功能(中、高年級要乖乖寫國字,不會就查字典啦!)。而且可以國字、注音混合使用哦!不過,寫字時要注意一些小技巧才會用得順手。

    ▋ 手寫注音小祕訣

    花瓣造詞可以由 HTML5 FUN 筆順練習中直接啟動。為了提高注音符號的辨識度,以下幾點供手寫時參考:

    • ✅ 注音符號之間,保持距離,不能黏在同一個框。參考 [圖2]。
    • ✅ 調號寫大一點比較好辨識。
    • ✅ 寫完,如果一秒沒動作,會自動辨識,出現紅色虛線框,可用來檢視注音符號或是國字是不重疊了,重疊就只能按復原鈕,或是「長按一秒」擦掉重寫。
    • ✅ 「ㄥ」、「ㄐ」比較容易誤判,寫完要特別注意是否正確。
    • ✅ 國字、注音混用時,「國字」的各部件要「同框」;注音符號「不能同框」。
    注意 [圖1] 中「山高水長」的「水」,和「山頂洞人」的「頂」,如果「水」和「頂」的各部件沒有同框,國字就可能被誤判。(我有秘招:在「丁」「頁」之間按一、兩個點,就會「縫合」為「頂」了)

    [圖2] 注音符號不可以黏在同一個框框內

    ▋ 相關連結


    2025年12月6日 星期六

    注音符號手寫輸入提升辨識率

    2025年12月6日 星期六

    Google 輸入工具的手寫輸入,中文的辨識率滿高的,當然也包括注音符號。但是如果將多個注音符號寫在一起,例如「ㄒㄩㄥˊ」,那大概就會失望了;直寫會被辨識為國字,橫寫可能變別的文字。一次一個注音符號可以正確辨識,多個就亂掉。山不轉路轉,路不轉人轉,轉個念,就可以讓 Google 輸入工具應用在手寫注音辨識了!

    關鍵就在「一次一個」注音符號,所以如果在傳送筆跡座標時,一次只送出一個注音符號的,這樣就能發揮它的高辨識率了,底下可以測試一下成果。

    ▋ 手寫注音測試

    小秘訣:

    • ✅ 手寫時會有虛線框,一個框一個符號。
    • ✅ 聲調符號不要寫太小。
    • ✅ 寫完,如果一秒沒動作,會自動辨識。


    ▋增強了什麼

    為了提升手寫注符號的辨識率,利用程序做了一些調整:

    • ✅ 以手寫字跡佔最大矩形是否有重疊,將重疊的視為同一符號來辨識。
    • ✅ ㄥ容易被辨識為ㄑ,但是ㄑ正確率很高,所以辨識結果兩者都有,而且ㄥ順序在較低時,視為是ㄥ。
    • ✅ 調號較不容易辨識正確,所以用最常被認錯的符號來修正為正確的。
    • ✅ 以虛線框為單位,一次辨識一個虛線框的筆跡;所有筆跡都辨識完以後,分析出注音符號與非注音符號,再依注音符的拼音原則,將字的注音切割。

    ▋已知問題

    先列出一些待解決的問題:

    • 「ㄗㄨㄟˋ」 目前解析為「ㄗㄨㄟˋ」,但它也可能是「ㄗ」「ㄨㄟˋ」;「ㄗ」「ㄨ」到底是不是屬同一個字的注音,可能還需要參考筆跡的位置才行。
    • 提升調號的辨識率。

    ▋應用發展

    HTML5 FUN 花瓣字目前的版本有開放低年級的生字造詞,寫一個注音符號啟用虛擬鍵盤,接下來可以將本篇的技術應用在全注音手寫的方式輸入答案(註: 已於2025.12.09 整合完成)。另外,或許可以開發一個看圖、看國字,或是聽語音,手寫注音的注音高手。

    ▋ 相關連結


    2025年9月23日 星期二

    你變我也變:解析雲端硬碟圖片縮圖網址更新

    2025年9月23日 星期二

    有人反應無法用「以雲端硬碟資料夾的圖片製作注音高手」影片中的方法出題;研究了半天,終於抓到問題點,看起來是 Google Drive 共用資料夾頁面的內容有異動,導致用來找出圖片縮圖網址的程序失效了。這麼一來,就影響了以下幾篇中的小工具:

    哈!有問題才有好玩的。「你變我也變」,只要找到關鍵的片段,去掉工程師故意加入的「雜質」,再加上看似被遺漏的符號,就能以 JSON.parse 將變數還原,最後擷取要的欄位,問題就可以解決了。簡單記錄幾個關鍵:

    • 有用的資訊: data-p
    • 雜質: %.@.
    • 需還原: "
    • 最前面少了一個 [

    當初花了不少時間研究還是有差,現在愈來愈會拆彈了。

    總之,壞掉的地方修好了,如果是使用試算表的,只要重新建立範例試算表的副本,就又能開心使用了。

    ▋ 後記

    2026.01.28 Google 又改了不知什麼地方,改找 data-tooltip ,再抓取 data-in 來組合縮圖的網址。

    2025年9月2日 星期二

    擷取 Google 雲端硬碟圖片縮圖網址

    2025年9月2日 星期二

    製作網頁遊戲時,可以使用放在 Google 雲端硬碟中的圖片嗎?一般來說,在瀏覽器中會因為 CORS 的跨域讀取限制,無法直接應用 Google 雲端硬碟的圖片在網頁遊戲裡,但如果是雲端硬碟圖片的縮圖,則不會受限制。所以如何取得圖片縮圖的網址就是關鍵。

    ▋ 擷取 Google 雲端硬碟圖片縮圖

    這個小工具可以解析出已共用為「知道連結的任何人」 Google 雲端硬碟「資料夾」中,所有圖片的縮圖網址,大小為 640x480,雖然解析度不高,但應用於一般的遊戲中的插圖,算還不錯。

    ▋ 使用方法

    程式會在已公開共用的雲端硬碟資料夾頁面中,找出所有圖片的清單,解析出檔名及其縮圖的網址,能輸出「主檔名」和「圖片網址」兩個欄位,欄位分隔符號使用跳格的符號,所以複製輸出結果後,可以直接貼到試算表中使用。操作程序如下:

    • ✅ 在 Google 雲端硬碟建立資料夾。
    • ✅ 設定共用為「知道連結的任何人」「檢視」。
    • ✅ 將要使用的圖片上載到雲端硬碟資料夾中。
    • ✅ 確認圖片的檔名(如果要輸出檔名為題目欄位)。
    • ✅ 複製共用的連結。
    • ✅ 在本文的工具中貼上連結,按 [開始解析] 鈕。
    如果順利,最後可以按 [複製到剪貼簿] 鈕,將資料複製後應用。

    ▋ 開始使用

    目標:解析雲端硬碟「資料夾」中圖片的縮圖網址

               

    OK

     

    複製到剪貼簿的資料可以直接貼到試算表中,會自動分欄、分行,貼在試算表的儲存格中。

    這個解析出縮圖網址的工具已經內建在 HTML5 FUN 的烘焙機中,另外,也可以在 Google 試算表中的自訂公式來進行解析應用,可以參考三月的舊文「雄:以雲端硬碟資料夾的圖片製作注音高手

    ▋ 相關連結

    ▋ 更新記錄

    • 2026.03.09 新增可以往下搜尋一層子目錄的選項。
    • 2026.01.28 改由 data-in 找縮圖的 ID。
    • 2025.09.23 Google Drive 更動了共享資料夾頁面的內容,改由其它參數中找到圖片的相關資訊。



    2025年8月26日 星期二

    利用 iframe 解決因為 CORS 而抓不到YT字幕的問題

    2025年8月26日 星期二

    前兩天發現 HTML5 FUN Audio Player 怎麼樣都抓不到 YouTube 字幕;查了一下,看來多變的 YouTube 又出招了,讓原本由影片頁面原始碼中擷取到的字幕網址失效了,只能抓到空白的內容。研究了大半天,找到了另一個方法 (有興趣可以參考 jdepoix 的 youtube-transcript-api ),主要就是先由影片的頁面原始碼中找到「INNERTUBE_API_KEY」,然後再利用 YouTube 的 API ,以 POST 的方式取得影片資訊,如果成功,就能拿到影片字幕軌資料的網址。

    以 JavaScript 使用新方法來擷取影片資訊的第一關會是在 POST 時的 CORS 問題,如果透過 CORS proxy 當然是可行的方法,一般來說,利用 Google Apps Script 也能寫出 CORS proxy,不過 YouTube 的 API 有在擋 bot,Google Apps Script 直接就被封殺了,網路上其它 CORS proxy 服務也沒好到哪裡去,多玩個幾次,就只能得到這樣的訊息了:

    status: 'LOGIN_REQUIRED',
    reason: 'Sign in to confirm you’re not a bot'

    想要的資料當然就撈不到了。

    後來發現,原來在瀏覽器中用 fetch ,不透過 CORS proxy 來 POST  ,就能使用 YouTube API 了,倒是有個小地方要注意,不然就會一直出現:

    Access to fetch at 'https://.......' from origin 'null' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

    裡面最重要的關鍵字是「preflight」,當 Content-Type 是 application/json 時,瀏覽器在未傳送資料給遠端前,會先自我審查是否違反 CORS,難怪我一直看不到 YouTube API 的回應。要怎麼改呢?

    只要在 fetch 參數中的 headers 指定 Content-Type 為 text/plain ,它就會使用 simple header ,然後可以避免 preflight,加入:

    headers: {
      'Content-Type': 'text/plain', 
    }

    不透過 CORS proxy 來呼叫 YouTube API 的好處是,比較不會動不動就被 YouTube 判定為 bot 而抓不到資料,是不是很棒?!

    不過,當我很開心地將改良過的程式應用到 Blogger 中的 Audio Player 時,居然還是出現違反 CORS 的訊息,為什麼?

    底下兩張截圖來探討原因,關鍵已經用箭頭標示出來了:

    [圖1] origin 為 blog 的網域

    [圖2] origin 為 null

    如 [圖1] 所示,當程式在 Blogger 中執行時,origin 被設定為 Blogger 所在的網域,瀏覽器送給 YouTube API ,因為兩者在不同的網域,這樣一來,就會因為 CORS 的問題,無法抓到資料了。同樣的程式碼,由本機載入並執行時,會像 [圖2] 一樣,origin 被設定為 null,YouTube API 接受了,會回傳資料 (這不知道是故意的,還是 API 的 bug,哈!但是我喜歡它現在的狀況)。

    本來在研究 YouTube embed 時的 iframe 結構,看看能不能從中挖出字幕,一份資料中提到如果使用 base64 編碼後的 data URI 當 iframe 的 src ,會導致 origin 被設定為 null,就會讓它變成與頁面不同源,而無法存取頁面中的資料 (這一點其實是可以利用 postMessage 來交換資料) ......。

    綜合前面看起來像問題的點,因為一個關鍵字「null」,讓我兜出了一個可行的方案。

    如果產生一個只用來透過 YouTube API 抓影片資訊的 iframe,而且程式碼用 base64 編碼以後,設定為 iframe 的 src,利用 postMessage 傳影片的 id 與 api key 給 iframe, iframe 收到後,執行抓資料的程序,再使用 postMessage 回傳影片資料給 Audio Player。

    哈!就這樣子,Audio Player 又能順利抓到 YouTube 的字幕了!

    ▋ 相關連結




    2025年5月16日 星期五

    SVG ID 衝突導致顯示異常

    2025年5月16日 星期五

    一般來說, 在頁面中,ID 應該是唯一的才對,但是實務上,不見得會有影響;不過,最近在測試開發中的新版筆順練習程式時,終於踢到鐵板了!

    筆順示範的動畫是先建立一個筆畫的「遮罩」,然後在遮罩下,不斷的用不同座標畫大大小小的圓,畫面上只會看到該筆畫慢慢地被「塗上」顏色,彷彿用筆在寫的樣子。所以「遮罩」是很重要的關鍵。在 SVG 中設定要使用哪一個遮罩是用類似這樣的語法:

    mask="url(#demo-mask)"

    也就是設定名稱為「mask」的屬性,而用「url(# ... )」來指定用 ID 為 ... 的 mask ;以前面的例子來說,它指定使用 ID 為「demo-mask」的遮罩。

    整個網頁中只有一個 ID 是「demo-mask」的話,當然不會有任何問題;如果有多個 ID 都叫作「demo-mask」,「url(#demo-mask)」只會取用第一個;應用在第一個 SVG 也不會有問題,其它的 SVG 就無法正常顯示了。之所以會踢到鐵板,完全是因為自己愛亂玩,一次在頁面中放了好幾個國字的 SVG ,而它們的遮罩的 ID 通通叫作「demo-mask」,玩到第二個字以後,只有第一個置入頁面的會正常顯示;這部份倒是和設定 style 不同,style 是後載入的會覆蓋先載入者。

    ▋ 動態產生 uuid 

    怎樣動態產生唯一的 ID 呢?

    之前曾看過別人使用這樣的語法:

    uuid = function b(a){return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,b)}

    只要呼叫 uuid() 就能得到一個不會重覆的 uuid。

    另外一個語法是:

    window.URL.createObjectURL(new Blob([])).slice(-36)

    哈!我是喜歡簡潔一點的後者。如果想讓 id 短一點,就將 36 改小一點。

    ▋ 測試

    以下的字,照設計的流程是:

    1. 多色的靜態筆畫。
    2. 按完播放鈕,筆畫變紅色。
    3. 黑色的書寫動畫。
    4. 黑色靜態筆畫,至全字動畫播畢。
    5. 多色的靜態筆畫。

    可以觀察一下,當 mask 使用相同 ID,與使用不同 ID 有何差異,按底下的按鈕,開始測試:

    [圖1] 是右邊的「試」 在 [測試1 (相同ID)] 中,第四筆畫時的截圖,本來應該是寫橫畫的,卻冒出了豎畫。我故意將「測」的輪廓疊上去,比較容易看出豎畫哪裡冒出來的;。那其實就是第一個字「測」的第四筆畫,「貝」的起筆豎畫製作的遮罩,「試」此時寫的是橫畫,所以只能看到短短的豎畫,這是因為遮罩 ID 衝突,所造成的誤用。而在 [測試2 (ID不同)] 中,兩個字都能正常的顯示遮罩內容。

    [圖1] 右邊的字誤用了左邊的遮罩而不正常

    所以在 SVG 中有使用到「url(# ... )」來指定對象者,ID 在命名時,務必要特別注意,不然就會像我,程式改來又改去,對的程序都會改成錯的了。


     
    雄::gsyan © 2009. Design by Pocket