阅读 2842

「寒草的中秋献礼🥮,实现30s前端创意动画」陪你看日落和月升|与你赏星空和诗歌

我正在参加中秋创意投稿大赛,详情请看:中秋创意投稿大赛

大家好,我是寒草🌿,一只草系码猿🐒。间歇性热血🔥,持续性沙雕🌟
如果喜欢我的文章,可以关注➕点赞,为我注入能量,与我一同成长吧~

工程师 · 中秋节🎑

雷猴啊,我是寒草~本来之前的文章说了我最近比较忙,一堆事情要做,而且也在准备我的一个几万字长篇文章,导致最近没有什么时间。但是,我前几天看到了掘金在搞一个活动,就是中秋创意投稿大赛,我这一看,喔~怪好玩的,于是打算起早贪黑筹备一下。

正好,我在掘金写文半年多了,也趁这个机会给大家送一个中秋节礼物。还是我的老套路嘛,这篇文章你可以看到:

  • 我是如何思考并设计一个中秋节礼物滴🥮
  • 我和大家一起将这个设计实现出来🌟

以及,我挤出时间来做这个动画,初衷希望大家都可以开心过中秋,主要分享这份礼物🎁的设计和实现,甚至为了搞本文中的gif我还花了30块钱买了个软件,呜呜呜,大家要不心疼寒草🌿一下,给我一个赞一个评论一个关注来作为给我的中秋礼物?嘿嘿嘿~

小寒草在这里提前祝各位读者以及作者朋友中秋节快乐啦🎆,biubiubiu~

浪漫又蹩脚的设计之路📖

