本红包雨项目是基于HTML5的游戏框架Phaser写的,最终形成的是一个canvas,所以性能很好,但是必须要说的是这个框架比较大,压缩后也有700K左右,所以请慎用.

代码地址: https://github.com/AmosXu/red-packet-rain

1. 效果展示

       

图片依次是倒计时页面,抢红包页面,拆红包页,红包展示页,这些页面都是写在一个canvas里面的,无刷新的切换效果,性能超级棒

2.代码展示

贴上主要的代码js代码和注释

  //初始化图片
let imgjishi = 'assets/img/daojishi.png'
let bgPlan = 'assets/img/bg-plan.jpg'
let bgRainer = 'assets/img/bg-rainer.jpg'
let redpacket = 'assets/img/redpacket.png'
let close = 'assets/img/close.png'
let dialogExit = 'assets/img/dialog-exit.png'
let buttonCancel = 'assets/img/button-cancel.png'
let buttonExit = 'assets/img/button-exit.png'
let openRedpacket = 'assets/img/open-redpacket.png'
let open = 'assets/img/open.png'
let redpacketResult = 'assets/img/redpacket-result.png'
let buttonUseTicket = 'assets/img/button-use-ticket.png'
let buttonContinue = 'assets/img/button-continue.png'
let cursorAnimation = 'assets/img/cursor-animation.png' let states = {}
let QingLvGroup;
let hitNum = 0;
let config = {
selfPool:40,
selfPic:'redpacket',
rate:0.5,
maxSpeed:1200,
minSpeed:400,
max:95
} let ids = [0, 1, 2, 3, 4, 5]
let redpackets = ['全场优惠50元', '20元代金券', '全场优惠50元', '20元代金券', '全场优惠50元', '20元代金券']
let time = 25;
let getIds = []
let radio = document.documentElement.clientWidth/375;
let e; function rfuc(n){
return n*radio;
} //初始化红包
function QingLv(config, game){
this.init = function(){
this.config = config;
QingLvGroup = game.add.group();
QingLvGroup.enableBody = true;
QingLvGroup.createMultiple(config.selfPool, config.selfPic); //初始化多个红包
QingLvGroup.setAll('anchor.y',1)
QingLvGroup.setAll('outOfBoundsKill', true);
QingLvGroup.setAll('checkWorldBounds', true);
this.maxWidth = game.width + 300; game.time.events.loop(Phaser.Timer.SECOND * config.rate, this.createQL, this);
};
this.createQL = function(){
e = QingLvGroup.getFirstExists(false); if(e) {
if(hitNum >= config.max) {
return;
}
hitNum++;
e.events.onInputDown.removeAll();
var ram= Math.random();
ram =ram<0.5?ram+=0.5: ram;
e.loadTexture(this.config.selfPic)
e.alpha = 1;
e.angle = 30
// e.scale.setTo(rfuc(ram));
e.reset(game.rnd.integerInRange(100, this.maxWidth), 100) //红包生成的位置
e.body.velocity.x = game.rnd.integerInRange(-300, -150); //红包移动的速度
e.body.velocity.y = game.rnd.integerInRange(config.minSpeed, config.maxSpeed);
e.inputEnabled = true;
e.events.onInputDown.add(this.hitted, this)
}
};
this.hitted = function(sprite){
if(Math.random() < 1/4 && ids.length > 0) {
sprite.kill(); //点击获得红包,游戏暂停
game.paused = true; //背景
let hexGraphics = new Phaser.Graphics().beginFill(0x000000, 0.5).drawRect(0,0,document.documentElement.clientWidth,document.documentElement.clientHeight + 2);
let pausedMask = game.add.sprite(0, 0, hexGraphics.generateTexture()) let openDialog = game.add.sprite(rfuc(62), rfuc(150), 'openRedpacket') let open = game.add.sprite(rfuc(130), rfuc(300), 'open')
open.inputEnabled = true; let result = game.add.sprite(rfuc(0), rfuc(120), 'redpacketResult')
result.visible = false let userTicket = game.add.sprite(rfuc(78), rfuc(445), 'buttonUseTicket')
userTicket.visible = false let goOn = game.add.sprite(rfuc(198), rfuc(445), 'buttonContinue')
goOn.visible = false let ticketText = {};
let link = '' //拆红包
let clickOpen = function() { //游戏暂停时,点击事件无效,只能通过这种画热点的形式来绑定事件
let openRect = new Phaser.Rectangle(rfuc(130), rfuc(315), 239, 239).copyFrom(open); if (openRect.contains(game.input.x, game.input.y)) {
let currentWidth = open.width //拆红包动画
let tempArr = [2, 4, 8, 4, 2, 1]
let index = 0;
let timer = setInterval(function() {
if (index > tempArr.length-1) { index = 0 }
open.width = currentWidth / tempArr[index]
open.height = open.height
open.left = game.world.centerX - open.width / 2
++index
}, 200)
game.input.onDown.remove(clickOpen, this);
let arrIndex = Math.floor(Math.random() * ids.length)
let redpacketId = ids.splice(arrIndex, 1)
getIds.push(redpacketId[0]) setTimeout(()=> {
timer && clearInterval(timer)
document.getElementById('audioOpen').play()
let text = redpackets[redpacketId[0]]
ticketText = game.add.text(0, rfuc(338), text, {fill: '#ffe67d', fontSize: '46px', fontWeight: 'bolder'})
ticketText.left = game.world.centerX - ticketText.width / 2 //文字相对于屏幕左右居中
openDialog.visible = false
open.visible = false
result.visible = true
userTicket.visible = true
goOn.visible = true
game.input.onDown.add(clickButton, this)
}, 1000)
}
}; let clickButton = function() {
let userTicketRect = new Phaser.Rectangle(rfuc(78), rfuc(445), 194, 66).copyFrom(userTicket);
let continueRect = new Phaser.Rectangle(rfuc(198), rfuc(445), 194, 66).copyFrom(goOn); if (userTicketRect.contains(game.input.x, game.input.y)) {
window.location.replace(link)
game.input.onDown.remove(clickButton, this); } else if (continueRect.contains(game.input.x, game.input.y)) {
result.visible = false
userTicket.visible = false
goOn.visible = false
pausedMask.visible = false
ticketText.visible = false
game.paused = false
game.input.onDown.remove(clickButton, this);
}
} game.input.onDown.add(clickOpen, this)
} else {
sprite.inputEnabled = false;
var anim = sprite.animations.add(config.selfPic);
sprite.play(config.selfPic, 40, false);
anim.onComplete.add(this.fade, this, sprite)
}
};
this.fade = function(sprite){
var tween = game.add.tween(sprite).to({alpha:0}, 300, 'Linear', true)
tween.onComplete.add(this.killed, this, sprite);
};
this.killed = function(sprite){
sprite.kill();
}
}
states.boot = function(game) {
this.preload = function() {
if (typeof(GAME) !== "undefined") {
this.load.baseURL = GAME + "/";
}
if (!game.device.desktop) {
this.scale.scaleMode = Phaser.ScaleManager.EXACT_FIT;
this.scale.forcePortrait = true;
this.scale.refresh();
}
};
this.create = function() {
game.stage.backgroundColor = '#FFF';
game.state.start('preload');
};
};
states.preload = function(game) {
this.preload = function(game) {
//加载图片
game.load.spritesheet('daojishi', imgjishi, 250,120, 4)
game.load.image('bgPlan', bgPlan)
game.load.image('bgRainer', bgRainer)
game.load.spritesheet('redpacket', redpacket, 144, 173, 2)
game.load.image('close', close)
game.load.image('dialogExit', dialogExit)
game.load.image('buttonExit', buttonExit)
game.load.image('buttonCancel', buttonCancel)
game.load.image('openRedpacket', openRedpacket)
game.load.image('open', open)
game.load.image('redpacketResult', redpacketResult)
game.load.image('buttonContinue', buttonContinue)
game.load.image('buttonUseTicket', buttonUseTicket)
game.load.spritesheet('cursorAnimation', cursorAnimation, 74, 108, 2)
};
this.create = function() {
game.state.start('main');
};
};
states.main = function(game) {
this.create = function() {
// 物理系统
game.physics.startSystem(Phaser.Physics.ARCADE); // 背景图
var bgPlan = game.add.sprite(0, 0, 'bgPlan');
bgPlan.width = game.width;
bgPlan.height = game.height; var cursorPointer = game.add.sprite(game.world.centerX - 36, game.world.centerY + 86, 'cursorAnimation');
var anim = cursorPointer.animations.add('cursorAnimation');
cursorPointer.play('cursorAnimation', 2, true); document.getElementById('audioCountDown').play() // 开始游戏倒计时
var daojishi = game.add.sprite(game.world.centerX - 140, game.world.centerY - 400, 'daojishi');
var anim = daojishi.animations.add('daojishi');
daojishi.play('daojishi', 1, false);
anim.onComplete.add(this.startGame, this, daojishi);
}; this.startGame = function(daojishi){
this.leftTime = time
let bgRainer = game.add.sprite(0, 0, 'bgRainer');
bgRainer.width = game.width;
bgRainer.height = game.height;
daojishi.visible = false;
this.createQingLv(); //添加按钮,并绑定事件
let closeImg = game.add.button(rfuc(20), rfuc(20), 'close', function(){
game.paused = true
pausedMask.visible = true
exitDialog.visible = true
exitButton.visible = true
cancelButton.visible = true game.input.onDown.add(buttonClick, this)
}.bind(this)) // 剩余时间
this.leftTimeText = game.add.text(0, 0, this.leftTime, {fill: '#FFF', fontSize: '40px', fontWeight: 'bolder'})
this.leftTimeText.scale.setTo(rfuc(1))
this.leftTimeText.fixedToCamera = true;
this.leftTimeText.cameraOffset.setTo(game.camera.width - rfuc(80), rfuc(20)); let hexGraphics = new Phaser.Graphics().beginFill(0x000000, 0.5).drawRect(0,0,document.documentElement.clientWidth,document.documentElement.clientHeight + 2);
let pausedMask = game.add.sprite(0, 0, hexGraphics.generateTexture())
pausedMask.visible = false; let exitDialog = game.add.sprite(rfuc(62), rfuc(150), 'dialogExit')
exitDialog.visible = false; let exitButton = game.add.button(rfuc(80), rfuc(315), 'buttonExit')
exitButton.visible = false; let isExit = false
let cancelButton = game.add.button(rfuc(200), rfuc(315), 'buttonCancel')
cancelButton.visible = false; game.time.events.repeat(Phaser.Timer.SECOND, this.leftTime, this.refreshTime, this) let buttonClick = function() {
let cancelRect = new Phaser.Rectangle(rfuc(200), rfuc(315), 194, 66).copyFrom(cancelButton);
if (cancelRect.contains(game.input.x, game.input.y)) {
game.input.onDown.remove(buttonClick, this)
game.paused = false
pausedMask.visible = false
exitDialog.visible = false
exitButton.visible = false
cancelButton.visible = false
}
}
}; this.createQingLv = function(){
this.qinglv = new QingLv(config, game);
this.qinglv.init();
this.qinglv = new QingLv(config, game);
this.qinglv.init();
}; this.refreshTime = function(){
this.leftTime--;
var tem = this.leftTime;
this.leftTimeText.text = tem;
if(this.leftTime === 0) {
game.paused = true;
}
}
}; //生成游戏
let game = null
if (game == null) {
game = new Phaser.Game(document.documentElement.clientWidth, document.documentElement.clientHeight + 2, Phaser.AUTO, document.getElementById('gameScreen'));
game.state.add('boot', states.boot.bind(game));
game.state.add('preload', states.preload.bind(game));
game.state.add('main', states.main.bind(game));
game.state.start('boot');
}

