如何在canvas中实现自定义路径动画(代码片段)

baimeishaoxia baimeishaoxia     2022-12-04     377

关键词:

在最近的项目中笔者需要做一个新需求:在canvas中实现自定义的路径动画。这里所谓的自定义路径不单单包括一条直线,也许是多条直线的运动组合,甚至还包含了贝塞尔曲线,因此,这个动画也许是下面这个样子的:

技术图片

那么如何才能在canvas中实现这种动画效果呢?其实很简单,对于路径的处理svg非常在行,因此在canvas中实现自定义路径动画,我们需要借助svg的力量。

创建Path

制作动画前,先要拿到动画的路径,对此我们可以直接使用svg的path定义规则,比如我们定义了一条较为复杂的路径(它到底长什么样大家可以自己试试,这里就不展示了),然后,我们需要将定义好的路径导入进一个新生成的path元素中(我们只是借助svg的api,因此并不需要将其插到页面内)

const path = ‘M0,0 C8,33.90861 25.90861,16 48,16 C70.09139,16 88,33.90861 88,56 C88,78.09139 105.90861,92 128,92 C150.09139,92 160,72 160,56 C160,40 148,24 128,24 C108,24 96,40 96,56 C96,72 105.90861,92 128,92 C154,93 168,78 168,56 C168,33.90861 185.90861,16 208,16 C230.09139,16 248,33.90861 248,56 C248,78.09139 230.09139,96 208,96 L48,96 C25.90861,96 8,78.09139 8,56 Z‘;

