以前一直搞.net,後來管理事務多了,不少技術就沒跟上,看過一些JS的書,但一直沒動手,前幾個月,寫了第一個JS程序,是一個簡單的產品規則引擎,利用v8引擎集成在.net程序中,用腳原本處理產品費用有關的計算。但那個只涉及數值計算。html
這幾天由於兒子的課本上學邏輯推理,有個數獨遊戲,兒子大感興趣,玩了幾個,想到若是能自動計算可選數字的話,就會很容易解開數獨,在網上找來找去,發現都沒有這樣功能的數獨。正好有點空閒,就決定自已寫一個。html5
因此這個數獨是我寫的第二個JS程序。在寫的過程當中也在同時學習。git
一開始,免不了受以前經驗的影響,想用winform的圖形程序原理來處理html5 canvas的繪圖。但發現有點水土不服:windows的圖形程序原理是說系統無論保存程序本身窗口裏內容,有須要顯示時——好比從後臺切到前臺啦、剛從別的程序下面露出臉來啦等等——就請程序本身再畫一遍。因此,程序只須要重寫form的OnPaint方法(對於win32程序,則是響應WM_PAINT消息),在其中繪製圖形就好了。github
在windows程序中,不能直接隨時向窗口上畫圖,好比你想在鼠標點擊時畫個點,但你無法在鼠標或鍵盤事件的響應方法中獲得窗口的繪圖上下文句柄(對winform,是Graphics參數,對於win32程序是WM_PAINT消息裏的某個參數),因而,你只好記下來「如今有個傢伙點了鼠標了,某某地方應該有個點!」,而後調用Invalidate()方法,這個方法會強制系統向窗口程序發重繪消息,而後,你事先準備好的OnPaint方法被調用,在這個方法裏,你有機會獲得繪圖句柄了,因而你檢查以前有沒有記錄過要在某個地方畫點的,並在這個地方畫個點。編程
有點像MVC的意思,哈?canvas
但在html5的canvas中,我發現瀏覽器是會幫canvas保存圖形的,不管是被其餘窗口蓋到,切到後臺,都沒問題,只要一露出到屏幕上,原來的圖形就仍是在那裏。這表示只要你並不想作動畫效果,用canvas寫圖形程序要比windows程序原理更簡單一些:你只要在合適的時候(鼠標點擊、鍵盤事件等)向canvas上畫圖就是了,畫上的東西就會老是在那裏。繪圖上下文context也不會消失掉,我把它在一開始時作爲構造方法的參數傳入,並保存爲類成員,隨時用隨時取。windows
惟一要注意的,是要快點畫完,別佔太長時間。瀏覽器
從網上看到的代碼都是用e.pageXY來獲得位置的。這個位置是鼠標事件在整個文檔中的絕對位置。也就是說,滾屏不會影響這個值。函數
只不過我用了getBoundingClientRect這個方法來取得canvas對應的box座標,用於把全局座標轉換到canvas內的座標。而這個方法取得的座標是相對於瀏覽窗口的,而不是相對於整個文檔的。這就與e.PageXY對不上了。學習
因此,這裏只好用e.XY,工做得很是好。
一開始在一箇舊筆記本上寫的這個程序,運行得挺好,但放到MacBook Pro上一看,功能挺正常,但內容很模糊。
仲麼辦!
模糊的原理是canvas在瀏覽器中的大小是由style中的width與height來決定的,但其畫布的大小是以canvas.width與height決定的。若是二者不同大,就會進行拉伸縮放,把畫布拉伸(或縮小)到style的大小。
在普通屏幕上,其style定義的像素大小與屏上的顯示結果是一對一的,因此沒問題。但在retina屏上,style的大小定義與屏幕上的像素大小是有個放大比例的(在MBP上,是2),也就是說,style定義的400px,顯示時會用到800px個屏幕像素。但因爲canvas裏畫布的大小是400px的,因此內容被拉伸,還自動消除了鋸齒,看起來就很模糊。
因此,解決辦法是就是:根據放大比例,把畫布的大小設置爲比外部大小更大的大小。
可是注意,鼠標器事件中的座標是按原點數值來提供的。好比說,你內部畫布大小是800px, 但屏幕大小算成了400px, 當鼠標點擊後,給的位置是(100,100),這個位置是按屏幕點數來計算的,要在畫布中計算其對應的點,應該把這個值乘以那個放大係數纔對。
可注意一下程序中的getPointOnCanvas函數。