本篇分享給大家一個有意思的小案例,同時我也會手摸手地教大家這個小案例是如何實現的,最後做一個簡短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