视觉高级篇18#如何生成简单动画让图形动起来?(代码片段)

凯小默 凯小默     2022-12-02     246

关键词:

说明

【跟月影学可视化】学习笔记。

动画的三种形式

  • 固定帧动画:预先准备好要播放的静态图像,然后将这些图依次播放,实现起来最简单,只需要为每一帧准备一张图片,然后循环播放即可。
  • 增量动画:就是在每帧给元素的相关属性增加一定的量,但也很好操作,就是不好精确控制动画细节。
  • 时序动画:使用时间和动画函数来计算每一帧中的关键属性值,然后更新这些属性,这种方法能够非常精确地控制动画的细节,所以它能实现的动画效果更丰富,应用最广泛。

实现固定帧动画

3个静态图像如下:

<!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>
        .bird 
            position: absolute;
            left: 100px;
            top: 100px;
            width:86px;
            height:60px;
            zoom: 0.5;
            background-repeat: no-repeat;
            background-image: url(./assets/img/bird.png);
            background-position: -178px -2px;
            animation: flappy .5s step-end infinite;
        

        @keyframes flappy 
            0% background-position: -178px -2px;
            33% background-position: -90px -2px;
            66% background-position: -2px -2px;
        
    </style>
</head>
<body>
    <div class="bird"></div>
</body>
</html>

实现增量动画

实现橙红色方块旋转的动画:给这个方块的每一帧增加一个 rotate 角度。

<!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>
            .block 
                width: 100px;
                height: 100px;
                top: 100px;
                left: 100px;
                transform-origin: 50% 50%;
                position: absolute;
                background: salmon;
            
        </style>
    </head>
    <body>
        <div class="block"></div>
        <script>
            const block = document.querySelector(".block");
            let rotation = 0;
            function update() 
                block.style.transform = `rotate($rotation++deg)`;
                requestAnimationFrame(update);
            
            update();
        </script>
    </body>
</html>

实现时序动画

以上面的方块旋转为例,首先定义初始时间和周期,然后在 update 中计算当前经过时间和进度 p,最后通过 p 来更新动画元素的属性。

<!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>
            .block 
                width: 100px;
                height: 100px;
                top: 100px;
                left: 100px;
                transform-origin: 50% 50%;
                position: absolute;
                background: salmon;
            
        </style>
    </head>
    <body>
        <div class="block"></div>
        <script>
            const block = document.querySelector(".block");
            const startAngle = 0; // 起始旋转角度
            const T = 2000; // 旋转周期
            let startTime = null; // 初始旋转的时间
            function update() 
                startTime = startTime == null ? Date.now() : startTime;
                const p = (Date.now() - startTime) / T; // 旋转进度 = 当前经过的时间 / 旋转周期
                const angle = startAngle + p * 360; // 当前角度
                block.style.transform = `rotate($angledeg)`;
                requestAnimationFrame(update);
            
            update();
        </script>
    </body>
</html>

虽然时序动画实现起来比增量动画写法更复杂,但我们可以更直观、精确地控制旋转动画的周期(速度)、起始角度等参数。

定义标准动画模型

定义一个类 Timing 用来处理时间:

// 类 Timing 用来处理时间
export class Timing 
    constructor( duration, iterations = 1  = ) 
        this.startTime = Date.now();
        this.duration = duration;
        this.iterations = iterations;
    

    get time() 
        return Date.now() - this.startTime;
    

    get p() 
        const progress = Math.min(this.time / this.duration, this.iterations);
        return this.isFinished ? 1 : progress % 1;
    

    get isFinished() 
        return this.time / this.duration >= this.iterations;
    

实现一个 Animator 类,用来真正控制动画过程:

import  Timing  from './timing.js';
// Animator 类,用来真正控制动画过程
export class Animator 
    constructor( duration, iterations ) 
        this.timing =  duration, iterations ;
    

    animate(target, update) 
        let frameIndex = 0;
        const timing = new Timing(this.timing);

        return new Promise((resolve) => 
            function next() 
                if (update( target, frameIndex, timing ) !== false && !timing.isFinished) 
                    requestAnimationFrame(next);
                 else 
                    resolve(timing);
                
                frameIndex++;
            
            next();
        );
    

