总结一下常见的动画库

  1. 提升用户体验&增加交互感: 动画可以使用户界面更加生动和有趣,提供更好的视觉体验。动画也可以增加界面的交互感,使用户感觉更加真实。例如,过渡动画可以使页面切换更加平滑,加载动画可以在数据加载时提供反馈,提示用户正在进行的操作。
  2. 引导用户注意力&记录用户行为:动画可以引导用户的注意力,帮助用户理解界面的布局和操作方式。动画也可以记录用户的页面行为和操作偏好,从而引申出对产品优化迭代的思考。例如弹出动画可以突出显示重要的信息或操作,滑动动画可以指示可滚动的区域。
  3. 增强品牌形象:独特的动画效果可以用于展示品牌的个性和特色,帮助品牌在用户心中留下深刻的印象。通过动画设计,可以传达品牌的价值观、理念和风格,从而增强品牌的认知度和忠诚度。

UI改版和页面玩法升级

  1. UI改版背景下的用户体验:UI改版可以更好地呈现新功能,提高用户的使用效率和满意度,还可以帮助App适应市场变化和用户需求变化,不断更新和改进以保持竞争力。除了静态的界面元素所带来的视觉效果和交互指引之外,动画也可以在UI改版中扮演重要的角色,可以形成自己的App页面风格体系。
  2. 直播语聊页面的玩法升级:直播语聊业务线承接着绝大部分的H5活动页面,且具有高可玩性和娱乐性,且在后续的产品迭代中追求新玩法的挖掘和实施,动画效果可以提高直播语聊活动页的视觉体验和交互体验,提高用户的活动参与度和活动粘性,进而创造更好的营收。

动画库及实现方式

1. CSS动画

CSS animations 使得可以将从一个 CSS 样式配置转换到另一个 CSS 样式配置。动画包括两个部分:描述动画的样式规则(animation)和用于指定动画开始、结束以及中间点样式的关键帧(@keyframes)。
image
image
效果预览:

2. Animate.css

Animate.css 是一个流行的 CSS 动画库,它为开发者提供了一系列预设的 CSS 动画类,为标签添加想要实现的类名即可实现效果,使得为网页元素添加动画变得非常简单,可以让你轻松地给网站添加动画效果。

Animate.css 提供了多种动画效果,如淡入、淡出、滑动、缩放、旋转等。这些动画效果都是使用纯 CSS 编写的,不需要依赖 JavaScript 或其他库。官方文档:https://animate.style/
Animate.css 优点

  1. 易于使用:Animate.css使用起来非常简单,只需在HTML元素中添加特定的类名即可实现动画效果。无需编写复杂的JavaScript代码或CSS动画代码,大大简化了开发过程。
  2. 丰富的动画效果:Animate.css提供了大量的预设动画效果,如淡入淡出、弹跳、旋转等,涵盖了常见的动画需求。同时,开发者还可以根据自己的需求,通过修改CSS类名来定制动画效果。
  3. 性能良好:Animate.css使用CSS3动画技术,相较于使用JavaScript实现的动画,具有更好的性能和兼容性。此外,由于Animate.css文件体积较小,加载速度快,不会对页面性能产生太大影响。
  4. 兼容性强:Animate.css兼容大多数现代浏览器,包括Chrome、Firefox、Safari等。同时,也支持旧版浏览器,通过添加一些前缀来确保动画的兼容性。

Animate.css 使用说明

  1. npm安装animate.css动画库
1
2
$ npm install animate.css --save

  1. 引入animate.css动画
1
import 'animate.css';
  1. 在标签类名中叠加使用需求所需的动画效果(必须指定animate__animated类名才能使其它动画类名生效)

image

  1. 效果预览

  1. 实际项目应用参考:英语定级评测的答题界面,题目切换时的淡入淡出以及答题按钮的交互均运用Animate.css

3. SVGA

SVGA 是一种跨平台的开源动画格式,同时兼容 iOS / Android / Web。动画设计师专注动画设计,通过工具输出 svga 动画文件,提供给开发工程师在集成 SVGA Player 之后直接使用。官方文档:https://svga.dev/index.html

SVGA 优点

  1. 可缩放性:由于SVGA是基于矢量图形的,所以它可以在不失真的情况下进行缩放。这使得SVGA非常适合用于各种不同的屏幕和设备。
  2. 资源包小和序列化结构:相比于传统的位图动画,SVGA通常可以生成更小的文件。SVGA使用Protobuf序列化结构数据格式,使得序列化的数据体更小,传递效率比XML、JSON更高。这有助于减小资源包的大小,这可以减少网络传输的时间和存储空间的需求,加快动画的加载速度。
  3. 高性能和流畅性:SVGA动画具有卓越的性能,能够呈现出流畅且复杂的动画效果。它解决了canvas实现动画时的卡顿和优化问题,提供了高质量的动画播放体验。
  4. 丰富的交互功能:虽然SVGA本身是一个动画文件,但它提供了丰富的交互功能。例如,通过SVGA player提供的API方法,开发者可以控制动画的播放开始、播放某一帧、播放结束等整个过程,从而实现与用户的交互行为。
    image

    SVGA 使用说明

  5. npm安装animate.css动画库
    1
    $ npm install svgaplayerweb --save
  6. 引入并配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<!-- SVGA动画 -->
<template>
<div class="svga" ref="svgaRef"></div>
</template>
<script lang="ts" setup>
// 引入vue功能
import { onMounted, ref } from "vue";
// 引入svga动画
import SVGA from "svgaplayerweb";

// svga动画容器
const svgaRef = ref()
// 播放器实例
const svgaPlayer = ref()
// 解析器实例
const svgaParser = ref()

// 初始化svga动画
const initSvgaAnimation = () => {
// SVGA.Player是SVGA库中的一个类,用于创建播放器实例。
svgaPlayer.value = new SVGA.Player(svgaRef.value);
// SVGA.Parser是SVGA库中的一个类,用于创建解析器实例。
svgaParser.value = new SVGA.Parser();
// 加载 SVGA 动画文件的内容
svgaParser.value.load('/svga/doc-Img/posche.svga', (videoItem: any) => {
// 设置播放器要播放的动画项目
svgaPlayer.value.setVideoItem(videoItem);
// 开始播放动画
svgaPlayer.value.startAnimation();
// 监听当前播放帧数
svgaPlayer.value.onFrame((frame: number) => {
console.log('当前播放帧数', frame);
});
// 监听当前播放帧数百分比
svgaPlayer.value.onPercentage((percentage: number) => {
console.log('当前播放帧数百分比', percentage);
});
});

}
// 开始播放动画
const start = () => {
svgaPlayer.value.startAnimation();
}
// 暂停播放动画
const pause = () => {
svgaPlayer.value.pauseAnimation();
}
// 停止播放动画
const stop = () => {
svgaPlayer.value.stopAnimation();
}
// 清除动画
const clear = () => {
svgaPlayer.value.clear();
}
// 跳转到指定的帧,第一个参数是帧数,第二个参数是是否播放或者停留在当前帧
const stepToFrame = () => {
svgaPlayer.value.stepToFrame(10, true);
}
// 跳转到指定的百分比位置,第一个参数是百分比( 0.5指50% ),第二个参数是是否播放或者停留在当前帧
const stepToPercentage = () => {
svgaPlayer.value.stepToPercentage(0.5, true);
}

