一. 话不多说上图:
二. MPVue组件代码:
第一个(数字增加上翻,减小下翻):
<!-- 数字翻牌组件 number-flip.vue -->
<template>
<div>
<ul class="flip-box">
<li v-for="(item, index) in numArr" :key="index" :style="item.style + styleCss">
<span v-for="(num, idx) in numberList" :key="idx">{{ num }}</span>
</li>
</ul>
</div>
</template>
<script>
export default {
props: {
textNum: {
type: String,
default: ''
},
styles: {
type: String,
default: ''
}
},
data() {
return {
numberList: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, '.'], // 固定每列中的10个数字和.
numStr: '', // 需要展示的数字字符串
// 默认展示6个数字数组
numLen: 6,
numArr: [
{ num: 0, style: '' },
{ num: 0, style: '' },
{ num: 0, style: '' },
{ num: 0, style: '' },
{ num: 0, style: '' },
{ num: 0, style: '' }
]
}
},
watch: {
textNum: {
immediate: true,
handler(newVal) {
this.numStr = newVal
this.updateNumber()
}
}
},
computed: {
styleCss() {
return this.styles
}
},
methods: {
// 更新数字
updateNumber() {
this.numArr = this.numStr.split('').map(n => {
return { num: n, style: '' }
})
const length = this.numArr.length
// 展示数字的处理,不够前面补0
for (let i = 0; i < this.numLen - length; i++) {
this.numArr.unshift({ num: 0, style: '' })
}
this.numArr.forEach(item => {
const ty = item.num === '.' ? this.numberList.length - 1 : +item.num
item.style = `transform: translateY(-${ty * 100}%);`
})
}
}
}
</script>
<style lang="less" scoped>
.flip-box {
display: flex;
overflow: hidden;
margin: 15rpx auto;
justify-content: center;
li {
width: 60rpx;
height: 60rpx;
line-height: 60rpx;
list-style: none;
flex-direction: column;
font-size: 28rpx;
color: #ffffff;
margin-left: 6rpx;
transition: transform 1s ease-in-out;
span {
width: 100%;
height: 100%;
text-align: center;
background-image: linear-gradient(90deg, #0396FF 0%, #0396FF 100%);
box-shadow: 0 6rpx 30rpx 0 rgba(3, 5, 21, 0.12);
display: inline-block;
}
}
}
</style>
第二个(数字单页上翻):
<!-- 数字翻牌组件2 number-flip-two.vue-->
<template>
<div>
<ul class="flip-box">
<!-- 需要显示默认6列 -->
<li v-for="(item, index) in numArr" :key="index" :style="styleCss">
<div v-for="(n, idx) in item.list" :key="idx" :class="{'scroll-top': (item.scroll)}">{{ n }}</div>
</li>
</ul>
</div>
</template>
<script>
export default {
props: {
textNum: {
type: String,
default: ''
},
styles: {
type: String,
default: ''
}
},
data() {
return {
numStr: '', // 需要展示的数字字符串
// 默认展示6个数字数组
numLen: 6,
numArr: [
{ list: ['0', '0'], scroll: false },
{ list: ['0', '0'], scroll: false },
{ list: ['0', '0'], scroll: false },
{ list: ['0', '0'], scroll: false },
{ list: ['0', '0'], scroll: false },
{ list: ['0', '0'], scroll: false }
]
}
},
computed: {
styleCss() {
return this.styles
}
},
watch: {
textNum: {
immediate: true,
handler(newVal) {
this.numStr = newVal
this.updateNumber()
}
}
},
methods: {
// 更新数字
updateNumber() {
const numStrArr = this.numStr.split('') || []
// 展示数字的处理,不够默认6个前面补0
const len = this.numLen - numStrArr.length
for (let i = 0; i < len; i++) {
numStrArr.unshift('0')
}
// 补全超过this.numArr.length的数字
const length = numStrArr.length - this.numArr.length
for (let i = 0; i < length; i++) {
this.numArr.unshift({ list: ['0', '0'], scroll: false })
}
const newNumArr = numStrArr.map((n, i) => {
const item = this.numArr[i] || {}
return { list: [item.list.pop() || '0', n], scroll: false }
})
this.numArr = newNumArr
setTimeout(() => {
this.numArr.forEach(item => {
if (item.list.length >= 2 && item.list[0] !== item.list[1]) {
item.scroll = true
}
})
}, 200)
}
}
}
</script>
<style lang="less" scoped>
.flip-box {
display: flex;
overflow: hidden;
margin: 15rpx auto;
justify-content: center;
li {
width: 60rpx;
height: 60rpx;
line-height: 60rpx;
list-style: none;
flex-direction: column;
font-size: 28rpx;
color: #ffffff;
margin-left: 6rpx;
.scroll-top {
transform: translateY(-100%) !important;
transition: transform 1s ease-in-out;
}
div {
width: 100%;
height: 100%;
text-align: center;
background-image: linear-gradient(90deg, #0396FF 0%, #0396FF 100%);
box-shadow: 0 6rpx 30rpx 0 rgba(3, 5, 21, 0.12);
display: inline-block;
transform: translateY(0);
}
}
}
</style>
第三个(数字多页上翻 ):
<!-- 数字翻牌组件3 number-flip-three.vue -->
<template>
<div>
<ul class="flip-box">
<!-- 需要显示默认6列 -->
<li v-for="(item, index) in numArr" :key="index" :style="item.style + styleCss">
<div v-for="(n, idx) in item.list" :key="idx">{{ n }}</div>
</li>
</ul>
</div>
</template>
<script>
export default {
props: {
textNum: {
type: String,
default: ''
},
styles: {
type: String,
default: ''
}
},
data() {
return {
numStr: '', // 需要展示的数字字符串
// 默认展示6个数字数组
numLen: 6,
numArr: [
{ list: ['0'], style: '' },
{ list: ['0'], style: '' },
{ list: ['0'], style: '' },
{ list: ['0'], style: '' },
{ list: ['0'], style: '' },
{ list: ['0'], style: '' }
]
}
},
computed: {
styleCss() {
return this.styles
}
},
watch: {
textNum: {
immediate: true,
handler(newVal) {
this.numStr = newVal
this.updateNumber()
}
}
},
methods: {
// 更新数字
updateNumber() {
const numStrArr = this.numStr.split('') || []
// 展示数字的处理,不够默认6个前面补0
const len = this.numLen - numStrArr.length
for (let i = 0; i < len; i++) {
numStrArr.unshift('0')
}
// 补全超过this.numArr.length的数字
const length = numStrArr.length - this.numArr.length
for (let i = 0; i < length; i++) {
this.numArr.unshift({ list: ['0'], style: '' })
}
const newNumArr = numStrArr.map((n, i) => {
const item = this.numArr[i] || {}
const start = item.list.pop() || '0'
if (n === start) {
return { list: [start], style: '' }
}
if (isNaN(parseInt(n)) || isNaN(parseInt(start))) {
return { list: [start, n], style: '' }
}
const count = parseInt(start) - parseInt(n)
const temp = [start]
for (let i = 1; i <= Math.abs(count); i++) { // 构建翻牌的数组
if (count > 0) {
temp.push((parseInt(start) - i).toString())
} else {
temp.push((parseInt(start) + i).toString())
}
}
return { list: temp, style: '' }
})
this.numArr = newNumArr
setTimeout(() => {
this.numArr.forEach(item => {
item.style = `transition: transform 1s ease-in-out; transform: translateY(-${(item.list.length - 1) * 100}%);`
})
}, 200)
}
}
}
</script>
<style lang="less" scoped>
.flip-box {
display: flex;
overflow: hidden;
margin: 15rpx auto;
justify-content: center;
li {
width: 60rpx;
height: 60rpx;
line-height: 60rpx;
list-style: none;
flex-direction: column;
font-size: 28rpx;
color: #ffffff;
margin-left: 6rpx;
div {
width: 100%;
height: 100%;
text-align: center;
background-image: linear-gradient(90deg, #736EFE 0%, #7367F0 100%);
box-shadow: 0 6rpx 30rpx 0 rgba(3, 5, 21, 0.12);
display: inline-block;
}
}
}
</style>
父组件中使用:
<number-flip :text-num="num" styles="height: 80rpx; line-height: 80rpx" />
<number-flip-two :text-num="num" styles="height: 80rpx; line-height: 80rpx" />
<number-flip-three :text-num="num" styles="height: 80rpx; line-height: 80rpx" />
data() {
return {
num: '102.23'
}
},
mounted() {
this.$nextTick(() => {
setInterval(() => { // 模拟随机数字改变
let num = parseInt(this.num) || '0'
const random = Math.floor(Math.random() * 100)
num += random
this.num = num.toString() + '.' + random.toString()
}, 3000)
})
},
三. 总结
之前参考别人写的都是基于操作dom改变样式的。
因为小程序中没有DOM和BOM对象,所以MPVue中不能使用dom操作节点,使用$refs操作原生html节点也是undefined,因此改变样式翻牌(translateY)都是通过数据双向绑定来实现的, 不过小程序中也可以使用createSelectorQuery来实现。
写第二、三个组件是因为可能有需求--只能向上翻动 ( (σ`д′)σ 产品过来时掏出一把刀也可以解决🤣)
如果想直接在vue项目中使用,应该修改一下style样式绑定方式和rpx单位就可以了。
代码有问题或可以改进的地方欢迎各位大佬指正(‾◡◝)。