第 08 章 - 時間與時間函數的應用
上次更新日期 2020/11/02
有時候我們會需要用到時間來展示某些特別的時刻,所以需要取得某個時間點以及目前的時間點,所以,就需要時間物件。 另外,有時候我們會需要使用到計時器,這種功能也需要用到時間函數!這樣你就可以自由的指定時間來循環執行某個事件, 對於網頁程式來說,有時候也是相當重要的!
學習目標:
- 了解 DOM 物件,包括使用新增元素的方式
- 了解 Date 物件,包括如何計算日期等任務
- 學會使用 setInterval 與 setTimeout 函數,進行倒數或者是運動等任務
8.1: DOM 物件
上個章節我們介紹了不少 JavaScript 的物件,而其中我們一直在使用的 document 物件反而沒有詳細談到。 DOM 模型裡面,我們主要是透過 document 這個物件來處理各個元件,而 document 其實還有底下的屬性可用:
屬性 | 說明 |
.charset | 目前使用的文字編碼 |
.characterSet | 預設的文字編碼 |
.domain | 其實就是伺服器的主機名稱 (或領域名稱) |
.lastModified | 此網頁的最後編輯日期與時間 |
.referrer | 此文件的來源,意即是從哪一個網頁點擊過來的 |
.title | 這個網頁的 title |
.URL | 就是這個網頁的網址 |
事實上,很多資料似乎在 location 物件裡面可以找到~沒關係,有用的資料越多越好。
- 建立新檔 unit08-1-1.php,並在 index.php 裡面加上相關的超連結,target 指向 js 視窗。
- 將 這個檔案內容 加到 unit08-1-1.php 當中,並查閱網頁顯示的狀態。
- 建立一個名為 gogo() 的函數,內容以 for ... in 的語法,將 document 的屬性列出來。 可以透過類似『 res = res + "<li>" + i + ":" + document[i] + "</li>"; 』之類的語法來處理。
- 最後在 body 載入完畢後,執行 gogo() 函數即可。
同時透過上面的輸出,我們也能知道 document 這個 DOM 的屬性真的很多!包括 getElement... 之類的陣列物件也在這裡面列表, 查一查覺得相當有趣!
- DOM 的新增/刪除元素功能
先來看看之前我們就玩過的 DOM 物件的幾個常見的方法:
方法 | 說明 |
.getElementById() | 使用 id 名稱去取得某個元素的控制權 |
.getElementsByName() | 使用 name 名稱去取得某個元素的『陣列』控制權 |
.getElementsByTagName() | 使用 HTML 某標籤名稱去取得某個標籤的『陣列』控制權 |
.getElementsByClassName() | 使用 HTML 某類別(class)名稱去取得某個類別的『陣列』控制權 |
屬性 | 說明 |
.innerHTML | 修改某元件的內部 HTML 資料,可以涵蓋標籤 |
.innerText | 修改某元件的內部資料,不可以涵蓋標籤 |
.outerHTML | 修改某元件的 HTML 資料,包括標籤本身資料,也可以涵蓋標籤 |
.outerText | 修改某元件的資料,包括標籤本身資料,不含有標籤功能 |
這幾個玩意兒是我們一直在課堂上玩弄的東西,不過,瞧一瞧,上面的方法,大部分都需要原本就有的一個 ID 或者是標籤等等, 才有辦法去『修改』它,那如果我們需要的是『憑空生出來』的元件呢?可能上頭的方案行不通。這時,就得要有新增元件 (createElement) 的功能! 而且,也需要有『放置該元件到何處 (appendchild)』 的功能才對。這幾個方法大概是:
方法 | 說明 |
.createElement('tag') | 新增一個標籤,標籤名稱最好用大寫,例如 P, BUTTON... |
.createTextNode('msg') | 增加一段內容文字,通常搭配上面的方式,來改變某元素的內容 |
.appendChild('nodename') | 在這個標籤後新增子標籤 |
.insertBefore(新元件,舊元件處) | 在舊元件後面插入新元件 |
.childNodes() | 控制該元素的子元素,例如抓取 ul 的 li,為陣列用法 |
.remove() | 移除整個元件 |
.removeChild() | 移除該元件的某個子元件(通常搭配陣列) |
.setAttribute() | 設定某元素的屬性,例如 setAttribute("type","text") |
.execCommand('動作') | 將某物件進行某動作(加粗、加黑等,通常搭配陣列) |
屬性 | 說明 |
.clientHeight | 取得/調整元件的高度 |
.clientWidth | 取得/調整元件的寬度 |
基本上,如果要在某個元件上面產生一個新的元件,例如,在某個 div 裡面增加許多圖示,可以這樣做:
var newitem = document.createElement('IMG'); // 建立新標籤,是圖片 newitem.src = 'where/to/find'; // 給予圖片檔名 document.getElementById('where').appendChild(newitem); // 將圖片在 where 之後新增
現在,利用上面的特徵,讓我們來產生好幾個星星看看:
- 建立新檔 unit08-1-2.php,並在 index.php 裡面加上相關的超連結,target 指向 js 視窗。
- 將 這個檔案內容 加到 unit08-1-2.php 當中,並查閱網頁顯示的狀態。
- 將 這個圖片 加到 images 的目錄去 (可以用其他圖片來取代)
- 建立一個名為 gogo() 的函數,開始新增星星的標籤 (tag) 了:
- 取得 mystar 元件的控制權,變數名稱也取 mystar 好了。
- 取得 mystar 的高度與寬度,等等用來設計星星所在位置的 X 與 Y 軸。變數取名為 ttx 與 tty 好了。
- 透過 for 迴圈,建立 10 次的設計 (最終修改為 1000 次)
- 建立新的 IMG 元件,取名為 star 好了
- 給予 star 的圖片位置 (src),記得圖片放置到 images 目錄去
- 設計 x 與 y 的座標位置,例如: Math.floor(Math.random()* ttx)
- 給予 star 的 style 屬性有: position, width, borderRadius, overflow, left, top 等
- 最終將這個元件新增到 mystar 裡面去。
- 測試結果如果沒有問題,再將星星放大到 1000 個,同時將圖示縮小到大約 5~10px,原形半徑自己設計。 要注意的是, x, y 為單純的數值,得要加上 px 才會是正常的 CSS 單位喔!
- 最後在 body 載入完畢後,執行 gogo() 函數即可。
你也可以使用 innerHTML 的作法來處理,不過根據強者我同事蔡董大大說,createElement 的作法,要比 innerHTML 來的更加快速! 而且數量越大,感受越明顯!
- 新增/刪除子元素功能
而除了這些基本的元素之外,如果你想要增加的是類似 input 這種表單的標籤,我們知道這種標籤需要的參數比較多, 例如 type, name 等等,這種屬性的設定,得要使用到 .setAttribute 的功能才行!而如果你的元素資料並沒有設定 name 或者是 id 時, 也可以透過 childNodes 的功能來抓取元素,作為控制的標的!
- 建立新檔 unit08-1-3.php,並在 index.php 裡面加上相關的超連結,target 指向 js 視窗。
- 將 這個檔案內容 加到 unit08-1-3.php 當中,並查閱網頁顯示的狀態。
- 開始建立載入網頁就能執行的 gogo() 函數內容-目標是建立四個可輸入興趣的 input 欄位:
- 取得 myinput 的 ul 控制權,變數名稱也設定為 myinput 即可
- 開始建立清單項目,使用 for 迴圈,指定設定 0~3 的數值範圍即可,開始迴圈內容設計:
- 指定一個新的 LI 元素,使用 createElement 處理,元素名稱設為 newitem 好了;
- newitem 的 innerHTML 撰寫,請參考底下圖示的文字部份處理
- 增加名為 newinput 的元素,增加的元素為 INPUT 元素
- 增加 newinput 的屬性 (setAttribute),設定 type 為 text
- 增加 newinput 的屬性 (setAttribute),設定 name 為 inter
- 增加 myinput 的子元素為 newitem
- 增加 newitem 的子元素為 newinput
- 上述函數完成後,請先查閱到網頁是否已經呈現 4 個輸入框格?
- 開始處理 addli() 函數:
- 取得 myinput 的控制權
- 設定變數 oldnum ,這個數值的內容為 myinput 子元素的個數
- 使用與 gogo 函數相同的方式,新增 newitem 與 newinput,只是增加的時候,給予的個數使用 oldnum 來處理。
- 開始處理 delli() 函數:
- 取得 myinput 的控制權
- 設定 oldnum 變數,其值為 myinput 子元素個數 -1 個,因為編號從 0 號開始的。
- 使用 myinput.removeChild 的方式,將最後一個子元素 (myinput.childNodes[oldnum]) 刪除。
透過這種功能,就可以幫使用者在輸入狀況不足或過多的情況下,自行修改 input 的數量了。
8.2: Date 物件
時間函數我們在最前面第二章就稍微談到過,那就是 Date() 這個玩意兒。不過,我們有時候並不是單純的想要知道時間而已, 可能因為時間的判斷,因此需要取得更正確的時間點,包括只取得月份、日期或時間等。更有甚者,我們有時還得要設定一個『時間』, 目的是需要知道新/舊時間的差異!所以,先來理解一下 Date 的物件方法。假設你設定了:
var nowtime = new Date();
那麼常見的取得時間參數的方法有:
方法 | 說明 |
nowtime.getYear() | 取得當年的西元年,超過 2000 年就回傳 4 位數,否則回傳 2 位數 |
nowtime.getFullYear() | 取得當年的西元年,回傳 4 位數 |
nowtime.getMonth() | 取得月份,數值為 0~11,個別代表 1~12 月,這個很怪異! |
nowtime.getDate() | 取得該月的日期,數值為 1~31 |
nowtime.getDay() | 取得當週的星期幾,數值為 0~6,代表星期日到星期六 |
nowtime.getHours() | 取得當日的小時,數值為 0~23 |
nowtime.getMinutes() | 取得分鐘值,數值為 0~59 |
nowtime.getSeconds() | 取得秒鐘值,數值為 0~59 |
nowtime.getMilliseconds() | 取得千分之一秒值,數值為 0~999 |
nowtime.getTime() | 取得自 1970/01/01 開始計算的總計秒數*1000 (就是總毫秒數) |
nowtime.getUTCXX() | 跟上面的語法相同,但取得的是國際標準時間 (UTC) 的資料,非本地資料 |
nowtime.getUTCFullYear() | 例如這個是:取得的是國際標準時間 (UTC) 的年份 |
nowtime.toString() | 將時間轉成字串直接輸出 |
nowtime.toLocaleString() | 回傳某地區的時間 |
nowtime.toISOString() | 回傳 ISO 格式的時間字串,結果類似:2020-11-01T08:24:52.029Z |
來看看顯示的方法吧:
- 建立新檔 unit08-2-1.php,並在 index.php 裡面加上相關的超連結,target 指向 js 視窗。
- 將 這個檔案內容 加到 unit08-2-1.php 當中,並查閱網頁顯示的狀態。
- 建立一個名為 gogo() 的函數,主要內容為:
- 建立名為 nowd 的時間物件,內容為目前的時間 (new Date())
- 建立名為 nowtime 的變數,內容為使用 getElementsByName 來取得 nowtime 的控制權
- 透過一個一個的 nowtime[] 陣列的 innertHML,來設計好所有的輸出。
- 最後在 body 載入完畢後,執行 gogo() 函數即可。
JavaScript 在日期的使用上,比較需要注意的其實是月份,因為月份的 1 月份輸出的數值會是 0 !這部份跟我們常用的習慣不太一樣。 第一次接觸時,要特別注意。那另外我們如果需要知道地球上各重要地區的時間,該如何處理?舉例來說,你需要知道日本東京、美國紐約、 美國舊金山、英國倫敦的時間,該如何設計?基本上,就是透過 toLcaleString 來設計即可。例如想要知道目前東京時間, 可以這樣做:
nowtime.toLocaleString('語系',{timeZone:'時區'}); nowtime.toLocaleString('zh-TW',{timeZone:'Asia/Taipei'}); nowtime.toLocaleString('ja',{timeZone:'Asia/Tokyo'});
更多語系、時間等資料,可以參考文末的連結來查詢。來思考一下,如何設計一個你需要知道的時區時間吧:
- 建立新檔 unit08-2-2.php,並在 index.php 裡面加上相關的超連結,target 指向 js 視窗。
- 將 這個檔案內容 加到 unit08-2-2.php 當中,並查閱網頁顯示的狀態。
- 建立一個名為 gogo() 的函數,主要內容為:
- 建立名為 nowd 的時間物件,內容為目前的時間 (new Date())
- 建立名為 nowtime 的變數,內容為使用 getElementsByName 來取得 nowtime 的控制權
- 透過一個一個的 nowtime[] 陣列的 innertHML,來設計好所有的輸出。
- 最後在 body 載入完畢後,執行 gogo() 函數即可。
- 計算經過幾日後的日期
許多時候我們可能會遇到,再過幾天之後,會是幾月幾號禮拜幾的情境。舉例來說,我們在模擬數值模式,通常會模擬個 15 天類似這樣。 那請問 15 天之後,是幾月幾號禮拜幾呢?基本上,你應該需要這樣處理的:
var mydate = new Date(); mydate.setDate(mydate.getDate()+15); mydate.toISOString().substring(0,10); mydate.getDay();
- 建立新檔 unit08-2-3.php,並在 index.php 裡面加上相關的超連結,target 指向 js 視窗。
- 將 這個檔案內容 加到 unit08-2-3.php 當中,並查閱網頁顯示的狀態。
- 事先宣告全域變數,名稱為 nowd,內容就是新的時間物件
- 建立一個名為 gogo() 的函數,主要內容為:
- 取得 dnum 的 value 值,記得需要轉成整數
- 以上述方法取得 dnum 之後的時間,然後分別取得 ISO 格式的日期,以及星期幾的條件
- 將結果輸出到 show 的 id 物件中。
- 倒數計算時間
有參加過馬拉松,或者是某些競賽的朋友都知道,在官方網站都會有列出一個還剩下多少時間的倒數時間器。 要取得倒數時間,就得要有兩個時間參數才行!那如何設定新的時間而不是目前的時間呢? 既然有所謂的 get[xx] 函數,例如 getDate(),那自然就有 setDate() 之類的函數啊!沒錯! 剛剛你看到的一堆 getDate() 函數,將 get 改成 set 就是設定新時間函數了。而其中還有一個很簡單的設計方式, 那就是透過 Date() 函數,例如設定時間為 2020/11/11 的 8:00 時,可以這樣做:
var newd = new Date( 年, 月, 日,時,分); var newd = new Date(2020, 10, 11, 8, 0);
那要如何計算時間?最簡單的方法就是透過 getTime() 函數!因為這個函數會計算從 1970/01/01 0:00 總計來的毫秒數, 所以,兩者的毫秒數量相減,再分別去計算日子,就可以搞定了!例如底下這樣:
var t1 = new Date( 2020, 11, 11, 8, 0).getTime() / 1000; var t2 = new Date( 2021, 1, 5, 15, 15).getTime() / 1000; showd = Math.floor( (t2-t1) / (24*60*60) ); showh = Math.floor( (t2 - (t1 + showd*24*60*60))/ 3600 ); showm = Math.floor( (t2 - (t1+ showd*24*60*60+ showh*3600) )/60 ); shows = t2 - ( t1+ showd*24*60*60+ showh*3600 + showm*60); res.innerHTML = showd + '天' + showh + '時' + showm + '分' + shows + '秒' ;
- 建立新檔 unit08-2-4.php,並在 index.php 裡面加上相關的超連結,target 指向 js 視窗。
- 將 這個檔案內容 加到 unit08-2-4.php 當中,並查閱網頁顯示的狀態。
- 建立一個名為 gogo() 的函數,主要內容為:
- 取得 myform 的元素控制權
- 取得目前的西元年
- 進入 for 迴圈,簡單的設計類似『mynew = mynew + '<option value="' + i + '">' + i + '</option>';』的內容, 但是西元年只會列出今年到 3 年後的日期而已。
- 同上,但是列出 12 個月份,比較重要的是, javascript 的月份數值 (value) 會 -1 喔!
- 同上,但是列出 31 天。
- 最終將結果放入到所屬的 select 的 value 當中即可得到下拉式選單。
- 建立一個名為 shome() 的函數,內容將上面的程式碼抓下來,更改需要的項目來處理即可。
8.3: 間隔時間的應用 setTimeout() 與 setInterval()
很多時候我們會需要動到時間參數,其中一個很重要的功能,就是每隔多少時間需要重新執行某一個函數的概念! 舉例來說,8.2 我們提到的倒數計時,每次都要自己刷新,實在很不合理~如果系統能夠自動每秒鐘更新一次, 那就太完美了。基本上,執行這個程式的範本有兩個常見的函數,大概是這樣做的:
// 方法一,每隔一段時間去進行 var timer = setInterval('func()',milliseconds); function func() { .... } function mystop() { clearInterval(timer); } // 方法二,延遲一段時間去進行 var timer = setTimeout('func()',milliseconds); function func() { .... } function mystop() { clearTimeout(timer); }
基本上, setTimeout 是只會進行一次,而 setInterval 則是持續進行,因此,我們會比較常用 setInterval 這個物件來進行相關的持續運作行為。 舉例來說,如果我想要每 0.01 秒就跑一次『碼表』的設計時,可以參考底下的範例來看看:
- 建立新檔 unit08-3-1.php,並在 index.php 裡面加上相關的超連結,target 指向 js 視窗。
- 將 這個檔案內容 加到 unit08-3-1.php 當中,並查閱網頁顯示的狀態。
- 未來都會用到碼表的 timer 功能,因此,直接指定一個全域變數,直接以 var timer 指定即可,不須給予內容。
- 建立名為 tstart() 函數,內容指定 timer (不能宣告,直接指定) 會每 10 毫秒執行一次 gogo() 函數。
- 建立名為 gogo() 函數,內容是:
- 分別定義 hh, mm, ss, ms 四個變數,內容分別是個別的 id 元件
- 分別定義 h1, m1, s1, s2 四個變數,內容為上述四個元件的 innerHTML 轉成整數後的數值
- 讓 s2 = s2 + 1
- 判斷 s2 是否等於 60,若是的話,s2 等於 0 且 s1 = s1 +1 (秒數 +1 的意思)
- 讓 ms 的 innerHTML 等於新的 s2
- 判斷 s1 是否等於 60,若是的話, s1 等於 0 ,且 m1 = m1 +1 (分數 +1 的意思)
- 讓 ss 的 innerHTML 等於新的 s1
- 判斷 m1 是否等於 60,若是的話, m1 等於 0 ,且 h1 = h1 +1 (小時數 +1 的意思)
- 讓 mm 的 innerHTML 等於新的 m1
- 讓 hh 的 innerHTML 等於新的 h1
- 注:如果要讓資料呈現好看的兩位數,應該加上一個函數,可以自動家讓 0 的函數來處理,會比較好!
- 建立名為 tstop() 函數,內容主要就是 clearInterval 而已!
- 建立名為 treset() 函數,內容除了 clear Interval 之外,也會將 hh, mm, ss, ms 歸零。
既然碼表計數器沒有問題,那麼倒數計時的功能就沒有問題了!
- 將 unit08-2-4.php 另存新檔 unit08-3-2.php,並在 index.php 裡面加上相關的超連結,target 指向 js 視窗。
- 在 script 最上方增加 timer 為全域變數
- 將 showme 函數名稱改為 mygogo() 函數名稱。
- 撰寫新的 showme() 函數,內容為:
- 先執行一次 mygogo();
- 設定每 1000 毫秒執行一次 mygogo() 函數
- 修改舊的 mygogo() 函數內容,在函數的最後面增加一個條件判斷式:
- 若 t2 <= t1 ,則代表現在的日期 (t1) 已經比較未來,因此顯示 '時間到期了' 的字樣
- 清除 timer
- 跑馬燈的淡出淡入功能
網頁設計課程曾經使用到 CSS 動畫調整三張圖片的淡出淡入功能,而這裡我們既然已經談到了 serInterval 這個好用的東西, 那麼能不能拿他來設計隨機計算圖片的功能呢?因為可以使用隨機指令,加上可以取得陣列的數量,因此就不需要考量圖片張數, 讓系統自動算就可以了!設計方式很簡單:
- 建立新檔 unit08-3-3.php,並在 index.php 裡面加上相關的超連結,target 指向 js 視窗。
- 隨意下載三個圖片檔案,這三個檔案最好尺寸相同。但是也不要太大!檔名假設為 8-3-3a.jpg, 8-3-3b.jpg, 8-3-3c.jpg
- 將 這個檔案內容 加到 unit08-3-3.php 當中,並查閱網頁顯示的狀態。
- 設計名為 gogo() 的函數,這個函數主要在:更換圖片檔名的功能:
- 先取得 myfig 元素的控制權,取名為 myfig 變數名稱即可
- 進入 while 迴圈,條件判斷設定為 true,此迴圈目的在判斷亂數取得的檔名是否與原有的圖片重複,
若重複就重新隨機找檔名一次,直到找到不同的檔名為止。
- 使用 Math.floor( Math.random()* myfigs.length ) 找出需要的圖片陣列索引值 (本例為 0~2)
- 設定 newfig = myfigs[myindex] 之類的方式,設計好新的檔名資料
- 使用 myfig.src.split('/').reverse()[0] 找出現有的圖片檔名,只需要檔名即可,變數名假設為 oldfig
- 判斷上面兩者是否相同,若不同,則 break。
- 設計 check++ ,這個數值若大於 100 的話 (判斷 100 次了),就跳開 (避免迴圈跑不完)
- 將 myfig.src 的檔名改成新的檔名
- 設計名為 mymain() 的函數,這個函數的主要目的在循環播放隨機圖片:
- 設計 timer 為每 3 秒執行一次 gogo() 函數功能。
單獨顯示照片很突兀的,那麼能不能拿他來設計淡出淡入的功能呢?可以透過循環或者是亂數的方法來呈現淡出淡入幻燈片的功能。 那該如何設計呢?基本上,需要用到底下的 setTimeout 方法:
// 透過一個名為 fun1() 的函數,進行 timeout 設計的範例!很重要!不要向外呼叫! function fun1() { if ( some conditions ) { setTimeout(function() { some programs; fun1()' }, timestep ); } }
基本設計的想法是:
- 先讓原有的圖片淡出、這個時間可能需要使用到 setTimeout 功能
- 淡出完畢之後,才呼叫下一個更換圖片的函數
- 更換圖片完畢後,開始透過淡入的功能,將圖片呈現到網頁上。
- 等待一段時間,回到第一步反覆執行。
跟剛剛上面的作法不會差太多,但是需要多兩個函數,而且,這三個函數之間是有相依性的,一個呼叫一個,而不是直接在主程式裡面運作。 這一點需要相當注意喔!否則就可能會失敗!
- 將 unit08-3-3.php 另存新檔為 unit08-3-4.php,並在 index.php 裡面加上相關的超連結,target 指向 js 視窗。
- 設計名為 gogoout() 的函數,這個函數目的在進行圖片的淡出功能:
- 事先取得 myfig 的元素控制權,變數名稱設定為 myfig 即可
- 取得原本的濾鏡值,亦即 myfig.style.opacity 這個數值,並請轉成『浮點數』,不是整數喔!因為 opacity 的數值為 0~1 之間而已。 0 為全透明, 1 為全不透明。
- 如果原本的濾鏡值大於 0 時,就開始進入淡出的特效,開始設定 setTimeout ( function() { .... }, 10 ); 的功能
- 設定新的濾鏡值為『 舊的 - 1/50 』
- 給予 myfig.style.opacity 新的濾鏡值
- 重複執行 gogoout() 函數 (就是重新執行自己!於是程式碼就在自己的函數間進行!)
- 若原本的濾鏡值不是大於 0 (就是用 else 處理的情境),就呼叫之前的 gogo() 函數,去變更圖片檔名。
- 修改 gogo() 變更圖片的函數:其實只要在最後一行呼叫淡入函數即可。亦即在最後一行增加 gogoin() 的執行即可。
- 設計名為 gogoin() 的函數,內容是進行新照片的淡入效果,基本上,跟 gogoout() 函數幾乎一模一樣, 但是 (1)條件判斷為舊的濾鏡值是否小於 1, (2)若小於 1 ,就進行 setTimeout 運作, (3)新的濾鏡值為 +1/50, (4)重複執行的函數是自己 gogoin()。
- 修改名為 mymain() 的函數,要注意,第一個被主動呼叫的函數應該要改為 gogoout() 才對!
- 設計 myfig.style.opacity 預設值為 1 ,若沒有設定,這個數值會是空值,會導致淡出淡入的失敗。
- 修改 setInterval 的執行函數,並將時間放大到 3000 毫秒進行一次。
你會發現系統開始進行淡出淡入的照片處理,如果要加快、放慢整個淡出淡入的效果,就更改 gogoin 與 gogoout 的新濾鏡數值變更幅度 (1/50),即可更改!
- 繞圈圈的圖示
有時候,我們可能會製作一些可以在某個方塊裡面運動的小圖示,修改小圖示的位置其實很簡單,就透過 object.style.top 與 object.style.left 來修改即可。 但是,我們有時候得先要知道外層方塊的大小與內層方塊的大小,這樣才有辦法進行各項基本設定的,否則,會讓內部框框超出外部啊!
- 建立新檔 unit08-3-5.php,並在 index.php 裡面加上相關的超連結,target 指向 js 視窗。
- 將 這個檔案內容 加到 unit08-3-5.php 當中,並查閱網頁顯示的狀態。
- 預計要取得許多共用變數,因此需要訂定全域變數,變數名稱大致與外層寬高、內層寬高的設計有關。
- 設計載入系統就取得的共用資料,使用 mymain() 函數處理: (注意,因為是全域變數,所以只能設定不能宣告!)
- 取得 myblock, mycar, res 等元素的控制權
- 取得 myblock 的寬度與高度,使用的方式為 myblock.clientWidth 等方法。
- 取得 mycar 的寬度與高度
- 注意,上面的四個寬度與高度,需要轉成整數喔!
- 設計 timer 為每隔 10 毫秒運行一次 gogo() 函數。
- 設計名為 gogo() 的函數,這個函數主要在取得實際的 mycar 元素位置,然後根據該位置給予修訂任務
- 設定 mycar 能走得最大寬度為 myblock 寬度 - mycar 寬度
- 設定 mycar 能走得最大高度為 myblock 高度 - mycar 高度
- 取得目前的 mycar x 定位點 (mycar.style.left)
- 取得目前的 mycar y 定位點
- 測試 x 與 y ,如果是非數值, (isNaN) 的話,就直接設定為 0 即可。
- 進入上方水平運動:如果 y 小於等於 0 ,那 y 直接設定為 0,且 x 會加 2 個像素;
- 進入右方垂直運動:如果 x 大於等於 maxw ,那 x 直接設定為 maxw,且 y 會加 2 個像素;
- 進入下方水平運動:如果 y 大於等於 maxh ,那 y 直接設定為 maxh,且 x 會減 2 個像素;
- 進入左方垂直運動:如果 x 小於等於 0 ,那 x 直接設定為 0 ,且 y 會減 2 個像素;
- 設定 mycar 最新的 left 為 x + "px" 數值
- 設定 mycar 最新的 top 為 y + "px" 數值
- 讓 res.innerHTML 顯示出方塊寬高、車子寬高、最大寬高、目前車子座標
8.4: 課後作業
- 8-4-1、搭配萬聖節,製作出滿街的殭屍圖片
參考例題 8-1-2 的設計方式,建立一個滿版的或半滿版樣式的方塊,將自己取得的不能有侵權的殭屍圖示 (也可以自製), 讓使用者自己選擇殭屍數量,但數量不能超過 500 個,然後讓殭屍產生在畫面中。除了不同的位置之外,請盡量使用遠近 (放大與縮小), 旋轉等功能 (參考 transform),最終情境有點像底下這樣:
- 8-4-2、搭配畫面美化,建立倒數 1 小時 2 分的畫面
以例題 8-3-2 為範本,當你進入到 8-x-2.php 時,程式會主動算出 01:02 (時:分) 的未來時間,未來時間的算法參考 8-2-3 的方式。 然後透過 UI 的設計,讓倒數版面黏在瀏覽器的右上角,並持續開始倒數。若時間到達 0 的時候,就顯示『倒數結束』的字樣即可。
另外,你需要注意的是,我們在 8-3-2 題當中,抓取的是『日期』而已,這個題目只需要用到『 01:02 (小時:分)』而已, 所以,你需要額外撰寫取得的小時、分鐘、秒鐘資料,才有辦法進入倒數的畫面,否則都會一直出現時間到了喔!要注意!要注意!
- 8-4-3、車子隨便移動的狀態
模仿 8-3-5 例題的設計方式,只是,當你算出最大可移動的範圍 (0~maxw, 0~maxh) 之後,隨機計算一個座標點,然後再透過類似 setTimeout 函數的作法,讓車子方塊移動到該定位點,之後再往下一個定位點移動。因此,這個車子方塊會在裡面亂跑!這就是隨機處理的方式。
8.5: 參考資料
- W3 schools 的 DOM 物件:https://www.w3schools.com/jsref/dom_obj_document.asp
- 時區資料庫 (timezone database): https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
- 相關語系的區域設置列表:https://zh.wikipedia.org/wiki/区域设置#列表
- W3 school 對 timezone 的說明:https://www.w3schools.com/jsref/jsref_tolocalestring.asp
- 有趣的對 timezone 應用的說明: https://w3c.hexschool.com/blog/e69d8619