前言
本文将带大家一步步实现一个H5拼图小游戏,考虑到H5游戏的轻量级和代码体积,我没有使用react或vue这些框架,而采用我自己写的dom库和原生javascript来实现业务功能,具体库代码可见我的文章如何用不到200行代码写一款属于自己的js类库,构建工具我采用了自己搭建的gulp4开发项目脚手架。你将学到:
- 洗牌算法
- 洗牌动画实现原理
- 用FileReader API实现本地预览文件
- 用Canvas生成海报
零零总总花了半天的时间,希望对自己后面涉及H5游戏有所帮助,也希望大家通过这篇文章有所收获。
设计思路
效果演示
具体实现
接下来我将贴出每一步的核心代码,供大家参考学习。
1.文件上传解析
``` js // 文件上传解析 var file = $('#file'); file.on('change', function(e){ var file = this.files[0]; var fileReader = new FileReader(); // 读取完成触发的事件 fileReader.onload = function(e) { $('.file-wrap')[0].style.backgroundImage = 'url(' + fileReader.result + ')'; imgSrc = fileReader.result; } file && fileReader.readAsDataURL(file); }) ```
2.生成canvas海报
具体实现思路如下,canvas代码后期会封装成一个类,基本用法和思路大致如下:
function generateImg() { var canvas = document.createElement("canvas"); if(canvas.getContext) { var winW = window.innerWidth, winH = window.innerHeight, ctx = canvas.getContext('2d'); canvas.width = winW; canvas.height = winH; // 绘制背景 // ctx.fillStyle = '#06c'; var linear = ctx.createLinearGradient(0, 0, 0, winH); linear.addColorStop(0, '#a1c4fd'); linear.addColorStop(1, '#c2e9fb'); ctx.fillStyle = linear; ctx.fillRect(0, 0, winW, winH); ctx.fill(); // 绘制顶部图像 var imgH = 0; img = new Image(); img.src = imgSrc; img.onload = function(){ // 绘制的图片宽为.7winW, 根据等比换算绘制的图片高度为 .7winW*imgH/imgW imgH = .6*winW*this.height/this.width; ctx.drawImage(img, .2*winW, .1*winH, .6*winW, imgH); drawText(); drawTip(); drawCode(); } // 绘制文字 function drawText() { ctx.save(); ctx.fillStyle = '#fff'; ctx.font = 20 + 'px Helvetica'; ctx.textBaseline = 'hanging'; ctx.textAlign = 'center'; ctx.fillText('我只用了' + (180 -dealtime) + 's,' + '快来挑战!', winW/2, .15*winH + imgH); ctx.restore(); } // 绘制提示文字 function drawTip() { ctx.save(); ctx.fillStyle = '#000'; ctx.font = 14 + 'px Helvetica'; ctx.textBaseline = 'hanging'; ctx.textAlign = 'center'; ctx.fillText('关注下方公众号回复【拼拼乐】开始游戏', winW/2, .25*winH + imgH); ctx.restore(); } // 绘制二维码 function drawCode() { var imgCode = new Image(); imgCode.src = './images/logo.png'; imgCode.onload = function(){ ctx.drawImage(imgCode, .35*winW, .3*winH + imgH, .3*winW, .3*winW); // 生成预览图 var img = new Image(); img.src= convertCanvasToImage(canvas, 1).src; img.className = 'previewImg'; img.onload = function(){ $('.preview-page')[0].appendChild(this); startDx = startDx - 100; transformX(wrap, startDx + 'vw'); } } } } else { alert('浏览器不支持canvas!') } } // 将canvas转化为图片 function convertCanvasToImage(canvas, quality) { var image = new Image(); image.src = canvas.toDataURL("image/png", quality); return image; }
3.切换元素动画和洗牌算法
我们用transform实现洗牌动画和拼图切换的动画,洗牌算法主要通过维护一个矩阵序列来实现。接下来是基本的工具方法:
// 滑动元素,用于切换页面 function transformX(el, dx) { el.style.transform = 'translateX(' + dx + ')'; } // 生成n维矩阵 function generateMatrix(n, dx, dy) { var arr = [], index = 0; for(var i = 0; i< n; i++) { for(var j=0; j< n; j++) { arr.push({x: j*dx, y: i*dy, index: index}); index++; } } return arr } // 数组置换 function swap(arr, indexA, indexB) { var cache = arr[indexA]; arr[indexA] = arr[indexB]; arr[indexB] = cache; } // 数组乱序 function upsetArr(arr) { arr.sort(function(a,b){ return Math.random() > 0.5 ? -1 : 1 }) } // 洗牌方法 function shuffle(els, arr) { upsetArr(arr); for(var i=0, len=els.length; i< len; i++) { var el = els[i]; el.setAttribute('index', i); // 将打乱后的数组索引缓存到元素中 el.style.transform = 'translate(' + arr[i].x + 'vw,' + arr[i].y + 'vh'+ ')'; } } // 校验是否成功方法 function isTestSuccess(arr) { return arr.every(function(item, i){ return item.index === i }) } //
有了工具方法,我们可以通过如下调用实现洗牌:
//初始数组 let pool = generateMatrix(3, 28, 20); // 洗牌 pieces是拼图的dom集合 shuffle(pieces, pool);
该游戏的核心算法已经交给大家了,如想体验真实游戏,欢迎交流哈,如果想