我和我亲爱的祖国(用CSS来为祖国母亲庆生)

8,425 阅读7分钟

“我和我的祖国一刻也不能分割,无论我走到哪里都流出一首赞歌。”

相信大家最近已经被 《我和我的祖国》 这首歌给刷屏了,鱼头每次在听到这首歌的时候,总会感慨万千,我们伟大的新中国,这70年真的经历了太多太多了。

但是就是在一代又一代的人不懈的努力下,如今的中国,已经成为了各个国家,乃至西方列强所无法忽视的强大存在。

从辉煌到屈辱

中国是世界上文明发达最早的国家之一,有将近5000年的有文字可考的历史。

曾经,西汉开辟了丝绸之路,通过丝绸连接了从古都长安到罗马帝国这条路上的各个国家;

曾经,成吉思汗和他的后代,从公元1205年起,先后灭掉了西夏和金朝,然后又征服了中亚、西亚和亚洲西部的许多国家,一直打到欧洲的多瑙河畔。

但是,从1840年 第一次鸦片战争 开始,中国受了将近100年的屈辱。

从1840年鸦片战争到1919年五四运动前夕的旧民主主义革命阶段,

从1919年五四运动到1949年中华人民共和国成立前夕的新民主主义革命阶段,

我们经历了太多太多。

曾经在1936年柏林奥运会69人的代表团因全军覆没,途经新加坡时,被当地报刊讽刺为 “东亚病夫” 。就如同下面的GIF一样,残缺,破损。

dybf

上面GIF的风格被称之为是“故障风(Glitch Effect)”,是近期比较流行的一种设计风格。所以是怎么实现的呢?

思路拆分

<style>
    .ge-text {
        position: relative;
        color: #fff;
        font-size: 72px;
        line-height: 1;
        letter-spacing: 0.01em;
        transform: scale3d(1, 1, 1);
        padding: 10px 20px;
        background: linear-gradient(to right, #232526, #414345);
        overflow: hidden;
    }
    .ge-text::before, .ge-text::after {
        content: attr(aria-title);
        position: absolute;
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;
        color: #fff;
        background: linear-gradient(to right, #232526, #414345);
        clip-path: inset(79px 50px 43px 0px);
        overflow: hidden;
    }
    .ge-text::before {
        left: 7px;
        animation: glitch-effect 3s infinite linear alternate-reverse;
    }
    .ge-text::after {
        left: 3px;
        animation: glitch-effect 2s infinite linear alternate-reverse;
    }
    @keyframes glitch-effect {
        0% {
          clip-path: inset(4px 50px 61px 0px);
        }
        5% {
          clip-path: inset(99px 50px 30px 0px);
        }
        ...(省略了不同百分比下的的值)
        95% {
          clip-path: inset(7px 50px 59px 0px);
        }
        100% {
          clip-path: inset(79px 50px 43px 0px);
        }
    }
</style>
<div class="glitch-effect">
    <h1 class="ge-text" aria-title="东亚病夫">东亚病夫</h1>
</div>

(重复代码太多,就不全部放出来了,具体实现可以看我的codepen

我们看上面的GIF,不难发现所谓的故障风就是相同图形不同的位置跳动。只是都是浮在正常图形之上的跳动,所以看起来有物体故障的感觉,抖音的LOGO GIF就是这么个操作。

既然知道这一点,我们第一步就是做三个一模一样,但是位置不同的图形了。

上述DEMO我是用了::before::after,当然,重复的图形,你可以只用一个,或者多个都可以,随你喜欢,就看你喜欢怎样的故障了。

之后的核心就是clip-path: inset@keyframes的配合。

clip-path

clip-path 可以引入或者创建一个指定裁剪区域的强大属性,它在代替了已经弃用的clip属性的同时还多了需要有趣的功能。

本文不打算详细讲解这个属性,有兴趣的可以通过MDN 或者 CSS clip-path 生成器 来深入了解。

clip-path主要由以下4个可选函数:

函数作用
clip-path: inset( <shape-arg>{1,4} [round <border-radius>]? )用于定义裁剪的矩形区域
clip-path: circle( [<shape-radius>]? [at <position>]? )用于定义裁剪的圆形区域
clip-path: ellipse( [<shape-radius>{2}]? [at <position>]? )用于定义裁剪的椭圆区域
clip-path: polygon( [<fill-rule>,]? [<shape-arg> <shape-arg>]# )用于定义裁剪的多边形区域

下面图示便是用 clip-path: polygon 所画出来的。

clipy

图片来自CSS clip-path 生成器

那么我们的故障风该如何画呢?

首先我们来看看clip-path: inset不同值的状态。

clipy1

上面随手输入了几组值,我们不难发现,不同的值所裁剪的区域是不一样的,这个时候只要我们用::before::after去做这个变动的处理,然后给它们各自加上不同的left值,则会有最上面的效果。

从弱小到强大

从东亚病夫,到金砖四国,再到国家GDP排名全世界前三;

从开国大典红旗冉冉升起,到第一科原子弹爆炸成功,到97香港回归,再到08年奥运会,再到如今,中国经历了太多太多。

所以时常在听到国歌,看到国旗的时候,心里总会百感交集。

说起国旗,我们不来尝试下用代码来画个五星红旗吗?

具体属性

首先我们要知道,国旗是五星红旗,旗面为红色,长宽比例为3:2。左上方缀黄色五角星五颗,四颗小星(其外接圆直径为旗高1/10)环拱在一颗大星(其外接圆直径为旗高3/10)的右面,并各有一个角尖正对大星的中心点。

通用尺寸有以下五种:

  1. 长288厘米,高192厘米;
  2. 长240厘米,高160厘米;
  3. 长192厘米,高128厘米;
  4. 长144厘米,高96厘米;
  5. 长96厘米,高64厘米。

国旗墨线图如下:

flag

知道这个之后我们就可以按照上述的比例去做一画一个五星红旗出来,效果如下:

flag

此效果DEMO核心实现灵感来自此文:前端实现旗帜飘动效果系列 (Ⅰ):dom+css实现,有兴趣的可以去看看原文。

代码实现

所以该如何实现上述红旗飘飘的功能呢?

首先我们来看伪代码

<style>
    * {
        margin: 0;
        padding: 0;
    }
    html,
    body {
        height: 100%;
        width: 100%;
    }
    li {
        list-style: none;
    }
    .flag {
        position: absolute;
        left: 50%;
        top: 50%;
        animation: wave ease-in-out infinite;
    }
    .flag > li {
        height: 100%;
        float: left;
        background-image: url("flag.jpg");
        background-size: auto 100%;
        animation: flag ease-in-out infinite;
    }
</style>
<ul id="flag" class="flag"></ul>
<script>
    'use strict'
    const flag = document.querySelector('#flag')
    const image = new Image()
    image.src = 'flag.jpg'
    const flagWidth = 800
    const flagHeight = 640
    let imgWidth = ''
    let imgHeight = ''
     /**
       * @func
       * @name 分割图片
       * @param sliceCount 切片数量
       * @param amplitude 振幅
       * @param period 固定周期个数
       * @param duration 一个周期的时长
       */
    const imgRender = ({
        sliceCount = 70,
        amplitude = 20,
        period = 1.5,
        duration = 2,
    }) => {/* 具体生成红旗DOM片段的逻辑 */}
    image.onload = () => {
        imgWidth = image.width
        imgHeight = image.height
        const ratio = image.width / image.height
        if (imgWidth > flagWidth) {
            imgWidth = flagWidth
            imgHeight = imgWidth / ratio
        }
        if (imgHeight > flagHeight) {
            imgWidth = imgHeight * ratio
            imgHeight = flagHeight
        }
        flag.style.width = imgWidth + 'px'
        flag.style.height = imgHeight + 'px'
        flag.style.marginLeft = -imgWidth / 2 + 'px'
        flag.style.marginTop = -imgHeight / 2 + 'px'
        imgRender({
            sliceCount: 70,
            amplitude: 20,
            period: 1.5,
            duration: 2,
        })
    }
</script>

所添加的 imgRender 究竟做了什么?

如果你们知道什么是 雪碧图(CSS Sprite) ,这个应该就好理解了,在 HTTP1 的时代里, 雪碧图(CSS Sprite) 是一个很常见的技术概念,由于译名的原因,也有人叫它CSS精灵,是一种CSS图像合并技术,该方法是将小图标和背景图像合并到一张图片上,然后利用css的背景定位来显示需要显示的图片部分。

CSS雪碧的基本原理是把你的网站上用到的一些图片整合到一张单独的图片中,然后使用CSS backgroundbackground-position 属性渲染。

所以 imgRender 就是做了这么一件事,将红旗图片按等分切割出来,然后进行循环,生成 sliceCount 量的DOM节点,然后添加background-position,这时候的background-position值是跟循环到的下标有关的,具体逻辑其实可以按需去改,之后就是动画属性的配置了。

具体实现可以看鱼头的CODEPEN

Mmmm,本文标题是CSS,其实这里也不算用了JS,毕竟它在这里只是作为一个脚本生成重复的代码,主要核心还是雪碧图 + animation

SVG版本的红旗

我们实现了CSS版的五星红旗,那么现在就让我们来实现一次SVG版本的。

用SVG去处理一些复杂的图案是非常轻松的,我们只需要知道路径,就可以对它进行操作。

当然我们该怎么知道路径?这可以让我们的UI设计师从AI中导出SVG,或者去www.iconfont.cn找也可以,之后我们就可以做我们想做的操作了。

如果我们要画上面的红旗,也非常简单。

这里直接上代码:

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="900" height="600" viewBox="0 0 30 20">
    <defs>
        <path id="s" d="M0,-1 0.587785,0.809017 -0.951057,-0.309017H0.951057L-0.587785,0.809017z" fill="#ffde00"/>
    </defs>
    <rect width="30" height="20" fill="#de2910"/>
    <use xlink:href="#s" transform="translate(5,5) scale(3)"/>
    <use xlink:href="#s" transform="translate(10,2) rotate(23.036243)"/>
    <use xlink:href="#s" transform="translate(12,4) rotate(45.869898)"/>
    <use xlink:href="#s" transform="translate(12,7) rotate(69.945396)"/>
    <use xlink:href="#s" transform="translate(10,9) rotate(20.659808)"/>
</svg>

从上面我们不难看出,这个五星红旗只需要定义一个红色画布,一个星星的轮廓,然后use进去之后给定不同的位置就可以了。

效果如下:

Hongqi

“五星红旗,你是我的骄傲;五星红旗,我为你自豪;为你欢呼,我为你祝福;你的名字,比我生命更重要;”

写到这里的时候,脑子自动响起了红旗飘飘的旋律。祝愿祖国越来越强盛,也祝大家国庆节快乐。

如果你喜欢探讨技术,或者对本文有任何的意见或建议,非常欢迎加鱼头微信好友一起探讨,当然,鱼头也非常希望能跟你一起聊生活,聊爱好,谈天说地。 鱼头的微信号是:krisChans95