js
import cloud from '@tbmp/mp-cloud-sdk';
import * as PIXI from "@tbminiapp/pixi-miniprogram-engine";
const { registerCanvas, devicePixelRatio } = PIXI.miniprogram;
cloud.init({
env: 'test'
});
const resource = require('./resource.json');//资源
let resourceCopy = JSON.parse(JSON.stringify(resource));//重置资源
let Application = PIXI.Application,
Container = PIXI.Container,
loader = PIXI.loader,
Rectangle = PIXI.Rectangle,
TextureCache = PIXI.utils.TextureCache,
resources = PIXI.loader.resources,
TilingSprite = PIXI.extras.TilingSprite,
Sprite = PIXI.Sprite;
let application = null;//舞台
let gameTicker = null;//定时器
const reels = []; // 建立转盘
let REEL_WIDTH = 122;
let SYMBOL_SIZE = 143;
let running = false;
const tweening = [];
Component({
app: null,
pixiCanvas: null,
stageSize: null,
systemInfo: null,
contextType: '2d',
props: {
options: {
// 手动指定application的尺寸
// 全屏-以窗口宽高作为application的尺寸,当设置此选项后,手动设置的width\height会失效
isFullScreen: false,
// application是否背景透明
transparent: true,
// 背景颜色
backgroundColor: 0x111,
// 是否强制用2d上下文渲染,如果为false,则优先使用webgl渲染
forceCanvas: true
}
},
data: {
// 模块宽|高
moduleWidth: 726,
moduleHeight: 826,
moduleBg: "https://img.alicdn.com/imgextra/i4/39767794/O1CN01AOvG4T27RhdblKAbL_!!39767794.png",//背景图片
// 奖品数组
prizeData: [
{
"prizeName": "优惠卷",
"name": "prize_1",
"url": "https://img.alicdn.com/imgextra/i3/39767794/O1CN010T3TJZ27RhdUnHn1p_!!39767794.png",
},
{
"prizeName": "谢谢参与",
"name": "prize_2",
"url": "https://img.alicdn.com/imgextra/i2/39767794/O1CN013nYFLi27RhdZ4yi34_!!39767794.png",
},
{
"prizeName": "会员积分",
"name": "prize_3",
"url": "https://img.alicdn.com/imgextra/i1/39767794/O1CN01f5FSOF27RhdYa7dxN_!!39767794.png",
}
]
},
onInit() {
this.turnNum = 5; // 抽奖转圈数(必须大于1)
this.prevIndex = 0; // 上次摇到的编号
this.rollIndex = 2; // 抽中的编号 +1红包 +2积分 +3手机 +4耳机
this.prizeLes = this.data.prizeData.length;
this.firstIndexArr = [0, 1, 2];//刚开始随机数组
this.prevRIndexArr = [0, 0, 0]; // 上次摇到的随机数组
// 奖品数组
this.data.prizeData.map((item, index) => {
resourceCopy.texture.push(item)
});
this.setData({
resourceCopy: resourceCopy,
});
},
didMount() {
console.log("[Api-Test] didMount")
},
didUpdate() { },
didUnmount() { },
methods: {
onCanvasReady() {
// 建立canvas引用
my.createCanvas({
id: "canvas",
success: (canvas) => {
const { options } = this.props;
if (!options) {
console.warn('pixi-canvas 缺少options');
this.props.onError && this.props.onError.call(this, { code: -1, msg: 'pixi-canvas 缺少options' });
return;
}
this.pixiCanvas = canvas;
const systemInfo = this.systemInfo || my.getSystemInfoSync();
const { windowWidth } = systemInfo;
const contextType = options.forceCanvas ? '2d' : 'webgl';
const context = canvas.getContext(contextType);
let baseSize = { width: this.data.moduleWidth, height: this.data.moduleWidth };
const resolution = windowWidth / (baseSize.width / 2) * (devicePixelRatio / 2);
options.resolution = resolution;
options.width = baseSize.width;
options.height = baseSize.height;
registerCanvas(canvas);
this.mainCanvas = canvas;
this.app = new PIXI.Application({
view: canvas,
context: context,
...options
});
// 资源加载
this.initLoad();
},
});
},
// 初始化加载
initLoad() {
loader
.add(resourceCopy.texture)
.on("progress", (event, resources) => {
// this.progress(event.progress);
})
.load((event, resources) => {
this.initConfig();
this.app.ticker.add(delta => this.gameLoop(delta));
});
},
// 初始化样式
initConfig() {
const { stage } = this.app;
// 老虎机背景
this.bgContainer = new Container();
this.bgContainer.width = 589;
this.bgContainer.height = 438;
stage.addChild(this.bgContainer);
// 中层
this.middleContainer = new Container();
stage.addChild(this.middleContainer);
this.upperContainer = new Container();
// 资源
let slotTextures = [];
Object.keys(resources).map((item) => {
let sprite = new Sprite(resources[item].texture);
if (item.includes('老虎机')) {
this.addSprite(sprite, item, this.bgContainer, (this.data.moduleWidth - 589) / 2, 280);
} else if (item.includes('左箭头')) {
this.addSprite(sprite, item, this.upperContainer, 120, 520);
} else if (item.includes('右箭头')) {
this.addSprite(sprite, item, this.upperContainer, 556, 520);
} else if (item.includes('prize')) {
// this.addSprite(sprite, item, this.bgContainer, 0, 0);
// 添加奖品纹理
slotTextures.push(PIXI.Texture.fromFrame(item));
}
else if (item.includes('抽奖按钮')) {
this.addSprite(sprite, item, this.upperContainer, (this.data.moduleWidth - 244) / 2, 630);
}
});
// 滚动容器
const reelContainer = new Container();
reelContainer.width = 467;
reelContainer.height = 260;
reelContainer.position.x = (this.data.moduleWidth - 465) / 2;
reelContainer.position.y = 310;
// 滚动遮罩
const thing = new PIXI.Graphics();
stage.addChild(thing);
thing.drawRoundedRect(0, 0, 467, 255, 20);
thing.x = (this.data.moduleWidth - 430) / 2;
thing.y = 406;
reelContainer.mask = thing;
for (let i = 0; i < 3; i++) {
const rc = new PIXI.Container();
rc.x = i * (REEL_WIDTH + 30) + 10;
reelContainer.addChild(rc);
const reel = {
container: rc,
symbols: [],
position: 0,
previousPosition: 0,
blur: new PIXI.filters.BlurFilter(),
};
reel.blur.blurX = 0;
reel.blur.blurY = 0;
rc.filters = [reel.blur];
// 建立symbol
for (let j = 0; j < slotTextures.length; j++) {
// 初始化位置
let index = (this.firstIndexArr[i] + j) > (this.prizeLes - 1) ? ((this.firstIndexArr[i] + j) - (this.prizeLes)) : (this.firstIndexArr[i] + j);
const symbol = new PIXI.Sprite(slotTextures[index]);
// const symbol = new PIXI.Sprite(slotTextures[j]);
// 缩放symbol以适合symbol区域。
symbol.y = j * (SYMBOL_SIZE);
symbol.scale.x = symbol.scale.y = Math.min(SYMBOL_SIZE / symbol.width, SYMBOL_SIZE / symbol.height);
symbol.x = Math.round((SYMBOL_SIZE - symbol.width) / 2);
reel.symbols.push(symbol);
rc.addChild(symbol);
}
reels.push(reel);
}
stage.addChild(reelContainer);
// 上层
stage.addChild(this.upperContainer);
this.upperContainer.interactive = true;
this.upperContainer.buttonMode = true;
this.upperContainer.on('pointerup', () => {
if (running) return;
// if (this.data.rollCount > 0) {
// this.startPlay();
// } else {
// my.alert({ title: '抽奖次数不足' });
// }
this.startPlay();
});
//
this.app.ticker.add((delta) => {
// Update the slots.
for (let i = 0; i < reels.length; i++) {
const r = reels[i];
// Update blur filter y amount based on speed.
// This would be better if calculated with time in mind also. Now blur depends on frame rate.
r.blur.blurY = (r.position - r.previousPosition) * 8;
r.previousPosition = r.position;
// Update symbol positions on reel.
for (let j = 0; j < r.symbols.length; j++) {
const s = r.symbols[j];
const prevy = s.y;
s.y = ((r.position + j) % r.symbols.length) * (SYMBOL_SIZE + 10);
if (s.y < 0 && prevy > SYMBOL_SIZE) {
// 检测外部并交换纹理。
// 随机添加
// s.texture = slotTextures[Math.floor(Math.random() * slotTextures.length)];
// 顺序添加
s.texture = slotTextures[j];
s.scale.x = s.scale.y = Math.min(SYMBOL_SIZE / s.texture.width, SYMBOL_SIZE / s.texture.height);
s.x = Math.round((SYMBOL_SIZE - s.width) / 2);
}
}
}
});
},
// 开始
startPlay() {
if (running) return;
running = true;
this.rIndexArr = [0, 0, 1];
for (let i = 0; i < reels.length; i++) {
const r = reels[i];
const extra = Math.floor(Math.random() * 3);
const target = r.position + (this.prizeLes * this.turnNum) + this.rIndexArr[i] + this.firstIndexArr[i]+1 + (this.prizeLes - this.prevRIndexArr[i]);
// const target = r.position + 10 + i * 5 + extra;
const time = 2500 + i * 600;
// const time = 2500 + i * 600 + extra * 600;
this.tweenTo(r, 'position', target, time, this.backout(0.5), null, i === reels.length - 1 ? this.reelsComplete : null);
this.prevRIndexArr[i] = this.rIndexArr[i] + this.firstIndexArr[i]+1;
}
this.prevIndex = this.rollIndex;
},
// 过渡结束
reelsComplete() {
running = false;
},
// 时间
backout(amount) {
return (t) => (--t * t * ((amount + 1) * t + amount) + 1);
},
tweenTo(object, property, target, time, easing, onchange, oncomplete) {
const tween = {
object,
property,
propertyBeginValue: object[property],
target,
easing,
time,
change: onchange,
complete: oncomplete,
start: Date.now(),
};
tweening.push(tween);
return tween;
},
randomArr(max, min, len) {
let rIndexArr = [];
for (let i = 0; i < len; i++) {
let rNum = Math.floor(Math.random() * max + min);
if (rIndexArr.indexOf(rNum) == -1) {
rIndexArr.push(rNum)
} else {
i--;
}
}
return rIndexArr;
},
lerp(a1, a2, t) {
return a1 * (1 - t) + a2 * t;
},
//添加精灵到容器
addSprite(sprite, name, stage, x, y) {
const { posConfig } = resourceCopy;
this[name] = sprite;
sprite.x = x;
sprite.y = y;
sprite.name = name;
stage && stage.addChild(sprite);
},
// 游戏循环
gameLoop(delta) {
const now = Date.now();
const remove = [];
for (let i = 0; i < tweening.length; i++) {
const t = tweening[i];
const phase = Math.min(1, (now - t.start) / t.time);
t.object[t.property] = this.lerp(t.propertyBeginValue, t.target, t.easing(phase));
if (t.change) t.change(t);
if (phase === 1) {
t.object[t.property] = t.target;
if (t.complete) t.complete(t);
remove.push(t);
}
}
for (let i = 0; i < remove.length; i++) {
tweening.splice(tweening.indexOf(remove[i]), 1);
}
},
// 点击触发
onTouchHandle(event) {
if (this.pixiCanvas && event.changedTouches && event.changedTouches.length) {
this.pixiCanvas.dispatchEvent(event);
}
},
// 云函数
invokeHelloWord() {
cloud.function.invoke('helloworld').then(res => {
my.alert({
content: res
})
})
}
},
});
axml
<view style="width:{{moduleWidth}}rpx;height:{{moduleHeight}}rpx;position:relative;margin:0rpx auto;">
<!-- 背景图 -->
<view style="width:{{moduleWidth}}rpx;height:{{moduleHeight}}rpx;">
<image mode="scaleToFill" src="{{moduleBg}}" style="width:{{moduleWidth}}rpx;height:{{moduleHeight}}rpx;"/>
</view>
<!-- 老虎机 -->
<view style="position:absolute;top:0rpx;left:0rpx;width:{{moduleWidth}}rpx;height:{{moduleHeight}}rpx;">
<canvas id="canvas" style="width:{{moduleWidth}}rpx;height:{{moduleHeight}}rpx;" type="webgl" onTouchStart="onTouchHandle" onTouchEnd="onTouchHandle" onTouchMove="onTouchHandle" onTouchCancel="onTouchHandle" onReady="onCanvasReady"></canvas>
</view>
</view>