onMounted(() => {
initSvgaAnimation()
});
  1. 效果预览

  1. 需要特别注意的点 ⚠️ ⚠️ ⚠️
    此次调研动画库时发现很多动画库的API加载本地资源时均会报错,如下所示:
    image
    image
    于是我把svga资源上传到OSS上,并加载线上地址。
    image
    好👌,很棒🎉,新的问题出现了😭
    image
    所以便在vue.config.js中采用代理的方式去解决跨域。
    image
    image
    这样就能顺利加载svga和其它动画库的资源了(此类处理方法适用于后面的其它动画库)。

4. Lottie

Lottie动画库是一个开源项目,由Airbnb公司开发并维护。它是一个跨平台的动画库,支持iOS、Android、ReactNative和Web等操作系统,能够解析由Adobe After Effects导出的动画,并让这些动画在原生应用中以静态素材的形式被使用,从而轻松实现复杂的动画效果。官方文档:https://airbnb.design/lottie/
Github地址:https://github.com/airbnb/lottie-web
相关文章:https://juejin.cn/post/6983670062293598221
image

Lottie 优点

  1. 资源消耗小:Lottie动画基于矢量图形,不需要大量的图片资源就可以实现复杂的动画效果,甚至在某些情况下不需要图片资源。相比于帧动画、GIF图、短视频等,Lottie的性能消耗和内存占用更小。
  2. 兼容性好:Lottie动画库具有良好的平台兼容性,无需进行平台和分辨率的适配工作,可以在不同的设备和操作系统上实现一致的动画效果。
  3. 轻量且高性能:相较于传统的GIF动画,Lottie更加轻量且性能更好,不易失真,支持复杂的动画效果,包括路径动画、蒙版、父子关系、形状变形、透明度动画等。

    Lottie 使用说明

  4. npm安装lottie-web动画库
1
$ npm install lottie-web --save
  1. 引入并配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
<!-- Lottie动画 -->
<template>
<div id="lottie-animation" ref="lottieContainer"></div>
</template>
<script lang="ts" setup>
// 引入Lottie动画库
import lottie from 'lottie-web'
// 引入vue功能
import { onMounted, ref } from 'vue';
// Lottie动画容器
const lottieContainer = ref()
// Lottie动画实例
const lottieInstance = ref()
// 初始化 Lottie动画
const initLottieAnimation = () => {
// 加载和配置动画
lottieInstance.value = lottie.loadAnimation({
// Lottie动画容器
container: lottieContainer.value,
// 渲染方式(svg、canvas或html)。svg支持交互且不会失帧,而canvas和html则支持3D和交互。
renderer: 'svg',
// 是否循环播放动画,默认为true。
loop: true,
// 是否自动播放动画,默认为true。
autoplay: true,
// 指定json文件的方式:animationData 或 path
animationData: require('../assets/wave.json'),
// path:'动画JSON文件的路径'
})
/**
* 动画事件监听
* complete: 播放完成(循环播放下不会触发)
* loopComplete: 当前循环下播放(循环播放/非循环播放)结束时触发
* enterFrame: 每进入一帧就会触发,播放时每一帧都会触发一次,stop 方法也会触发
* segmentStart: 播放指定片段时触发,playSegments、resetSegments 等方法刚开始播放指定片段 * 时会发出,如果 playSegments 播放多个片段,多个片段最开始都会触发。
* data_ready: 动画 json 文件加载完毕触发
* DOMLoaded: 动画相关的 dom 已经被添加到 html 后触发
* destroy: 将在动画删除时触发
*/
lottieInstance.value.addEventListener('loopComplete', () => {
console.log("完成一次循环播放");
});
}
// 获取动画的总帧数
const getTotalFrames = () => {
var totalFrames = lottieInstance.value.totalFrames;
console.log('获取动画的总帧数', totalFrames);
}
// 开始播放动画
const play = () => {
lottieInstance.value.play()
}
// 停止播放动画
const pause = () => {
lottieInstance.value.pause()
}
// 暂停播放动画
const stop = () => {
lottieInstance.value.stop()
}
// 设置动画的播放速度
const setSpeed = () => {
lottieInstance.value.setSpeed()
}
// 设置动画的播放方向。1为正向,-1为反向。
const setDirection = () => {
lottieInstance.value.setDirection(1);
}
// 跳转到指定的时间或帧,并开始播放。
const goToAndPlay = () => {
lottieInstance.value.goToAndPlay(0, true);
}
// 跳转到指定的时间或帧,并停止播放。
const goToAndStop = () => {
lottieInstance.value.goToAndStop(0, true);
}
// 摧毁动画
const destroy = () => {
lottieInstance.value.destroy()
}
onMounted(() => {
initLottieAnimation()
})

  1. 效果预览

5. PixiJS

Pixi.js是一个强大的2D渲染库,它允许开发者使用WebGL和HTML5 Canvas创建丰富的交互式图形和动画。Pixi.js的目标是提供一个轻量级、灵活且快速的库,用于创建2D图形和动画。它广泛用于游戏开发、数据可视化、交互式应用程序等领域。
官方文档:https://pixijs.com/
中文文档:https://pixi.nodejs.cn/

