這兩天大家的朋友圈應該都被日環(huán)食刷屏了吧,有幸親眼目睹了日環(huán)食的小伙伴一定被這十年才能一見的天文奇觀給震撼了一番娇跟,我作為一個業(yè)余天文愛好者岩齿,業(yè)余到連一個望遠鏡都沒有,只能在日環(huán)食這天仰望天空苞俘,卻被強烈的陽光刺痛了雙眼盹沈,毛也看不到。
作為一個有追求的前端碼農(nóng),怎么能在這么有意義的日子里什么也不做呢乞封,于是我靈機一動做裙,干脆手擼一個日環(huán)食效果吧。
擼頁面之前肃晚,我們先腦補一下頁面要實現(xiàn)的效果锚贱,碧藍的天空里懸掛著一輪孤獨的烈日,突然关串,她渾圓的身體開始出現(xiàn)黑色的殘缺拧廊,隨著時間的推移,她的身體逐漸被黑色吞噬晋修,蒼茫的天空也隨之籠罩下壓抑的陰霾吧碾,天地之間,在一片混沌的漆黑之中墓卦,一輪詭異的金色圓環(huán)出現(xiàn)了倦春,啊,打住落剪,跑題了睁本。
如圖,頁面元素比較簡單:
- 藍天
- 太陽
- 月亮
因為要做動畫效果著榴,這三個元素都用絕對定位添履,其中藍天的寬高都設置為100%就好屁倔,太陽元素要設置一個投影濾鏡(作為發(fā)光效果)脑又。需要注意的是:月亮要對太陽進行遮擋,并且顯示為黑色锐借,但超出太陽的部分是不可見的问麸,因此在層次結(jié)構(gòu)上,月亮div屬于太陽div的子元素钞翔,然后太陽div要設置overflow為hidden严卖,這點需要注意。另外布轿,由于是日環(huán)食哮笆,月亮div的寬高要略小于太陽,具體數(shù)值根據(jù)效果微調(diào)即可汰扭。
以下css代碼僅供參考
.sky {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #7ad8fb;
}
.sun {
position: absolute;
top: 200px;
right: 200px;
width: 204px;
height: 204px;
border-radius: 200px;
background: #fcf6dc;
overflow: hidden;
box-shadow: 0 0 60px rgba($color: #ffffff, $alpha: 0.6);
}
.moon {
position: absolute;
top: 7px;
left: 7px;
width: 190px;
height: 190px;
border-radius: 190px;
background: #000000;
}
隨著日食的推進稠肘,天空會逐漸變暗直至黑色,因此還需要一個元素萝毛,用來控制天空的明暗程度项阴,這個元素我們就叫它mask吧,它也是絕對定位笆包,并且寬高跟天空一樣是鋪滿全屏的环揽,在層次上略荡,它應該處于天空上方,太陽下方歉胶。這個div我們設置它的背景色為黑色汛兜,但透明度默認是0,后續(xù)用腳本控制透明度來達到天空逐漸變暗的效果通今。
css代碼如下:
.mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba($color: #000000, $alpha: 0.9);
opacity: 0;
}
頁面的html結(jié)構(gòu)非常簡單序无,如下:
<div class="sky">
<div class="mask"></div>
<div class="sun">
<div class="moon"></div>
</div>
</div>
通過上面的代碼,我們已經(jīng)實現(xiàn)了一個靜態(tài)的日環(huán)食效果衡创,接下來我們就來編寫js腳本帝嗡,控制月亮的移動以及天空的明暗變化。
我用的是vue璃氢,但這個不重要哟玷,我們只需要關(guān)注實現(xiàn)原理即可。實現(xiàn)原理其實也很簡單一也,我們只需要控制好月球的坐標走向即可巢寡,為了盡可能實現(xiàn)逼真的效果,我們讓月球從太陽的左下角逐漸走到太陽的右上角直至消失椰苟,當走到中間的一瞬間抑月,月球和太陽的圓心會重合,由于月球的半徑比太陽小舆蝴,因此正好會出現(xiàn)日環(huán)食的效果谦絮,注意,當月球圓心和太陽圓心重合的一瞬間洁仗,mask遮罩層的透明度應該正好是1层皱,也就是天空完全變黑的一個效果。
大致的原理就是這樣赠潦,以下是這個頁面需要用到的變量:
opacity_step: 0.001, // 透明度的增量(每一次渲染增加的透明度)
sun_width: 0, // 太陽寬度
sun_height: 0, // 太陽高度
moon_width: 0, // 月亮寬度
sun: null, // 太陽dom對象
moon: null, // 月亮dom對象
mask: null, // 遮罩層dom對象
distanceX: 0, // 月球到太陽中心重合點的橫向距離
distanceY: 0 // 月球到太陽中心重合點的縱向距離
在頁面加載完畢后叫胖,我們先對這些變量進行初始化,并且讓月球處于左下角的位置她奥,我用的是vue瓮增,所以將這部分代碼寫在mounted函數(shù)里:
this.sun = document.querySelector('.sun')
this.moon = document.querySelector('.moon')
this.mask = document.querySelector('.mask')
this.sun_width = this.sun.clientWidth
this.sun_height = this.sun.clientHeight
this.moon_width = this.moon.clientWidth
this.moon.style.left = (this.moon_width * -1) + 'px'
this.moon.style.top = this.sun_height + 'px'
const offsetSize = (this.sun_width - this.moon_width) * 0.5
this.distanceX = this.moon_width + offsetSize
this.distanceY = this.moon_width + offsetSize
this.render()
注意月球初始位置的計算規(guī)則,left應該是負的月球的寬度哩俭,top應該是太陽的高度绷跑。初始化的最后一行代碼,調(diào)用了render方法携茂,我們將在這個方法里不斷更新月球的位置和遮罩層的透明度你踩,為了實現(xiàn)這個不斷刷新,可以使用setInterval,但這里我用的是window.requestAnimationFrame带膜,也推薦大家用這個吩谦,此方法在MDN的說明如下:
window.requestAnimationFrame() 告訴瀏覽器——你希望執(zhí)行一個動畫,并且要求瀏覽器在下次重繪之前調(diào)用指定的回調(diào)函數(shù)更新動畫膝藕。該方法需要傳入一個回調(diào)函數(shù)作為參數(shù)式廷,該回調(diào)函數(shù)會在瀏覽器下一次重繪之前執(zhí)行
詳見:window.requestAnimationFrame
我們在render方法中引入這個requestAnimationFrame方法
render () {
window.requestAnimationFrame(() => {
// 更新dom元素 todo...
this.render() // 再次調(diào)用自己
}
)
通過requestAnimationFrame方法,我們就得到了一個屏幕不斷刷新的回調(diào)函數(shù)芭挽,接下來我們只需要告訴瀏覽器滑废,每次刷新,dom元素的位置或透明度如何變化即可袜爪。
首先我們來分析遮罩層mask蠕趁,根據(jù)上文描述,它的透明度需要從0變?yōu)?(月球和太陽的圓心重合)辛馆,然后再從1變?yōu)?(月球離開太陽)俺陋,因此我們只需要在每次渲染時讓它的透明度逐漸遞增就行,參考代碼如下:
let opacity = Number(this.mask.style.opacity) // 獲取當前透明度
if (opacity >= 1 || opacity < 0) { // 如果透明度已經(jīng)大于1 或者小于0
this.opacity_step *= -1 // 就讓增量值反轉(zhuǎn)
}
opacity += this.opacity_step
this.mask.style.opacity = opacity // 更新遮罩層的透明度
緊接著我們來分析月球的位置昙篙,我們的目標是讓月球移動到左上角腊状,說明每次移動,月球的橫向偏移量和縱向偏移量都是一樣的苔可,因此月球就會沿著一個45°角的軌跡來移動缴挖。但這里要注意一個問題,就是月球每次移動多少偏移量焚辅,才能在遮罩層正好為1(天空完全變暗)的時候映屋,移動到太陽的正中間?我們在初始化的時候法焰,已經(jīng)計算好了月球距離太陽圓心的橫向距離distanceX和縱向距離distanceY秧荆,而遮罩層的透明度增量也是知道的倔毙,就是變量opacity_step(0.001)埃仪,因此可以得出:月球每次偏移量 = 月球到太陽圓心的距離 * opacity_step,然后陕赃,當月球已經(jīng)離開太陽時卵蛉,讓所有變量復原,日食重新開始么库。
月球的位移計算代碼如下:
let left = Number(this.moon.style.left.replace('px', ''))
let top = Number(this.moon.style.top.replace('px', '')) //先獲取月球當前的left和top
left += this.distanceX * Math.abs(this.opacity_step)
top -= this.distanceY * Math.abs(this.opacity_step) // 計算月球的位置
this.moon.style.left = left + 'px'
this.moon.style.top = top + 'px' // 更新月球dom元素的位移
if (left > this.sun_width) { // 如果月球已超出太陽的邊界傻丝,所有變量復原
this.opacity_step = Math.abs(this.opacity_step)
this.mask.style.opacity = 0
this.moon.style.left = (this.moon_width * -1) + 'px'
this.moon.style.top = this.sun_height + 'px'
}
好了,所有的代碼都寫完了诉儒,運行你的頁面葡缰,你就會看到一個還不錯的日環(huán)食效果。
完整效果請訪問:h5日環(huán)食效果
喜歡這篇文章的小伙伴,記得轉(zhuǎn)發(fā)泛释、點贊哦滤愕,原創(chuàng)不易,請大家多多支持怜校。