效果:
学习啦:
Vue 是一个渐进式框架,鼓励通过组件化来构建应用,其组件化优势:
- 代码复用:不同的视图和功能被封装成独立的组件,便于复用。
- 易于维护:每个组件职责单一、耦合度低,便于维护和扩展。
- 清晰的结构:应用的结构变得更清晰,逻辑拆分更加简洁。
CSS 动画与
transform:
CSS
transform
属性允许你在不影响文档流的情况下对元素进行位移、缩放、旋转等操作。相比使用margin
、top
等属性,transform
性能更好,因其不会触发浏览器的重排和重绘。
innerHTML
的使用:
innerHTML
是 JavaScript 中用来获取或设置元素的 HTML 内容的属性。虽然它很方便,但使用时需要注意性能问题,因为它会导致页面重新渲染。
map
和join
方法:
map
是数组的一个高阶方法,用于遍历数组并对每个元素应用一个函数,返回一个新的数组。应用场景:用于生成每个数字项的 HTML 代码。join
方法用于将数组元素连接成一个字符串,并且可以指定分隔符,应用于将所有生成的 HTML 字符串拼接成一个长字符串
实现思路:
- 格式化数字:处理负数、小数点和符号。
- 数字转数组:拆分数字为字符,便于渲染。
- 创建 DOM 元素:为每个字符生成对应的滚动容器。
- 初始化渲染和动画:根据数字创建滚动项并启动动画。
- 滚动动画优化:使用
transform: translateY()
替代margin-top
,提升性能。 - 响应数字变化:实时更新数字和滚动效果。
- 样式优化:确保显示效果清晰,滚动平滑。
封装代码:
<template><div class="scroll-number-container" ref="scrollContainer"></div>
</template><script setup>
import { ref, watch, onMounted } from 'vue';const props = defineProps({number: {type: [Number, String],required: true,},duration: {type: Number,default: 500,},
});// 用于引用 DOM 元素
const scrollContainer = ref(null);// 格式化数字,支持格式化负数、小数和千位分隔符
const formatNumber = (num) => {// 数字转字符串并考虑负数if (num < 0) {return `-${Math.abs(num).toString()}`;}return num.toString();
};// 将数字字符串转为数组,支持负号和小数点
const numberToArr = (num) => {const formatted = formatNumber(num);return formatted.split('');
};// 创建每个数字的滚动效果 DOM 元素
const createScrollItem = (digit) => {if (/\d/.test(digit)) {return (`<div class="scrollItem"><div class="scrollItem-content" data-show="${digit}"><span>0</span><span>1</span><span>2</span><span>3</span><span>4</span><span>5</span><span>6</span><span>7</span><span>8</span><span>9</span></div></div>`);} else {// 对于负号、小数点等非数字字符单独处理return `<div class="sign-box"><span>${digit}</span></div>`;}
};// 更新滚动动画
const animateScroll = () => {const height = scrollContainer.value.clientHeight;const scrollItems = scrollContainer.value.querySelectorAll('.scrollItem-content');scrollItems.forEach((item) => {const num = parseInt(item.getAttribute('data-show'));const translateY = height * num;item.style.transition = `transform ${props.duration}ms ease-in-out`;item.style.transform = `translateY(-${translateY}px)`;});
};// 初始化,渲染数字列表
const init = () => {const numArr = numberToArr(props.number);scrollContainer.value.innerHTML = numArr.map(createScrollItem).join('');animateScroll();
};onMounted(() => {init();
});// 监听数字变化
watch(() => props.number, init);
</script><style lang="scss">
.scroll-number-container {display: flex;height: 28px;overflow: hidden;font-size: 20px;font-weight: bold;margin: 10px;
}.scrollItem {display: flex;flex-direction: column;line-height: 28px;width: 25px;text-align: center;
}.scrollItem-content {display: flex;flex-direction: column;transition: transform 0.5s ease-in-out;
}.scrollItem-content span {font-size: 32px;font-family: 'YouSheBiaoTiHei';
}.sign-box {display: flex;align-items: center;justify-content: center;font-size: 32px;font-family: 'YouSheBiaoTiHei';
}
</style>
封装组件应用:
<template><div id="app"><h1>数字滚动效果</h1><ScrollNumber :number="currentNumber" :duration="1000" /><button @click="changeNumber">更改数字</button></div>
</template><script setup>
// 导入子组件
import ScrollNumber from './components/ScrollNumber.vue';import { ref } from 'vue';const currentNumber = ref(1234); // 初始数字// 事件处理函数,用于更改数字
const changeNumber = () => {currentNumber.value = Math.floor(Math.random() * 10000); // 随机生成一个数字
};
</script><style>
#app {text-align: center;margin-top: 50px;
}button {margin-top: 20px;padding: 10px 20px;font-size: 16px;cursor: pointer;
}
</style>