中秋特辑!用最简单的Animation动画做出最土最潮的节日祝福贺卡,小陈同学的这份浪漫男默女泪。

807 阅读3分钟

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

前言

之前见过不少节假日“程序员的浪漫”,这不中秋节快到了,我也来蹭一蹭。没有用到Canvas,只是简单的HTML+CSS+JS三件套,好了废话不多说,我们直接看看最终的动画效果。

动画2.gif (好了好了别骂了,土到极致就是潮!PS:动图中火箭尾部留有瑕疵是ScreenToGif渲染的问题)

如图所示,动画中共有月亮、飞机、星星、火箭、对联和海平面6种元素。我们只需要分别实现它们的动画效果,简单地布局一下就好,开干!

元素实现

  • 月亮🌕 什么月亮,它就是一个div。设定好宽高之后,修改border-radius:50%,然后设置一下背景为线性渐变linear-gradient(),这样一个有渐变色背景的圆就画好了。为了让这个圆更像一个发光体(月球本身是不会发光的),我们可以设置一下box-shadow,这样一个看起来像太阳的月亮就做好了。
.moon {
    width: 30vw;
    height: 30vw;
    border-radius: 50%;
    background: linear-gradient(#f1c40f,#f39c12);
    box-shadow: 0px 0px 50px 5px #f39c12;
    position: absolute;
    right: 30%;
    top: 10%;
    animation: moon 5s forwards;
}
  • 飞机✈️ 为什么要放一个飞机呢,因为中秋佳节是团圆的日子,这架飞机里搭载的都是回家的人们,承载了浓浓的思乡之情。没错,我不仅仅画了个飞机,您若仔细看了,机内的乘客张三李四也都是我的作品。

这架飞机它不是div拼接出来的,而是用了CSS3的一个属性mask-image。它可以通过特定的素材图将我们想要的形状“刻出来”,并可以自定义背景色或背景图。我在网上找了一张飞机的透明矢量图,通过这个属性我搞了这架飞机。更多关于mask-image的使用场景,可以看看我之前写的一篇文章 探究mask-image的魔力——Win10日历的探照灯效果原来可以这样做!

.airplane {
    width: 100px;
    height: 100px;
    background-color: #000;
    -webkit-mask-image: url(./image/airplane.png);
    -webkit-mask-size: 100px 100px;
    position: absolute;
    right: 10%;
    top: 20%;
    animation: fly 5s ease-in-out forwards;
    z-index: 5;
}
  • 星星⭐ 因为我太菜了,不会用纯CSS绘制五角星,因此我还是用的mask-image来搞定五角星的形状,至于星星的闪烁,只需要周期性修改opacity就好了。天上的星星参北斗啊!星星太多了,他们的位置、大小、闪烁状态都不会是完全一致的。这里就需要引入随机函数Math.random()来生成随机的位置、大小和闪烁状态。为了避免星星掉进海里,需要设置一个放星星的区域。

关于Math.random()这个方法,我觉得有必要做下笔记加深印象。

Math.random() [0,1)不包含1
Math.floor(Math.random() * (max - min) ) + min [min,max) 不包含max的整数
Math.floor(Math.random() * (max - min + 1) ) + min [min,max] 包含max的整数

写到这我突然想到我可以搞出流星雨来啊!陪Ta去看流星雨落在这地球上不比打代码有意思?可我都写到这了,算了流星雨说它不来了)

.star {
    position: absolute;
    background-color: #f1c40f;
    width: 20px;
    height: 20px;
    animation: star 3s linear infinite;
    box-shadow: 0px 0px 20px 5px rgb(255 255 255 / 30%);
    -webkit-mask-image: url(./image/star.png);
    -webkit-mask-size: 20px 20px;
}
window.onload = function(){
    let starWrap = document.querySelector('.star-wrap')
    for(let i=0;i<50;i++){
        let star = document.createElement('div')
        let {posX,posY,size,delay} = createRandom()
        star.className = "star"
        star.style.left = posX
        star.style.top = posY
        star.style.width = star.style.height = size
        star.style.webkitMaskSize = `${size} ${size}`
        star.style.animationDelay = delay
        star.style.animationDuration = delay
        starWrap.appendChild(star)
    }
}

function createRandom(){
    let posX,posY,size,min = 0,max = 90
    // 生成[10,90]的随机数
    posX = Math.floor(Math.random()*(max-min+1)+min)+'%';
    posY = Math.floor(Math.random()*(max-min+1)+min)+'%';
    size = Math.floor(Math.random()*(30-5+1)+5)+'px'
    delay = Math.floor(Math.random()*(10-1)+1)+'s'
    return {posX,posY,size,delay}
}
  • 火箭与海🚀🌊 说到中秋,我就想到了嫦娥,说到嫦娥,我就想到了嫦娥N号探月工程。但是我网上找不到合适的我们的运载火箭素材,就用了一张免费的素材图。“海上生明月” 算是这个动画的主旨吧,于是就又用了一张免费的海平面的素材,置于页面底部。这部分无非就是火箭起飞,加速,变小的过程,代码拉到最后看。

  • 对联🧧 最初是没有这个元素的,但事后我觉得两边好像太空了,就加上了这个。气球素材网上找的,主要靠气球把对联给飞起来,至于底部的月饼,是我强行添加的【中秋元素】。对联主体.content设置好宽高背景色之后,通过.content::before伪元素来设置对联内部的黄色边框。这里我是把文本内容写进伪元素的content里了,其实也是也可以写在外面的。关于文字竖排显示,我用了一种很笨的方法,就是当文字大小超过对联宽度的时候,它就会自动往下排,形成竖排列的效果,网上也有看到用writing-mode这个属性的。