PixiJS 优点

  1. 性能优秀:Pixi.js使用WebGL进行渲染,这意味着它可以利用GPU进行硬件加速,从而提供出色的性能和流畅度。这使得Pixi.js非常适合用于需要高帧率和高分辨率的应用程序,如游戏和交互式应用。
  2. 兼容性:Pixi.js支持多种浏览器和设备,包括移动设备。当WebGL不可用时,Pixi.js会自动降级到HTML5 Canvas,确保在各种设备和浏览器上都能正常工作。
  3. 灵活性:Pixi.js提供了一个丰富的API,允许开发者以多种方式创建和操作图形。你可以使用预定义的形状、纹理、滤镜等,也可以自定义渲染逻辑,使得开发者可以轻松地创建和控制图形和动画。
  4. 丰富的功能:Pixi.js支持纹理、精灵、粒子系统、滤镜、混合模式等多种功能,可以满足各种2D渲染的需求。

    PixiJS 使用说明

  5. npm安装PixiJS动画库
    1
    2
    $ npm install pixi.js --save

  6. 常见API和方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    /**1. 创建Pixi应用
    * PIXI.Application()
    * PIXI.Application配置项:
    * autoStart:布尔值,是否在创建应用后自动开始渲染循环。默认为 true。
    * width:数字,应用的宽度。默认为 800。
    * height:数字,应用的高度。默认为 600。
    * view:HTMLCanvasElement,用于渲染应用的canvas元素。如果没有提供,将会创建一个新的canvas。
    * transparent:布尔值,是否使背景透明。默认为 false。
    * antialias:布尔值,是否开启抗锯齿。默认为 false。
    * preserveDrawingBuffer:布尔值,是否保留绘图缓冲区,用于如截图或图片下载等功能。默认为 false。
    * resolution:数字,应用的分辨率。默认为 1。
    * forceCanvas:布尔值,是否强制使用HTML5 Canvas进行渲染,即使WebGL可用。默认为 false。
    * backgroundColor:数字,应用的背景颜色。默认为 0x00000
    */
    const app = new PIXI.Application({ width: 800, height: 600 });
    document.body.appendChild(app.view);

    // 2. 加载资源
    // PIXI.Loader.shared.add()
    PIXI.Loader.shared.add("example", "path/to/image.png").load((loader, resources) => {
    // 资源加载完成后的回调
    });

    // 3. 创建和添加精灵
    // PIXI.Sprite.from()
    const sprite = PIXI.Sprite.from("example");

    // 4. 精灵的属性和方法
    // sprite.x, sprite.y: 精灵的位置。
    // sprite.width, sprite.height: 精灵的尺寸。
    // sprite.anchor.set(): 设置精灵的锚点。
    // sprite.interactive: 设置精灵是否可交互。
    // sprite.buttonMode: 设置鼠标悬停时的样式。

    // 5. 创建一个新的容器,可以包含多个图形和精灵
    // new PIXI.Container()
    const container = new PIXI.Container();
    container.addChild(sprite);

    // 6. 绘制图形
    // new PIXI.Graphics()
    const graphics = new PIXI.Graphics();
    graphics.beginFill(0xFF0000);
    graphics.drawRect(0, 0, 100, 100);
    graphics.endFill();

    // 7. 创建动画和纹理
    // PIXI.Texture.from()
    const texture = PIXI.Texture.from("path/to/image.png");

    // 8. 创建一个可以无限平铺的精灵
    // new PIXI.TilingSprite():
    const tilingSprite = new PIXI.TilingSprite(texture, width, height);

    // 9. 创建文本精灵
    // PIXI.Text.from()
    const text = new PIXI.Text('Hello, world!', {fontSize: 24, fill: 0xFF0000});

    // 10. 计时器,用于管理和维护游戏或应用的更新循环
    // new PIXI.ticker.Ticker()
    let ticker = new PIXI.ticker.Ticker();
    ticker.add((delta) => {
    // 更新游戏或应用
    });
    ticker.start();

    // 11. 渲染和更新
    // app.renderer.render(stage): 手动渲染舞台。
    // app.update(): 更新应用状态。

    // 12. 声音
    // PIXI.Sound.from(): 加载和播放声音。

    // 13. 优化和性能
    // app.renderer.destroy(): 销毁渲染器以释放资源。
    // Container.removeChild(): 从容器中移除精灵。
  7. 引入并配置
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    <!-- PixiJS动画 -->
    <template>
    <h6>创建精灵图片并旋转</h6>
    <div id="pixi" ref="pixiContainerOne"></div>
    <h6>无限平铺的精灵</h6>
    <div id="pixi" ref="pixiContainerTwo"></div>
    <h6>绳段动画</h6>
    <div id="pixi" ref="pixiContainerThree"></div>
    </template>

    <script lang="ts" setup>
    import { onMounted, ref } from "vue";
    // pixi.js动画
    import * as PIXI from "pixi.js";

    // 创建 三个DOM容器
    const pixiContainerOne = ref()
    const pixiContainerTwo = ref()
    const pixiContainerThree = ref()

    // 初始化图形
    const initDraw = () => {
    // 创建 pixi画布视图
    const drawApp = new PIXI.Application({ width: 375, height: 300, backgroundColor: 0x000000, antialias: true });
    // 将画布视图添加到容器中
    pixiContainerOne.value.appendChild(drawApp.view);
    // 创建精灵,渲染图片
    let sprite = PIXI.Sprite.from('/svga/doc-Img/earth.jpeg');
    // 将精灵添加到画布视图舞台
    drawApp.stage.addChild(sprite);
    // 实现图片自适应
    sprite.scale.set(0.36);
    // 渲染图片并使图片沿着中心不断旋转
    drawApp.ticker.add(() => {
    sprite.x = drawApp.screen.width / 2;
    sprite.y = drawApp.screen.height / 2;
    sprite.anchor.set(0.5);
    sprite.rotation += 0.01;
    });
    }
    // 初始化循环背景
    const initLoopBg = () => {
    // 创建 pixi 画布视图
    const LoopBgApp = new PIXI.Application({ width: 375, height: 300, backgroundColor: 0xffffff, antialias: true });
    // 将画布视图添加到容器中
    pixiContainerTwo.value.appendChild(LoopBgApp.view);
    // 加载纹理
    let texture = PIXI.Texture.from('/svga/doc-Img/loop-background-one.webp');
    // tilingSprite用于创建一个可以无限平铺的精灵,提供纹理以及宽高
    let tilingSprite = new PIXI.TilingSprite(
    texture,
    window.innerWidth,
    236
    );
    // 将精灵添加到画布视图舞台
    LoopBgApp.stage.addChild(tilingSprite);
    // ticker设置计时器用于管理更新循环
    LoopBgApp.ticker.add(() => {
    tilingSprite.tilePosition.x -= 1;
    });
    }
    // 初始化绳段动画
    const initRope = () => {
    // 创建 pixi画布视图
    let ropeApp = new PIXI.Application({ width: 375, height: 300, backgroundColor: 0xfefefe });
    // 将画布视图添加到容器中
    pixiContainerThree.value.appendChild(ropeApp.view);
    // 设置图片高度
    const imageHeightidth = 650;
    // 设置绳段数
    const numberOfSegments = 10;
    // 计算每段绳段的长度
    const ropeLength = imageHeightidth / numberOfSegments;
    // 根据绳段数计算每一绳段的位置,生成表示二维空间的points对象
    let points: any = [];
    for (let i = 0; i < numberOfSegments; i++) {
    points.push(new PIXI.Point(i * ropeLength, 0));
    }
    // 创建绳段对象
    let ropeObject = new PIXI.SimpleRope(PIXI.Texture.from('/svga/doc-Img/qq.png'), points);
    // 设置绳段对象的位置
    ropeObject.position.set(180, 530);
    // 将绳段对象添加到画布视图舞台
    ropeApp.stage.addChild(ropeObject);
    // 实现动画
    let count = 0;
    // ticker设置计时器用于管理更新循环
    ropeApp.ticker.add(() => {
    count += 0.1;
    // 通过 for 循环将数组中的每个点按照椭圆形的轨迹移动,形成波浪效果。
    for (let i = 0; i < points.length; i++) {
    points[i].x = -Math.sin(i * 0.5 + count) * 30;
    points[i].y = -i * ropeLength + Math.cos(i * 0.3 + count) * numberOfSegments;
    }
    });
    }

    onMounted(() => {
    initDraw();
    initLoopBg();
    initRope();
    });
    </script>

    <style scoped lang="less">
    h6{
    margin:0px 0px 15px 0px;
    }

    #pixi {
    width: 375px;
    height: 300px;
    margin-bottom: 20px;
    }
    </style>
  8. 效果预览

6. CreateJS