3. 疑难问题

1. 游戏暂停时,点击事件无效,需要点击,怎么解决

答:  通过全局事件画热点的形式绑定事件,一定要记得移除事件,一定一定要记得

game.input.onDown.add(clickOpen, this)   //给游戏绑定全局事件

let userTicket = game.add.sprite(rfuc(78), rfuc(445), 'buttonUseTicket')
let userTicketRect = new Phaser.Rectangle(78, 445, 194, 66).copyFrom(userTicket); //获得button的区域 //如果点击的位置为button的位置就执行下一步
if (userTicketRect.contains(game.input.x, game.input.y)) {
  //移除全局事件
game.input.onDown.remove(clickButton, this);
}

2. 文字或图片相对于屏幕居中(暂时只能做屏幕居中)

答: 添加文字到游戏中,文字向左的偏移量等于游戏屏幕的宽度减去文字宽度的一般,就能达到居中的效果

ticketText = game.add.text(0, rfuc(338), '我想居中', {fill: '#ffe67d', fontSize: '46px', fontWeight: 'bolder'})
ticketText.left = game.world.centerX - ticketText.width / 2 //文字相对于屏幕左右居中

代码地址: https://github.com/AmosXu/red-packet-rain

 

仿淘宝,京东红包雨(基于Phaser框架)的更多相关文章

  1. iOS开发 仿淘宝,京东商品详情3D动画

    - (void)show { [[UIApplication sharedApplication].windows[0] addSubview:self.projectView]; CGRect fr ...

  2. vue mint-ui 实现省市区街道4级联动(仿淘宝京东收货地址4级联动)

    demo及源码地址 https://github.com/artiely/citypicker 先去下载一个“省份.城市.区县.乡镇” 四级联动数据,然后 引入 import { Picker } f ...

  3. 一款基于jQuery仿淘宝红色分类导航

    今天给大家分享一款基于jQuery仿淘宝红色分类导航.这款分类导航适用浏览器:IE8.360.FireFox.Chrome.Safari.Opera.傲游.搜狗.世界之窗.效果图如下: 在线预览    ...

  4. 基于Bootstrap仿淘宝分页控件实现

    .header { cursor: pointer } p { margin: 3px 6px } th { background: lightblue; width: 20% } table { t ...

  5. 原生js仿淘宝手机购买选项代码

    这是一款基于原生js实现仿淘宝手机信息购买选项效果源码,界面整体效果仿照淘宝购物选项设计,点击不同选项还可实时显示不同的价格计算结果,界面简洁大方.美观实用.可兼容目前最新的各类主流浏览器. 在线演示 ...

  6. Android中仿淘宝首页顶部滚动自定义HorizontalScrollView定时水平自动切换图片

    Android中仿淘宝首页顶部滚动自定义HorizontalScrollView定时水平自动切换图片 自定义ADPager 自定义水平滚动的ScrollView效仿ViewPager 当遇到要在Vie ...

  7. jquery仿淘宝规格颜色选择效果

    jquery实现的仿淘宝规格颜色选择效果源代码如下 jquery仿淘宝规格颜色选择效果 -收缩HTML代码 运行代码 [如果运行无效果,请自行将源代码保存为html文件运行] <script t ...

  8. 高仿淘宝和聚美优品商城详情页实现《IT蓝豹》

    高仿淘宝和聚美优品商城详情页实现 android-vertical-slide-view高仿淘宝和聚美优品商城详情页实现,在商品详情页,向上拖动时,可以加载下一页. 使用ViewDragHelper, ...

  9. android版高仿淘宝客户端源码V2.3

    android版高仿淘宝客户端源码V2.3,这个版本我已经更新到2.3了,源码也上传到源码天堂那里了,大家可以看一下吧,该应用实现了我们常用的购物功能了,也就是在手机上进行网购的流程的,如查看产品(浏 ...

随机推荐

  1. 属于自己的MES(一)概念

    什么叫MES(生产制造执行系统)? 从几个方面来简单说下: 1.定位 没有MES前的工厂生产模式,公司MRP系统与生产现场之间透过人为方式沟通,使生产现场如同黑箱作业,无法掌握实时正确信息. MES的 ...

  2. 关于ie的h5 刷新和ctrl+5刷新 以及图标刷新的问题

    最近在做一个表单验证,当表单失去焦点的时候触发错误提示. 可是..... 火狐 欧朋 刷新都没有问题,而在ie edge 用f5刷新的时候 都能记住之前的焦点 造成提示混乱. 问题算是解决: < ...

  3. VR全景智慧城市-梦幻城市降临

    有人说,创业分为两种,一种是富创业,一种是穷创业! 什么是富创业呢?就是拿钱砸,我觉得这种说法有点荒唐,为什么创业,因为没钱才会去创业,有钱的那不叫创业的,那是拿钱投资点项目. 看看老一辈的富豪,不都 ...

  4. Linux parent process and child process when 'sudo'

    如果在一般用户下如user,执行sudo命令,会产生两个进程. ps -ef | grep Container root 4305 643 0 16:37 pts/39 00:00:00 sudo . ...

  5. Vue常用经典开源项目汇总参考-海量

    Vue常用经典开源项目汇总参考-海量 Vue是什么? Vue.js(读音 /vjuː/, 类似于 view) 是一套构建用户界面的 渐进式框架.与其他重量级框架不同的是,Vue 采用自底向上增量开发的 ...

  6. Log4net日志记录、详细配置(自己使用)

    [来自百度百科的一句介绍]log4net库是Apache log4j框架在Microsoft .NET平台的实现,是一个帮助程序员将日志信息输出到各种目标(控制台.文件.数据库等)的工具. 1.首先添 ...

  7. A Bayesian election prediction, implemented with R and Stan

    If the media coverage is anything to go by, people are desperate to know who will win the US electio ...

  8. HBuilder 安装使用教程

    前段时间朋友让我帮忙打包一个 IPA 文件(使用 HTML5 开发的 Web 应用),了解到 HBuilder 这款 H5 开发神器.之前一直使用 WebStorm 开发 H5,闲来无事也学习下 HB ...

  9. js常用的4种截取字符串方法

    平常经常把这几个api的参数记混了,于是打算记录下来,当不确定的时候在拿出来翻翻: 在做项目的时候,经常会需要截取字符串,所以常用的方法有slice().substr().substring().ma ...

  10. Java常用类之【Math类、Random类、System类、Runtime类】

    一.Math类 Math类 [绝对值]Math.abs();//返回对应类型的绝对值 [最大值和最小值]Math.max(int a, int b) ,Math.min(int a,int b);(其 ...