惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

IT之家
IT之家
Recent Announcements
Recent Announcements
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The GitHub Blog
The GitHub Blog
MyScale Blog
MyScale Blog
爱范儿
爱范儿
GbyAI
GbyAI
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
美团技术团队
Y
Y Combinator Blog
博客园 - 叶小钗
Apple Machine Learning Research
Apple Machine Learning Research
Martin Fowler
Martin Fowler
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
罗磊的独立博客
M
MIT News - Artificial intelligence
博客园 - Franky
V
Visual Studio Blog
I
InfoQ
V
V2EX
Hugging Face - Blog
Hugging Face - Blog
腾讯CDC
博客园 - 司徒正美
L
LangChain Blog

博客园 - HAL9000

旧文重发:漫谈字符集和编码 如何破解.net软件 夜晚才能工作的程序员 公司让各部门写春联,这是我们部门写的对子 盖茨和乔布斯 利用简单的力学法则来模拟天体运行(2) 利用简单的力学法则来模拟天体运行 一个仿windows泡泡屏保的实现 informix cli 汉字乱码的解决办法 msdn版windows 8无法激活的解决办法 谁是DAC2.8和jet4.0的接班人? 一个长事务引起的血案——Informix 长事务回滚失败引起的阻塞故障处理 一些常见的星际旅行的科技名词与其"理论根据": 笔记:连接SCO上的FTP时为何那么慢 关于中文编程的段子的一个实现 VC非托管代码调用webservice,并支持代理服务器设置 天哪,我遇上了一个windows api BUG! 并贡献一个完全支持代理服务器的http文件下载VC代码 找到锁域账号的元凶 求高手指点:VC中wininet的代理服务器问题,耗了两天了,没有一点办法
用html5 canvas和JS写个数独游戏
HAL9000 · 2015-05-14 · via 博客园 - HAL9000

为啥要写这个游戏?

  1. 因为我儿子二年级数字下册最后一章讲到了数独。他想玩儿。
  2. 因为我也想玩有提示功能的数独。
  3. 因为我也正想决定要把HTML5和JS搞搞熟。熟悉一个编程平台,最好的办法,就是了解其原理与思想之后,做个真正完整的东西练练。

之前一直搞.net,后来管理事务多了,很多技术就没跟上,看过一些JS的书,但一直没动手,前几个月,写了第一个JS程序,是一个简单的产品规则引擎,利用v8引擎集成在.net程序中,用脚本来处理产品费用有关的计算。但那个只涉及数值计算。

这几天因为儿子的课本上学逻辑推理,有个数独游戏,儿子大感兴趣,玩了几个,想到如果能自动计算可选数字的话,就会很容易解开数独,在网上找来找去,发现都没有这样功能的数独。正好有点空闲,就决定自已写一个。

所以这个数独是我写的第二个JS程序。在写的过程中也在同时学习。

几个要点

canvas与windows的图形程序原理的不同

一开始,免不了受之前经验的影响,想用winform的图形程序原理来处理html5 canvas的绘图。但发现有点水土不服:windows的图形程序原理是说系统不管保存程序自己窗口里内容,有需要显示时——比如从后台切到前台啦、刚从别的程序下面露出脸来啦等等——就请程序自己再画一遍。所以,程序只需要重写form的OnPaint方法(对于win32程序,则是响应WM_PAINT消息),在其中绘制图形就行了。

在windows程序中,不能直接随时向窗口上画图,比如你想在鼠标点击时画个点,但你没法在鼠标或键盘事件的响应方法中得到窗口的绘图上下文句柄(对winform,是Graphics参数,对于win32程序是WM_PAINT消息里的某个参数),于是,你只好记下来“现在有个家伙点了鼠标了,某某地方应该有个点!”,然后调用Invalidate()方法,这个方法会强制系统向窗口程序发重绘消息,然后,你事先准备好的OnPaint方法被调用,在这个方法里,你有机会得到绘图句柄了,于是你检查之前有没有记录过要在某个地方画点的,并在这个地方画个点。

有点像MVC的意思,哈?

但在html5的canvas中,我发现浏览器是会帮canvas保存图形的,无论是被其他窗口盖到,切到后台,都没问题,只要一露出到屏幕上,原来的图形就还是在那里。这表示只要你并不想做动画效果,用canvas写图形程序要比windows程序原理更简单一些:你只要在合适的时候(鼠标点击、键盘事件等)向canvas上画图就是了,画上的东西就会总是在那里。绘图上下文context也不会消失掉,我把它在一开始时做为构造方法的参数传入,并保存为类成员,随时用随时取。

唯一要注意的,是要快点画完,别占太长时间。

滚屏后座标的问题

从网上看到的代码都是用e.pageXY来得到位置的。这个位置是鼠标事件在整个文档中的绝对位置。也就是说,滚屏不会影响这个值。

只不过我用了getBoundingClientRect这个方法来取得canvas对应的box座标,用于把全局座标转换到canvas内的座标。而这个方法取得的座标是相对于浏览窗口的,而不是相对于整个文档的。这就与e.PageXY对不上了。

所以,这里只好用e.XY,工作得非常好。

支持Retina屏

一开始在一个旧笔记本上写的这个程序,运行得挺好,但放到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函数。

源代码在: https://github.com/haoxiaobo/SudoJS