Create.js是一个基于HTML5的包含多个 JavaScript 库的完整工具集,这些套件共同提供了丰富的功能,专为构建互动性和丰富的网络体验而设计,用于创建交互式的 Web 内容,如处理图形、动画、音频和加载项等各种开发需求,特别适用于游戏和交互式Web应用的开发。官方文档:https://createjs.com/
CreateJS 包含以下四个主要的库:

  • EaselJS:提供了一个易于使用的 API 来处理 HTML5 Canvas 的图形和交互。
  • TweenJS:一个简单但强大的 JavaScript 库,用于在 HTML5 和 JavaScript 属性之间进行 tweening 和动画序列。
  • SoundJS:一个提供 Web 音频功能的库,包括一个预加载系统和一个抽象的音频 API。
  • PreloadJS:一个用于管理和协调加载资源(如图像、音频、JSON、等等)的库。

    CreateJS 优点

  1. 强大的功能:Create.js提供了全面的工具集,开发者可以使用EaselJS来绘制Sprites、动画、向量和位图,使用TweenJS来处理动画调整,使用SoundJS来简化音频处理,以及使用PreLoadJS来管理和协调加载项。

  2. 易于学习和使用:对于熟悉JavaScript的开发者来说,Create.js的API设计直观且易于理解。同时,由于它是一个开源项目,有许多社区资源和教程可供参考,这使得学习和使用Create.js变得更加容易。

  3. 跨平台兼容性:由于Create.js基于HTML5,因此它具有很好的跨平台兼容性。无论是PC、手机还是平板等设备,只要能支持HTML5,就可以无缝运行Create.js创建的内容。

  4. 性能优化:Create.js在性能优化方面做得很好,它使用了一些先进的技术来提高渲染性能和交互性能,如使用canvas进行渲染,以及使用事件委托等方式处理用户交互。

  5. 模块化:你可以根据需要选择使用 CreateJS 的哪些库,每个库都可以独立使用,也可以和其他库一起使用。

  6. 降低开发难度和成本:Create.js旨在降低HTML5项目的开发难度和成本,让开发者能够以更熟悉的方式打造更具现代感的网络交互体验。这对于那些希望在短时间内创建出高质量的网络应用的开发者来说,无疑是一个巨大的优势。

    CreateJS 使用说明

  7. npm安装CreateJS动画库
    $ npm install createjs –save

  8. 常见API和方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    // 1.createjs.Stage:创建一个新的舞台,所有的显示对象都会被添加到舞台上。
    let stage = new createjs.Stage("canvasId");

    // 2.createjs.Shape:创建一个新的形状,可以用于绘制矩形、圆形、多边形等。
    let shape = new createjs.Shape();
    shape.graphics.beginFill("red").drawRect(0, 0, 100, 100);
    stage.addChild(shape);

    // 3.createjs.Bitmap:创建一个新的位图,可以用于显示图像。
    let bitmap = new createjs.Bitmap("path/to/image.png");
    stage.addChild(bitmap);

    // 4.createjs.Text:创建一个新的文本。
    let text = new createjs.Text("Hello, world!", "20px Arial", "#ff7700");
    stage.addChild(text);

    // 5.createjs.Tween:创建一个新的补间动画。
    createjs.Tween.get(target).to({x: 100}, 1000);

    // 6.createjs.Sound:加载和播放声音。
    createjs.Sound.registerSound("path/to/sound.mp3", "sound");
    createjs.Sound.play("sound");

    // 7.createjs.LoadQueue:加载资源
    let queue = new createjs.LoadQueue();
    queue.loadFile("path/to/image.png");
    queue.on("complete", handleComplete);

    // 8.update:更新舞台
    stage.update();

    // 9.stagemousedown:事件监听
    stage.on("stagemousedown", function(event) {
    // 处理鼠标点击事件
    });

    // 10.addEventListener:定时器
    createjs.Ticker.addEventListener("tick", function(event) {
    // 每帧更新逻辑
    stage.update();
    });

    // 11.createjs.Container:容器
    var container = new createjs.Container();
    container.addChild(shape);
    container.addChild(bitmap);
    stage.addChild(container);
    1. 引入并配置
    <!-- CreateJS动画 -->
    <template>
    <div class="create-animation">
    <canvas ref="createJSCanvas" :style="{ 'visibility': finishLoad ? 'visible' : 'hidden' }"></canvas>
    <div class="create-animation-percent" v-if="!finishLoad">加载中:{{ loadPercent }}%</div>
    </div>
    <div class="button">
    <button @click="raiseFlower">升级</button>
    <button @click="resetFlower">还原</button>
    </div>
    </template>

    <script lang="ts" setup>
    import { onMounted, ref } from "vue";
    // 引入createjs
    import 'script-loader!createjs/builds/1.0.0/createjs.min.js';
    // createjs舞台
    const stage = ref()
    // createjs精灵
    const sprite = ref()
    // canvas DOM
    const createJSCanvas = ref(null);
    // 花蕾成长阶段
    const growthStage = ref('flower_seed')
    // 花蕾阶段
    const flowerStage = ['flower_seed', 'flower_sprout', 'flower_flowerbud', 'flower_burst', 'flower_florescence']
    // 升级阶段
    const changeLevel = ['update_level_one', 'update_level_two', 'update_level_three', 'update_level_four']
    // 动画总帧数
    const frameCount = ref();
    // 动画源
    const animationSource = ref()
    // 图像源
    const imageSource = ref<Array<string>>([]);
    // 图片加载百分比
    const loadPercent = ref(0)
    // 图片加载完毕
    const finishLoad = ref(false)
    // 阶段数
    const stageCount = ref(1)
    // 生成preloadjs实例
    let queue = new window.createjs.LoadQueue();

    // 监听加载完成
    queue.on("complete", () => {
    console.log('加载完成');
    finishLoad.value = true
    });
    // 监听加载失败
    queue.on("error", () => {
    console.log('加载错误');
    });
    // 监听图片加载百分比
    queue.on('progress', () => {
    console.log('加载进度', queue.progress);
    loadPercent.value = decimalToPercentage(queue.progress);
    })
    // 花蕾升级
    const raiseFlower = () => {
    // 播放指定的动画阶段
    sprite.value.gotoAndPlay(changeLevel[stageCount.value - 1]);
    stageCount.value++;
    }
    // 还原
    const resetFlower = () => {
    stageCount.value = 1;
    // 播放指定的动画阶段
    sprite.value.gotoAndPlay('flower_seed');
    }
    // 业务逻辑
    const generateAnimationSource = (growthStageValue: any) => {
    const initIndex = flowerStage.indexOf(growthStageValue);
    let frameIndex = 0;
    const animationSource: any = {};
    for (let i = initIndex; i < flowerStage?.length; i++) {
    const currentStage = flowerStage[i];
    const currentLevel = changeLevel[i];
    const nextStage = flowerStage[i + 1];
    animationSource[currentStage] = [frameIndex, frameIndex + 12];
    if (i === flowerStage?.length - 1) break
    animationSource[currentLevel] = [frameIndex + 13, frameIndex + 35, nextStage];
    frameIndex += 36;
    }
    return animationSource;
    }
    // 判断浏览器是否支持webp
    const isWebPSupported = () => {
    return new Promise((resolve) => {
    const image = new Image();
    image.onload = function () {
    const result = (image.width > 0) && (image.height > 0);
    resolve(result);
    };
    image.onerror = function () {
    resolve(false);
    };
    // 体积极小的占位图片检测浏览器是否支持webp格式
    image.src = 'https://ali-global-cdn.hellotalk8.com/prod/other/7c51c638a0011b88b41facec5398fb9e.png?x-oss-process=image/format,webp/quality,q_80';
    });
    }
    // 转换百分比方法
    const decimalToPercentage = (decimal: number) => {
    return Math.round(Number(decimal) * 100);
    }
    // 初始化create.js
    const initCreateJS = async () => {
    if (!createJSCanvas.value) return
    // 根据花蕾种类加载不同的图片
    let isSupported = await isWebPSupported()
    let allImageSource: string[]
    if (isSupported) {
    allImageSource = [
    `https://ali-hk-statics.hellotalk8.com/hybrid_h5_activity/violet/common/common-seed.png?x-oss-process=image/format,webp/quality,q_80`,
    `https://ali-hk-statics.hellotalk8.com/hybrid_h5_activity/violet/common/common-raise-one.png?x-oss-process=image/format,webp/quality,q_80`,
    `https://ali-hk-statics.hellotalk8.com/hybrid_h5_activity/violet/common/common-raise-two.png?x-oss-process=image/format,webp/quality,q_80`,
    `https://ali-hk-statics.hellotalk8.com/hybrid_h5_activity/violet/common/common-sprout.png?x-oss-process=image/format,webp/quality,q_80`,
    `https://ali-hk-statics.hellotalk8.com/hybrid_h5_activity/violet/common/common-raise-three.png?x-oss-process=image/format,webp/quality,q_80`,
    `https://ali-hk-statics.hellotalk8.com/hybrid_h5_activity/violet/common/common-raise-four.png?x-oss-process=image/format,webp/quality,q_80`,
    `https://ali-hk-statics.hellotalk8.com/hybrid_h5_activity/violet/common/common-flowerbud.png?x-oss-process=image/format,webp/quality,q_80`,
    `https://ali-hk-statics.hellotalk8.com/hybrid_h5_activity/violet/common/common-raise-five.png?x-oss-process=image/format,webp/quality,q_80`,
    `https://ali-hk-statics.hellotalk8.com/hybrid_h5_activity/violet/rose/rose-raise-six.png?x-oss-process=image/format,webp/quality,q_80`,
    `https://ali-hk-statics.hellotalk8.com/hybrid_h5_activity/violet/rose/rose-burst.png?x-oss-process=image/format,webp/quality,q_80`,
    `https://ali-hk-statics.hellotalk8.com/hybrid_h5_activity/violet/rose/rose-raise-seven.png?x-oss-process=image/format,webp/quality,q_80`,
    `https://ali-hk-statics.hellotalk8.com/hybrid_h5_activity/violet/rose/rose-raise-eight.png?x-oss-process=image/format,webp/quality,q_80`,
    `https://ali-hk-statics.hellotalk8.com/hybrid_h5_activity/violet/rose/rose-florescence.png?x-oss-process=image/format,webp/quality,q_80`
    ];
    // 设置动画图片源
    imageSource.value = allImageSource;
    // 设置初始动画源
    animationSource.value = generateAnimationSource('flower_seed')

    // animationSource:
    // {
    // "flower_seed": [0,12],
    // "update_level_one": [13,35,"flower_sprout"],
    // "flower_sprout": [36,48],
    // "update_level_two": [49,71, "flower_flowerbud"],
    // "flower_flowerbud": [72,84],
    // "update_level_three": [85,107,"flower_burst"],
    // "flower_burst": [108,120],
    // "update_level_four": [121,143,"flower_florescence"],
    // "flower_florescence": [144,156]
    // }
    // 总帧数
    frameCount.value = 157;
    // 监听加载的动画图片源
    queue.loadManifest(imageSource.value);
    }
    // 将canvas挂载到舞台
    stage.value = new window.createjs.Stage(createJSCanvas.value);
    // 设置舞台宽高
    stage.value.canvas.width = 375;
    stage.value.canvas.height = 350;
    // 初始化精灵画布
    var spriteSheet = new window.createjs.SpriteSheet({
    // 源图像
    images: imageSource.value,
    /**
    * width 和 height 是必需的,用于指定帧的尺寸。
    regX 和 regY 指示帧的注册点或“原点”。
    spacing 指定帧之间的间距。
    margin 指定图像周围的边距。
    count 允许你指定精灵表中的总帧数;如果省略,将根据源图像和帧的尺寸计算。帧将根据它们在源图像中的位置分配索引(从左到右,从上到下)。
    */
    frames: {
    height: 610,
    width: 750,
    regX: 0,
    regY: 0,
    count: frameCount.value
    },
    /**
    * animations
    可选。定义要作为命名动画播放的帧序列的对象。每个属性对应于相同名称的动画。每个动画必须指定要播放的帧,
    并且还可以包括相对播放速度(例如,2 表示以双倍速度播放,0.5 表示以一半速度播放)和完成后要转到的下一个动画的名称。
    有三种支持的格式来定义动画中的帧,可以根据需要混合使用:对于单帧动画,可以简单地指定帧索引。
    */

    // 定义动画并划分阶段
    animations: animationSource.value
    });
    // 创建精灵实例
    sprite.value = new window.createjs.Sprite(spriteSheet);
    // 放大倍率
    sprite.value.scale = 0.5;
    // 初始化播放阶段
    sprite.value.gotoAndPlay(growthStage.value);
    // 舞台挂载动画
    stage.value.addChild(sprite.value);
    // 设置帧频
    window.createjs.Ticker.framerate = 18;
    // 设置计时模式 --- 与屏幕的刷新率同步
    window.createjs.Ticker.timingMode = window.createjs.Ticker.RAF_SYNCHED;
    // 触发动画
    window.createjs.Ticker.addEventListener("tick", stage.value);
    }
    onMounted(() => {
    initCreateJS()
    });
    </script>

    <style scoped lang="less">
    // create.js动画
    .create-animation {
    position: relative;
    width: 375px;
    height: 375px;

    &-percent {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-size: 20px;
    }
    }

    .button {
    display: flex;
    justify-content: center;
    align-items: center;

    button {
    margin: 0px 10px;
    padding: 10px 20px;
    border: none;
    border-radius: 5px;
    background-color: #409eff;
    color: #fff;
    cursor: pointer;
    }
    }
    </style>
  9. 效果预览

  10. 实际项目应用参考:直播语聊“花语秘境”活动

