浪漫又蹩脚的设计之路📖
那么!第一个问题就来了,我该如何设计我给大家的中秋礼物捏,还是像往常一样,我要列出我想要展现的要素:
- 月亮(废话,中秋肯定要有月亮呀,听君一席话,就是一席话)
- 星空:皎洁的月色需要星空点缀
- 诗歌:中秋节是一个中华传统节日,并且无数文人墨客在中秋创作诗词歌赋,我要用诗歌元素让我的作品包含中华文化
这几个要素是必选的,但是感觉形成一个动画的要素要需要更多,而且需要更多细节,比如:
- 星空怎么展现?
- 诗歌与作品怎么结合?
关于更多要素我想到了,不妨在我这个作品里体现日落到月亮升起的过程,来一个“一镜到底”的动画。
而细节如何设计,请容我卖个关子~毕竟,我要留有悬念~
落地设计,逃不开的编码之路💻
现在我已经有了一个初步的构想了,下面就是如何去实现出来,那么这一章就请大家跟随我一步一步把这个动画的内容充实起来吧🌟
请给位读者耐心看完~
诶,终究是逃不开写代码呀,寒草,你跑不掉滴~
日落月升🌛
这里我的动画希望采用的是那种简约派的画风,第一是好做(我好耿直。。。),第二是感觉这个画风类似于之前剪纸之类的风格,简约的日落,圆月升起之中似乎带着一点点的中国风。
这个环节全都是采用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>
镜头对月色的追随💓
下一步我想的就是,镜头已经记录了太阳落下到月亮升起的过程,下面我们拉近镜头,去更近距离的观察月亮,所以我们要营造一个镜头拉近的效果:
- 月亮放大并到镜头的偏中心的位置
- 山峦也会跟随像右侧移动,并逐渐消失在镜头里
// 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);
将银河撒向夜空🌃
接下来我要让星空浮现,因为我想星星也不是一下子全出来的,而是渐渐浮现出来的,所以要随机的时间点来产生星星✨,并让他有闪烁的效果。
// 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()); }
但愿人长久 · 千里共婵娟🎋
之后就是那一句诗句了:
“但愿人长久,千里共婵娟”
我是如何让他一点一点像是用笔写出来的呢?其实我在我生日文里面也用了这个库用技术创造惊喜|成熟的前端工程师一定要学会亲手制作生日礼物 🎁,只不过:
- 上次是生日礼物,这次是中秋礼物
- 上次是送给我自己,这次是送给你们
嘿嘿嘿,感动不~这个库是 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:
静态细节(注意中间写着寒草呈献):
考虑作品立意时彰显中华传统诗词文化的宗旨,我有个想法,就是去搜寻更多有关中秋的诗歌,并把他们印在月亮上,这里我用了两个手段:
- 透明度渐变
- 词云图
把月亮中心的透明度降低,让藏在后面的诗词显示出来。
// 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
结束语 · 举杯邀月饮🥤
下面就来到了结束语,寒草就是最喜欢写结束语,因为在这里我喜欢写一些有的没的,可以放松自由的表达~