用 Animator 实现四个方块的轮换转动:

<!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>
            .container 
                display: flex;
                flex-wrap: wrap;
                justify-content: space-between;
                width: 300px;
            
            .block 
                width: 100px;
                height: 100px;
                margin: 20px;
                flex-shrink: 0;
                transform-origin: 50% 50%;
            
            .block:nth-child(1) 
                background: salmon;
            
            .block:nth-child(2) 
                background: slateblue;
            
            .block:nth-child(3) 
                background: seagreen;
            
            .block:nth-child(4) 
                background: sandybrown;
            
        </style>
    </head>
    <body>
        <div class="container">
            <div class="block"></div>
            <div class="block"></div>
            <div class="block"></div>
            <div class="block"></div>
        </div>
        <script type="module">
            import  Animator  from "./common/lib/animator/index.js";
            const blocks = document.querySelectorAll(".block");
            const animator = new Animator(
                duration: 1000,
                iterations: 1.5
            );
            (async function () 
                let i = 0;
                while (true) 
                    await animator.animate(
                        blocks[i++ % 4],
                        ( target, timing ) => 
                            target.style.transform = `rotate($timing.p * 360deg)`;
                        
                    );
                
            )();
        </script>
    </body>
</html>

插值与缓动函数

用 Animator 实现一个方块,让它从 0px 处匀速运动到 400px 处。

<!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>
            .block 
                position: relative;
                width: 100px;
                height: 100px;
                background: salmon;
            
        </style>
    </head>
    <body>
        <div class="block"></div>
        <script type="module">
            import  Animator  from "./common/lib/animator/index.js";
            const block = document.querySelector(".block");
            const animator = new Animator( duration: 3000 );
            document.addEventListener('click', () => 
                animator.animate(
                    el: block,
                    start: 0,
                    end: 400
                , (
                    target:  el, start, end ,
                    timing:  p 
                ) => 
                    // 线性插值方法
                    const left = start * (1 - p) + end * p;
                    el.style.left = `$leftpx`;
                );
            );
        </script>
    </body>
</html>

下面加入缓动函数,抽象出一个映射函数专门处理 p 的映射,这个函数叫做缓动函数(Easing Function)

// 类 Timing 用来处理时间
export class Timing 
    constructor( duration, iterations = 1, easing = p => p  = ) 
        this.startTime = Date.now();
        this.duration = duration;
        this.iterations = iterations;
        this.easing = easing;
    

    get time() 
        return Date.now() - this.startTime;
    

    get p() 
        const progress = Math.min(this.time / this.duration, this.iterations);
        return this.isFinished ? 1 : this.easing(progress % 1);
    查看详情  

视觉高级篇23#如何模拟光照让3d场景更逼真?(上)(代码片段)

说明【跟月影学可视化】学习笔记。光照效果简介物体的光照效果是由光源、介质(物体的材质)和反射类型决定的,而反射类型又由物体的材质特点决定。在3D光照模型中,根据不同的光源特点分为四种:环... 查看详情

scratch如何让画笔绘制的图案移动?

一般来说,scratch当中是无法将画笔画好的图案移动起来的,这受限于画笔自身模块的功能单一性,但是可以换个想法,动画片会动也是一帧一帧的图片构成的。可以通过将相同的图案不断擦除再不断绘制的方式,就能让画好的... 查看详情

视觉高级篇24#如何模拟光照让3d场景更逼真?(下)(代码片段)

说明【跟月影学可视化】学习笔记。什么是镜面反射?如果若干平行光照射在表面光滑的物体上,反射出来的光依然平行,这种反射就是镜面反射。越光滑的材质,它的镜面反射效果也就越强,并且物体表面... 查看详情

视觉高级篇24#如何模拟光照让3d场景更逼真?(下)(代码片段)

说明【跟月影学可视化】学习笔记。什么是镜面反射?如果若干平行光照射在表面光滑的物体上,反射出来的光依然平行,这种反射就是镜面反射。越光滑的材质,它的镜面反射效果也就越强,并且物体表面... 查看详情

wpf如何让两个动画同时动起来

要C#后台代码,O(∩_∩)O谢谢!定义好两个动画后,直接在后台同时调用Begin方法啊。不过建议将两个Animation动画写在一个Storyboard中。然后对Storyboard定义x:Key。最后在后台代码中对动画调用Begin方法。例如:(没经过编译器确认,... 查看详情