7. Three.js

Three.js 是一个基于 WebGL 的 JavaScript 库,用于在浏览器中创建和显示 3D 图形。WebGL是一种在浏览器中渲染3D图形的技术,而Three.js则提供了一种更易于使用和理解的接口,它提供了一套完整的 3D 图形编程接口,使得开发者可以在不直接操作 WebGL 的情况下创建复杂的 3D 场景,如物体、材质、光照等等。官方文档:https://threejs.org/

Three.js 优点

  1. 易用性:Three.js 提供了一套高级的 API,使得开发者可以更容易地创建和控制 3D 场景。无需事先了解复杂的 3D 数学和 WebGL 详细知识,使得开发者可以快速地创建出各种3D场景和物体。
  2. 功能强大:Three.js支持多种几何体、材质、光源、摄像机、动画等并提供了许多功能,包括3D场景的渲染、物体的变换、材质和光照的处理、动画的创建等等。这些功能使得开发者可以创建出非常逼真和复杂的3D场景。
  3. 跨平台兼容性:Three.js可以在任何支持WebGL的浏览器上运行,因此它具有很好的跨平台兼容性。这意味着开发者可以使用它来创建在各种设备和操作系统上运行的3D应用程序。
  4. 性能优化:Three.js 对渲染进行了优化,包括减少绘制调用、自动合并几何体、使用预编译着色器等,以提高渲染性能。

    Three.js 使用说明

  5. npm安装Three.js动画库
    $ npm install three –save
  6. 常见API和方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    // 1.场景(Scene)
    // THREE.Scene:创建一个新的场景。
    let scene = new THREE.Scene();
    // 将一个物体添加到场景中
    scene.add(object);
    // 从场景中移除一个物体
    scene.remove(object);

    // 2.物体(Object)
    // THREE.Mesh() 创建一个由几何体(geometry)和材质(material)组成的网格物体(mesh)。
    const object = new THREE.Mesh(geometry, material);

    // 3.相机(Camera)
    // THREE.PerspectiveCamera:创建一个透视相机。
    // fov:视场角,单位为度。它描述了摄像机视锥体的开口角度,即摄像机能够看到的场景广度。值越大,摄像机能 // 看到的场景就越广,但同时物体会显得更小。
    // aspect:宽高比。摄像机视锥体的宽度和高度的比值,通常设置为画布的宽度和高度的比值,以保证渲染的图像
    // 不会被拉伸或压缩。
    // near:近裁剪面的距离。摄像机只能看到离它大于等于这个距离的物体。
    // far:远裁剪面的距离。摄像机只能看到离它小于等于这个距离的物体。
    const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
    // THREE.OrthographicCamera:创建一个正交相机。
    const camera = new THREE.OrthographicCamera(left, right, top, bottom, near, far);
    // position.set:设置相机的位置。
    camera.position.set(x, y, z)
    // lookAt:设置相机的朝向,使其面向指定的物体。
    camera.lookAt(object)

    // 4.渲染器(Renderer)
    // THREE.WebGLRenderer:创建一个新的 WebGL 渲染器。
    let renderer = new THREE.WebGLRenderer();
    // 设置渲染器的输出尺寸。
    renderer.setSize(width, height);
    // 使用指定的相机渲染场景。
    renderer.render(scene, camera);

    // 5.几何体(Geometry)
    // THREE.BoxGeometry:创建一个新的立方体几何体。
    const boxGeometry = new THREE.BoxGeometry(width, height, depth);
    // THREE.BoxGeometry:创建一个新的立方体几何体。
    const sphereGeometry = new THREE.SphereGeometry(radius, widthSegments, heightSegments);
    // THREE.CylinderGeometry:创建一个圆柱体几何体。
    const cylinderGeometry=new THREE.CylinderGeometry(radiusTop, radiusBottom, height, radiusSegments, heightSegments, openEnded, thetaStart, thetaLength);

    // 6.材质(Material)
    // THREE.MeshBasicMaterial:创建一个基本材质,可以设置颜色等属性。
    const basicMaterial = new THREE.MeshBasicMaterial({color: 0x00ff00});
    // THREE.MeshLambertMaterial:创建一个Lambert材质,用于模拟漫反射效果。
    const lambertMaterial = new THREE.MeshLambertMaterial({color: 0x00ff00});
    // THREE.MeshPhongMaterial:创建一个Phong材质,用于模拟光滑表面的反射效果。
    const phongMaterial = new THREE.MeshPhongMaterial({color: 0x00ff00});
    // THREE.MeshStandardMaterial:创建一个物理基础的材质,用于创建一个看起来非常真实的物体
    const standardMaterial = new THREE.MeshStandardMaterial({color: 0x00ff00});
    // 设置物体的位置。
    object.position.set(x, y, z);
    // 设置物体的旋转角度(以弧度为单位)。
    object.rotation.set(x, y, z);
    // 设置物体的缩放比例。
    object.scale.set(x, y, z);

    // 7.光照(Light)
    // THREE.AmbientLight:创建一个环境光,影响场景中所有物体的颜色。
    const ambientLight = new THREE.AmbientLight(color);
    // THREE.DirectionalLight:创建一个方向光,从特定方向照射场景。
    const directionalLight = new THREE.DirectionalLight(color, intensity);
    // THREE.PointLight:创建一个点光源,从一点发出光线。
    const pointLight = new THREE.PointLight(color, intensity, distance);

    // 8.时钟(Clock)
    // THREE.Clock:创建一个新的时钟,可以用于动画和游戏逻辑。
    const clock = new THREE.Clock();

    // 9.射线投射(Raycaster)
    // THREE.Raycaster:创建一个新的射线投射器,可以用于鼠标拾取等。
    const raycaster = new THREE.Raycaster();
    1. 引入并配置
    <!-- three.js动画 -->
    <template>
    <div ref="threeJSRef"></div>
    </template>
    <script lang="ts" setup>
    import { onMounted, ref } from "vue";
    // three.js动画
    import * as THREE from "three";
    // 引入交互控制的API
    import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
    // 引入地板图片
    import floor from "../assets/floor.jpg";
    // 引入立方体的顶部纹理图片
    import cubeTop from "../assets/cube-top.jpg";
    // 引入立方体的侧边纹理图片
    import cubeSide from "../assets/cube-side.jpg";
    // 引入立方体的底部纹理图片
    import cubeBottom from "../assets/cube-bottom.jpg";
    // three.js动画容器
    const threeJSRef = ref()
    // 初始化three.js
    const initThreeJS = () => {
    // 创建渲染器
    const renderer = new THREE.WebGLRenderer({ antialias: true });
    // 指定渲染器的分辨率
    renderer.pixelRatio = window.devicePixelRatio;
    // 指定渲染器的尺寸
    renderer.setSize(375, 400);
    // 渲染器添加到容器中
    threeJSRef.value.append(renderer.domElement);

    // 初始化纹理加载器
    const textloader = new THREE.TextureLoader();

    // 创建场景
    const scene = new THREE.Scene();

    // 创建相机,设置75度视野,相机的纵横比设置为画布的宽度除以其高度,近平面距离为0.01,远平面距离为1000;
    const camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    1000
    );
    // 统一设置相机的位置
    camera.position.set(3, 3, 3);

    // 单独设置相机的位置
    // camera.position.z = 3;
    // camera.position.x = 3;
    // camera.position.y = 3;

    // 设置相机看向原点的方向
    camera.lookAt(0, 0, 0);

    // 创建坐标系物体
    const axis = new THREE.AxesHelper(2);
    // 创建了一个坐标辅助线物体,用来标识坐标位置;
    scene.add(axis);

    // 创建长宽高都为2的立方几何体
    const geometry = new THREE.BoxGeometry(2, 2, 2);
    // 设置立方体颜色
    // 统一设置所有面的颜色
    // 因为 MeshBasicMaterial不受光源的影像,所以需要将 MeshBasicMaterial改成MeshStandardMaterial
    // const material = new THREE.MeshStandardMaterial({ color: "#ff0000" });
    // 在构造Mesh对象的时候,传入6个 MeshBasicMaterial对象的数组,数组的纹理顺序是: x正方向轴的面,x负方向轴的面,y正方向轴的面,y负方向轴的面,z正方向轴的面,z负方向轴的面.
    const material = [
    new THREE.MeshBasicMaterial({
    map: textloader.load(cubeSide),
    }),
    new THREE.MeshBasicMaterial({
    map: textloader.load(cubeSide),
    }),
    new THREE.MeshBasicMaterial({
    map: textloader.load(cubeTop),
    }),
    new THREE.MeshBasicMaterial({
    map: textloader.load(cubeBottom),
    }),
    new THREE.MeshBasicMaterial({
    map: textloader.load(cubeSide),
    }),
    new THREE.MeshBasicMaterial({
    map: textloader.load(cubeSide),
    }),
    ];

    // 设置环境光,环境光是一种均匀的光照,它会均匀地照亮场景中的所有物体,不考虑光照源的位置和方向。
    // 添加一个白色透明度为0.4的环境光,这个环境光会均匀地照亮场景中的所有物体表面,并且使用PBR(Physically-Based Rendering)渲染模型和材质自身的颜色进行混合得到新的颜色;
    const ambientLight = new THREE.AmbientLight("#ffffff", 0.4);
    // 场景添加环境光
    scene.add(ambientLight);

    // 设置方向光,方向光是一种平行光源,它具有确定的方向和强度,类似于太阳光。
    const directionalLight = new THREE.DirectionalLight("#ffffff", 1);
    // 添加一个白色的方向光,方向光从(10, 0, 10)照向原点(10, 0, 10), 所以有两个面会收到这个方向光,表面的颜色会更偏亮。
    directionalLight.position.set(10, 0, 10);
    // 场景添加方向光
    scene.add(directionalLight);

    // 渲染器能够渲染阴影效果
    renderer.shadowMap.enabled = true;
    // 该方向会投射阴影效果
    directionalLight.castShadow = true;

    // 将集合体和材质组合为Mesh对象;
    const cube = new THREE.Mesh(geometry, material);
    // 表示该立方体会产生影像效果
    cube.castShadow = true;
    // 将 Mesh 出来的立方体添加到场景中。
    scene.add(cube);
    // 实现与场景进行交互,可以添加一个控制器
    const controls = new OrbitControls(camera, renderer.domElement);
    // 控制器更新
    controls.update();

    // 新建了一个平面,该平面能够接受投射过来的阴影效果;
    const planeGeometry = new THREE.PlaneGeometry(20, 20);
    // 创建一个物理基础的材质,用于创建一个看起来非常真实的物体,map: textloader.load(floor)给地板加载纹理
    const planeMaterial = new THREE.MeshStandardMaterial({ color: 0xffffff, map: textloader.load(floor) });
    // 创建一个由几何体(geometry)和材质(material)组成的网格物体(mesh)
    const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);
    planeMesh.rotation.x = -0.5 * Math.PI;
    planeMesh.position.set(0, -3, 0);
    planeMesh.receiveShadow = true;
    scene.add(planeMesh);

    // 为了清晰展示方向光的方向和位置,添加了一个辅助线。
    const directionalLightHelper = new THREE.DirectionalLightHelper(
    directionalLight
    );
    scene.add(directionalLightHelper); // 辅助线

    // 渲染并不停旋转
    const clock = new THREE.Clock();
    (function animate() {
    // 通过requestAnimationFrame不断的回调animate函数,使得立方体不停的旋转
    requestAnimationFrame(animate);
    // 返回已经过去的时间, 以秒为单位
    const elapsedTime = clock.getElapsedTime();
    // 1秒自转一圈
    cube.rotation.y = elapsedTime * Math.PI * 0.5;
    // 进行重复绘制。
    renderer.render(scene, camera);
    })();
    }
    onMounted(() => {
    initThreeJS()
    });
    </script>
  7. 预览效果