那么!第一个问题就来了,我该如何设计我给大家的中秋礼物捏,还是像往常一样,我要列出我想要展现的要素:

  • 月亮(废话,中秋肯定要有月亮呀,听君一席话,就是一席话
  • 星空:皎洁的月色需要星空点缀
  • 诗歌:中秋节是一个中华传统节日,并且无数文人墨客在中秋创作诗词歌赋,我要用诗歌元素让我的作品包含中华文化

这几个要素是必选的,但是感觉形成一个动画的要素要需要更多,而且需要更多细节,比如:

  • 星空怎么展现
  • 诗歌与作品怎么结合

关于更多要素我想到了,不妨在我这个作品里体现日落到月亮升起的过程,来一个“一镜到底”的动画。

而细节如何设计,请容我卖个关子~毕竟,我要留有悬念~

落地设计,逃不开的编码之路💻

image.png

现在我已经有了一个初步的构想了,下面就是如何去实现出来,那么这一章就请大家跟随我一步一步把这个动画的内容充实起来吧🌟

请给位读者耐心看完~

诶,终究是逃不开写代码呀,寒草,你跑不掉滴~

日落月升🌛

日落月升.gif

这里我的动画希望采用的是那种简约派的画风,第一是好做(我好耿直。。。),第二是感觉这个画风类似于之前剪纸之类的风格,简约的日落,圆月升起之中似乎带着一点点的中国风。

这个环节全都是采用css的帧动画实现,主要麻烦的地方是:
调色调色调色还是调色!鬼知道没有ui和产品的情况下,我自己挑颜色花了多长时间,我需要让天空,太阳,月亮,山峦的颜色达成一个一致性:

  • 正午:天空蓝色,太阳金黄色,山峦绿油油的~
  • 傍晚:天空逐渐变成金色,太阳逐渐变红,山峦也逐渐变暗~
  • 日落:天空被落日染成红色,太阳也是变得通红,山峦也被染成深红色~
  • 月升:天空变成墨色,月亮是金色的,山峦要变成墨绿色~

所以说,艺术也是需要现实的观察的,哈哈哈,我不是说自己做的东西是艺术,只是简单去做一个效果都要做日常观察,有感而发~

所以下面给大家看一下代码实现:

//css
* {
  padding: 0px;
  margin: 0px;
}

@keyframes nightfall {
  from {
    background: #9dc1df;
  }

  16% {
    background: #416cc9;
  }

  32% {
    background: #e58732;
  }

  50% {
    background: #e55327;
  }

  to {
    background: #2a2d38;
  }
}

.container {
  width: 100vw;
  height: 100vh;
  transition: all ease;
  animation: nightfall 8s;
  background: #2a2d38;
  overflow: hidden;
}

@keyframes sunfallmoonrise {
  from {
    background: #ffffff;
    top: 10vh;
  }

  16% {
    background: #ffff54;
  }

  32% {
    background: #e63724;
  }

  50% {
    background: #e93324;
    top: 80vh;
  }

  to {
    background: #f9dc60;
    top: 10vh;
  }
}

.sun-and-moon {
  width: 200px;
  height: 200px;
  animation: sunfallmoonrise 8s;
  border-radius: 50%;
  position: absolute;
  left: 20vw;
  top: 10vh;
  background: #f9dc60;
  box-shadow: 0px 0px 20px #f9dc60;
  transition: all 3s ease;
  z-index: 10;
  ;
}

@keyframes mountain {
  from {
    background: rgb(25, 175, 75);
  }

  16% {
    background: rgb(168, 192, 35);
  }

  32% {
    background: rgb(199, 106, 31);
  }

  50% {
    background: rgb(167, 66, 26);
  }

  to {
    background: rgb(56, 56, 27);
  }
}

.mountain-common {
  position: absolute;
  z-index: 999;
  border-radius: 50%;
  background: rgb(56, 56, 27);
  animation: mountain 8s;
}

.mountain-a {
  width: 80vw;
  height: 400px;
  top: 80vh;
  left: -20vw;
}

.mountain-b {
  width: 100vw;
  height: 800px;
  top: 65vh;
  left: 40vw;
}

复制代码
// Dom
<div class="container">
    <div class="sun-and-moon"></div>
    <div class="mountain-common mountain-a"></div>
    <div class="mountain-common mountain-b"></div>
</div>
复制代码

镜头对月色的追随💓

月亮变大.gif

下一步我想的就是,镜头已经记录了太阳落下到月亮升起的过程,下面我们拉近镜头,去更近距离的观察月亮,所以我们要营造一个镜头拉近的效果:

  • 月亮放大并到镜头的偏中心的位置
  • 山峦也会跟随像右侧移动,并逐渐消失在镜头里
// css
@keyframes moon-bigger {
  from {
    width: 200px;
    height: 200px;
    left: 20vw;
    top: 10vh;
  }

  to {
    width: 600px;
    height: 600px;
    top: calc(50vh - 300px);
    left: calc(40vw - 300px);
  }
}

@keyframes mountain-down-a {
  from {
    top: 80vh;
    left: -20vw;
  }

  to {
    top: 115vh;
    left: -300px;
    opacity: 0.6;
  }
}

@keyframes mountain-down-b {
  from {
    top: 65vh;
    left: 40vw;
  }

  to {
    top: 100vh;
    left: calc(60vw - 300px);
    opacity: 0.6;
  }
}
复制代码
// Dom,暂时还是之前的Dom
<div class="container">
    <div class="sun-and-moon"></div>
    <div class="mountain-common mountain-a"></div>
    <div class="mountain-common mountain-b"></div>
</div>
复制代码

但是这次有了js, 因为之前的动画时长是8s,所以我这里设置了9s的定时器去触发月亮变大,山峦消失的效果。

const timerA = setTimeout(() => {
  clearTimeout(timerA);

  const moon = document.getElementsByClassName('sun-and-moon')[0];
  moon.style.animation = 'moon-bigger 5s';
  moon.style.width = '600px';
  moon.style.height = '600px';
  moon.style.top = 'calc( 50vh - 300px )';
  moon.style.left = 'calc( 40vw - 300px )';

  const mountainList = document.getElementsByClassName('mountain-common');
  mountainList[0].style.animation = 'mountain-down-a 5s';
  mountainList[0].style.top = '100vh';
  mountainList[1].style.animation = 'mountain-down-b 5s';
  mountainList[1].style.top = '100vh';
}, 9000);
复制代码

将银河撒向夜空🌃

星空与文字.gif

接下来我要让星空浮现,因为我想星星也不是一下子全出来的,而是渐渐浮现出来的,所以要随机的时间点来产生星星✨,并让他有闪烁的效果。

// css,星星闪烁的效果
@keyframes star-scale {
  from {
    transform: scale(1, 1);
  }

  25% {
    transform: scale(0.1, 0.1);
  }

  50% {
    transform: scale(1, 1);
  }

  25% {
    transform: scale(2, 2);
  }

  to {
    transform: scale(1, 1);
  }
}

.star {
  height: 3px;
  width: 3px;
  background-color: #f9dc60;
  border-radius: 50%;
  position: absolute;
  animation: star-scale 2s;
  animation-iteration-count: infinite;
}
复制代码
// js 随机时间随机位置,产出星星
const arr = new Array(60);
    for (const item of arr) {
      const dom = document.createElement('div');
      dom.className = 'star';
      dom.style.left = `${Math.random() * 100}vw`;
      dom.style.top = `${Math.random() * 100}vh`;
      setTimeout(() => {
        document.body.appendChild(dom);
      }, 15000 * Math.random());
    }
复制代码

但愿人长久 · 千里共婵娟🎋

星空与文字.gif

之后就是那一句诗句了:
但愿人长久,千里共婵娟

我是如何让他一点一点像是用笔写出来的呢?其实我在我生日文里面也用了这个库用技术创造惊喜|成熟的前端工程师一定要学会亲手制作生日礼物 🎁,只不过:

  • 上次是生日礼物,这次是中秋礼物
  • 上次是送给我自己,这次是送给你们

嘿嘿嘿,感动不~这个库是 hanzi-writer, 大家可以去看一看这个文档docs,感觉除了可以用来练习书法,也可以做很多效果~

<div class="poetry-top poetry">
  <div id="dan"></div>
  <div id="yuan"></div>
  <div id="ren"></div>
  <div id="chang"></div>
  <div id="jiu"></div>
</div>
<div class="poetry-down poetry">
  <div id="qian"></div>
  <div id="li"></div>
  <div id="gong"></div>
  <div id="chan"></div>
  <div id="juan"></div>
</div>
复制代码
.poetry div {
  width: 60px;
  height: 60px;
  margin-top: 6px;
}

.poetry-top {
  position: absolute;
  top: 20vh;
  right: 48px;
}

.poetry-down {
  position: absolute;
  top: 30vh;
  right: 130px;
}
复制代码
const BASE_CONFIG = {
    width: 60,
    height: 60,
    padding: 2,
    delayBetweenStrokes: 0,
    strokeAnimationSpeed: 2,
    showCharacter: false,
    showOutline: false,
  }
  const WRITER_CONFIG = {
    ...BASE_CONFIG,
    strokeColor: '#f9dc60'
  };

  const getWriterList = () => {
    let writerList = [];
    writerList.push(HanziWriter.create('dan', '但', WRITER_CONFIG));
    writerList.push(HanziWriter.create('yuan', '愿', WRITER_CONFIG));
    writerList.push(HanziWriter.create('ren', '人', WRITER_CONFIG));
    writerList.push(HanziWriter.create('chang', '长', WRITER_CONFIG));
    writerList.push(HanziWriter.create('jiu', '久', WRITER_CONFIG));
    writerList.push(HanziWriter.create('qian', '千', WRITER_CONFIG));
    writerList.push(HanziWriter.create('li', '里', WRITER_CONFIG));
    writerList.push(HanziWriter.create('gong', '共', WRITER_CONFIG));
    writerList.push(HanziWriter.create('chan', '婵', WRITER_CONFIG));
    writerList.push(HanziWriter.create('juan', '娟', WRITER_CONFIG));
    return writerList;
  }

  const generateAnimateWriter = async (writerList) => {
    const writerCount = writerList.length;
    for (const writer of writerList) {
      await writer.animateCharacter();
    }
  }
  generateAnimateWriter(getWriterList());
复制代码

将诗歌献给皎洁的月🎵

动态gif:

诗词月亮.gif

静态细节(注意中间写着寒草呈献):

image.png

考虑作品立意时彰显中华传统诗词文化的宗旨,我有个想法,就是去搜寻更多有关中秋的诗歌,并把他们印在月亮上,这里我用了两个手段:

  • 透明度渐变
  • 词云图

把月亮中心的透明度降低,让藏在后面的诗词显示出来。

// css
#word-cloud__container {
  width: 600px;
  height: 600px;
  top: calc(50vh - 300px);
  left: calc(40vw - 300px);
  position: absolute;
  transition: all 2s;
  border-radius: 50%;
  overflow: hidden;
  opacity: 0.6;
  z-index: 1;
}
复制代码
// dom 词云图
<div id="word-cloud__container"></div>
复制代码
// 词云图数据处理和展示
const poetryList = [
  '明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。转朱阁,低绮户,照无眠。不应有恨,何事长向别时圆?人有悲欢离合,月有阴晴圆缺,此事古难全。',
  '西风来劝凉云去,天东放开金镜。照野霜凝,入河桂湿,一一冰壶相映。殊方路永。更分破秋光,尽成悲境。有客踌躇,古庭空自吊孤影。江南朋旧在许,也能怜天际,诗思谁领。梦断刀头,书开虿尾,别有相思随定。忧心耿耿。对风鹊残枝,露_荒井。斟酌嫦娥,九秋宫殿冷。',
  '琼楼玉宇。分明不受人间暑。寻常岂是无三五。惟有今宵,皓彩皆同普。素娥阅尽今和古。何妨小驻听吾语。当年弄影婆娑舞。妙曲虽传,毕竟人何许。',
  '快上西楼,怕天放、浮云遮月。但唤取、玉纤横笛,一声吹裂。谁做冰壶浮世界,最怜玉斧修时节。问常娥、孤冷有愁无。应华发。云液满,琼杯滑。长袖起,清歌咽。叹十常八九,欲磨还缺。若得长圆如此夜,人情未必看承别。把从前、离恨总成欢,归时说。',
  '碧天如水,一洗秋容净。何处飞来大明镜。谁道斫却桂,应更光辉,无遗照,泻出山河倒影。人犹苦余热,肺腑生尘,移我超然到三境。问姮娥、缘底事,乃有盈亏,烦玉斧、运风重整。教夜夜、人世十分圆,待拚却长年,醉了还醒。',
  '海上生明月,天涯共此时。情人怨遥夜,竟夕起相思。灭烛怜光满,披衣觉露滋。不堪盈手赠,还寝梦佳期。',
  '一轮飞镜谁磨?照彻乾坤,印透山河。玉露泠泠,洗秋空银汉无波,比常夜清光更多,尽无碍桂影婆娑。老子高歌,为问嫦娥,良夜恹恹,不醉如何?',
  '碧海年年,试问取、冰轮为谁圆缺?吹到一片秋香,清辉了如雪。愁中看、好天良夜,知道尽成悲咽。只影而今,那堪重对,旧时明月。花径里、戏捉迷藏,曾惹下萧萧井梧叶。记否轻纨小扇,又几番凉热,。只落得,填膺百感,总茫茫、不关离别。一任紫玉无情,夜寒吹裂。',
  '青烟幂处,碧海飞金镜。永夜闲阶卧桂影。露凉时、零乱多少寒螀,神京远,惟有蓝桥路近。水晶帘不下,云母屏开,冷浸佳人淡脂粉。待都将许多明,付与金尊,投晓共、流霞倾尽。更携取、胡床上南楼,看玉做人间,素秋千顷。',
  '砧声送风急,蟠蟀思高秋。我来对景,不学宋玉解悲愁。收拾凄凉兴况,分付尊中醽醁,倍觉不胜幽。自有多情处,明月挂南楼。怅襟怀,横玉笛,韵悠悠。清时良夜,借我此地倒金瓯。可爱一天风物,遍倚阑干十二,宇宙若萍浮。醉困不知醒,欹枕卧江流。',
  '凭高眺远,见长空万里,云无留迹。桂魄飞来光射处,冷浸一天秋碧。玉宇琼楼,乘鸾来去,人在清凉国。江山如画,望中烟树历历。我醉拍手狂歌,举怀邀月,对影成三客。起舞徘徊风露下,今夕不知何夕。便欲乘风,翻然归去,何用骑鹏翼。水晶宫里,一声吹断横笛。',
  '桂花浮玉,正月满天街,夜凉如洗。风泛须眉并骨寒,人在水晶宫里。蛟龙偃蹇,观阙嵯峨,缥缈笙歌沸。霜华满地,欲跨彩云飞起。记得去年今夕,酾酒溪亭,淡月云来去。千里江山昨梦非,转眼秋光如许。青雀西来,嫦娥报我,道佳期近矣。寄言俦侣,莫负广寒沈醉',
  '满月飞明镜,归心折大刀。转蓬行地远,攀桂仰天高。水路疑霜雪,林栖见羽毛。此时瞻白兔,直欲数秋毫。稍下巫山峡,犹衔白帝城。气沈全浦暗,轮仄半楼明。刁斗皆催晓,蟾蜍且自倾。张弓倚残魄,不独汉家营。',
  '世事一场大梦,人生几度秋凉。夜来风叶已鸣廊。看取眉头鬓上。酒贱常愁客少,月明多被云妨。中秋谁与共孤光。把盏凄然北望。',
  '中秋佳月最端圆。老痴顽。见多番。杯酒相延,今夕不应慳。残雨如何妨乐事,声淅淅,点斑斑。天应有意故遮阑。拍人间。等闲看。好处时光,须用著些难。直待黄昏风卷霁,金滟滟,玉团团。',
  '丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。转朱阁,低绮户,照无眠。不应有恨,何事长向别时圆?人有悲欢离合,月有阴晴圆缺,此事古难全。',
];
const reg = new RegExp(',|?|。');
let chartList = [];
for(const poetry of poetryList) {
  const sentenceList = poetry.split(reg).filter(item => item);
  chartList.push(...sentenceList);
}
chartList = chartList.map(item => ({
  name: item,
  value: Math.random() * 400
}));
chartList.push({
  name: '寒草呈献',
  value: 1000
})

