使用GSAP制作动画视频

gsap · 浏览次数 : 9

小编点评

本文介绍了如何使用GSAP3Blue1Brown制作动画视频,包括选择GSAP引擎、创建时间线、协调多个动作和对齐时间等步骤。文章详细讲解了如何声明HTML元素并在适当时机将其呈现出来,并提供了代码示例以便直接在浏览器中预览。 1. **选择GSAP引擎**:文章开头提到作者在前两天学习如何使用代码制作动画,并选择了GSAP作为动画制作工具。 - 作者了解到GSAP是一个功能强大的动画引擎,适合用于制作简洁、清晰的动画视频。 - 作者强调了选择合适的工具对于制作高质量动画的重要性。 2. **创建时间线**:作者详细说明了如何创建两个时间线,分别播放字幕和动画,并解释了如何使用相对时间、标签等模式进行协调。 - 作者描述了如何创建两个时间线,一个用于字幕,另一个用于动画。 - 作者强调了使用时间线的重要性,以及如何处理多个动作和元素对齐时间的问题。 3. **声明HTML元素**:文章展示了如何声明HTML元素,并在适当的时候将其呈现出来,以预览代码的效果。 - 作者提供了具体的HTML元素代码示例,以及如何在JavaScript中控制这些元素的显示和隐藏。 - 作者解释了如何使用JavaScript来控制动画的开始和结束。 4. **预览代码**:作者提供了一个简单的HTML文件,其中包含了所有的代码和CSS样式,可以直接在浏览器中预览动画效果。 - 作者分享了如何将代码粘贴到HTML文件中,并在浏览器中打开文件进行预览。 - 作者指出了B站视频中的问题,即B站在发布视频时错误地去掉了一些帧率,导致动画效果不佳。 总的来说,文章通过详细的步骤和示例代码,向读者展示了如何使用GSAP3Blue1Brown制作动画视频的过程,同时也反映了在实际操作中可能遇到的问题和解决方案。

正文

GSAP

3Blue1Brown给我留下了深刻印象。利用动画制作视频,内容简洁,演示清晰。前两天刚好碰到一件事,我就顺便学习了一下怎么用代码做动画。
javascrip为例,有两个动画引擎,GSAPAnimajs。由于网速的原因,询问了GPT后,我选择了GSAP来制作我的第一个动画视频。
制作动画视频不同于动画,要协调多个动作,还需要对齐时间,所以使用时间线Timeline是必要的。
需要两条时间线分别播放字幕和动画,这需要协调好他们
而且很多片段需要后期调整,所以也必须使用相对时间><,label这三种模式。
制作动画重要的是想好脚本,然后声明HTML元素,再恰当时刻将元素呈现出来

预览

image

代码

代码无需开启web服务器,可以直接再浏览器中打开