8. PAG

PAG(Portable Animated Graphics)由腾讯 QQ 表情团队开发而成,是一种专为跨平台应用设计的高性能、它支持通过矢量图形和位图图形创建复杂的动画效果,并且因其高压缩比和轻量级特性,使得 PAG 动画具有很高的灵活性和效率。官方文档:https://pag.art/

PAG 优点

  1. 高效率:由于 PAG 动画是通过程序生成的,所以它的文件大小通常比预先录制的帧动画小得多。这使得 PAG 动画在网络传输和存储上都非常高效。
  2. 高质量:PAG 动画支持矢量图形,这意味着无论动画的尺寸如何,都保证了动画在任何分辨率下的清晰度,无论放大还是缩小。这是传统的位图帧动画无法做到的。
  3. 高灵活性:PAG 动画可以通过程序参数来控制,这使得它可以根据用户的输入或者程序的状态来动态改变动画的行为。这种灵活性使得 PAG 动画非常适合用于交互设计。
  4. 跨平台:PAG 动画支持多种平台和语言,包括 iOS、Android、Web 和 C++。这使得开发者可以在不同的平台上复用同一套 PAG 动画,大大提高了开发效率。
  5. 文件小,解码速度快:通过利用动画文件本身的特点,获得了极高的压缩率,相同动画内容可以比同类型方案平均减少50%左右的文件大小。同时,其实时渲染性能平均可以达到同类方案的1.5到2.5倍左右。
    PAG 使用说明
  6. 标签引入PAG动画库
