仿淘宝,京东红包雨(基于Phaser框架)
本红包雨项目是基于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框架)的更多相关文章
- iOS开发 仿淘宝,京东商品详情3D动画
- (void)show { [[UIApplication sharedApplication].windows[0] addSubview:self.projectView]; CGRect fr ...
- vue mint-ui 实现省市区街道4级联动(仿淘宝京东收货地址4级联动)
demo及源码地址 https://github.com/artiely/citypicker 先去下载一个“省份.城市.区县.乡镇” 四级联动数据,然后 引入 import { Picker } f ...
- 一款基于jQuery仿淘宝红色分类导航
今天给大家分享一款基于jQuery仿淘宝红色分类导航.这款分类导航适用浏览器:IE8.360.FireFox.Chrome.Safari.Opera.傲游.搜狗.世界之窗.效果图如下: 在线预览 ...
- 基于Bootstrap仿淘宝分页控件实现
.header { cursor: pointer } p { margin: 3px 6px } th { background: lightblue; width: 20% } table { t ...
- 原生js仿淘宝手机购买选项代码
这是一款基于原生js实现仿淘宝手机信息购买选项效果源码,界面整体效果仿照淘宝购物选项设计,点击不同选项还可实时显示不同的价格计算结果,界面简洁大方.美观实用.可兼容目前最新的各类主流浏览器. 在线演示 ...
- Android中仿淘宝首页顶部滚动自定义HorizontalScrollView定时水平自动切换图片
Android中仿淘宝首页顶部滚动自定义HorizontalScrollView定时水平自动切换图片 自定义ADPager 自定义水平滚动的ScrollView效仿ViewPager 当遇到要在Vie ...
- jquery仿淘宝规格颜色选择效果
jquery实现的仿淘宝规格颜色选择效果源代码如下 jquery仿淘宝规格颜色选择效果 -收缩HTML代码 运行代码 [如果运行无效果,请自行将源代码保存为html文件运行] <script t ...
- 高仿淘宝和聚美优品商城详情页实现《IT蓝豹》
高仿淘宝和聚美优品商城详情页实现 android-vertical-slide-view高仿淘宝和聚美优品商城详情页实现,在商品详情页,向上拖动时,可以加载下一页. 使用ViewDragHelper, ...
- android版高仿淘宝客户端源码V2.3
android版高仿淘宝客户端源码V2.3,这个版本我已经更新到2.3了,源码也上传到源码天堂那里了,大家可以看一下吧,该应用实现了我们常用的购物功能了,也就是在手机上进行网购的流程的,如查看产品(浏 ...
随机推荐
- DDD理论学习系列(2)-- 领域
DDD理论学习系列目录 1. 引言 领域一词,主要有以下两个意思: 一国主权所达之地. 学术思想或社会活动的范围. 不管是指国家的主权范围也好还是学术活动范围,都是在讲一个范围,一个界限. 比如我们常 ...
- jQuery手风琴制作
jQuery手风琴制作 说起手风琴,想必大家应该都知道吧,简单的来说就是可以来回收缩的这么一个东西,接下来,我就给大家演示一下用jQuery制作一个手风琴菜单! 写jQuery前,我们需要引用一个jQ ...
- 文本主题模型之LDA(二) LDA求解之Gibbs采样算法
文本主题模型之LDA(一) LDA基础 文本主题模型之LDA(二) LDA求解之Gibbs采样算法 文本主题模型之LDA(三) LDA求解之变分推断EM算法(TODO) 本文是LDA主题模型的第二篇, ...
- 学java网络编程的心得体会
网络编程简单思路 一.发送端1创建udp服务,通过DatagramSocket对象;2确定数据,封装成包DatagramPacket(byte[] buf, int length, InetAddre ...
- jQuery-强大的jQuery选择器、过滤器
1. 基础选择器 Basics 名称 说明 举例 #id 根据元素Id选择 $("divId") 选择ID为divId的元素 element 根据元素的名称选择, $(" ...
- GoodReads: Machine Learning (Part 3)
In the first installment of this series, we scraped reviews from Goodreads. In thesecond one, we per ...
- 2017-5-18 Repeater 重复器的使用
Repeater - 重复器HeaderTemplate - 先执行,执行一次FooterTemplate - 最后执行,执行一次ItemTemplate - 在Header之后执行,有多少条数据绑定 ...
- 支付宝支付-常用支付API详解(查询、退款、提现等)
所有的接口支持沙盒环境的测试 1.前言 前面几篇文件详细介绍了 支付宝提现.扫码支付.条码支付.Wap支付.App支付 支付宝支付-提现到个人支付宝 支付宝支付-扫码支付 支付宝支付-刷卡支付(条码支 ...
- 【2017-06-01】Linq基础+Lambda表达式实现对数据库的增删改查
一.Linq to sql 类 高集成化的数据库访问技术 使用Linq可以代替之前的Ado.Net.省去了自己敲代码的实体类和数据访问类的大量工作. 实体类: 添加一个Linq to sql 类 -- ...
- 平面之后3D成主流?VR全景表示不服!——全景智慧城市常诚
目前很多人对VR全景这个词汇没有明确的概念,更没有人做过全面的研究,VR全景是什么?VR全景可以做什么?不同于我们经常听到的VR色情,全景智慧城市常诚今天就来为各位做一个系统全面的介绍,深入了解之后, ...
