HTML 简单拼图游戏
先不废话,请看演示。
公司要搞这么个微信活动,可现在没有前端开发,没办法,身为打杂总监只好临时顶下这个空缺了。先找了一些 JS 代码,试用了下都不太理想,好一点的写的又太复杂,改起来有难度,干脆撸起袖子自己干了。
基本需求
有一个固定区域,被拆分成 c*r 个同等大小的碎片,拿走其中一块,靠近缺口的块可以向缺口方向移动;当拼出原来的图样视为完成。
依照此需求,需要经历 加载图片-》拆分图片-》随机打散-》移动碎片-》判定完成 这些步骤。为了更有可玩性,能自行选择自己的图片就更妙了。
下面就重点说明下各个步骤,为编写方便,引入 jQuery 作为辅助库。
加载图片
首先当然是载入图片,计算宽高,对比拼图区域的尺寸进行缩放,如果比例不同,还得“裁剪”掉多余的部分。
/
加载图片
cal 的回调参数为:
ox 横向偏移
oy 纵向偏移
this 指向载入的图片的 jQuery 对象
@param {String} src 图片路径
@param {int} w 额定宽
@param {int} h 额定高
@param {Fucntion} cal 加载完成后的回调方法
/
function loadr(src, w, h, cal) {
var img = new Image();
img.onload = function() {
var xw = img.width ;
var xh = img.height;
var zw = xh w / h;
if (zw > xw) {
// 宽度优先
img.width = w;
img.height = xh w / xw;
xh = (h - img.height) / 2;
xw = 0;
} else {
// 高度优先
img.height = h;
img.width = xw h / xh;
xw = (w - img.width ) / 2;
xh = 0;
}cal.call(img, xw, xh);
};
img.src = src ;
}
以上的“裁剪”仅仅是计算出偏移,然后将其传递给加载就绪的回调函数。
拆分图片
图有了,已缩放,现在需要“拆分”成碎片。这里自然不是真的切割了,而是将图片 clone 出 c*r 片,然后利用负的坐标定位,其实质是用一个块遮盖了“切除”的部分,仅显示需要的碎片部分。
/
拆分图片
@param {jQuery} that 容器对象
@param {int} cols 行
@param {int} rows 列
@param {int} ew 板块宽度
@param {int} eh 板块高度
@param {int} ox 图片横向偏移
@param {int} oy 图片纵向偏移
@param {Image} im 图片对象
*/
function split(that, cols, rows, ew, eh, ox, oy, im) {
that.empty();for(var j = 0 ; j ');
pic.attr("id", "pt-pic-"+k);
pic.data("idx", k);
pic.appendTo(that);
pic.css ({
"position": "relative",
"overflow": "hidden",
"border" : "0",
"width" : ew + "px",
"height" : eh + "px"
});var img = $(im.cloneNode()); img.appendTo(pic); img.css ({ "position": "absolute", "z-index" : "88", "border" : "0", "left" : (0 - i * ew + ox) + "px", "top" : (0 - j * eh + oy) + "px" }); // 因边框可能影响宽高计算, 故边框单独用一个块来放 var bor = $(''); bor.appendTo(pic); bor.css ({ "position": "absolute", "z-index" : "99", "width" : "100%", "height" : "100%" }); // 由于样式宽高并不含边框, 故再次计算尺寸的偏移量 bor.css ({ "width" : (2 * bor.width () - bor.outerWidth ()) + "px", "height" : (2 * bor.height() - bor.outerHeight()) + "px" }); }
}
}
稍微注意,为方便人眼分辨碎片,最好给碎片加个边框,但加边框必然影响坐标的计算,故在图片上再覆盖一层,边框设在他上面,就算加个撕裂效果的透明图做边框都没问题了。这样碎片内图片的偏移坐标的计算就少了些麻烦了。
随机打散
这游戏当然是跟电脑玩了,总不能自己打散自己玩吧?但这个打散不能给每个图片一个随机位置,那很可能你永远也拼不回去了。小时拿那种拼图游戏板整人就干过这种事,故意抠下来把头和脚交换再打散,然后跟其他小朋友打赌。所以程序也得守规矩一块一块的移动。
/
打散图片
@param {jQuery} that 容器对象
@param {int} cols 列
@param {int} rows 行
@param {int} rand 打散步数
/
function upset(that, cols, rows, rand) {
var v ;
var r = Math.floor(Math.random() cols * rows);
var hole = that.children().eq(r).addClass("pt-pix");
var part ;
var step = [];
var dbug = [];
for(var i = 0, j = rand; i 0 && rx 0) {
rv.push(r - 1); // 可向左移动
} else
{
rv.push(r + 1); // 可向右移动
}
if (ry > 0 && ry 0) {
rv.push(r - z); // 可向上移动
} else
{
rv.push(r + z); // 可向下移动
}// 排除来源位置 if (step.length > 0) { v = step[step.length - 1]; v = $.inArray(v, rv); if (v > -1) { rv.splice(v, 1 ); } } // 排除回旋位置 if (step.length > 2 && rv.length > 1) { v = step[step.length - 3]; v = $.inArray(v, rv); if (v > -1) { rv.splice(v, 1 ); } } // 随机方向 r = rv[Math.floor(Math.random()* rv.length)]; v = hole.index(); step.push(v); // 交换位置 part = that.children().eq( r ); if (r v) { dbug.push("上"); } else if (r
把打散的步骤记录下来,然后反转数组,就是攻略啦。
不过随机时需要避免往回走,否则出现 左->右->左 这类情况就不好玩了;还得避免其他循环,如 上->右->下->左 这样的,这会回到原点,等于什么也没干;但更大的循环没想好怎么处理,暂时不去纠结了。
移动判定
移动碎片到缺口,也就是交换碎片与缺口的位置。左右移动很简单,序号大的 insertBefore 序号小的即可。上下移动有个小坑,开始自己没注意,我原本想不管横向还是纵向,没有两次 insertBefore 搞不定的,但是如果 3 和 7 交换位置(3x3, 0~8),3 移动到 7 前,7 再移动到 3 前,此时原来的 3 变成了 6。所以,确实是没有什么不能两次 insertBefore 解决的,但还得考虑让序号大的先动。
/
移动板块
@param {jQuery} that 容器对象
@param {int} cols 列数
@param {int} rows 行数
@param {jQuery} hole 缺口对象
@param {jQuery} part 板块对象
*/
function mover(that, cols, rows, hole, part) {
var move = false ;
var i = part.index();
var j = hole.index();
var ix = i % cols;
var jx = j % cols;
var iy = Math.floor(i / cols);
var jy = Math.floor(j / cols);if (iy == jy) { // 在同一行
move = ix == jx + 1 // 可向左边移动
|| ix == jx - 1; // 可向右边移动
} else
if (ix == jx) { // 在同一列
move = iy == jy + 1 // 可向上移动
|| iy == jy - 1; // 可向下移动
}// 互换位置
if (move) {
if (i
判断是否完成就来个笨办法了,依次遍历所有版块,只要有一个没对上就是还没成功了。
未处理滑动事件,以后闲了再加吧。
整合游戏程序
上面分散的几个函数用起来还是不太方便,整合成一个 jQuery 插件。
/
拼图游戏
@param {String} src 图片路径
@param {int} cols 列数
@param {int} rows 行数
@param {int} rand 打散步数
*/
$.fn.hsPintu = function(src, cols, rows, rand) {
var that = $(this);
var srz = that.data("src");
var img = that.data("img");var aw = that.width ();
var ah = that.height();
var ew = aw / rows;
var eh = ah / cols;// 状态: 0 进行中, 1 成功, 2 结束
that.data("hsPintuStatus", 2);
that.data("cols", cols);
that.data("rows", rows);/
img 存在且 src 没变化
则不需要再次加载图片
直接取出存储好的数据
*/
if (img && srz === src) {
var ox = that.data("pos_x");
var oy = that.data("pos_y");
console.log("Note: 图片无变化");split(that, cols, rows, ew, eh, ox, oy, img );
// 未给 rand 则仅拆分而不打散
if (rand === undefined) return;upset(that, cols, rows, rand);
that.data("hsPintuStatus", 0);
that.trigger("hsPintuLaunch");
} else
loadr(src, aw, ah, function(ox, oy) {
that.data("src", src );
that.data("img", this);
that.data("pos_x", ox);
that.data("pos_y", oy);
console.log("Note: 载入新图片");split(that, cols, rows, ew, eh, ox, oy, this);
// 未给 rand 则仅拆分而不打散
if (rand === undefined) return;upset(that, cols, rows, rand);
that.data("hsPintuStatus", 0);
that.trigger("hsPintuLaunch");
});
// 已经初始化过就不要再绑定事件了
if (! that.data("hsPintuInited")) {
that.data("hsPintuInited", 1);that.on("click", ".pt-pic:not(.pt-pix)", function() { if (that.data("hsPintuStatus") === 0) { var cols =that.data("cols"); var rows =that.data("rows"); var hole =that.children(".pt-pix"); if (mover(that, cols, rows, hole, $(this))) { that.data("hsPintuStatus", 1); that.trigger("hsPintuFinish"); } } });
}
return this;
};
用 $("# pt-box").hsPintu(图片URL, 列数, 行数[, 随机步数]); 即可初始化拼图游戏了, 拼图区域需要固定宽高;随机步数参数不提供时,仅拆解不打散。
图片没变化时没必要重新加载,避免下时间损耗。当然了,更好的办法是再判断行、列和区域尺寸,没变化则直接排列好碎片。懒得写了,就先这样了。
选择任意图片
上面都是固定的图片,参与感不好,让用户自行“上传”图片岂不更有意思。其实不必真的上传到服务器,既然“缩放”、“裁剪”上面都有了,直接加载本地图片不就好了嘛。
/
- 预载文件
- @param {Function} cal 回调函数
- @returns {jQuery} 当前文件节点
*/
$.fn.hsFileLoad = function(cal) {
this.each(function() {
var that = this;
if (window.FileReader) {
var fr = new FileReader( );
fr.onloadend = function(e) {
cal.call(that, e.target.result);
}; cal.call(that);
$.each( this.files, function(i, fo) {
fr.readAsDataURL( fo );
});
} else
if (this.getAsDataURL) {
cal.call(that, that.getAsDataURL());
} else {
cal.call(that, that.value);
}
});
return this;
};
这段代码也能从我的开源项目内找到 预载文件 方法,此工具包还有些其他的文件上传预览类的方法,这是我对 bootstrap-fileinput 没有图片裁剪功能(与最终服务端处理后的结果一致)而“一气之下”自己写的一点零散代码。
完整的代码及演示可在 这里 看到,有朋友说看不到图,但图片我用的百度图片搜索的缩略图,不清楚怎么回事,看不到可以自己从本地选择图片,只是那个“加载”(Image.onload)和“切片”(Image.cloneNode)比较耗时,比较大的图片等等就好。
当然了,也可以光顾我们的活动页玩一把 拼图抽奖。
关键字:html, html5, JavaScript, 小游戏
版权声明
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处。如若内容有涉嫌抄袭侵权/违法违规/事实不符,请点击 举报 进行投诉反馈!