var chart = echarts.init(document.getElementById('word-cloud__container'));
    chart.setOption({
      series: [
        {
          type: "wordCloud",
          left: "center",
          top: "center",
          width: "100%",
          height: "100%",
          right: null,
          bottom: null,
          sizeRange: [4, 30],
          rotationRange: [0, 0],
          rotationStep: 10,
          gridSize: 1,
          drawOutOfBound: false,
          layoutAnimation: true,
          textStyle: {
            fontFamily: "sans-serif",
            fontWeight: "bold",
            color: '#f9dc60'
          },
          emphasis: {
            focus: "self",
            textStyle: {
              shadowBlur: 10,
              shadowColor: "#333",
            },
          },
          data: chartList
        },
      ],
    });
    const timerD = setTimeout(() => {
      clearTimeout(timerD);
      // moon.style.opacity = 0.9;
      moon.style.background = "radial-gradient(rgba(249, 220, 96, 0.9), rgba(249, 220, 96, 1))"
      //radial-gradient
    }, 1000);
复制代码

走,与我见家长🌟

设计与编码完成肯定需要拉出来溜溜的,所以经过我上面一通说,也到了给大家看看全貌的时候了,但是由于现在掘金不支持放视频,并且动画过长有36s的长度,转成gif确只有2s,导致速度很快,大家看到的体验可能不是很好,但是相信我,实际运行一下,效果还是很棒的~