<!DOCTYPE html>
<html>
    <head>
        <script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/TextPlugin.min.js"></script>
        <link href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.5.2/css/fontawesome.min.css" rel="stylesheet"/>
        <script src="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.5.2/js/all.min.js"></script>
        <style>
            #screen{
                height: 720px;
                width: 1280px;
                position: relative;
                background-color: black;
            }
            #screen > *{
                position: absolute;
                display: inline-block;
            }
            .person_{
                top:20px;
                color: white;
                transform: scale(0,0);
            }
            #person3_{
                left: 905px;
            }
            #person2_{
                left: 590px;
            }
            #person1_{
                left: 290px;
            }
            #person1,#person2,#person3{
                width: 50px;
                height: 50px;
                background-color: green;
                border: 5px;
                color: white;
                text-align: center;
                display: flex;
                justify-content: center;
                align-items: center;
                border-radius: 5px;
                padding: 5px;
                top: 50px;
                left: -100px;
            }
            #text{
                position: absolute;
                width: 100%;
                text-align: center;
                color: white;
                bottom: 10px;
            }

            .year{
                position: absolute;
                width: 120px;
                height: 30px;
                left:130px;
                color: white;
                font-size: 18px;
                transform:scale(0,0)
            }
            #year2000{
                top:120px;
            }
            #year2000_{
                top:200px;
            }
            #year2001{
                top:600px;
            }
            .food{
                color: bisque;
                width: 50px;
                height: 30px;
                transform: scale(0,0);
            }
            #food1{
                top:120px;
                left: 290px;
            }
            #food2{
                top:120px;
                left: 560px;
            }
            #food3{
                top:120px;
                left: 890px;
            }
            #food4{
                top:120px;
                left: 290px;
            }
            #food5{
                top:120px;
                left: 560px;
            }
            #food6{
                top:120px;
                left: 890px;
            }
            #food7,#food8,#food9,#food10{
                top:200px;
                left: 290px;
            }
            #food11{
                top:600px;
                left: 290px;
            }
            #food12{
                top:600px;
                left: 560px;
            }
            #food13{
                top:600px;
                left: 890px;
            }
            #food14{
                top:600px;
                left: 890px;
            }
            .coins{
                color: gold;
                width: 50px;
                height: 30px;
                transform: scale(0,0);
            }
            .iconnum{
                padding-left: 5px;
            }
            #coin1{
                top: 120px;
                left: 610px;
            }
            #coin2{
                top: 120px;
                left: 610px;
            }
            #coin3{
                top: 200px;
                left: 610px;
            }
            #coin4{
                top: 280px;
                left: 610px;
            }
            #coin5{
                top: 600px;
                left: 610px;
            }
            .industry{
                color: rgb(204, 203, 203);
                width: 50px;
                height: 30px;
                transform: scale(0,0);
            }
            #ind1{
                top: 120px;
                left: 340px;
            }
            #ind2{
                top: 120px;
                left: 660px;
            }
            #ind3{
                top: 120px;
                left: 940px;
            }
            #ind4{
                top: 120px;
                left: 340px;
            }
            #ind5{
                top: 120px;
                left: 660px;
            }
            #ind6{
                top: 120px;
                left: 940px;
            }
            #ind7{
                top: 200px;
                left: 940px;
            }
            #ind8{
                top: 200px;
                left: 940px;
            }
            #ind9{
                top: 600px;
                left: 340px;
            }
            #ind10{
                top: 600px;
                left: 660px;
            }
            .yearnext{
                color: white;
                width: 50px;
                height: 50px;
                transform: scale(1,0);
                left: 160px;
            }
            #yn1{
                top: 140px;
            }
            #yn2{
                top: 220px;
            }
            .yearnext_{
                color: white;
                width: 50px;
                height: 50px;
                transform: scale(1,0);
            }
            #yn1_1{
                left: 120px;
                top:160px;
            }
            #yn1_2{
                left: 180px;
                top:160px;
            }
            #yn2_3{
                left: 120px;
                top:300px;
            }
            .changeline{
                color: white;
                transform: scale(0,1);
            }
            #cl1{
                top: 285px;
                left: 530px;
            }
            #cl2{
                top: 285px;
                left: 710px;
            }
            #cl3{
                top: 290px;
                left: 900px;
            }
            #cl4{
                top: 340px;
                left: 340px;
            }
            #cl5{
                top: 350px;
                left: 900px;
            }
            #curve{
                top: 455px;
                left: -180px;
                transform: scale(1,0);
            }
            #complete{
                color: white;
                left: 50px;
                top:340px;
                transform: scale(0);
            }
        </style>
    </head>
    <body>
        <div id="screen">
            <div id="person1_" class="person_">租地农场主</div>
            <div id="person2_" class="person_">领主、地主</div>
            <div id="person3_" class="person_">工厂主</div>
            <div id="person1" class="person">生产阶级</div>
            <div id="person2" class="person">土地所有者</div>
            <div id="person3" class="person">非生产阶级</div>
            <div id="text"></div>
            <div class="year" id="year2000">1758 年初</div>
            <div class="year" id="year2000_">1758 年末</div>
            <div class="year" id="year2001">1759 年初</div>
            <div class="coins" id="coin1"><i class="fa-solid fa-coins"></i><span class="iconnum">20</span></div>
            <div class="coins" id="coin2"><i class="fa-solid fa-coins"></i><span class="iconnum">20</span></div>
            <div class="coins" id="coin3"><i class="fa-solid fa-coins"></i><span class="iconnum" id="coin3_num">20</span></div>
            <div class="coins" id="coin4"><i class="fa-solid fa-coins"></i><span class="iconnum" id="coin4_num">10</span></div>
            <div class="coins" id="coin5"><i class="fa-solid fa-coins"></i><span class="iconnum" id="coin5_num">20</span></div>
            <div class="industry" id="ind1"><i class="fa-solid fa-gears"></i><span class="iconnum">10</span></div>
            <div class="industry" id="ind2"><i class="fa-solid fa-gears"></i><span class="iconnum">10</span></div>
            <div class="industry" id="ind3"><i class="fa-solid fa-gears"></i><span class="iconnum">10</span></div>
            <div class="industry" id="ind4"><i class="fa-solid fa-gears"></i><span class="iconnum" id="ind4_num">10</span></div>
            <div class="industry" id="ind5"><i class="fa-solid fa-gears"></i><span class="iconnum" id="ind5_num">10</span></div>
            <div class="industry" id="ind6"><i class="fa-solid fa-gears"></i><span class="iconnum" id="ind6_num">0</span></div>
            <div class="industry" id="ind7"><i class="fa-solid fa-gears"></i><span class="iconnum" id="ind7_num">10</span></div>
            <div class="industry" id="ind8"><i class="fa-solid fa-gears"></i><span class="iconnum" id="ind8_num">10</span></div>
            <div class="industry" id="ind9"><i class="fa-solid fa-gears"></i><span class="iconnum" id="ind9_num">10</span></div>
            <div class="industry" id="ind10"><i class="fa-solid fa-gears"></i><span class="iconnum" id="ind10_num">10</span></div>
            <div class="food" id="food1"><i class="fa-solid fa-wheat-awn"></i><span class="iconnum">20</span></div>
            <div class="food" id="food2"><i class="fa-solid fa-wheat-awn"></i><span class="iconnum">10</span></div>
            <div class="food" id="food3"><i class="fa-solid fa-wheat-awn"></i><span class="iconnum">20</span></div>
            <div class="food" id="food4"><i class="fa-solid fa-wheat-awn"></i><span class="iconnum" id="food4_num">20</span></div>
            <div class="food" id="food5"><i class="fa-solid fa-wheat-awn"></i><span class="iconnum" id="food5_num">10</span></div>
            <div class="food" id="food6"><i class="fa-solid fa-wheat-awn"></i><span class="iconnum" id="food6_num">10</span></div>
            <div class="food" id="food7"><i class="fa-solid fa-wheat-awn"></i><span class="iconnum" id="food7_num">10</span></div>
            <div class="food" id="food8"><i class="fa-solid fa-wheat-awn"></i><span class="iconnum" id="food8_num">10</span></div>
            <div class="food" id="food9"><i class="fa-solid fa-wheat-awn"></i><span class="iconnum" id="food9_num">10</span></div>
            <div class="food" id="food10"><i class="fa-solid fa-wheat-awn"></i><span class="iconnum" id="food10_num">20</span></div>
            <div class="food" id="food11"><i class="fa-solid fa-wheat-awn"></i><span class="iconnum" id="food11_num">20</span></div>
            <div class="food" id="food12"><i class="fa-solid fa-wheat-awn"></i><span class="iconnum" id="food12_num">10</span></div>
            <div class="food" id="food13"><i class="fa-solid fa-wheat-awn"></i><span class="iconnum" id="food13_num">10</span></div>
            <div class="food" id="food14"><i class="fa-solid fa-wheat-awn"></i><span class="iconnum" id="food14_num">10</span></div>
            <div class="yearnext" id="yn1"><i class="fa-solid fa-arrow-down-long"></i></div>
            <div class="yearnext" id="yn2"><i class="fa-solid fa-arrow-down-long"></i></div>
            <div class="yearnext_" id="yn1_1">生产</div>
            <div class="yearnext_" id="yn1_2">消费</div>
            <div class="yearnext_" id="yn2_3">流通</div>
            <div class="changeline" id="cl1"><i class="fa-solid fa-minus"></i></div>
            <div class="changeline" id="cl2"><i class="fa-solid fa-minus"></i></div>
            <div class="changeline" id="cl3"><i class="fa-solid fa-minus"></i></div>
            <div class="changeline" id="cl4"><i class="fa-solid fa-minus"></i></div>
            <div class="changeline" id="cl5"><i class="fa-solid fa-minus"></i></div>
            <div id="complete">完成循环</div>
            <div id="curve">
                <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
                    <!-- 定义箭头样式 -->
                    <defs>
                        <marker id="arrow" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto">
                        <path d="M0,0 L0,6 L9,3 z" fill="white" />
                        </marker>
                    </defs>
                    <path d="M300 150 Q 150 75, 300 0 " style="fill:none;stroke:white;stroke-width:1;marker-end: url(#arrow);" />
                  </svg>
            </div>
        </div>
        <div style="padding: 10px;">
            <input id="seek" type="range" oninput="setcur()" style="width: 800px;"/>
            <button onclick="seekto()">跳转</button>
            <span>当前</span>
            <input id="cur" type="number" />
            <span>总时长</span>
            <input id="total" type="number" />
        </div>
        <!-- 字幕 -->
        <script>
            let tlzm = gsap.timeline();

            tlzm
            .to("#text", { 
                text: {
                    value: "在魁奈的《经济表》中",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },1)
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=3')
            .to("#text", { 
                text: {
                    value: "商品货币循环被一个连线图表构造出来",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=6')
            .to("#text", { 
                text: {
                    value: "这个循环在3大阶级之间进行",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=4')
            .to("#text", { 
                text: {
                    value: "而阶级内部的交换不在考虑范围内",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=5')
            .to("#text", { 
                text: {
                    value: "这三个阶级分别是",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=4')
            .to("#text", { 
                text: {
                    value: "非生产阶级,可以认为是工厂主和工人",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=5')
            .to("#text", { 
                text: {
                    value: "土地所有者阶级,通常是领主、地主",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=6')
            .to("#text", { 
                text: {
                    value: "生产阶级,可以认为是租地农场主和农业工人",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=7')
            .to("#text", { 
                text: {
                    value: "所有循环都有开端",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=4')
            .to("#text", { 
                text: {
                    value: "我们把《经济表》发表的那年作为开端",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=4')
            .to("#text", { 
                text: {
                    value: "首先是生产阶级",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=3')
            .to("#text", { 
                text: {
                    value: "生产阶级手中拥有价值20亿利弗尔的农产品,作为种子和食物",
                    delimiter:" "
                },
                duration:6, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=7')
            .to("#text", { 
                text: {
                    value: "同时,他们还拥有价值100亿利弗尔的工业品作为生产资料和生活用品",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>+1')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=8')
            .to("#text", { 
                text: {
                    value: "分摊到每一年,生产资料和生活用品会损耗或消耗10亿利弗尔",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=7')
            .to("#text", { 
                text: {
                    value: "然后是土地所有者阶级",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=3')
            .to("#text", { 
                text: {
                    value: "他们有价值10亿利弗尔的食物,20亿利弗尔货币和价值10亿利弗尔的工业品",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=7')
            .to("#text", { 
                text: {
                    value: "最后是非生产阶级",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=4')
            .to("#text", { 
                text: {
                    value: "他们的初始条件是价值20亿利弗尔的农业品原料和食物",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=8')
            .to("#text", { 
                text: {
                    value: "在这一年内,三大阶级进行了生产和消费,直到年末",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=8')
            .to("#text", { 
                text: {
                    value: "生产阶级消耗工业品和农产品,生产出价值50亿利弗尔的农产品原料",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=8')
            .to("#text", { 
                text: {
                    value: "土地所有者阶级则什么也没生产,只是消费。最终剩下的是20亿利弗尔货币",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=8')
            .to("#text", { 
                text: {
                    value: "非生产阶级则消耗20亿利弗尔农产品,生产出20亿利弗尔的工业品",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=7')
            .to("#text", { 
                text: {
                    value: "生产消费阶段结束,接下来是交换阶段",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=5')
            .to("#text", { 
                text: {
                    value: "我们把各阶级的产品和货币各自划分为若干份",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=5')
            .to("#text", { 
                text: {
                    value: "以方便接下来的交换",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=5')
            .to("#text", { 
                text: {
                    value: "首先发起交换的,只能是拥有货币的土地所有者阶级",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=6')
            .to("#text", { 
                text: {
                    value: "他们用10亿利弗尔货币从生产阶级那交换等价的农产品",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=6')
            .to("#text", { 
                text: {
                    value: "再用剩下的10亿利弗尔货币从非生产阶级那交换等价的工业品",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=7')
            .to("#text", { 
                text: {
                    value: "之后土地所有者阶级退出流通环节",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=5')
            .to("#text", { 
                text: {
                    value: "接下来非生产者阶级使用手中的10亿利弗尔货币,从生产阶级那里交换10亿利弗尔的农产品",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=9')
            .to("#text", { 
                text: {
                    value: "然后他们就耗尽货币,只能等待生产阶级先发起交换",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=6')
            .to("#text", { 
                text: {
                    value: "生产阶级花费10亿利弗尔货币从非生产阶级那里交换10亿利弗尔的工业品",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=8')
            .to("#text", { 
                text: {
                    value: "非生产阶级再次获得10亿利弗尔货币",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=5')
            .to("#text", { 
                text: {
                    value: "他们将用来交换到最后所需的农产品原料和粮食",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=5')
            .to("#text", { 
                text: {
                    value: "至此三大阶级交换完成,货币和产品都到了所需的人手中",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=7')
            .to("#text", { 
                text: {
                    value: "接下来就是领主和地主老爷的收税和收租环节",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=5')
            .to("#text", { 
                text: {
                    value: "生产阶级交换所得到的货币又作为租金,回到了土地所有者阶级手中",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=7')
            .to("#text", { 
                text: {
                    value: "好了,现在其实整个经济循环已经回到了初始状态,但为了明显一点,让我们整理一下",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=8')
            .to("#text", { 
                text: {
                    value: "将各阶级的产品和货币各自汇总",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=5')
            .to("#text", { 
                text: {
                    value: "和上一年初的初始状态对比一下会发现他们并没有什么变化",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=6')
            .to("#text", { 
                text: {
                    value: "马克思评价说",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>+=4')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=3')
            .to("#text", { 
                text: {
                    value: "魁奈的《经济表》用几根粗线条表明,国民生产的具有一定价值的年产品",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>+=1')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=8')
            .to("#text", { 
                text: {
                    value: "要怎样通过流通进行分配,才能在其他条件不变的情况下,使它的的简单再生产(即原有规模的再生产)进行下去",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>+=1')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=9')
            .to("#text", { 
                text: {
                    value: "上一年的收获,当然构成生产期间的起点",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>+=1')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=5')
            .to("#text", { 
                text: {
                    value: "无数单个流通(交换)行为,从一开始就被综合为,根据他们所具有的社会特征划分出来的大体量运动。——几个巨大的、职能确定的、经济上的社会阶级之间的流通",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>+=1')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=15')
            .to("#text", { 
                text: {
                    value: "《经济表》提醒我们,价值的创造固然重要,但在目前的商品社会中,商品和货币的循环也是极其重要的、为生产创造条件的活动",
                    delimiter:" "
                },
                duration:0, 
                ease: "none"
            },'>+=1')
            .to("#text", {  text: { value: "", delimiter:" " }, duration:0,  },'>+=12')
        </script>
        <!-- 动画 -->
        <script>
             gsap.registerPlugin(TextPlugin)
            // 创建Timeline类型的实例
            let tl = gsap.timeline();
            function seekto(){
                var cur=document.getElementById("seek").value;
                tl.seek(cur);
                tlzm.seek(cur);
            }
            function setcur(){
                var cur=document.getElementById("seek").value;
                document.getElementById("cur").value=cur;
            }

            //非生产阶级,可以认为是工厂主和工人 4s
            tl.to("#person3", { 
                x: 1000,
                duration:1
            },3+1+5+1+3+1+4+1+3+1)
            tl.to("#person3_", { 
                scale: 1,
                duration:1
            },'>+0.5')
            //土地所有者阶级,通常是领主、地主 5s
            .to("#person2", { 
                x: 700,
                duration:1
            },'>+2.5')
            tl.to("#person2_", { 
                scale: 1,
                duration:1
            },'>+0.5')
            //生产阶级,可以认为是租地农场主和农业工人 6s
            .to("#person1", { 
                x: 400,
                duration:1
            },'>+3.5')
            tl.to("#person1_", { 
                scale: 1,
                duration:0.5
            },'>+0.5')
            //所有循环都有开端 4s
            .to("#year2000", { 
                scale: 1,
                duration:0.5
            },'>+5')
            //首先是生产阶级
            .to("#food1", { 
                scale: 1,
                duration:0.5
            },'>+14')
            //同时,他们还拥有价值100亿利弗尔的工业品作为生产资料和生活用品
            .to("#ind1", { 
                scale: 1,
                duration:0.5
            },'>+18')
            //然后是土地所有者阶级
            .to("#food2", { 
                scale: 1,
                duration:0.5
            },'>+10')
            .to("#coin1", { 
                scale: 1,
                duration:0.5
            },'>+1.5')
            .to("#ind2", { 
                scale: 1,
                duration:0.5
            },'>+1.5')
            //最后是非生产阶级
            .to("#food3", { 
                scale: 1,
                duration:0.5
            },'>+8')
            //在这一年内,三大阶级进行了生产和消费,直到年末
            .to("#year2000_", { 
                scale: 1,
                duration:0.5
            },'>+8')
            .to("#yn1", { 
                scaleY: 3,
                duration:0.5,
                transformOrigin: "center 0%"
            },'>+1')
            .to("#yn1_1", { 
                scaleY: 1,
                duration:0.5,
                transformOrigin: "center 0%"
            },'>+1')
            .to("#yn1_2", { 
                scaleY: 1,
                duration:0.5,
                transformOrigin: "center 0%"
            },'>+1')
            .addLabel("food4")
            //生产阶级消耗工业品和农产品,生产出价值50亿利弗尔的农产品原料
            .to("#food4", { 
                y: 80,
                duration:1
            },'food4+=3')
            .to("#food4", { 
                scale: 1,
                duration:0
            },'food4+=3')
            .to("#food4_num", { 
                innerHTML: 50,
                snap: 'innerHTML',
                duration:1,
                ease: "none",
            },'food4+=3')
            .to("#ind4", { 
                y: 80,
                duration:1
            },'food4+=3')
            .to("#ind4", { 
                scale: 1,
                duration:0
            },'food4+=1')
            .to("#ind4_num", { 
                innerHTML: 0,
                snap: 'innerHTML',
                duration:1,
                ease: "none",
            },'food4+=3')
            .to("#ind4", { 
                scale:0,
                duration:0.5,
            },'>+0.5')            
            .addLabel("food5")
            //土地所有者阶级则什么也没生产,只是消费。最终剩下的是20亿利弗尔货币
            .to("#food5", { 
                scale: 1,
                duration:0
            },'food5+=8')
            .to("#food5", { 
                y: 80,
                duration:1
            },'food5+=8')
            .to("#coin2", { 
                scale: 1,
                duration:0
            },'food5+=8')
            .to("#coin2", { 
                y: 80,
                duration:1
            },'food5+=8')
            .to("#ind5", { 
                scale: 1,
                duration:0
            },'food5+=8')
            .to("#ind5", { 
                y: 80,
                duration:1
            },'food5+=8')
            .to("#food5_num", { 
                innerHTML: 0,
                snap: 'innerHTML',
                duration:1,
                ease: "none",
            },'food5+=8')
            .to("#ind5_num", { 
                innerHTML: 0,
                snap: 'innerHTML',
                duration:1,
                ease: "none",
            },'food5+=8')
            .to("#food5", { 
                scale: 0,
                duration:0.5,
            },'>+1')
            .to("#ind5", { 
                scale: 0,
                duration:0.5,
            },'<')
            .addLabel("food6")
            //非生产阶级则消耗20亿利弗尔农产品,生产出20亿利弗尔的工业品
            .to("#food6", { 
                scale: 1,
                duration:0
            },'food6+=2')
            .to("#food6", { 
                y: 80,
                duration:1
            },'food6+=2')
            .to("#ind6", { 
                scale: 1,
                duration:0
            },'food6+=2')
            .to("#ind6", { 
                y: 80,
                duration:1
            },'food6+=2')
            .to("#food6_num", { 
                innerHTML: 0,
                snap: 'innerHTML',
                duration:1,
                ease: "none",
            },'food6+=2')
            .to("#ind6_num", { 
                innerHTML: 20,
                snap: 'innerHTML',
                duration:1,
                ease: "none",
            },'food6+=2')
            .to("#food6", { 
                scale: 0,
                duration:0.5
            },'>+1')
            //生产消费阶段结束,接下来是交换阶段
            .to("#yn2", { 
                scaleY: 15,
                duration:0.5,
                transformOrigin: "center 0%"
            },'>+5')
            .to("#yn2_3", { 
                scaleY: 1,
                duration:0.5,
                transformOrigin: "center 0%"
            },'>+1')
            .addLabel("food7")
            //我们把各阶级的产品和货币各自划分为若干份
            .to("#food7", { 
                y: 80,
                duration:1
            },'food7+=2')
            .to("#food7", { 
                scale: 1,
                duration:0
            },'food7+=2')
            .to("#food8", { 
                y: 140,
                duration:1
            },'food7+=2')
            .to("#food8", { 
                scale: 1,
                duration:0
            },'food7+=2')
            .to("#food9", { 
                y: 200,
                duration:1
            },'food7+=2')
            .to("#food9", { 
                scale: 1,
                duration:0
            },'food7+=2')
            .to("#food10", { 
                y: 260,
                duration:1
            },'food7+=2')
            .to("#food10", { 
                scale: 1,
                duration:0
            },'food7+=2')
            .to("#coin3", { 
                scale: 1,
                duration:0
            },'>+2')
            .to("#coin3", { 
                y: 80,
                duration:1
            },'<')
            .to("#ind7", { 
                scale: 1,
                duration:0
            },'>+2')
            .to("#ind7", { 
                y: 80,
                duration:1
            },'<')
            .to("#ind8", { 
                scale: 1,
                duration:0
            },'<')
            .to("#ind8", { 
                y: 140,
                duration:1
            },'<')
            .addLabel("change1")
            //首先发起交换的,只能是拥有货币的土地所有者阶级            
            //他们用10亿利弗尔货币从生产阶级那交换等价的农产品
            .to("#coin3_num", { 
                text: {
                    value:10
                },
                duration:0
            },'change1+=12')
            .to("#coin4", { 
                scale:1,
                duration:0
            },'change1+=12')
            .to("#coin4", { 
                x:-320,
                duration:1
            },'change1+=12')
            .to("#food7", { 
                x:270,
                duration:1
            },'change1+=12')
            .to("#cl1", { 
                scaleX:14,
                duration:0.5,
                transformOrigin: "100% center"
            },'change1+=10')
            .addLabel("change2")
            //再用剩下的10亿利弗尔货币从非生产阶级那交换等价的工业品
            .to("#cl2", { 
                scaleX:14,
                duration:0.5,
                transformOrigin: "0% center"
            },'change2+=3')
            .to("#coin3", { 
                x:335,
                duration:1,
            },'change2+=4')
            .to("#ind7", { 
                x:-285,
                duration:1,
            },'change2+=4')
            .addLabel("change3")
            //生产阶级花费10亿利弗尔货币从非生产阶级那里交换10亿利弗尔的工业品
            .to("#cl3", { 
                scaleX:40,
                rotation:-5,
                duration:0.5,
                transformOrigin: "100% center"
            },'change3+=11')
            .to("#food8", { 
                x:655,
                y:80,
                duration:1,
            },'change3+=13')
            .to("#coin3", { 
                y:140,
                x:-320,
                duration:1,
            },'change3+=13')
            .addLabel("change4")
            .to("#cl4", { 
                scaleX:41,
                duration:0.5,
                transformOrigin: "0% center"
            },'change4+=12')
            .to("#coin3", { 
                y:140,
                x:335,
                duration:1,
            },'change4+=14')
            .to("#ind8", { 
                x:-655,
                duration:1,
            },'change4+=14')
            .addLabel("change5")
            //非生产阶级再次获得10亿利弗尔货币
            //他们将用来交换到最后所需的农产品原料和粮食
            .to("#cl5", { 
                scaleX:40,
                rotation:-5,
                duration:0.5,
                transformOrigin: "100% center"
            },'change5+=9')
            .to("#food9", { 
                x:655,
                y:140,
                duration:1,
            },'change5+=11')
            .to("#coin3", { 
                x:-320,
                y:200,
                duration:1,
            },'change5+=11')
            .addLabel("tex")
            //三大阶级交换完成
            .to("#coin3", { 
                x:0,
                y:80,
                duration:1,
            },'tex+=12.5')
            .to("#coin4", { 
                x:0,
                y:0,
                duration:1,
            },'tex+=12.5')
            .to("#coin4", { 
                scaleY:0,
                duration:0,
            },'tex+=13.5')
            .to("#coin3_num", { 
                innerHTML: 20,
                snap: 'innerHTML',
                duration:1,
                ease: "none",
            },'tex+=13.5')
            //好了,现在其实整个经济循环已经回到了初始状态,但为了明显一点,让我们整理一下
            .to("#year2001",{
                scale:1,
                duration:0.5
            },">+5.5")
            .to("#food11",{
                scale:1,
                duration:0
            },">+5.5")
            .fromTo("#food11",{
                y:-140,
            },{
                y:0,
                duration:1
            },">")
            .to("#ind9",{
                scale:1,
                duration:0
            },">+0.5")
            .fromTo("#ind9",{
                y:-260,
                x:-50,
            },{
                y:0,
                x:0,
                duration:1
            },">")
            .to("#food12",{
                scale:1,
                duration:0
            },">+0.5")
            .fromTo("#food12",{
                y:-320,
            },{
                y:0,
                duration:1
            },">")
            .to("#coin5",{
                scale:1,
                duration:0
            },">+0.5")
            .fromTo("#coin5",{
                y:-320,
            },{
                y:0,
                duration:1
            },">")
            .to("#ind10",{
                scale:1,
                duration:0
            },">+0.5")
            .fromTo("#ind10",{
                y:-320,
            },{
                y:0,
                duration:1
            },">")
            .addLabel("food13")
            .to("#food13",{
                scale:1,
                duration:0
            },"food13+=0.5")
            .fromTo("#food13",{
                y:-320,
                x:55
            },{
                y:0,
                x:0,
                duration:1
            },"food13+=0.5")
            .to("#food14",{
                scale:1,
                duration:0
            },"food13+=0.5")
            .fromTo("#food14",{
                y:-320,
                x:55
            },{
                y:0,
                x:0,
                duration:0.7
            },"food13+=0.5")
            .addLabel("food14")
            .to("#food14",{
                scale:0,
                duration:0
            },"food14+=0")
            .to("#food13_num", { 
                innerHTML: 20,
                snap: 'innerHTML',
                duration:1,
                ease: "none",
            },'food14+=0')
            .to("#curve",{
                scaleY:3.1,
                duration:1,
                transformOrigin: "100% 100%"
            },">+1")
            .to("#complete",{
                scale:1,
                duration:1
            },">+1")


            // .to("#coin1", { 
            //     x: 330,
            //     y:80,
            //     duration:1
            // },'>+1')

            let duration=tl.duration();
            document.getElementById("total").value=duration;
            document.getElementById("seek").max=duration;
            document.getElementById("seek").min=0;
            document.getElementById("seek").value=0;
        </script>
        <script type="text/template">
            经济循环由3大阶级构成
            1.租地农场主、土地所有者、工厂主依次进场
        </script>
    </body>
</html>

视频观看

B站链接——《经济表》动画
我录制运行结果后,又通过tts给视频配了音,发布在了bilibili,欢迎来查看效果
不过要吐槽的是B站把60帧的选项给搞没了,投上去的720P60帧视频全都变成了30帧
导致高速动画效果惨不忍睹,要是没有大会员,那观看起来真的不太好

与使用GSAP制作动画视频相似的内容:

使用GSAP制作动画视频

GSAP 3Blue1Brown给我留下了深刻印象。利用动画制作视频,内容简洁,演示清晰。前两天刚好碰到一件事,我就顺便学习了一下怎么用代码做动画。 以javascrip为例,有两个动画引擎,GSAP和Animajs。由于网速的原因,询问了GPT后,我选择了GSAP来制作我的第一个动画视频。 制作动

使用Cloudflare Worker加速docker镜像

前言 开发者越来越难了,现在国内的docker镜像也都️了,没有镜像要使用docker太难了,代理又很慢 现在就只剩下自建镜像的办法了 GitHub上有开源项目可以快速搭建自己的镜像库,不过还是有点麻烦,还好Cloudflare暂时还活着‍ 本文记录一下使用 Cloudf

使用C#/.NET解析Wiki百科数据实现获取历史上的今天

创建一个webapi项目做测试使用。 创建新控制器,搭建一个基础框架,包括获取当天日期、wiki的请求地址等 创建一个Http请求帮助类以及方法,用于获取指定URL的信息 使用http请求访问指定url,先运行一下,看看返回的内容。内容如图右边所示,实际上是一个Json数据。我们主要解析 大事记 部

Pybind11和CMake构建python扩展模块环境搭建

使用pybind11的CMake模板来创建拓展环境搭建 从Github上下载cmake_example的模板,切换分支,并升级pybind11子模块到最新版本 拉取pybind11使用cmake构建工具的模板仓库 git clone --recursive https://github.com/mr

说说RabbitMQ延迟队列实现原理?

使用 RabbitMQ 和 RocketMQ 的人是幸运的,因为这两个 MQ 自身提供了延迟队列的实现,不像用 Kafka 的同学那么苦逼,还要自己实现延迟队列。当然,这都是题外话,今天咱们重点来聊聊 RabbitMQ 延迟队列的实现原理,以及 RabbitMQ 实现延迟队列的优缺点有哪些? 很多人

使用FModel提取游戏资产

目录前言FModel简介FModel安装FModel使用初次使用资产预览资产导出附录dumperDumper-7生成usmap文件向游戏中注入dll 前言 这篇文章仅记录我作为初学者使用FModel工具提取某款游戏模型的过程。 FModel简介 FModel是一个开源软件,可以用于查看和提取UE4-

使用ML.NET训练一个属于自己的图像分类模型,对图像进行分类就这么简单!

前言 今天大姚给大家分享一个.NET开源、免费、跨平台(支持Windows、Linux、macOS多个操作系统)的机器学习框架:ML.NET。并且本文将会带你快速使用ML.NET训练一个属于自己的图像分类模型,对图像进行分类。 ML.NET框架介绍 ML.NET 允许开发人员在其 .NET 应用程序

使用libzip压缩文件和文件夹

简单说说自己遇到的坑: 分清楚三个组件:zlib、minizip和libzip。zlib是底层和最基础的C库,用于使用Deflate算法压缩和解压缩文件流或者单个文件,但是如果要压缩文件夹就很麻烦,主要是不知道如何归档,在zip内部形成对应的目录。这时就需要用更高级别的库,也就是minizip或li

使用gzexe加密shell脚本

使用 gzexe 加密 shell 脚本是一个相对简单的过程。以下是具体的步骤: 编写你的 shell 脚本:首先,你需要有一个 shell 脚本文件,比如 myscript.sh。 确保脚本可执行:使用 chmod 命令确保你的脚本文件是可执行的: chmod +x myscript.sh 使用

WSL2连接USB设备(以USRP B210为例)

使用WSL2时,发现其无法直接识别到宿主机上插入的USB设备。 可利用USPIPD-WIN项目进行连接。 以下以USRP B210设备连接为例,展示连接过程: 安装USBIPD-WIN 项目 参考连接 USB 设备 | Microsoft Learn,我选择通过.msi文件安装: 转到 usbipd