结语

至此,所有的元素都写好了,只需要添加动画animation就好了。

小陈预祝看到这的和没看到这的各位,中秋节快乐,阖家幸福!🤞

完整代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>中秋</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        body {
            width: 100vw;
            height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            background: rgb(37, 37, 37);
            overflow: hidden;
        }

        .moon {
            width: 30vw;
            height: 30vw;
            border-radius: 50%;
            background: linear-gradient(#f1c40f,#f39c12);
            box-shadow: 0px 0px 50px 5px #f39c12;
            position: absolute;
            right: 30%;
            top: 10%;
            animation: moon 5s forwards;
        }

        @keyframes moon {
            from {
                transform: translate3d(0,100%,0);
            }
        }

        .airplane {
            width: 100px;
            height: 100px;
            background-color: #000;
            -webkit-mask-image: url(./image/airplane.png);
            -webkit-mask-size: 100px 100px;
            position: absolute;
            right: 10%;
            top: 20%;
            animation: fly 5s ease-in-out forwards;
            z-index: 5;
        }

        @keyframes fly {
            to {
                transform: translate3d(-30vw,0,0);
            }
        }

        .star-wrap {
            width: 100%;
            height: 60%;
            position: absolute;
            top: 0;
            z-index: -1;
        }

        .star {
            position: absolute;
            background-color: #f1c40f;
            width: 20px;
            height: 20px;
            animation: star 3s linear infinite;
            box-shadow: 0px 0px 20px 5px rgb(255 255 255 / 30%);
            -webkit-mask-image: url(./image/star.png);
            -webkit-mask-size: 20px 20px;
        }

        @keyframes star {
            from {
                opacity: 0;
            }
            50% {
                opacity: 1;
            }
            to {
                opacity: 0;
            }
        }

        .rocket {
            width: 100px;
            height: 200px;
            background: url(/image/rocket.png);
            background-size: 100%;
            position: absolute;
            bottom: 0;
            right: 30%;
            animation: rocket 5s ease-in forwards;
            z-index: 9;
        }

        @keyframes rocket {
            to {
                transform: translate3d(0,-80vh,0) scale(.2);
            }
        }

        .sea {
            width: 100%;
            height: 50%;
            position: absolute;
            bottom: 0;
            background: url(/image/sea.png) no-repeat;
            background-size: 100%;
            z-index: 0;
        }

        .couplet-wrap {
            width: 90%;
            height: 90vh;
            background-color: transparent;
            z-index: 10;
            display: flex;
            justify-content: space-between;        
            animation: couplet 2s ease 5s backwards;    
        }

        @keyframes couplet {
            from {
                transform: translate3d(0,120%,0);
            }
            to {
                transform: translate3d(0,0,0);
            }
        }

        .couplet {
            width: 150px;
            display: flex;
            flex-direction: column;
            align-items: center;
        }

        .balloon {
            width: 100%;
            height: 120px;
            background: url(/image/balloon.png) no-repeat center;
            background-size: contain;
        }

        .content {
            background-color: #c0392b;
            flex: 1;
            width: 100%;
            position: relative;
        }

        .content::before {
            content: "海上生明月";
            position: absolute;
            width: 80%;
            height: 90%;
            border: 5px solid #f1c40f;
            left: 50%;
            top: 50%;
            transform: translate3d(-50%,-50%,0);
            text-align: center;
            font: 70px '宋体';
            line-height: 90px;
            color: rgb(44, 44, 44);
        }


        #content2::before {
            content: "天涯共此时";
        }

        .content::after {
            content: "";
            position: absolute;
            top: 100%;
            left: 50%;
            width: 5px;
            height: 50px;
            background-color: #c0392b;
            border-left: 2px dashed #f1c40f;
            border-right: 2px dashed #f1c40f;
            box-sizing: border-box;
        }

        .mooncake {
            width: 100%;
            height: 80px;
            margin-top: 30px;
            background: url(/image/mooncake.png) no-repeat center;
            background-size: contain;
            z-index: 10;
        }
    </style>
</head>
<body>
    <!-- 图片资源就自己找吧~ -->
    <div class="moon"></div>
    <div class="airplane"></div>
    <div class="star-wrap"></div>
    <div class="rocket"></div>
    <div class="sea"></div>
    <div class="couplet-wrap">
        <div class="couplet">
            <div class="balloon"></div>
            <div class="content"></div>
            <div class="mooncake"></div>
        </div>
        <div class="couplet">
            <div class="balloon"></div>
            <div class="content" id="content2"></div>
            <div class="mooncake"></div>
        </div>
    </div>

    <script>
        window.onload = function(){
            let starWrap = document.querySelector('.star-wrap')
            for(let i=0;i<50;i++){
                let star = document.createElement('div')
                let {posX,posY,size,delay} = createRandom()
                star.className = "star"
                star.style.left = posX
                star.style.top = posY
                star.style.width = star.style.height = size
                star.style.webkitMaskSize = `${size} ${size}`
                star.style.animationDelay = delay
                star.style.animationDuration = delay
                starWrap.appendChild(star)
            }
        }

        function createRandom(){
            let posX,posY,size,min = 0,max = 90
            // 生成[10,90]的随机数
            posX = Math.floor(Math.random()*(max-min+1)+min)+'%';
            posY = Math.floor(Math.random()*(max-min+1)+min)+'%';
            size = Math.floor(Math.random()*(30-5+1)+5)+'px'
            delay = Math.floor(Math.random()*(10-1)+1)+'s'
            return {posX,posY,size,delay}
        }
    </script>
</body>
</html>