蹭一波日食流量,手擼h5日環食效果

這兩天大家的朋友圈應該都被日環食刷屏了吧,有幸親眼目睹了日環食的小伙伴一定被這十年才能一見的天文奇觀給震撼了一番,我作為一個業余天文愛好者,業余到連一個望遠鏡都沒有,只能在日環食這天仰望天空,卻被強烈的陽光刺痛了雙眼,毛也看不到。

作為一個有追求的前端碼農,怎么能在這么有意義的日子里什么也不做呢,于是我靈機一動,干脆手擼一個日環食效果吧。

擼頁面之前,我們先腦補一下頁面要實現的效果,碧藍的天空里懸掛著一輪孤獨的烈日,突然,她渾圓的身體開始出現黑色的殘缺,隨著時間的推移,她的身體逐漸被黑色吞噬,蒼茫的天空也隨之籠罩下壓抑的陰霾,天地之間,在一片混沌的漆黑之中,一輪詭異的金色圓環出現了,啊,打住,跑題了。


日食效果

如圖,頁面元素比較簡單:

  1. 藍天
  2. 太陽
  3. 月亮

因為要做動畫效果,這三個元素都用絕對定位,其中藍天的寬高都設置為100%就好,太陽元素要設置一個投影濾鏡(作為發光效果)。需要注意的是:月亮要對太陽進行遮擋,并且顯示為黑色,但超出太陽的部分是不可見的,因此在層次結構上,月亮div屬于太陽div的子元素,然后太陽div要設置overflow為hidden,這點需要注意。另外,由于是日環食,月亮div的寬高要略小于太陽,具體數值根據效果微調即可。

以下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,后續用腳本控制透明度來達到天空逐漸變暗的效果。
css代碼如下:

.mask {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background: rgba($color: #000000, $alpha: 0.9);
        opacity: 0;
    }

頁面的html結構非常簡單,如下:

<div class="sky">
        <div class="mask"></div>
        <div class="sun">
            <div class="moon"></div>
        </div>
    </div>

通過上面的代碼,我們已經實現了一個靜態的日環食效果,接下來我們就來編寫js腳本,控制月亮的移動以及天空的明暗變化。

我用的是vue,但這個不重要,我們只需要關注實現原理即可。實現原理其實也很簡單,我們只需要控制好月球的坐標走向即可,為了盡可能實現逼真的效果,我們讓月球從太陽的左下角逐漸走到太陽的右上角直至消失,當走到中間的一瞬間,月球和太陽的圓心會重合,由于月球的半徑比太陽小,因此正好會出現日環食的效果,注意,當月球圓心和太陽圓心重合的一瞬間,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函數里:

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()

注意月球初始位置的計算規則,left應該是負的月球的寬度,top應該是太陽的高度。初始化的最后一行代碼,調用了render方法,我們將在這個方法里不斷更新月球的位置和遮罩層的透明度,為了實現這個不斷刷新,可以使用setInterval,但這里我用的是window.requestAnimationFrame,也推薦大家用這個,此方法在MDN的說明如下:

window.requestAnimationFrame() 告訴瀏覽器——你希望執行一個動畫,并且要求瀏覽器在下次重繪之前調用指定的回調函數更新動畫。該方法需要傳入一個回調函數作為參數,該回調函數會在瀏覽器下一次重繪之前執行

詳見:window.requestAnimationFrame

我們在render方法中引入這個requestAnimationFrame方法

render () {
  window.requestAnimationFrame(() => {
    // 更新dom元素 todo...
    this.render() // 再次調用自己
  }
)

通過requestAnimationFrame方法,我們就得到了一個屏幕不斷刷新的回調函數,接下來我們只需要告訴瀏覽器,每次刷新,dom元素的位置或透明度如何變化即可。

首先我們來分析遮罩層mask,根據上文描述,它的透明度需要從0變為1(月球和太陽的圓心重合),然后再從1變為0(月球離開太陽),因此我們只需要在每次渲染時讓它的透明度逐漸遞增就行,參考代碼如下:

let opacity = Number(this.mask.style.opacity) // 獲取當前透明度
if (opacity >= 1 || opacity < 0) { // 如果透明度已經大于1 或者小于0
  this.opacity_step *= -1 // 就讓增量值反轉
}
opacity += this.opacity_step
this.mask.style.opacity = opacity // 更新遮罩層的透明度

緊接著我們來分析月球的位置,我們的目標是讓月球移動到左上角,說明每次移動,月球的橫向偏移量和縱向偏移量都是一樣的,因此月球就會沿著一個45°角的軌跡來移動。但這里要注意一個問題,就是月球每次移動多少偏移量,才能在遮罩層正好為1(天空完全變暗)的時候,移動到太陽的正中間?我們在初始化的時候,已經計算好了月球距離太陽圓心的橫向距離distanceX和縱向距離distanceY,而遮罩層的透明度增量也是知道的,就是變量opacity_step(0.001),因此可以得出:月球每次偏移量 = 月球到太陽圓心的距離 * opacity_step,然后,當月球已經離開太陽時,讓所有變量復原,日食重新開始。
月球的位移計算代碼如下:

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'
}

好了,所有的代碼都寫完了,運行你的頁面,你就會看到一個還不錯的日環食效果。

完整效果請訪問:h5日環食效果

喜歡這篇文章的小伙伴,記得轉發點贊哦,原創不易,請大家多多支持。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,250評論 6 530
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 97,923評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,041評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,475評論 1 308
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,253評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,801評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,882評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,023評論 0 285
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,530評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,494評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,639評論 1 366
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,177評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,890評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,289評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,552評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,242評論 3 389
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,626評論 2 370