mirror of
https://gitee.com/TSpecific/tuniao-ui.git
synced 2026-06-07 03:53:57 +08:00
227 lines
6.3 KiB
Vue
227 lines
6.3 KiB
Vue
<template>
|
|
<view class="template-bubble">
|
|
|
|
<!-- 顶部自定义导航 -->
|
|
<tn-nav-bar fixed alpha customBack>
|
|
<view slot="back" class='tn-custom-nav-bar__back'
|
|
@click="goBack">
|
|
<text class='icon tn-icon-left'></text>
|
|
<text class='icon tn-icon-home-capsule-fill'></text>
|
|
</view>
|
|
</tn-nav-bar>
|
|
|
|
<canvas canvas-id="bubble" id="bubble" class="bubble" :style="{width: `${windowWidth}px`, height: `${windowHeight}px`}"></canvas>
|
|
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import template_page_mixin from '@/libs/mixin/template_page_mixin.js'
|
|
export default {
|
|
name: 'TemplateBubble',
|
|
mixins: [template_page_mixin],
|
|
data(){
|
|
return {
|
|
windowHeight: 0,
|
|
windowWidth: 0,
|
|
actionTimer: null,
|
|
animationTimer: null,
|
|
queue: {},
|
|
ctx: null
|
|
}
|
|
},
|
|
onLoad() {
|
|
this.getSystemInfo()
|
|
},
|
|
onReady() {
|
|
this.$nextTick(() => {
|
|
this.queue = {}
|
|
this.ctx = uni.createCanvasContext("bubble", this)
|
|
|
|
setTimeout(() => {
|
|
this.actionTimer = setInterval(() => {
|
|
this.generateBubble()
|
|
}, 500)
|
|
}, 1000)
|
|
})
|
|
},
|
|
onUnload() {
|
|
this.clearActionTimer()
|
|
this.clearAnimationTimer()
|
|
},
|
|
methods: {
|
|
// 获取系统信息
|
|
getSystemInfo() {
|
|
const systemInfo = uni.getSystemInfoSync()
|
|
if (!systemInfo) {
|
|
setTimeout(() => {
|
|
this.getSystemInfo()
|
|
}, 50)
|
|
return
|
|
}
|
|
|
|
this.windowHeight = systemInfo.safeArea.height
|
|
this.windowWidth = systemInfo.safeArea.width
|
|
},
|
|
|
|
// 生成泡泡
|
|
generateBubble() {
|
|
const image = "https://resource.tuniaokj.com/images/bubble/" + this.$tn.number.randomInt(1, 33) + ".png"
|
|
uni.getImageInfo({
|
|
src: image,
|
|
success: (res) => {
|
|
if (res.errMsg === 'getImageInfo:ok') {
|
|
const anmationData = {
|
|
id: new Date().getTime(),
|
|
timer: 0,
|
|
opacity: 0,
|
|
pathData: this.generatePathData(),
|
|
image: res.path,
|
|
factor: {
|
|
speed: 0.0006, // 运动速度,值越小越慢
|
|
t: 0.1 // 贝塞尔函数系数,当为0,就是从无到有,这时候屏幕高度也要调一下
|
|
}
|
|
}
|
|
if (Object.keys(this.queue).length > 0) {
|
|
this.queue[anmationData.id] = anmationData
|
|
} else {
|
|
this.queue[anmationData.id] = anmationData
|
|
this.bubbleAnimate()
|
|
}
|
|
}
|
|
}
|
|
})
|
|
},
|
|
|
|
/* 动画相关 */
|
|
// 生成运动的路径数据
|
|
generatePathData() {
|
|
let width = this.windowWidth,
|
|
height = this.windowHeight;
|
|
const p0 = {
|
|
x: 0.72 * width,
|
|
y: height
|
|
}
|
|
const p1 = {
|
|
x: this.$tn.number.random(0.22 * width, 0.33 * width),
|
|
y: this.$tn.number.random(0.5 * height, 0.75 * height)
|
|
}
|
|
const p2 = {
|
|
x: this.$tn.number.random(0, 0.88 * width),
|
|
y: this.$tn.number.random(0.25 * height, 0.5 * height)
|
|
}
|
|
const p3 = {
|
|
x: this.$tn.number.random(0, 0.88 * width),
|
|
y: this.$tn.number.random(0, 0.125 * height)
|
|
}
|
|
return [p0, p1, p2, p3]
|
|
},
|
|
// 更新运动的路径
|
|
updatePath(data, factor) {
|
|
const p0 = data[0]
|
|
const p1 = data[1]
|
|
const p2 = data[2]
|
|
const p3 = data[3]
|
|
|
|
const t = factor.t
|
|
|
|
/*计算多项式系数 (下同)*/
|
|
const cx1 = 3 * (p1.x - p0.x)
|
|
const bx1 = 3 * (p2.x - p1.x) - cx1
|
|
const ax1 = p3.x - p0.x - cx1 - bx1
|
|
|
|
const cy1 = 3 * (p1.y - p0.y)
|
|
const by1 = 3 * (p2.y - p1.y) - cy1
|
|
const ay1 = p3.y - p0.y - cy1 - by1
|
|
|
|
const x = ax1 * (t * t * t) + bx1 * (t * t) + cx1 * t + p0.x
|
|
const y = ay1 * (t * t * t) + by1 * (t * t) + cy1 * t + p0.y
|
|
// console.log(p0.y, p1.y, p2.y, p3.y, y);
|
|
return {
|
|
x,
|
|
y
|
|
}
|
|
},
|
|
// 执行泡泡动画
|
|
bubbleAnimate() {
|
|
let width = this.windowWidth,
|
|
height = this.windowHeight;
|
|
Object.keys(this.queue).forEach(key => {
|
|
const anmationData = this.queue[+key];
|
|
const {
|
|
x,
|
|
y
|
|
} = this.updatePath(
|
|
anmationData.pathData,
|
|
anmationData.factor
|
|
)
|
|
const speed = anmationData.factor.speed
|
|
anmationData.factor.t += speed
|
|
|
|
var curWidth = 30
|
|
curWidth = (height - y) / 1.5
|
|
curWidth = Math.min(30, curWidth)
|
|
|
|
var curAlpha = anmationData.opacity
|
|
curAlpha = y / (0.3 * height) //消失的高度适当调一下
|
|
curAlpha = Math.min(1, curAlpha)
|
|
this.ctx.globalAlpha = curAlpha
|
|
this.ctx.drawImage(anmationData.image, x - curWidth / 2, y, curWidth, curWidth)
|
|
// this.ctx.setFillStyle('red')
|
|
// this.ctx.fillRect(x - curWidth / 2, y, 50, 50)
|
|
if (anmationData.factor.t > 1) {
|
|
delete this.queue[anmationData.id]
|
|
}
|
|
if (y > height) {
|
|
delete this.queue[anmationData.id]
|
|
}
|
|
})
|
|
this.ctx.draw()
|
|
if (Object.keys(this.queue).length > 0) {
|
|
this.animationTimer = setTimeout(() => {
|
|
this.bubbleAnimate()
|
|
}, 5)
|
|
} else {
|
|
this.clearAnimationTimer()()
|
|
this.ctx.draw() // 清空画面
|
|
}
|
|
},
|
|
|
|
// 清除定时器
|
|
clearActionTimer() {
|
|
if (this.actionTimer) {
|
|
clearInterval(this.actionTimer)
|
|
}
|
|
},
|
|
clearAnimationTimer() {
|
|
if (this.animationTimer) {
|
|
clearTimeout(this.animationTimer)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
@import '@/static/css/templatePage/custom_nav_bar.scss';
|
|
|
|
.template-bubble {
|
|
position: relative;
|
|
background: linear-gradient(-120deg, #9A5CE5, #01BEFF, #00F5D4, #43e97b);
|
|
width: 100vw;
|
|
height: 100vh;
|
|
|
|
.bubble {
|
|
position: fixed;
|
|
left: 0;
|
|
right: 0;
|
|
top: 0;
|
|
bottom: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
z-index: 1024;
|
|
pointer-events: none;
|
|
}
|
|
}
|
|
</style>
|