本篇分享给大家一个有意思的小案例,同时我也会手摸手地教大家这个小案例是如何实现的,最后做一个简短2019年总结和2020年的展望,毕竟人还活着呢。
看到这个动图不知道是否勾起了你的好奇心,如果你心想这是如何来实现的呢?那么请不要着急,接下来我会带你来实现它。再如果你只对动图里的字体感兴趣,同样也不要着急,文章底部已经贴好了源码链接,字体库就在其中。如果你想了解如何实现它的话,接下来就跟随我一探究竟。
分析
当让你根据动图所呈现的效果来实现它的话,我们的第一个想法会是什么?去 Google ...(sleep 3s)显然在这不现实。 那我们来换一种想法,先看它都有哪些元素。
从动图来看它有 文字、小球 和 动画 这三种重要元素。接下来我们只需要一一实现它就可以了。
每当我们实现一个需求或者完成一项任务的时候,首先最最需要的是抽象和结构化思维,在脑海中有一个大概的构思,实现思路大致如下:
- 插入文字并计算好文字距离左边的距离
- 根据距离计算出小球的每个落点
- 添加动画改变各自的状态
实现
第一步
首先我们什么都不用思考,上来先定义一个 class
名字叫做 BounceBall
class BounceBall {}
我们打算当用户创建一个实例时,传给我们一段文字和一个绑定在 dom
上的 id
,我们就可以把文字插入在这个 dom
内。这样会在 html
中呈现类似下面的效果
<div>Goodbye 2019 Hello 2020</div>
问题出现了,当一段文字只放在一个元素内的话,我们无法计算出每个文字距离左边的距离,这时候我们就需要做一些改变。
根据空格切分字符串成为数组,将每一个文字插入单独的元素内,同样中间用空格分开。
class BounceBall {
constructor (config) {
const { id, text } = config
this.id = id
this.text = text
this.$id = document.getElementById(this.id)
this.init()
}
init () {
const contentArray = this.text.split(' ')
// this.append(this.$ball)
for (let i = 0, len = contentArray.length; i < len; i++) {
const text = contentArray[i]
const $text = getSpan(text, 'text')
this.append($text)
const textLen = $text.offsetWidth
if (i + 1 < contentArray.length) {
this.append(getSpan(' '))
}
}
}
append (element) {
this.$id.appendChild(element)
}
}
这里有一个 getSpan
方法没有在上面代码中体现出来,它的目的是创建一个 span
标签元素,然后把字符串插入进去,添加 classname
等。 因为我没有借助任何库,所以很多 dom
操作需要单独封装成工具库。再看一下 html
中呈现的效果
<div id="main">
<!-- <div class="ball"></div> -->
<span class="text">Goodbye</span>
<span> </span>
<span class="text">2019</span>
<span> </span>
<span class="text">Hello</span>
<span> </span>
<span class="text">2020</span>
</div>
这样我们就可以根据 classname
获得每个文字的属性了。接下来小球的 dom 同样一并添加上,小球为上面注释部分。
给小球添加样式让它在文字的左上角位置。
.ball {
position: absolute;
top: 0;
left: -20px;
width: 10px;
height: 10px;
border-radius: 100%;
background-color: green;
margin-left: -5px;
}
第二步
计算出每段文字距离左边的距离、宽度和位置
init () {
...
for (let i = 0, len = contentArray.length; i < len; i++) {
const text = contentArray[i]
const $text = getSpan(text, 'text')
this.append($text)
const textLen = $text.offsetWidth
...
const ballLeft = $text.offsetLeft + textLen / 2
const ballProps: BallProps = {
left: ballLeft,
textLen,
textIndex: i
}
this.ballPropsArray.push(ballProps)
}
}
我们定义了一个 ballPropsArray
数组,它存放着小球运动的轨迹。ballLeft
为每段文字中间距离左边的距离,也就是小球要到的位置。textLen
为每段文字的宽度。textIndex
为下标。
我们假设小球移动到每个 ballProps
的时间为 ${textLen} ms
。当 ${textLen / 2} ms
时,小球距离左边为 ${ballLeft} px
,高度设置一个定值,再过 ${textLen / 2} ms
小球下落到底部。根据这个思路我们实现出以下代码:
let incrementingDelay = 0
for (let i = 0, len = this.ballPropsArray.length; i < len; i++) {
const ballProps = this.ballPropsArray[i]
setTimeout(() => {
this.$ball.style.left = `${ballProps.left}px`
this.$ball.style.top = '-1em'
// 小球开始上升
const halfwayReached = ballProps.textLen / 2
setTimeout(() => {
this.$ball.style.left = `${ballProps.left}px`
this.$ball.style.top = '0px'
// 小球开始下落
}, halfwayReached)
}, incrementingDelay)
incrementingDelay += ballProps.textLen
}
第三步
为小球添加动画改变文字的颜色,首先给小球添加 transition
属性。
.ball {
...
transition-property: left, top;
transition-timing-function: cubic-bezier(0.25, 0.1, 0.25, 1), cubic-bezier(0.25, 0.1, 0.25, 1);
}
在每次运动中设置 transition-duration
的值。
const leftDuration = `${ballProps.textLen}ms`
const topDuration = `${ballProps.textLen / 2}ms`
this.$ball.style.transitionDuration = `${leftDuration}, ${topDuration}`
在小球开始下落的 ${textLen / 2} ms 之后,修改文字的颜色。
let incrementingDelay = 0
for (let i = 0, len = this.ballPropsArray.length; i < len; i++) {
const ballProps = this.ballPropsArray[i]
setTimeout(() => {
this.$ball.style.left = `${ballProps.left}px`
this.$ball.style.top = '-1em'
// 小球开始上升
const halfwayReached = ballProps.textLen / 2
setTimeout(() => {
this.$ball.style.left = `${ballProps.left}px`
this.$ball.style.top = '0px'
// 小球开始下落
setTimeout(() => {
// 修改颜色
}, halfwayReached)
}, halfwayReached)
}, incrementingDelay)
incrementingDelay += ballProps.textLen
}
一个简版的跳动小球就做好了,之后我们可以继续为它添加更多的属性和进一步优化,比如 为它添加 speed
属性,或者让小球淡入淡出,或者改变它的形状等等。
结语
2019年马上要过去了,借着这篇文章做个年终总结。今年是我收获之年,有很多朋友、同事和亲戚,不管在工作中还是生活中都给我很大帮助。年初为自己制定的一些学习计划和工作计划也都完成的不错,比如每个月读一本书,每个月发一篇文章等。工作中要学一些新技术、做一些对团队有帮助意义的事情等。希望在2020年自己依旧能坚持下去,尝试更多新领域、探索更多未知。毕竟人活着总要给这个世界留下的什么吧。最后祝大家2020年一夜暴富。
附上小球源码 bounce-ball