Commit 1cde35b5 authored by 徐士卿's avatar 徐士卿

Merge branch 'master' of gitlab2.dui88.com:sparkprojects/kww_daydaygetcredits_250512

parents 09fe7d51 d9c31501
......@@ -31,11 +31,11 @@ export default [
creditsNum: 100,
expireTime: Date.now() + 10000000,
},
bubbleRecords: new Array(4).fill(1).map((_, index) => {
bubbleRecords: new Array(1 + (Math.random() * 4 >> 0)).fill(1).map((_, index) => {
return {
id: index,
creditsNum: 100 + Math.random() * 100 >> 0,
expireTime: Date.now() + 10000000,
expireTime: Date.now() + 10000 + (Math.random() * 10 >> 0) * 1000,
type: 1 + Math.random() * 2 >> 0,
}
}),
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -78,6 +78,7 @@ const API = generateAPI({
completeGuide: "main/completeGuide.do",
sign: "main/sign.do",
receiveBubble: "main/receiveBubble.do",
startVideo: "main/startVideo.do",
})
......
src/assets/Game/标语.png

644 Bytes | W: | H:

src/assets/Game/标语.png

4.27 KB | W: | H:

src/assets/Game/标语.png
src/assets/Game/标语.png
src/assets/Game/标语.png
src/assets/Game/标语.png
  • 2-up
  • Swipe
  • Onion skin
src/assets/Game/红色气泡.png

12.4 KB | W: | H:

src/assets/Game/红色气泡.png

13.6 KB | W: | H:

src/assets/Game/红色气泡.png
src/assets/Game/红色气泡.png
src/assets/Game/红色气泡.png
src/assets/Game/红色气泡.png
  • 2-up
  • Swipe
  • Onion skin
......@@ -25,6 +25,7 @@
top: 78px;
width: 644px;
height: 665px;
pointer-events: none;
}
.credits {
......
......@@ -28,6 +28,7 @@ import { SvgaPlayer } from "@grace/svgaplayer";
import lightSvga from "@/assets/svga/5光点飘散.svga";
import API from "@/api";
import VideoPanel from "@/panels/VideoPanel/VideoPanel.tsx";
@observer
......@@ -41,8 +42,6 @@ class HomePage extends React.Component<any, any> {
// if (getUrlParam('inviteCode')) {
// store.doAssist()
// }
// ModalCtrl.showModal(Fail_challenge);
}
clickPrize = () => {
......
import { Base } from "@/pages/HomePage/Top/base/Base.ts";
import { Assets, Sprite, Text } from "pixi.js";
import { Base, IBase } from "@/pages/HomePage/Top/base/Base.ts";
import { Assets, Container, Sprite, Text, Ticker } from "pixi.js";
import { Ease, Tween } from "@/core/tween";
import store from "@/store/store.ts";
import { _asyncThrottle, prefixInteger } from "@/utils/utils.ts";
import API from "@/api";
import { GameEvent, globalEvent } from "@/pages/HomePage/Top/GameEvent.ts";
import { BubbleMgr } from "@/pages/HomePage/Top/Components/BubbleMgr.ts";
export interface IBubbleInfo {
id: string;
......@@ -8,15 +14,20 @@ export interface IBubbleInfo {
type: "1" | "2", // 1-签到气泡 2-积分气泡
}
export class Bubble extends Base {
export class Bubble extends Container implements IBase {
text: Text;
time: Text;
id: string;
protected onLoad() {
constructor() {
super();
this.onLoad();
this.on("pointerup", this.onPointerUp, this);
}
onLoad() {
this.addChild(new Sprite(Assets.get("黄色气泡.png")))
.anchor.set(0.5, 0.5);
.anchor.set(0.5);
this.text = this.addChild(new Text({
text: `210`,
......@@ -27,7 +38,7 @@ export class Bubble extends Base {
fontWeight: "bold",
}
}));
this.text.anchor.set(0.5, 0.5);
this.text.anchor.set(0.5);
this.time = this.addChild(new Text({
text: `23:59:59`,
......@@ -38,13 +49,83 @@ export class Bubble extends Base {
fontWeight: "bold",
}
}));
this.time.anchor.set(0.5, 0.5);
this.time.anchor.set(0.5);
this.time.position.set(0, 65);
}
onPointerUp = _asyncThrottle(async () => {
const { x, y, parent } = this;
const { bubbleArr, bubbleMap } = parent as BubbleMgr;
const i = bubbleArr.indexOf(this);
if (i !== -1) bubbleArr[i] = null;
bubbleMap.delete(this.id);
this.removeFromParent();
this.destroy();
const { success, data } = await API.receiveBubble({ id: this.id });
store.updateIndexThrottle();
if (success) {
globalEvent.emit(GameEvent.ReceiveEffect, { x, y });
}
});
ideaEffect(wait: number, dir = 1) {
const y = this.y;
Tween.get(this)
.wait(wait)
.call(() => {
Tween.get(this, { loop: true })
.to({ y: y + 25 * dir }, 2000, Ease.quadInOut)
.to({ y: y }, 2000, Ease.quadInOut);
});
}
cdTarget = { cd: 0 }
setInfo(info: IBubbleInfo) {
const { id, creditsNum, expireTime, type } = info;
this.id = id;
this.text.text = creditsNum;
this.cdTarget.cd = Math.max((expireTime - Date.now()) / 1000 >> 0, 1);
this.setTime();
Tween.removeTweens(this.cdTarget);
Tween.get(this.cdTarget, { loop: true })
.wait(1000)
.call(() => {
this.cdTarget.cd -= 1;
if (this.cdTarget.cd <= 0) {
this.cdTarget.cd = 0;
store.updateIndexThrottle();
Tween.removeTweens(this.cdTarget);
}
this.setTime();
});
}
setTime() {
const cd = this.cdTarget.cd;
const h = Math.floor(cd / 3600);
const min = Math.floor((cd - h * 3600) / 60);
const sec = Math.floor(cd - h * 3600 - min * 60);
this.time.text = `${prefixInteger(h, 2)}:${prefixInteger(min, 2)}:${prefixInteger(sec, 2)}`;
}
onUpdate(time: Ticker) {
}
onDestroy() {
Tween.removeTweens(this);
Tween.removeTweens(this.cdTarget);
}
destroy() {
this.onDestroy();
super.destroy();
}
}
import { Base } from "@/pages/HomePage/Top/base/Base.ts";
import { observer, reactor } from "@/pages/HomePage/Top/mobx/decorators.ts";
import store from "@/store/store.ts";
import { Bubble, IBubbleInfo } from "@/pages/HomePage/Top/Components/Bubble.ts";
import { OverflowBubble } from "@/pages/HomePage/Top/Components/OverflowBubble.ts";
const posArr = [
{ x: 570, y: 250 },
{ x: 675, y: 450 },
{ x: 90, y: 440 },
{ x: 645, y: 660 },
]
@observer
export class BubbleMgr extends Base {
bubbleArr: Bubble[] = [];
bubbleMap: Map<string, Bubble> = new Map();
overflowBubble: OverflowBubble;
onLoad() {
this.overflowBubble = this.addChild(new OverflowBubble());
}
@reactor(() => store.indexData.bubbleRecords)
updateBubble(bubbleRecords: IBubbleInfo[]) {
const newBubbleKeys = [];
bubbleRecords.forEach((info) => newBubbleKeys.push(info.id));
// 找到bubbleArr中不在newBubbleKeys中的bubble
this.bubbleArr.forEach((bubble, i) => {
if (bubble && !newBubbleKeys.includes(bubble.id)) {
bubble.destroy();
this.bubbleArr[i] = null;
this.bubbleMap.delete(bubble.id);
}
});
// 更新数据
bubbleRecords.forEach((info, index) => {
const { id } = info;
let bubble = this.bubbleMap.get(id);
if (!bubble) {
bubble = this.addChild(new Bubble());
// 找到bubbleArr第一个空的位置
let emptyPos = -1;
for (let i = 0; i < 4; i++) {
if (!this.bubbleArr[i]) {
emptyPos = i;
break;
}
}
bubble.position.set(posArr[emptyPos].x, posArr[emptyPos].y)
bubble.ideaEffect(150 * index, Math.random() > 0.5 ? 1 : -1);
this.bubbleArr[emptyPos] = bubble;
this.bubbleMap.set(id, bubble);
}
bubble.setInfo(info);
});
}
onDestroy() {
}
}
import { Base, IBase } from "@/pages/HomePage/Top/base/Base.ts";
import { Assets, Container, Sprite, Text, Ticker } from "pixi.js";
import { Ease, Tween } from "@/core/tween";
import store from "@/store/store.ts";
import { prefixInteger } from "@/utils/utils.ts";
import { reactor, observer } from "@/pages/HomePage/Top/mobx/decorators.ts";
export interface IOverflowBubbleInfo {
id: string;
creditsNum: string,
expireTime: number,
}
@observer
export class OverflowBubble extends Container implements IBase {
text: Text;
time: Text;
id: string;
constructor() {
super();
this.onLoad();
}
onLoad() {
this.position.set(200, 240);
this.addChild(new Sprite(Assets.get("红色气泡.png")))
.anchor.set(0.5);
this.text = this.addChild(new Text({
text: `210`,
style: {
fontSize: 32,
fill: 0xffffff,
align: "center",
fontWeight: "bold",
}
}));
this.text.anchor.set(0.5);
this.time = this.addChild(new Text({
text: `23:59:59`,
style: {
fontSize: 21,
fill: 0xffffff,
align: "center",
}
}));
this.time.anchor.set(0.5);
this.time.position.set(0, 26);
const tsp = this.addChild(new Sprite(Assets.get("标语.png")));
tsp.anchor.set(0.5);
tsp.position.set(0, 58);
this.effect();
}
effect() {
const y = this.y;
Tween.get(this, { loop: true })
.to({ y: y + 25 }, 2000, Ease.quadInOut)
.to({ y: y }, 2000, Ease.quadInOut);
}
cdTarget = { cd: 0 }
@reactor(() => store.indexData.overflowBubble)
updateOverflow(info: IOverflowBubbleInfo) {
if (!info) {
this.visible = false;
Tween.removeTweens(this.cdTarget);
return;
}
const { id, creditsNum, expireTime } = info;
this.id = id;
this.text.text = creditsNum;
this.cdTarget.cd = Math.max((expireTime - Date.now()) / 1000 >> 0, 1);
this.setTime();
Tween.removeTweens(this.cdTarget);
Tween.get(this.cdTarget, { loop: true })
.wait(1000)
.call(() => {
this.cdTarget.cd -= 1;
if (this.cdTarget.cd <= 0) {
this.cdTarget.cd = 0;
store.updateIndexThrottle();
Tween.removeTweens(this.cdTarget);
}
this.setTime();
});
}
setTime() {
const cd = this.cdTarget.cd;
const h = Math.floor(cd / 3600);
const min = Math.floor((cd - h * 3600) / 60);
const sec = Math.floor(cd - h * 3600 - min * 60);
this.time.text = `${prefixInteger(h, 2)}:${prefixInteger(min, 2)}:${prefixInteger(sec, 2)}`;
}
onUpdate(time: Ticker) {
}
onDestroy() {
Tween.removeTweens(this);
Tween.removeTweens(this.cdTarget);
}
destroy() {
this.onDestroy();
super.destroy();
}
}
......@@ -5,13 +5,16 @@ import { Base } from "@/pages/HomePage/Top/base/Base.ts";
import { observer, reactor } from "@/pages/HomePage/Top/mobx/decorators.ts";
import store from "@/store/store.ts";
import { GifSprite } from "pixi.js/gif";
import { Bubble } from "@/pages/HomePage/Top/Components/Bubble.ts";
import { BubbleMgr } from "@/pages/HomePage/Top/Components/BubbleMgr.ts";
import { Ease, Tween } from "@/core/tween";
import { GameEvent, globalEvent } from "@/pages/HomePage/Top/GameEvent.ts";
@observer
export class Game extends Base {
fullAni: AnimatedSprite;
dropAni: GifSprite;
bubbleMgr: BubbleMgr;
onLoad() {
const bg = this.addChild(new Sprite(Assets.get("招财猫_底.png")));
......@@ -22,7 +25,7 @@ export class Game extends Base {
});
const fullAni = this.fullAni = this.addChild(new AnimatedSprite(fullTextures));
fullAni.scale.set(0.66, 0.66);
fullAni.scale.set(0.66);
fullAni.anchor.set(0.5);
fullAni.position.set(375, 553);
fullAni.loop = false;
......@@ -41,13 +44,12 @@ export class Game extends Base {
const smile = this.addChild(new Sprite(Assets.get("表情_微笑.png")));
smile.anchor.set(0.5);
smile.scale.set(0.66, 0.66);
smile.scale.set(0.66);
smile.position.set(378, 564);
this.addChild(new Bubble()).position.set(375, 564);
this.bubbleMgr = this.addChild(new BubbleMgr());
this.on("pointerdown", this.onPointerDown, this);
this.on("pointerup", this.onPointerUp, this);
globalEvent.on(GameEvent.ReceiveEffect, this.receiveEffect, this);
}
playTo(progress: number) {
......@@ -69,7 +71,6 @@ export class Game extends Base {
this.fullAni.stop();
}
};
}
@reactor(() => store.indexData.currentStoreNum)
......@@ -82,19 +83,25 @@ export class Game extends Base {
this.playTo(store.indexData.currentStoreNum / storeLimitNum);
}
onPointerDown(e: any) {
console.log(e)
}
onPointerUp(e: any) {
console.log(e)
receiveEffect(point: PointData) {
for (let i = 0; i < 5; i++) {
const sp = this.addChild(new Sprite(Assets.get("黄色气泡.png")));
sp.anchor.set(0.5)
sp.position.set(point.x, point.y);
Tween.get(sp)
.wait(i * 50)
.to({ alpha: 0.7, scaleX: 0.5, scaleY: 0.5, x: 80, y: 110 }, 888, Ease.quadOut)
.call(() => {
sp.destroy();
});
}
}
onUpdate(time: Ticker) {
const dt = time.deltaMS / 1000;
// const dt = time.deltaMS / 1000;
}
onDestroy() {
this.dropAni.removeFromParent();
}
}
import { EventEmitter } from "pixi.js"
/** 游戏事件 */
export enum GameEvent {
DIR_CHANGE = "DIR_CHANGE",
ReceiveEffect = "RECEIVE_EFFECT",
}
export const globalEvent = new EventEmitter();
......@@ -22,7 +22,7 @@
top: 520px;
width: 201px;
height: 292px;
touch-action: auto !important;
pointer-events: none;
}
.credits {
......@@ -33,6 +33,7 @@
font-size: 26px;
text-align: center;
color: #6f243d;
pointer-events: none;
span {
color: #f3304a;
......
......@@ -9,9 +9,13 @@ import { Game } from "@/pages/HomePage/Top/Game.ts";
import { initBundle } from "@/pages/HomePage/Top/Helper.ts";
import { Tween } from "@/core/tween";
import { SvgaPlayer } from "@grace/svgaplayer";
import "./base/mix.ts";
import handSvga from "@/assets/svga/3牌子转动.svga";
import { initDevtools } from '@pixi/devtools';
import.meta.env.DEV && initDevtools({});
export function getApp(): Application {
return window["__app"];
}
......@@ -32,8 +36,11 @@ class Top extends React.Component<any, any> {
componentWillUnmount() {
Tween.removeAllTweens();
this.app.ticker.remove(this.onUpdate);
this.game.removeFromParent();
this.game.destroy();
this.game.destroy({
children: true,
texture: false,
textureSource: false,
});
}
async initStage() {
......
import { Container, Ticker } from "pixi.js";
import { Container, Ticker, DestroyOptions } from "pixi.js";
export class Base extends Container {
export interface IBase {
onLoad: () => void,
onDestroy: () => void,
onUpdate: (time: Ticker) => void,
}
export class Base extends Container implements IBase {
constructor() {
super();
......@@ -9,20 +15,20 @@ export class Base extends Container {
}, 0);
}
protected onLoad() {
onLoad() {
}
protected onDestroy() {
onDestroy() {
}
protected onUpdate(time: Ticker) {
onUpdate(time: Ticker) {
}
destroy() {
destroy(options?: DestroyOptions) {
this.onDestroy();
super.destroy();
super.destroy(options);
}
}
import { Container } from "pixi.js";
Object.defineProperty(Container.prototype, "scaleX", {
get: function () {
return this.scale.x;
},
set: function (value) {
this.scale.x = value;
},
});
Object.defineProperty(Container.prototype, "scaleY", {
get: function () {
return this.scale.y;
},
set: function (value) {
this.scale.y = value;
},
});
......@@ -7,6 +7,7 @@ import { ModalCtrl } from "@/core/ctrls/ModalCtrl.tsx";
import { SvgaPlayer } from "@grace/svgaplayer";
import bgEffectSvga from "@/assets/svga/2输出签到成功弹窗.svga";
import VideoPanel from "@/panels/VideoPanel/VideoPanel.tsx";
export interface ISkyFullGoldPanelProps {
creditsNum: number;
......@@ -26,9 +27,10 @@ class SkyFullGoldPanel extends React.Component<ISkyFullGoldPanelProps> {
};
clickVideo = _asyncThrottle(async () => {
const { creditsNum, multipleValue, url, taskId } = this.props;
const { url, taskId } = this.props;
ModalCtrl.closeModal();
ModalCtrl.showModal(VideoPanel, { taskId, url });
});
render() {
......
@import "../../res.less";
.VideoPanel {
width: 750px;
height: 1624px;
position: absolute;
left: 0;
top: 0;
.video {
border: solid rgba(255, 255, 255, 0.4) 3px;
border-radius: 60px;
background-color: rgb(0, 0, 0);
position: absolute;
left: 45px;
top: 86px;
width: 648px;
height: 1148px;
}
.cd {
border: 4px solid rgba(255, 255, 255, 0.4);
border-radius: 50%;
background-color: white;
position: absolute;
left: 535px;
top: 127px;
width: 112px;
height: 112px;
display: flex;
align-items: center;
justify-content: center;
font-size: 37px;
font-weight: bold;
}
.progressBar {
background-color: rgb(196, 196, 196);
position: absolute;
left: 522px;
top: 225px;
width: 146px;
height: 33px;
border-radius: 33px;
overflow: hidden;
}
.progressFill {
background-color: #e51f29;
position: absolute;
left: 0;
top: 0;
width: 146px;
height: 33px;
border-radius: 33px;
}
.close {
position: absolute;
left: 336px;
top: 1283px;
width: 79px;
height: 79px;
.webpBg("common/close.png");
}
}
import React from "react";
import { observer } from "mobx-react";
import styles from "./VideoPanel.module.less";
import { Button } from "@grace/ui";
import { _asyncThrottle } from "@/utils/utils.ts";
import { ModalCtrl } from "@/core/ctrls/ModalCtrl.tsx";
import classNames from "classnames";
import API from "@/api";
export interface IVideoPanelProps {
url: string;
taskId: string;
}
@observer
class VideoPanel extends React.Component<IVideoPanelProps> {
state = {
cd: 15,
}
video: HTMLVideoElement;
componentDidMount() {
this.video.onpause = this.onPause;
this.video.play();
this.startCd();
}
intervalId: number = 0;
startCd = () => {
this.intervalId = window.setInterval(() => {
const cd = this.state.cd - 1;
this.setState({ cd });
if (cd <= 0) {
clearInterval(this.intervalId);
this.finishVideoTask();
return;
}
}, 1000);
}
finishVideoTask = _asyncThrottle(async () => {
const { taskId } = this.props;
const { success, data } = await API.startVideo({ taskId });
if (!success) return;
const { creditsNum } = data;
})
onPause = () => {
}
clickClose = () => {
ModalCtrl.closeModal();
};
render() {
const { url } = this.props;
const { cd } = this.state;
return <div className={classNames(styles.VideoPanel, "modal_center")}>
<video
ref={(el) => this.video = el}
id="my-player"
className={styles.video}
preload="auto"
controls={true}
poster={`${url}?x-oss-process=video/snapshot,t_1000,f_jpg,w_648,h_1148,m_fast`}
>
<source src={url} type="video/mp4"/>
<p className="vjs-no-js">
To view this video please enable JavaScript, and consider upgrading to a
web browser that
<a href="https://videojs.com/html5-video-support/" target="_blank">
supports HTML5 video
</a>
</p>
</video>
<div className={styles.cd}>{cd ? `${cd}s` : "已完成"}</div>
<div className={styles.progressBar}>
<div className={styles.progressFill} style={{
width: `${100 - cd / 15 * 100}%`,
}}/>
</div>
<div className="com_banner"/>
<Button className={styles.close} onClick={this.clickClose}/>
</div>;
}
}
export default VideoPanel;
......@@ -7,6 +7,7 @@ import { ModalCtrl } from "@/core/ctrls/ModalCtrl.tsx";
import { SvgaPlayer } from "@grace/svgaplayer";
import bgEffectSvga from "@/assets/svga/2输出签到成功弹窗.svga";
import VideoPanel from "@/panels/VideoPanel/VideoPanel.tsx";
export interface IWedExpPanelProps {
signCredits: number;
......@@ -29,9 +30,10 @@ class WedExpPanel extends React.Component<IWedExpPanelProps> {
};
clickVideo = _asyncThrottle(async () => {
const { signCredits, extraCredits, boolLimit, multipleValue, url, taskId, newLuckCreditsNum } = this.props;
const { url, taskId } = this.props;
ModalCtrl.closeModal();
ModalCtrl.showModal(VideoPanel, { taskId, url });
});
render() {
......
......@@ -2,13 +2,15 @@ import { makeAutoObservable, } from 'mobx';
import API from '../api/index';
import { Toast } from "@grace/ui";
import { IWxShareInfo } from "@/built-in/share/weixin/weixin.ts";
import { getUrlParam } from '@/utils/utils';
import { _asyncThrottle, _debounce, getUrlParam } from '@/utils/utils';
import { PageCtrl } from '@/core/ctrls/PageCtrl';
import { ModalCtrl } from '@/core/ctrls/ModalCtrl';
import Choice_challenge from '../../src/components/choice_challenge/choice_challenge';
import SkyFullGoldPanel from "@/panels/SkyFullGoldPanel/SkyFullGoldPanel.tsx";
import Renewmodule from '../../src/components/renewmodule/renewmodule';
import Finish_task from '../../src/components/finish_task/finish_task';
import { IBubbleInfo } from "@/pages/HomePage/Top/Components/Bubble.ts";
import { IOverflowBubbleInfo } from "@/pages/HomePage/Top/Components/OverflowBubble.ts";
class Store {
......@@ -66,17 +68,9 @@ class Store {
boolSign: boolean,
}[],
overflowBubble?: {
id: number,
creditsNum: number,
expireTime: number,
},
overflowBubble?: IOverflowBubbleInfo,
bubbleRecords?: {
id: number,
creditsNum: number,
expireTime: number,
}[],
bubbleRecords?: IBubbleInfo[],
returnAwardCreditsNum?: number,
......@@ -102,6 +96,13 @@ class Store {
}
/**
* 更新首页信息,给倒计时用的
*/
updateIndexThrottle = _debounce(async () => {
return await this.updateIndex();
}, 500);
judgeActTime(brakeStart = true, brakeEnd = true) {
if (brakeStart && this.indexData.currentTime < this.indexData.actStartTime) {
Toast.show("活动未开始");
......
......@@ -7,7 +7,6 @@ import {viteMockServe} from "vite-plugin-mock";
import DuibaPublish from "./config/DuibaPublish/DuibaPublish.ts";
import dotenv from 'dotenv';
import * as path from "node:path";
import tailwindcss from "@tailwindcss/postcss";
import cssnano from 'cssnano';
// https://vitejs.dev/config/
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment