CSS3 中的动画之 transition 和 transform

CSS3 使运动变得更加简单了,我们只需用少量的代码,就可以写出数行甚至更多 JavaScript 代码的效果,并且 CSS3 处理诸如旋转、倾斜、3D 等效果的能力是远强于 JavaScript 的。你能用 JavaScript 让一个 div 渲染任意角度吗?也许能,但复杂度肯定超乎想象,而这个效果可以使用 CSS3 一个函数轻松搞定。
有了 CSS3 写动画,就可以完全抛弃 JavaScript 了吗?当然不是。CSS3 强在表现,JavaScript 强在逻辑,二者结合可以发挥最大的威力。
CSS3 中实现动画效果可能会用到这几个属性:

  • transition
  • transform
  • animation

这几个属性构成了 CSS3 丰富多彩的动画世界。

transition

这是 CSS3 的一个属性,意为过渡。该属性用在某个元素上,用来定义该元素的属性发生变化时,呈现出的过渡效果
transition 有几个属性值:

  • transition-property:需要应用过渡效果的属性名称
  • transition-duration:完成该过渡效果所需的时间
  • transition-timing-function:过渡效果
  • transition-delay:延迟时间(默认为 0)

transition-timing-function 有下列可选值:

  • linear:匀速过渡
  • ease:先慢后快再慢过渡
  • ease-in:先慢后快过渡
  • ease-out:先快后慢过渡
  • ease-in-out:先慢后快再慢过渡
  • cubic-bezier:根据贝赛尔曲线过渡

这系列规则可以合写为:

transiton:<transition-property> <transition-duration> <transition-timing-function> <transition-delay>

如果想根据属性定义不同的过渡,可以这样写(以宽高为例):

transition:width 1s ease-in,height 0.5s ease-out 1s;

贝赛尔曲线用来定义更加复杂的过渡,如果我们想要一个自定义的过渡效果,就可以使用贝塞尔曲线,这个网站可以将贝塞尔曲线转换为相应的 CSS3 函数,推荐使用。

关于这些属性的记忆,这里给出了一个羞羞的方式。

transitionend 事件

transitionend 事件是在过渡效果完成后触发

ele.addEventListener("transitionend",fun);

注:元素的每个属性过渡完成后都会触发一次 transitionend 事件

transform

transform 用来对元素进行 2D/3D 上的属性转换。简单说来,使用该属性,可以对物体上进行形状上的变化,如倾斜,旋转。可以让物体在 2D 或者 3D 空间下展示,让物体在空间中进行位移等。常用的有这么些属性:

  • scale:对元素进行缩放
  • rotate:对元素进行旋转
  • skew:对元素进行倾斜变换
  • translate:对元素进行位移
  • scale3d:3D 场景下缩放
  • rotate3d:3D 场景下旋转
  • skew3d:3D 场景下倾斜
  • translate3d:3D 场景下位移

详细的用法可以参考这里
下面是一个动态时钟的例子。

动态时钟

先来看下布局:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>取个什么名字好呢</title>
    <style>
        /* 外层容器 */
        #box{
            width: 460px;
            height: 460px;
            margin: 100px auto;
            position: relative;
            border-radius: 50%;
        }
        /* 指针容器 */
        .pointers{
            width: 20px;
            height: 20px;
            position: absolute;
            left: calc(50% - 10px);
            top: calc(50% - 10px);
            background: #000;
            border-radius: 50%;
        }
        /* 时针 */
        #hour{
            width: 10px;
            height: 70px;
            background: #000;
            position: absolute;
            left: calc(50% - 5px);
            top: -60px;
            transform-origin: center 70px;
            transform: rotate(-45deg);
            z-index: -1;
        }

        /* 分针 */
        #min{
            width: 6px;
            height: 100px;
            background: #ccc;
            position: absolute;
            left: calc(50% - 3px);
            top: -90px;
            transform: rotate(45deg);
            transform-origin: center 100px;
            z-index: -1;

        }

        /* 秒针 */
        #sec{
            width: 4px;
            height: 140px;
            background: red;
            position: absolute;
            left: calc(50% - 2px);
            top: -130px;
            transform: rotate(90deg);
            transform-origin: center 140px;
            z-index: -1;
        }
        #dial{
            width: 460px;
            height: 460px;
            background: rgba(0,0,0,0.2);
            margin: 0;
            padding: 0;
            list-style: none;
            position: relative;
            display: flex;
            justify-content: center;
        }

        #dial li{
            width: 4px;
            height: 10px;
            background: #000;
            position: absolute;
            transform-origin: center 230px;
        }


        #dial li{
            width: 4px;
            height: 10px;
            background: #000;
            position: absolute;
            transform-origin: center 230px;
        }

        #dial li:nth-of-type(1){
            height: 20px;
            width: 10px;
        }

        #dial li:nth-of-type(2){
            transform: rotate(6deg);
        }

        #dial li:nth-of-type(3){
            transform: rotate(12deg);
        }

        #dial li:nth-of-type(4){
            transform: rotate(18deg);
        }

        #dial li:nth-of-type(5){
            transform: rotate(24deg);
        }
    </style>
</head>
<body>
    <div id="box">
        <!-- 刻度 -->
        <ul id="dial">
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
        </ul>
        <!-- 指针 -->
        <div class = "pointers">
            <div id = "hour"></div>
            <div id="min"></div>
            <div id="sec"></div>
        </div>

    </div>
</body>
</html>

transform-orgin 属性指定元素 transform 时的基准点,默认为元素的中心(center center),我们可以将 transform-origin 指定为页面上的任意一个点。
布局效果如图:

时钟布局效果.png

接下来画出剩余的其他刻度:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>取个什么名字好呢</title>
    <style>
        /* 外层容器 */
        #box{
            width: 460px;
            height: 460px;
            margin: 100px auto;
            position: relative;
            border-radius: 50%;
        }
        /* 指针容器 */
        .pointers{
            width: 20px;
            height: 20px;
            position: absolute;
            left: calc(50% - 10px);
            top: calc(50% - 10px);
            background: #000;
            border-radius: 50%;
        }
        /* 时针 */
        #hour{
            width: 10px;
            height: 70px;
            background: #000;
            position: absolute;
            left: calc(50% - 5px);
            top: -60px;
            transform-origin: center 70px;
            transform: rotate(-45deg);
            z-index: -1;
        }

        /* 分针 */
        #min{
            width: 6px;
            height: 100px;
            background: #ccc;
            position: absolute;
            left: calc(50% - 3px);
            top: -90px;
            transform: rotate(45deg);
            transform-origin: center 100px;
            z-index: -1;

        }

        /* 秒针 */
        #sec{
            width: 4px;
            height: 140px;
            background: red;
            position: absolute;
            left: calc(50% - 2px);
            top: -130px;
            transform: rotate(90deg);
            transform-origin: center 140px;
            z-index: -1;
        }
        #dial{
            width: 460px;
            height: 460px;
            background: rgba(0,0,0,0.2);
            margin: 0;
            padding: 0;
            list-style: none;
            position: relative;
            display: flex;
            justify-content: center;
        }

        #dial li{
            width: 4px;
            height: 10px;
            background: #000;
            position: absolute;
            transform-origin: center 230px;
        }


        #dial li{
            width: 4px;
            height: 10px;
            background: #000;
            position: absolute;
            transform-origin: center 230px;
        }
    </style>
</head>
<body>
    <div id="box">
        <!-- 刻度 -->
        <ul id="dial"></ul>
        <!-- 指针 -->
        <div class = "pointers">
            <div id = "hour"></div>
            <div id="min"></div>
            <div id="sec"></div>
        </div>

    </div>
