前端项目,特别是Toc的项目,一定少不了各种动效和动画效果。
葫芦七兄弟:
- CSS 动画- 优点:兼容性强;浏览器针对的流畅度优化;语法简单;某些属性(如 transform 和 opacity)可以利用 GPU 加速,提高性能;- 缺点:复杂的动画和交互难以实现;调试困难;
- JavaScript 动画 requestAnimationFrame- 优点:可以实现复杂的动画效果和交互;精确控制动画的每一帧;运行时动态生成和修改动画;- 缺点:编写和维护复杂;浏览器兼容性需手动处理;
- Web Animations API (WAAPI)- 优点:结合了 CSS 动画的性能优势和 JavaScript 的灵活性;可以利用硬件加速。- 缺点:旧版本的浏览器可能不支持;API 较为复杂;
- SVG 动画- 优点:矢量图形无限缩放而不失真;简单的动画性能好;- 缺点:SVG 动画的开发工具较少;旧版本的浏览器可能存在问题;
- Lottie- 优点:可以导入 After Effects 动画,保持高质量的设计;支持 Web、iOS、Android 等多个平台;只需加载 JSON 文件即可渲染动画,无需手动编写动画代码;- 缺点:依赖工具;不支持所有 After Effects 特性,某些高级效果可能无法导出;
- GSAP (GreenSock Animation Platform)- 优点:高性能适合复杂的动画;提供了丰富的动画控制和插值函数;兼容性好;- 缺点:API 较为复杂;部分高级功能需要购买商业许可;
- Three.js- 相对比webgl简单,但都是相当难的一档了。需要大量专业知识。包括但不限于:数学、建模、绘图、3D等。掌握熟练后都可以直接转行3D了哈哈哈,一般需求不建议搞这个。
我工作的开发环境需要考虑兼容性,且有UI支持一部分复杂动画效果,所以常用的方案包括但不限于以下三种:
gsap动画
通过Gsap动画库,可以实现纯前端的动画。从一个dom丝滑转变为另一个dom,或者dom本身大小变化等。
- 文档 文档地址 比反人类的swiper文档好用太多
- 安装
npm install gsap
- 引入
import gsap from 'gsap'//奖品动画效果
- 代码1 不同Dom的丝滑切换
consttoggleAnimation=(ind)=>{const staticstate = document.querySelector(`.staticstate${ind}`);const spinner = document.querySelector(`.spinner${ind}`);
gsap.to(staticstate,{
opacity:0,
duration:0,onComplete:()=>{
staticstate.style.display ='none';
spinner.style.display ='block';}});}consttoggleAnimation2=(ind)=>{const spinner = document.querySelector(`.spinner${ind}`);const checkmark = document.querySelector(`.checkmark${ind}`);
gsap.to(spinner,{
opacity:0,
duration:0,onComplete:()=>{
gsap.to(checkmark,{
opacity:1,// 透明度
scale:1,// 放大效果
duration:1,// 动画持续时间
ease:'power1.inOut',// 动画缓动函数});}});}
- 动画效果1
- 从未完成 加载中 完成的状态转变的动画
- 代码2 同一DOM的放大缩小
consttoggleAnimation=(ind,reversal)=>{const prize = document.querySelector(`.prize-prize${ind}`);
gsap.to(prize,{
opacity:1,// 透明度
scale: reversal?1.05:0.9,// 放大缩小效果
duration:1.5,// 动画持续时间
ease:'power1.inOut',// 动画缓动函数onComplete:()=>{
gsap.to(prize,{
opacity:1,// 透明度
scale: reversal?0.9:1.05,// 放大缩小效果
duration:1,// 动画持续时间
ease:'power1.inOut',// 动画缓动函数});}});}
- 动画效果2 四周悬浮的就是gsap动画效果 中间的是lottie-JSON(png)动画,下面就会讲到
lottie-web
这个需要依赖UI 使用Adobe After Effects【AE】制作动画,导出对应的JSON文件后使用
- 文档 文档地址
- 仓库 文档地址
- 安装
npm install lottie-web
- 引入
import lottie from 'lottie-web';//JSON动画接入
- lottie JSON动画(svg) 引入项目打开后看到大概是这样,SVG类型没有附加文件,通过点和线的计算绘制就可以实现动画效果
- lottie JSON动画(png) 引入项目打开后看到大概是这样,PNG类型有附加文件,实现的效果一般较为复杂,需要通过基础的图片来实现动画。 这时会涉及到引用路径的问题 因为无法再JSON中使用相对路径和vite的import.meta.url 所以我把他们放在了public目录下,避免被打包附加的哈希值影响
序列帧动画
当动画复杂度超出一定程度时,JSON动画会无法承载,导出会丢失样式【UI说的】。所以这时前端就需要接入帧动画了。
帧动画,顾名思义就是把每一帧的图片都给到前端,然后手动组装进行播放。
解决思路有很多,我这里为了兼顾在安卓和IOS不同环境、webview版本下的播放效果【IOS特别注意因内存机制会有闪烁问题 改用雪碧图方案 计算位移和预加载略麻烦但效果和兼容性很好】
提供以下两种思路:
-------------------------html
<van-overlay style="z-index: 2;":show="frameAnimation":lock-scroll="false"><div id="frameAnimationDom"class="title-bg"></div></van-overlay>---------------------------JSlet timer4;//动画计时器functionpreLoad(){// 预加载帧动画图片const frameCount =58;// 帧数for(let i =0; i <= frameCount; i++){const img =newImage();
img.src =newURL(`../assets/images/blind/frame/${i}.png`,import.meta.url).href;
images.value.push(img);}
frameAnimation.value =true;// 方案一 帧动画setTimeout(()=>{//确认支付后再播放动画开奖const frameCount =58;// 帧数const frameElement = document.getElementById('frameAnimationDom');let currentFrameIndex =0;let lastTime =0;functionanimate(){if(newDate().getTime()- lastTime >=40){// 40毫秒一帧 一秒25帧
frameElement.style.backgroundImage =`url(${images.value[currentFrameIndex].src})`;
currentFrameIndex =(currentFrameIndex +1)% frameCount;
lastTime =newDate().getTime();}
timer4 =requestAnimationFrame(animate);}// 开始动画animate();},200)// 方案2 雪碧图// animate();// const frameCount = 51; // 总帧数// const frameWidth = 780; // 每帧宽度// let currentFrame = 0;// let lastTime = 0;// function animate() {// if (new Date().getTime() - lastTime >= 75) { // 75毫秒一帧 一秒13帧?// const frameElement = document.getElementById('frameAnimationDom');// const offset = currentFrame*(100/50);// frameElement.style.backgroundPosition = `${offset}% 0%`;// currentFrame = (currentFrame + 1) % frameCount;// lastTime = new Date().getTime();// }// timer4 = requestAnimationFrame(animate);// }setTimeout(()=>{cancelAnimationFrame(timer4)// 清除动画循环
timer4 =null;// 重置 ID
frameAnimation.value =false;},2650)-------------------------css
// 方案1 帧动画.title-bg{
width:375px;
height:812px;
background-size:100%100%;
margin:0 auto 0 auto;}// 方案2 雪碧图// .title-bg{ // width: 375px;// height: 812px;// background-image: url(../assets/images/blind/sprites.png);//动态高度做展开 后由于图片过多改为滚动展示// background-size: auto 100%; /* 确保背景图像的高度与容器高度一致 */// margin: 0 auto 0 auto;// }
未完待续…【不过一般的需求这些方式也都足够了,其他的我遇到了再补充,码友们也可以作为思路再自己向外拓展哈~】
版权归原作者 跳动的世界线 所有, 如有侵权,请联系我们删除。