canvas动画:自由落体运动(代码片段)

...但是这些画面都是禁止的,怎么样才能让他们动起来呢?如何绘制基本图形可以参考:canvas基本图形绘制如何对基本图形移动旋转缩放可以参考:canvas图形变换如何设置基本图形颜色和样式可以参考:canvas样式和颜色如何使用外... 查看详情

跟月影学可视化学习笔记41篇(完结)

...篇】09#如何用仿射变换对几何图形进行坐标变换?【视觉基础篇】10#图形系统如何表示颜色?【视觉基础篇】11#图案生成:如何生成重复图案、分形图案以及随机效果?【视觉基础篇】12#如何使用滤镜函数实现美... 查看详情

跟月影学可视化学习笔记41篇(完结)

...篇】09#如何用仿射变换对几何图形进行坐标变换?【视觉基础篇】10#图形系统如何表示颜色?【视觉基础篇】11#图案生成:如何生成重复图案、分形图案以及随机效果?【视觉基础篇】12#如何使用滤镜函数实现美... 查看详情

如何制作动态显示图表,让图表随数据动起来

ppt如何制作动态图表?所谓PPT就是为了生动形象的介绍自己想要给大家看的内容,为了让内容更加易懂,加强PowerPoint演示文稿的说服力,我们常常会在幻灯片中使用ppt动态图表。如果使用图表后再为它设置一下序列动画,让数... 查看详情

视觉高级篇21#如何添加相机,用透视原理对物体进行投影?(代码片段)

说明【跟月影学可视化】学习笔记。如何理解相机和视图矩阵?用一个三维坐标(Position)和一个三维向量方向(LookAtTarget)来表示WebGL的三维世界的一个相机。要绘制以相机为观察者的图形,需要用一个... 查看详情

时光煮雨unity3d让物体动起来③—uguidotween&unitynative2d实现

...有它特定的使用场合。   第一种动画适合创建简单的对象位移及直接性质的属性更改(在后面的教程中,我还将更深入的挖掘Storyboard动画的潜力,动态 查看详情

视觉高级篇26#如何绘制带宽度的曲线?(代码片段)

说明【跟月影学可视化】学习笔记。如何用Canvas2D绘制带宽度的曲线?Canvas2D提供了相应的API,能够绘制出不同宽度、具有特定连线方式(lineJoin)和线帽形状(lineCap)的曲线,绘制曲线非常简单。什么... 查看详情

简单动画-让你的背景图动起来!!!(代码片段)

效果:bodymargin:0px;height:100%;width:100%;background-image:url("bg.png");background-position-x:0px;background-position-y:0px;background-repeat:repeat;轮询改变body背景图动画,让世界地图作为背景正向平移。varbody=$(‘body‘);varx=0;functionpolling()x+=5;body.animate(‘ba... 查看详情

怎么让电脑上的gif图像在图标显示的时候就动起来

...片用windows图片浏览器打开后就可以看到Gif格式的图片的动画效果。 查看详情

css高级篇——过渡动画(代码片段)

CSSTransitions让我们不写一行JavaScript代码也能实现过渡动画。举一个最简单的例子:a:linkcolor:hsl(36,50%,50%);a:hovercolor:hsl(36,100%,50%);当鼠标悬浮于某个链接时,它的颜色会从hsl(36,50%,50%)瞬间变化为hsl(36,100%,50%)。这个变化因为没... 查看详情

css高级篇——过渡动画(代码片段)

CSSTransitions让我们不写一行JavaScript代码也能实现过渡动画。举一个最简单的例子:a:linkcolor:hsl(36,50%,50%);a:hovercolor:hsl(36,100%,50%);当鼠标悬浮于某个链接时,它的颜色会从hsl(36,50%,50%)瞬间变化为hsl(36,100%,50%)。这个变化因为没... 查看详情

视觉高级篇19#如何用着色器实现像素动画?(代码片段)

说明【跟月影学可视化】学习笔记。如何用着色器实现固定帧动画<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"/><metaname="viewport"content="width=device-width,initial-scale=1.0"/>&l... 查看详情