为啥要写这个游戏?

  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

用html5 canvas和JS写个数独游戏的更多相关文章

  1. 基于html5 canvas和js实现的水果忍者网页版

    今天爱编程小编给大家分享一款基于html5 canvas和js实现的水果忍者网页版. <水果忍者>是一款非常受喜欢的手机游戏,刚看到新闻说<水果忍者>四周年新版要上线了.网页版 ...

  2. HTML5 Canvas核心技术:图形、动画与游戏开发 PDF扫描版​

    HTML5 Canvas核心技术:图形.动画与游戏开发 内容简介: <HTML5 Canvas核心技术:图形.动画与游戏开发>中,畅销书作家David Geary(基瑞)先生以实用的范例程 ...

  3. 用 JS 做一个数独游戏(二)

    用 JS 做一个数独游戏(二) 在 上一篇博客 中,我们通过 Node 运行了我们的 JavaScript 代码,在控制台中打印出来生成好的数独终盘.为了让我们的数独游戏能有良好的体验,这篇博客将会为 ...

  4. 用 JS 做一个数独游戏(一)

    用 JS 做一个数独游戏(一) 数独的棋盘由 9x9 的方格组成,每一行的数字包含 1 ~ 9 九个数字,并且每一列包含 1 ~ 9 这 9 个不重复的数字,另外,整个棋盘分为 9 个 3x3 的块, ...

  5. canvas原生js写的贪吃蛇

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  6. html5+Canvas实现酷炫的小游戏

    最近除了做业务,也在尝试学习h5和移动端,在这个过程中,学到了很多,利用h5和canvas做了一个爱心鱼的小游戏.点这里去玩一下 PS: 貌似有点闪屏,亲测多刷新两下就好了==.代码在本地跑都不会闪, ...

  7. HTML5 Canvas核心技术—图形、动画与游戏开发.pdf1

    canvas元素可以说是HTML5元素中功能最强大的一个,它真正的能力是通过Canvas的context对象(绘图上下文)表现出来的 fillText()方法使用fillStyle属性来填充文本中的字 ...

  8. HTML5 Canvas核心技术—图形、动画与游戏开发.pdf7

    性能 运行putImageData()比drawImage()慢,同等条件下优先考虑drawImage() 操作图像数据需要遍历大量数据,应该注意几点: 1)避免在循环体中直接访问对象属性,应当保存在 ...

  9. HTML5 Canvas核心技术—图形、动画与游戏开发.pdf6

    操作图像的像素:getImageData() putImageData() ImageData对象 调用getImageData()方法实际是获取了一个指向ImageData对象的引用,返回的对象包含 ...

随机推荐

  1. express实现前后端通信上传图片,存储数据库(mysql)傻瓜教程(三)完结篇

    终于完成了所有自己想要的功能(鼓励下自己),虽然还是很简陋,但是还是挺有满足感的,哈哈. 附上前两篇的链接: 第一篇 第二篇 进入正题,在第二篇里面已经完成了连接数据库,并且实现了对数据库的增删改查, ...

  2. 路径分析之NetworkX实例

    #!/usr/bin/env python # -*- coding: utf-8 -*- import networkx as nx import numpy as np import json i ...

  3. Googlehack之Github hack

    site:aircrk.com smtpsite:aircrk.com smtp @mail.comsite:aircrk.com root passwordsite:aircrk.com smtp ...

  4. mvp+retrofit+rxjava

    引用 "retrofit" : "com.squareup.retrofit2:retrofit:2.0.1", "retrofit-adapter& ...

  5. iOS面试题总结(一)

    面试题总结 1.#import 跟#include.@class有什么区别?#import<> 跟 #import""又什么区别? include和#import都能完 ...

  6. LoadRunner 11 安装步骤

    loadrunner 安装步骤: LoadRunner11下载:  在网上可以搜索到,在这个就不提供了. LoadRunner11原理: 破解方法和以前版本相同,我用的是LR8.0的破解文件,同样实用 ...

  7. C++基础——函数指针 函数指针数组

    ==================================声明================================== 本文版权归作者所有. 本文原创,转载必须在正文中显要地注明 ...

  8. Python进阶【第一篇】socket

    1.socket模块 要使用socket.socket()函数来创建套接字.其语法如下: socket.socket(socket_family,socket_type,protocol=0) soc ...

  9. 每天一个linux命令目录

    出处:http://www.cnblogs.com/peida/archive/2012/12/05/2803591.html 开始详细系统的学习linux常用命令,坚持每天一个命令,所以这个系列为每 ...

  10. sql 计算周围公里语句

    $lng2 = set_post('lng'); //当前位置坐标 $lat2 = set_post('lat'); round((*ATAN2(SQRT(SIN((`gps`.`gps_lat`-$ ...