本红包雨项目是基于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. sqlmap连接Mysql实现getshell(原创)

    前言 昨天群友发了一知乎的帖子..才发现sqlmap玩了那么久有些玩意我居然没玩过...然后看着群友玩= =今天也想试试. 0x01 首先得知道这个玩意,sqlmap -help,不说大家也懂搜嘎. ...

  2. RowSet的使用

    ResultSet是使用Jdbc编程的人入门和常用的操作数据库的类,自 JDK1.4开始,易于使用RowSet接口被引入.RowSet 接口扩展了标准java.sql.ResultSet接口.RowS ...

  3. 《JavaScript高级程序设计》里对 call() 和 apply() 的解释 (116页)

    每个函数都包含两个非继承而来的方法:apply()和call().这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值. apply(): 方法接受两个参数:一个是在其 ...

  4. 我来说说XML文件中的xmlns、xmlns:xsi和xsi:schemaLocation的具体含义

      文章摘自:https://yq.aliyun.com/articles/40353               http://www.cnblogs.com/zhao1949/p/5652167. ...

  5. 【试验局】ReentrantLock中非公平锁与公平锁的性能测试

    硬件环境: CPU:AMD Phenom(tm) II X4 955 Processor Memory:8G SSD(128G):/ HDD(1T):/home/ 软件环境: OS:Ubuntu14. ...

  6. Chart.js – 效果精美的 HTML5 Canvas 图表库

    Chart.js 是一个令人印象深刻的 JavaScript 图表库,建立在 HTML5 Canvas 基础上.目前,它支持6种图表类型(折线图,条形图,雷达图,饼图,柱状图和极地区域区).而且,这是 ...

  7. 外部无法捕捉Realm的doGetAuthenticationInfo方法抛出的异常

    shiro权限框架,用户登录方法的subject.login(token)会进入自定义的UserNamePasswordRealm类的doGetAuthenticationInfo身份验证方法 通常情 ...

  8. 可视化Git版本管理工具SourceTree的使用

    最近去了新公司,发现公司使用的团队版本管理工具是SourceTree,本人一直是SVN的热衷粉,很少使用git,所以从头学习git及可视化客户端SourceTree的使用,本贴只针对新手,大牛可以无视 ...

  9. 利用formatter原理自动化参数化查询

    前言:对于经常忙于服务端开发的小伙伴来说,与DB层打交道是在正常不过的事了,但是每次页面的查询条件新增往往意味着后端代码参数化同比增长,当然你可以不使用sqlhelper自带的参数化条件查询,可以直接 ...

  10. R语言进行文件夹操作示例(转)

    rm(list=ls())path = 'J:/lab/EX29 --在R语言中进行文件(夹)操作'setwd(path)cat("file A\n", file="A& ...