仓库地址:mid-autumn-festival-juejin

中秋.gif

结束语 · 举杯邀月饮🥤

image.png

下面就来到了结束语,寒草就是最喜欢写结束语,因为在这里我喜欢写一些有的没的,可以放松自由的表达~

首先,在这里提前祝大家中秋节快乐,在此中华传统节日,我也在这里为大家赋诗一首:

鹅,鹅,鹅
曲项向天歌
...
复制代码

呸呸呸,才不是这首,但是我还是需要咏个鹅🦢,大鹅的鹅叫简直是天籁(我看来哈,只是我看来哈),这篇文章的代码也是在大鹅的叫声(歌声)中完成的,我每听到一首都感觉:“这首歌很耳熟”,我现在知道了,不管大鹅唱的是什么,名字就叫:

曲项向天歌

诶呀,我感觉我再去皮就可能发生恶性的大鹅打狗事件:
image.png

算了,那我不咏鹅了,在中秋前夕祝大鹅这么快乐的人「诶?大鹅这么快乐的。。。人?大鹅是人?」一直做一只快快乐乐,肥美好吃的大鹅。

下面才是本文的正题:

中秋快乐🥮
月圆之后,我们再会🌟
看在中秋礼物份上,点个赞吧👍
看在中秋礼物份上,点个关注吧➕
看在中秋礼物份上,整个评论吧📖

tip:最近关注到500会在交流群里送书哦~

加我微信:hancao97,邀你进群,了解寒草🌿 的github小组现状,一起学习前端,成为更优秀的工程师~(群二维码在这里->前端晚晚睡, 二维码过期了的话看链接沸点中的评论,我会把最新的二维码放在评论区,当然也可以加我微信我拉你进群,毕竟我也是有趣的前端,认识我也不赖🌟~)

文章分类
前端
文章标签