1
2
3
<!-- 引入libpag动画库 -->
<script src="https://cdn.jsdelivr.net/npm/libpag@latest/lib/libpag.min.js"></script>

  1. 常见API和方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // 1.初始化
    // PAGInit: 初始化PAG库,通常需要传入一个配置对象,该对象可能包含如locateFile等回调函数,用于定位 // WebAssembly文件。
    const PAG = await window.libpag.PAGInit();

    // 2.加载动画
    // PAGFile.load: 用于加载PAG动画文件,通常接受一个ArrayBuffer类型的参数,即加载完成的动画文件数据。
    const buffer = await fetch('path').then((response) => response.arrayBuffer());
    const pagFile = await PAG.PAGFile.load(buffer);

    // 3.创建视图并播放
    // PAGView.init: 使用加载的PAGFile和HTML的<canvas>元素来初始化一个PAG视图,准备播放动画。
    // play: 开始播放动画。
    // pause: 暂停动画播放。
    // resume: 恢复动画播放。
    // stop: 停止动画播放。
    const pagView = await PAG.PAGView.init(pagFile, pagContainer);
    await pagView.play();
  2. 引入并配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    <!-- PAG动画 -->
    <template>
    <canvas class="canvas" ref="pagContainer"></canvas>
    </template>

    <script lang="ts" setup>
    import { ref, onMounted } from 'vue';
    // 获取PAG动画容器
    const pagContainer = ref();
    // 初始化 PAG动画
    const initPag = async () => {
    // 实例化 PAG
    const PAG = await window.libpag.PAGInit();
    // 获取 PAG 素材数据
    const buffer = await fetch('/svga/doc-Img/blindBox.pag').then((response) => response.arrayBuffer());
    // 加载 PAG 素材为 PAGFile 对象
    const pagFile = await PAG.PAGFile.load(buffer);
    // 将画布尺寸设置为 PAGFile的尺寸
    pagContainer.value.width = pagFile.width();
    pagContainer.value.height = pagFile.height();
    // 实例化 PAGView 对象
    const pagView = await PAG.PAGView.init(pagFile, pagContainer.value);
    // 播放 PAGView
    await pagView.play();
    }

    onMounted(() => {
    initPag();
    })
    </script>

    <style scoped lang="less">
    canvas {
    width: 375px;
    height: 500px;
    margin-top: -100px;
    }
    </style>
  3. 预览效果

  4. 附PAG文件资源 📎

  1. Web浏览器的兼容性问题

10. GSAP

GSAP,全称 GreenSock Animation Platform,它是一个可以用来创建复杂的动画序列的JavaScript动画库,并提供了许多高级的动画功能,如时间线管理、缓动函数、动画暂停、恢复、反转等。官方文档:https://gsap.com/。