const pathElement = document.createElementNS(‘http://www.w3.org/2000/svg‘,"path"); 
pathElement.setAttributeNS(null, ‘d‘, path);

getTotalLength与getPointAtLength

SVGPathElement提供的这两个api很关键,可以说它是实现路径动画的最为核心的地方(在svg内实现自定义路径动画一般也是通过这两个api去解决)详情请戳:SVGPathElement MDN

getTotalLength方法可以获取SVGPathElement的总长度

getPointAtLength方法,传入一个长度x,将返回距离SVGPathElement起点的长度为x的终点坐标。

利用这两个api,通过循环的方式不断去更新canvas内所绘制的图形坐标,即可实现路径动画:

const length = pathElement.getTotalLength();
const duration = 1000; // 动画总时长
const interval = length / duration;
const canvas = document.querySelector(‘canvas‘);
const context = canvas.getContext(‘2d‘);
let time = 0, step = 0; 

const timer = setInterval(function() 
  if (time <= duration) 
    const x = parseInt(pathElement.getPointAtLength(step).x);
    const y = parseInt(pathElement.getPointAtLength(step).y);
    move(x, y);  // 更新canvas所绘制图形的坐标
    step++;
   else 
    clearInterval(timer)
  
, interval);

function move(x, y) 
   context.clearRect(0, 0, canvas.width, canvas.height);
   context.beginPath();
   context.arc(x, y, 25, 0, Math.PI*2, true);
   context.fillStyle = ‘#f0f‘;
   context.fill();
   context.closePath();

最后,我们把它封装一下,即可实现一个在canvas中实现自定义动画的简易函数啦:

function customizePath(path, func) 
    const pathElement = document.createElementNS(‘http://www.w3.org/2000/svg‘,"path"); 
    pathElement.setAttributeNS(null, ‘d‘, path);
      const length = pathElement.getTotalLength();
    const duration = 1000; 
    const interval = length / duration;
    let time = 0, step = 0; 
  
      const timer = setInterval(function() 
        if (time <= duration) 
              const x = parseInt(pathElement.getPointAtLength(step).x);
              const y = parseInt(pathElement.getPointAtLength(step).y);
              func(x, y);
              step++;
         else 
              clearInterval(timer)
        
     , interval);


const path = ‘M0,0 C8,33.90861 25.90861,16 48,16 C70.09139,16 88,33.90861 88,56 C88,78.09139 105.90861,92 128,92 C150.09139,92 160,72 160,56 C160,40 148,24 128,24 C108,24 96,40 96,56 C96,72 105.90861,92 128,92 C154,93 168,78 168,56 C168,33.90861 185.90861,16 208,16 C230.09139,16 248,33.90861 248,56 C248,78.09139 230.09139,96 208,96 L48,96 C25.90861,96 8,78.09139 8,56 Z‘;
const canvas = document.querySelector(‘canvas‘);
const context = canvas.getContext(‘2d‘);
function move(x, y) 
      context.clearRect(0, 0, canvas.width, canvas.height);
    context.beginPath();
      context.arc(x, y, 25, 0, Math.PI*2, true);
      context.fillStyle = ‘#f0f‘;
      context.fill();
      context.closePath();

customizePath(path, move);

实现思路大致如上所述,然而这并不是最终成果。当我们决定要在canvas制作自定义路径动画时,我们不仅要考虑如何实现,更要考虑性能优化,比如在这个实现思路中,我们是否可以减少不必要的渲染次数?帧率如何控制达到最优?等等。

虽然它们并不在这篇文章的讨论范围中,当也应当值得我们思考。

如何在 WordPress 中实现自定义标头的引导小部件代码?

】如何在WordPress中实现自定义标头的引导小部件代码?【英文标题】:HowtocanimplementbootstrapwidgetcodeforcustomheaderinWordPress?【发布时间】:2019-07-1703:22:25【问题描述】:我在网站上工作,我不是引导程序的专业人士。这是我的主题标... 查看详情

如何在 keras 中实现自定义指标?

】如何在keras中实现自定义指标?【英文标题】:howtoimplementcustommetricinkeras?【发布时间】:2016-10-0601:13:54【问题描述】:我得到这个错误:sum()得到了一个意外的关键字参数\'out\'当我运行这段代码时:importpandasaspd,numpyasnpimportkera... 查看详情

如何在 iPhone 中实现自定义相机功能? [关闭]

】如何在iPhone中实现自定义相机功能?[关闭]【英文标题】:HowtoimplementcustomcamerafunctionalityiniPhone?[closed]【发布时间】:2011-08-1605:07:13【问题描述】:我想开发一种功能,以便在iPhone应用程序中实现自定义相机功能,所以请给我... 查看详情

如何在黄瓜中实现自定义监听器?

】如何在黄瓜中实现自定义监听器?【英文标题】:Howtoimplementcustomlistenersincucumber?【发布时间】:2021-12-2214:55:47【问题描述】:如何在cucumber中实现客户监听?哪个可以记录到控制台/报告失败方法的发生?使用黄瓜4.0注意:钩... 查看详情

如何在 TCPDF 中实现自定义字体

】如何在TCPDF中实现自定义字体【英文标题】:HowtoimplementcustomfontsinTCPDF【发布时间】:2011-07-1222:11:47【问题描述】:在TCPDF中,只有几种字体可供选择,用于创建pdf文件。我想将Tahoma设置为我的pdf字体。如何在TCPDF中包含Tahoma?... 查看详情

如何在 UIImagePicker 中实现自定义“使用”和“重拍”按钮?

】如何在UIImagePicker中实现自定义“使用”和“重拍”按钮?【英文标题】:Howtoimplementcustom\'Use\'and\'Retake\'buttoninUIImagePicker?【发布时间】:2011-03-2507:06:04【问题描述】:我的意思是,我已经关闭了相机控制器pickerCam.showsCameraContro... 查看详情

如何在自定义 Spring 存储库中实现自定义方法? [复制]

】如何在自定义Spring存储库中实现自定义方法?[复制]【英文标题】:Howtoimplementcustommethodincustomspringrepository?[duplicate]【发布时间】:2016-03-2719:36:21【问题描述】:假设我想要一个方法,获取超级主客户,有id=0。我有客户类:@Ent... 查看详情

如何在 JSON.NET 中实现自定义 JsonConverter?

】如何在JSON.NET中实现自定义JsonConverter?【英文标题】:HowtoimplementcustomJsonConverterinJSON.NET?【发布时间】:2011-12-2306:02:32【问题描述】:我正在尝试扩展此处给出的JSON.net示例http://james.newtonking.com/projects/json/help/CustomCreationConverter.... 查看详情

如何在 QT 中实现自定义模型的 removeRow() 函数?

】如何在QT中实现自定义模型的removeRow()函数?【英文标题】:HowtoimplementremoveRow()functionforcustomizedmodelinQT?【发布时间】:2016-03-2404:02:48【问题描述】:我已经搜索了QT文档以供参考,但我有一些问题。这是QTdoc中的实现:boolTableMo... 查看详情

如何在 WCF 服务中实现自定义身份验证

】如何在WCF服务中实现自定义身份验证【英文标题】:HowtoImplementcustomauthenticationinWCFservice【发布时间】:2019-12-1403:26:43【问题描述】:我想为具有自定义身份验证的移动应用程序创建WCFrestful服务。第一个请求应该是登录,特别... 查看详情

如何在 ASP.NET Core 中实现自定义模型验证?

】如何在ASP.NETCore中实现自定义模型验证?【英文标题】:ASP.NETCore-Createcustommodelvalidation【发布时间】:2016-07-1215:07:56【问题描述】:在以前版本的ASP.NETMVC中,向模型添加自定义验证的方法是实现IValidatableObject并实现您自己的Val... 查看详情

在drf中实现自定义响应类(代码片段)

我的views.py。classOptiResponse(Response):def__init__(self,token=None,code=None,headers=None):ifcode==0:iftoken:data="status":"code":0,"error_message":"","data":"token":token,data="status":"code":0,"error_message":"","data":data="status":"code":1,"error_message":"","data":self.token=tokenself.dat... 查看详情

如何在android中实现自定义可折叠工具栏?

】如何在android中实现自定义可折叠工具栏?【英文标题】:HowtoimplementCustomCollapsableToolbarinandroid?【发布时间】:2015-11-2212:08:36【问题描述】:使用thistutorial实现灵活空间模式(带有折叠工具栏的模式)。我正在尝试实现与Lollipop... 查看详情

在 kvm 中实现自定义超级调用

...拟化非常陌生,最近我一直在尝试熟悉VMM的操作方式以及如何进行超级调用。谈到我打算在安装在我的Ubuntu桌面上的KVM中实现一个新的超级调用,然后可以从来宾环境中调用。通过这个超级调用,我打算只返回一个字符串“HelloW... 查看详情

如何在 ASP.NET MVC 中实现自定义主体和身份?

】如何在ASP.NETMVC中实现自定义主体和身份?【英文标题】:HowdoIimplementcustomPrincipalandIdentityinASP.NETMVC?【发布时间】:2011-03-1711:30:25【问题描述】:我想在经过身份验证的用户中存储额外的信息,以便我可以轻松访问它(例如User.... 查看详情

如何在 ASP.NET MVC 5 中实现自定义身份验证

】如何在ASP.NETMVC5中实现自定义身份验证【英文标题】:HowtoimplementcustomauthenticationinASP.NETMVC5【发布时间】:2015-10-1314:09:48【问题描述】:我正在开发一个ASP.NETMVC5应用程序。我有一个现有的数据库,我从中创建了我的ADO.NET实体... 查看详情

如何在 iOS 应用程序中实现自定义(平面图图像)地图..? [关闭]

】如何在iOS应用程序中实现自定义(平面图图像)地图..?[关闭]【英文标题】:HowdoIimplementacustom(floorplanimage)mapinaniOSapp..?[closed]【发布时间】:2012-03-0612:49:09【问题描述】:实现这一点的一些应用示例是..YorkdaleAppIconBrickell我是... 查看详情

我们如何在 Laravel 中实现自定义的仅 API 身份验证

】我们如何在Laravel中实现自定义的仅API身份验证【英文标题】:HowdoweimplementcustomAPI-onlyauthenticationinLaravel【发布时间】:2020-09-1019:04:42【问题描述】:这不是一个非常需要答案的问题,但欢迎提供进一步的建议和答案和建议。我... 查看详情