创建项目
pnpm i -g @vue/cli
vue create red_pakage
pnpm i sass sass-locader -D
pnpm i --save normalize.css
pnpm i --save-dev postcss-px-to-viewport
pnpm i vant@latest-v2 -S
pnpm i babel-plugin-import -D
https://vant.pro/vant/v2/#/zh-CN/
<van-button @click="test" type="info" size="large">信息按钮</van-button>
<script>
import { Toast } from "vant";
export default {
name: "RedPackageApp",
data() {
return {};
},
mounted() {},
methods: {
test: function () {
Toast.success("成功文案");
},
},
};
</script>
封装Axios
pnpm i axios
import axios from "axios";
import { Toast } from "vant";
import getToken from "./getToken";
const request = axios.create({
baseURL: "/api",
timeout: 10000,
});
request.interceptors.request.use(
(config) => {
config.headers.token = getToken();
return config;
},
(error) => {
Toast.fail("请求发送失败" + error);
return Promise.reject(error);
}
);
request.interceptors.response.use(
(response) => {
console.log(response);
if (response.data.code === 200) {
return response.data;
} else {
Toast.fail("请求失败");
return Promise.reject(response);
}
},
(error) => {
Toast.fail("请求失败" + error);
return Promise.reject(error);
}
);
export default request;
倒计时遮罩
<template>
<van-overlay :show="isShow">
<div class="wrapper">
<van-count-down v-if="isShow" :time="time" v-slot="{ seconds }" @finish="finish">
<span class="seconds">{
{ seconds }}</span>
</van-count-down>
</div>
</van-overlay>
</template>
<script>
export default {
name: "CountdownMask",
props: ["onFinish"],
data() {
return {
isShow: false,
time: 3500,
};
},
mounted() {},
methods: {
show(time = 3500) {
this.isShow = true;
this.time = time;
},
finish() {
this.isShow = false;
this.onFinish();
// this.$emit("finish");
},
},
};
</script>
<style lang="scss" scoped>
.wrapper {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
.seconds {
font-size: 40px;
color: #f00;
}
</style>
<template>
<div>
<CountdownMask ref="countdownMast" :onFinish="onCountFinish" />
</div>
</template>
<script>
import request from "@/utils/request";
import CountdownMask from "./components/CountdownMask";
export default {
name: "RedPackageApp",
components: {
CountdownMask,
},
data() {
return {};
},
mounted() {
setTimeout(() => {
this.$refs.countdownMast.show(5000);
}, 2000);
},
methods: {
onCountFinish() {
console.log("onFinish in RedPackageApp");
},
},
};
</script>
<style lang="scss" scoped></style>
记录页遮罩
<template>
<van-overlay :show="isShow">
<div class="wrapper">
<div class="content">
<span class="title">恭喜获得</span>
<span class="amount">{
{ amount }}</span>
<span class="unit">元</span>
</div>
<van-icon name="close" class="close_icon" @click="close" />
</div>
</van-overlay>
</template>
<script>
export default {
name: "RecordMask",
props: ["onClose"],
data() {
return {
isShow: false,
amount: 0,
};
},
mounted() {},
methods: {
show(amount = 0) {
this.isShow = true;
this.amount = amount;
},
close() {
this.isShow = false;
this.onClose();
},
},
};
</script>
<style lang="scss" scoped>
.wrapper {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
background-image: url("../assets/reward.jpg");
background-repeat: no-repeat;
background-size: 300px;
background-position: center;
.content {
transform: translateY(15%);
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(255, 255, 255, 0.8);
padding: 20px;
border-radius: 10px;
}
.close_icon {
position: absolute;
top: 10px;
right: 10px;
color: rgb(255, 255, 255);
font-size: 40px;
}
}
</style>
<template>
<div>
<CountdownMask ref="countdownMast" :onFinish="onCountFinish" />
<RecordMask ref="recordMask" :onClose="onRecordClose" />
</div>
</template>
//
<script>
// import request from "@/utils/request";
import CountdownMask from "./components/CountdownMask";
import RecordMask from "./components/RecordMask.vue";
export default {
name: "RedPackageApp",
components: {
CountdownMask,
RecordMask,
},
data() {
return {};
},
mounted() {
setTimeout(() => {
this.$refs.recordMask.show(5000);
}, 2000);
},
methods: {
onCountFinish() {
console.log("onFinish in RedPackageApp");
},
onRecordClose() {
console.log("onRecordClose in RedPackageApp");
},
},
};
</script>
<style lang="scss" scoped></style>
下红包雨
import defaultUrl from './../assets/red.jpg';
export default class RedPacket {
constructor(options) {
this.url = options.url || defaultUrl;
this.width = options.width || '20vw';
this.height = options.height || 'auto';
this.callback = options.callback || function () { };
this.parent = options.parent || document.body;
this.create();
}
create( ) {
const img = document.createElement('img');
img.src = this.url;
img.style.width = this.width;
img.style.height = this.height;
// border-radius: 10px;
img.style.borderRadius = '10px';
img.ontouchstart = this.destroy.bind(this, img);
this.parent.appendChild(img);
}
destroy (currentImg) {
currentImg.remove();
this.callback();
}
}
<template>
<div>
<CountdownMask ref="countdownMast" :onFinish="onCountFinish" />
<div class="rain-container" ref="rainContainer"></div>
<RecordMask ref="recordMask" :onClose="onRecordClose" />
</div>
</template>
//
<script>
// import request from "@/utils/request";
import CountdownMask from "./components/CountdownMask";
import RecordMask from "./components/RecordMask.vue";
import RedPacket from "./class/RedPacket.js";
export default {
name: "RedPackageApp",
components: {
CountdownMask,
RecordMask,
},
data() {
return {};
},
mounted() {
setTimeout(() => {
// this.$refs.countdownMast.show(5000);
}, 2000);
new RedPacket({
parent: this.$refs.rainContainer,
});
new RedPacket({
parent: this.$refs.rainContainer,
});
new RedPacket({
parent: this.$refs.rainContainer,
});
new RedPacket({
parent: this.$refs.rainContainer,
});
new RedPacket({
parent: this.$refs.rainContainer,
});
new RedPacket({
parent: this.$refs.rainContainer,
});
},
methods: {
onCountFinish() {
console.log("onFinish in RedPackageApp");
},
onRecordClose() {
console.log("onRecordClose in RedPackageApp");
},
},
};
</script>
<style lang="scss" scoped>
.rain-container {
position: fixed;
left: 0;
top: 0;
bottom: 0;
right: 0;
background-image: linear-gradient(180deg, orange, red);
overflow: hidden;
}
</style>
动画逻辑-随机位置
import defaultUrl from './../assets/red.jpg';
export default class RedPacket {
constructor(options) {
// 生成一个0-4的随机整数.
const random = Math.floor(Math.random() * 5);
this.track = options.track || random * 20;
}
create( ) {
// position: absolute;
img.style.position = 'absolute';
// left: 80vw;
img.style.left = this.track + 'vw';
}
}
动画逻辑-下落动画
import defaultUrl from './../assets/red.jpg';
export default class RedPacket {
constructor(options) {
this.url = options.url || defaultUrl;
this.width = options.width || '20vw';
this.height = options.height || 'auto';
// 生成一个0-4的随机整数.
const random = Math.floor(Math.random() * 5);
this.track = options.track || random * 20;
// 生成一个-360-360的随机整数. // 生成一个3-8的随机整数.
const random1 = Math.floor(Math.random() * 721) - 360;
const random2 = Math.floor(Math.random() * 6) + 3;
this.rotate = options.rotate || random1;
this.duration = options.duration || random2;
this.callback = options.callback || function () { };
this.parent = options.parent || document.body;
this.create();
}
create( ) {
const img = document.createElement('img');
img.src = this.url;
img.style.width = this.width;
img.style.height = this.height;
// border-radius: 10px;
img.style.borderRadius = '10px';
// position: absolute;
img.style.position = 'absolute';
// left: 80vw;
img.style.left = this.track + 'vw';
img.animate([
{ transform: 'translateY(0)' },
{ transform: `translateY(120vh) rotate(${this.rotate}deg)` }
], {
duration: this.duration * 1000,
iterations: 1,
fill: 'forwards'
});
img.ontouchstart = this.destroy.bind(this, img);
this.parent.appendChild(img);
setTimeout(() => {
img.remove();
}, this.duration * 1000);
}
destroy (currentImg) {
currentImg.remove();
this.callback();
}
}
红包雨基本逻辑
<template>
<div>
<CountdownMask ref="countdownMast" :onFinish="onCountFinish" />
<div v-show="rainContainerShow" class="rain-container" ref="rainContainer"></div>
<RecordMask ref="recordMask" :onClose="onRecordClose" />
</div>
</template>
//
<script>
// import request from "@/utils/request";
import CountdownMask from "./components/CountdownMask";
import RecordMask from "./components/RecordMask.vue";
import RedPacket from "./class/RedPacket.js";
export default {
name: "RedPackageApp",
components: {
CountdownMask,
RecordMask,
},
data() {
return {
rainContainerShow: false,
timer: null,
};
},
mounted() {
this.$refs.countdownMast.show();
},
methods: {
onCountFinish() {
console.log("倒计时结束");
this.createRain(5000, 200);
},
onRecordClose() {
console.log("点了关闭");
},
onRainEnd() {
console.log("下完雨了");
this.$refs.recordMask.show(888);
},
onPacketClick() {
console.log(" PacketClick ");
},
createRain(duration, speed) {
console.log("开始下红包雨");
this.rainContainerShow = true;
this.timer = setInterval(() => {
new RedPacket({
parent: this.$refs.rainContainer,
callback: this.onPacketClick,
});
}, speed);
setTimeout(() => {
clearInterval(this.timer);
this.onRainEnd();
}, duration);
},
},
};
</script>
<style lang="scss" scoped>
.rain-container {
position: fixed;
left: 0;
top: 0;
bottom: 0;
right: 0;
background-image: linear-gradient(180deg, orange, red);
overflow: hidden;
}
</style>
部署详解
部署Redis
负载均衡