</body>
<script>
    (function (){
        const dial = document.getElementById("dial"); 
        let dials = "";
        for(let i = 0; i < 60; i++){
            let tmp = "";
            if(i % 5 === 0){
                tmp = `<li style = " width:10px;height:20px;transform:rotate(${6 * i}deg)"></li>`;
            }else{
                tmp = `<li style = "transform:rotate(${6 * i}deg)"></li>`;
            }
            dials += tmp;
        }
        dial.innerHTML = dials;
    })();
</script>
</html>

看下效果:


js批量生成布局.png

获取当前的时、分、秒数值:

const hourEle = document.getElementById("hour");
const minEle = document.getElementById("min");
const secEle = document.getElementById("sec");
(function (){
    const dial = document.getElementById("dial"); 
    let dials = "";
    for(let i = 0; i < 60; i++){
        let tmp = "";
        if(i % 5 === 0){
            tmp = `<li style = " width:10px;height:20px;transform:rotate(${6 * i}deg)"></li>`;
        }else{
            tmp = `<li style = "transform:rotate(${6 * i}deg)"></li>`;
        }
        dials += tmp;
    }
    dial.innerHTML = dials;
    getTime()
})();

function getTime(){
    clearInterval(getTime.timer);
    let date = new Date();
    let sec = date.getSeconds();
    let min = date.getMinutes();
    let hour = date.getHours();
    // 秒针每秒走6deg
    // 分针分钟走6deg
    // 时针每小时走30deg
    secEle.style.transform = `rotate(${sec * 6}deg)`;
    minEle.style.transform = `rotate(${min * 6}deg)`;
    hourEle.style.transform = `rotate(${hour * 6}deg)`;
    getTime.timer = setInterval(getTime,1000);
}

看下效果:

时钟效果01.gif

现在时钟已经动起来了,并可以显示当前的时间。还有一个小问题,就是时钟的分针和时针总是正对着当前分钟的刻度,而实际情况下分针和时针应该是有一些偏移角度的。这是因为我们在计算旋转度数的时候,只单纯计算了当前的分钟或者小时数,正确的做法应该是:

  • 计算分针度数时加上当前的秒数(需将秒换算为分)
  • 计算时针度数时加上当前的分钟数(需将分换算为时)

对 getTime 函数稍作修改:

...
function getTime(){
    clearInterval(getTime.timer);
    let date = new Date();
    let sec = date.getSeconds();
    let min = date.getMinutes();
    let hour = date.getHours();
    // 秒针每秒走6deg
    // 分针分钟走6deg
    // 时针每小时走30deg
    secEle.style.transform = `rotate(${sec * 6}deg)`;
    minEle.style.transform = `rotate(${min * 6 + sec / 60}deg)`;
    hourEle.style.transform = `rotate(${hour * 6 + min / 60}deg)`;
    getTime.timer = setInterval(getTime,1000);
}
...

看下最终效果:


时钟效果02.gif

完。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,193评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,306评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,130评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,110评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,118评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,085评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,007评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,844评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,283评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,508评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,395评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,985评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,630评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,797评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,653评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,553评论 2 352

推荐阅读更多精彩内容

  • 选择qi:是表达式 标签选择器 类选择器 属性选择器 继承属性: color,font,text-align,li...
    love2013阅读 2,314评论 0 11
  • HTML5 1.HTML5新元素 HTML5提供了新的元素来创建更好的页面结构: 标签描述 定义页面独立的内容区域...
    L怪丫头阅读 2,809评论 0 4
  • 看了很多视频、文章,最后却通通忘记了,别人的知识依旧是别人的,自己却什么都没获得。此系列文章旨在加深自己的印象,因...
    DCbryant阅读 1,861评论 0 4
  • 选择器 CSS3中新添加了很多选择器,解决了很多之前需要用javascript才能解决的布局问题。· elemen...
    lovelydong阅读 479评论 0 2
  • 选择qi:是表达式 标签选择器 类选择器 属性选择器 继承属性: color,font,text-align,li...
    wzhiq896阅读 1,747评论 0 2