蹭一波日食流量,手撸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阅读 211,561评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,218评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,162评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,470评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,550评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,806评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,951评论 3 407
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,712评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,166评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,510评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,643评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,306评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,930评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,745评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,983评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,351评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,509评论 2 348