GSAP 优点

  1. 性能优秀:GSAP 是为性能优化而设计的,它的动画运行速度非常快,使之实现和CSS一样的高性能动画效果。
  2. 轻量与模块化:模块化与插件式的结构保持了核心引擎的轻量。GSAP提供了TweenLite、TimelineLite、TimelineMax和TweenMax等不同功能的动画模块,用户可以根据需求选择使用。例如,TweenLite包非常小,基本上低于7kb。
  3. 兼容性好:GSAP 支持所有主流浏览器,包括 IE9 及以上版本。
  4. 功能丰富:GSAP 提供了许多高级的动画功能,如时间线管理、缓动函数、动画暂停、恢复、反转等。
  5. 灵活性高:GSAP 可以用来创建各种复杂的动画序列,不受限于线性序列,用户可以重叠动画序列,并通过精确时间控制,灵活地使用最少的代码实现动画效果。

    GSAP 使用说明

  6. npm安装GSAP动画库
    1
    $ npm install gsap --save
  7. 常见API和方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    // 1.gsap.to 指定目标到特定位置
    // gsap.to() 参数1:运动的物体 参数2:{做出的动作,包括旋转,移动,周期时间} 参数3:提前时间/滞后时间
    gsap.to(".box", { rotation: 360, x: 100, duration: 1 })

    // 2.gsap.from to里面参数是指定目标到特定位置,from其实就是反过来罢了,给的参数是起始位置
    // gsap.to() 参数1:运动的物体 参数2:{做出的动作,包括旋转,移动,周期时间} 参数3:提前时间/滞后时间
    gsap.from(".box", { rotation: 360, x: 100, duration: 1 })

    // 3.gsap.timeline timeline就是一条时间线,可以更好地去控制动画将他们串联起来,如果说你想让动画有个执行的先后顺序,那么就要用到timeline了
    let gsap1 = gsap.timeline({ duration: 1 };
    gsap1.to(".box", { x: 100, opacity: 0.5 }).to(".box", { rotation: 360 });

    // 4.stagger参数 控制如果多目标的时候,每个目标动画的时间差
    gsap1.to(".box", { x: 100,stagger: .1 }).to(".box", { rotation: 360,stagger: .1 });

    // 5.repeat参数以及yoyo参数 重复动画以及往返效果
    // repeat参数为重复次数 循环为-1
    // yoyo是布尔值 想要有往复效果则为true 默认为false
    gsap1.to(".box", { x: 100,repeat: -1, yoyo: true });

    // 6.控制动画的暂停、开始、回放
    // 暂停:play()、开始:pause()、回放:reverse()
    gsap1.play()
    gsap1.pause()
    gsap1.reverse()

    // 7.触发动画
    import { ScrollTrigger } from 'gsap/ScrollTrigger';
    gsap.registerPlugin(ScrollTrigger);
    gsap.to(".trigger-box", {
    scrollTrigger: {
    trigger: ".trigger-box", // 触发元素
    start: "top center", // 开始动画的时机
    end: "bottom center", // 截止动画的时机
    toggleActions: "restart pause reverse pause", // 定义当滚动进入和离开触发器区域时应该如何处理动画
    markers: true, // 开启标注功能(更好看滚动开始以及截止的地方)
    },
    x: 100,
    duration: 2,
    });
    1. 引入并配置
    <template>
    <!-- 时间线动画特效 -->
    <h6>时间线动画特效</h6>
    <div class="gsap">
    <div class="box red" ref="redDiv"></div>
    <div class="box green" ref="greenDiv"></div>
    <div class="box blue" ref="blueDiv"></div>
    </div>
    <!-- 触发动画特效 -->
    <h6>触发动画特效</h6>
    <div class="trigger-box yellow"></div>
    <!-- 文字特效 -->
    <h6>文字特效</h6>
    <div class="content">
    <div class="char">a</div>
    <div class="char">b</div>
    <div class="char">c</div>
    <div class="char">d</div>
    <div class="char">e</div>
    <div class="char">f</div>
    <div class="char">g</div>
    <div class="char">h</div>
    <div class="char">i</div>
    <div class="char">j</div>
    </div>
    </template>
    <script lang="ts" setup>
    import { onMounted } from "vue";
    import { gsap } from 'gsap';
    import { ScrollTrigger } from 'gsap/ScrollTrigger';

    // 注册插件
    gsap.registerPlugin(ScrollTrigger);

    // 生成时间线实例对象
    const gsap1 = gsap.timeline()
    const gsap2 = gsap.timeline()

    onMounted(() => {
    // 时间线动画特效
    gsap1.to(".box", { x: 100, opacity: 0.5, stagger: 1 }).to(".box", { rotation: 360, stagger: 1, repeat: -1, yoyo: true });
    // 触发动画特效
    gsap.to(".trigger-box", {
    scrollTrigger: {
    trigger: ".trigger-box", // 触发元素
    start: "top center", // 开始动画的时机
    end: "bottom center", // 截止动画的时机
    toggleActions: "restart pause reverse pause", // 定义当滚动进入和离开触发器区域时应该如何处理动画。
    markers: true, // 开启标注功能(更好看滚动开始以及截止的地方)
    },
    x: 100, // 向右平移100
    duration: 2, // 动画周期为2秒
    });
    // 文字特效
    gsap2.to(".char", {
    opacity: 1,
    delay: 1,
    duration: .5,
    y: 0,
    stagger: 0.1,
    })

    })
    </script>
    <style scoped lang="less">
    h6 {
    margin: 10px 0px;
    }

    .box {
    height: 50px;
    width: 50px;
    }

    .red {
    background-color: red;
    }

    .green {
    background-color: green;
    }

    .blue {
    background-color: blue;
    }

    .trigger-box {
    height: 50px;
    width: 50px;
    margin: 50px 0px;
    background-color: yellow;
    }

    .content {
    font-size: 30px;
    position: relative;
    color: red;
    // clip-path: polygon(0 0, 100% 0, 100% 100%, 0% 100%);
    }

    .char {
    display: inline-block;
    margin: 80px 10px 500px 10px;
    opacity: 0.3;
    transform: translateY(-50px);
    transition: transform .3s ease-in-out;
    }
    </style>
  8. 预览效果

动画库特性总结

  • CSS动画:CSS动画是基于CSS3属性的动画技术,是最基础的动画形式,具有最高的兼容性。
  • Animate.css:Animate.css是一个基于CSS3的动画库,提供了70多种预设的动画效果,如弹跳、摇摆、颤抖、抖动、闪烁等。
  • SGVA:SVGA是一种基于SVGA文件的动画实现方式,SVGA文件体积小,加载速度快,适合用于移动端的动画。
  • Lottie:Lottie是一种基于json格式文件的动画实现方式,json格式文件通常比传统的GIF或WebP小,性能更好。
  • PixiJS:PixiJS是一个轻量级的2D渲染引擎,主要使用WebGL技术,适用于复杂的2D动画和交互式图形。
  • CreateJS:CreateJS 是一个包含多个库的套件,包括 EaselJS(用于操作 HTML5 Canvas)、TweenJS(用于创建动画)、SoundJS(用于处理音频)等。优点是功能全面,可以创建包含动画、音频、交互等多种元素的复杂场景。
  • Three.js:Three.js是一个基于WebGL的JavaScript 3D渲染引擎,它的优点是可以创建复杂的 3D 渲染效果,如模型、材质、光照、阴影等。
  • PAG:PAG是一种基于二进制格式的动画技术,支持高效的压缩和渲染,是主流的客户端动画库。
  • Swiper:Swiper是一个具有丰富轮播图和幻灯片特效的动画库,内置滑动效果支持触摸滑动。
  • GSAP:GSAP 是为性能优化而设计的,它的动画运行速度非常快,注重时间动画序列。

    结语

    在上文所示的关于前端动画库的技术分享中,我们可以看到各种库都拥有其独特的特性和优势,这些库在提升用户体验、增强交互性、以及创建引人入胜的视觉效果方面发挥着重要作用。然而使用动画库时,我们也需要注意以下一些问题:
  • 浏览器兼容性:不同的浏览器和设备可能对动画的支持程度不同,因此需要检查动画库在不同环境中的兼容性,并提供适当的回退策略以确保动画在所有情况下都能正常工作。
  • 文件大小和加载时间:动画库可能会增加页面的文件大小和加载时间。因此在选择动画库时,需要权衡其功能和性能,并且对资源文件的加载进行优化处理,确保不会对用户体验产生负面影响。
  • 性能考虑:动画可能会对页面的性能产生影响,特别是当动画复杂或频繁时。因此,需要考虑动画的帧速率、内存使用和CPU负载。确保动画在不影响用户体验的情况下运行流畅。
  • 代码管理和维护:动画代码可能会变得复杂和难以管理。应该尽可能地保持代码简洁和模块化,且不要在同一个页面内使用两种动画库,以便于维护和调试。
  • 设计师沟通对接:前端开发与UI设计师保持沟通的重要性不容忽视,因为开发动画效果需要高度依赖设计师的资源产出,需要相互明确动画效果的可行性,资源文件格式,资源文件大小等问题,因此保持沟通有助于开发工作的顺利开展、提高开发效率以及提升用户体验。
    随着前端技术的不断发展和创新,我们可以期待未来会有更多优秀的前端动画库涌现。这些库不仅将帮助我们创建出更加生动、有趣的应用界面,还将推动前端动画技术的不断进步和发展。在这个过程中,我们需要保持对新技术的学习和探索,以便更好地利用这些工具来增强我们的应用体验,为用户提供更加优质、高效的服务。