使用requestAnimationFrame和Canvas给按钮添加绕边动画
要给按钮添加酷炫的绕边动画,可以使用Canvas来实现。
基本的思路是创建一个和按钮大小相同的Canvas元素,内置在按钮元素中。然后在Canvas上实现边线环绕的动画。
requestAnimationFrame接口
我们使用requestAnimationFrame接口来实现动画帧的绘制,该接口告诉浏览器在重画(repaint)之前先执行一个动画回调函数。
通过这样的回调机制,浏览器可以把多个动画函数执行完成后再统一绘制,提高动画渲染性能。
(function() { for (var d = 0, a = ["webkit", "moz"], b = 0; b < a.length && !window.requestAnimationFrame; ++b) window.requestAnimationFrame = window[a[b] + "RequestAnimationFrame"], window.cancelAnimationFrame = window[a[b] + "CancelAnimationFrame"] || window[a[b] + "CancelRequestAnimationFrame"]; window.requestAnimationFrame || (window.requestAnimationFrame = function(b) { var a = (new Date).getTime(), c = Math.max(0, 16 - (a - d)), e = window.setTimeout(function() { b(a + c) }, c); d = a + c; return e }); window.cancelAnimationFrame || (window.cancelAnimationFrame = function(a) { clearTimeout(a) }) })();
上面实现的是一个兼容各浏览器并可以安全降级的requestAnimationFrame版本,当浏览器不支持时,回退到setTimeout/clearTimeout定时函数。
Border对象
现在我们需要创建一个Border对象,用来表示绕边的那个动画对象。该Border对象的宽高和按钮元素接近,并包含一个canvas对象。
function Border(opt) { // 参数opt即为传入的button元素 this.elem = opt.elem; this.active = false; this.canvas = document.createElement('canvas');//创建画布 this.ctx = this.canvas.getContext('2d');//获取画布上下文 this.width = this.canvas.width = this.elem.outerWidth();//设置宽 this.height = this.canvas.height = this.elem.outerHeight();//设置高 this.borderSize = parseInt(this.elem.css('border-left-width'), 10);//设置边宽 this.waypoints = [ [0, 0], [this.width - this.borderSize, 0], [this.width - this.borderSize, this.height - this.borderSize], [0, this.height - this.borderSize] ];//设置路标数组 this.tracer = { x: 0, y: 0, color: opt.color, speed: opt.speed, waypoint: 0 };//设置路径对象 this.canvas.style.top = -this.borderSize + 'px'; this.canvas.style.left = -this.borderSize + 'px'; this.elem.append($(this.canvas));//把canvas内置到button元素中。 }
然后我们实现循环绕边的函数,由于这是Border对象的一个行为,我们把它实现为其原型对象的一个方法:
Border.prototype.loop = function() { requestAnimationFrame($.proxy(this.loop, this));//定时调用自身 this.ctx.globalCompositeOperation = 'source-over';//源覆盖目标的合成模式 this.ctx.fillStyle = this.tracer.color; this.ctx.fillRect(this.tracer.x, this.tracer.y, this.borderSize, this.borderSize);//在当前路径上绘制小方块(本例是4*4px) //下面这段代码是通用算法,用来计算某段路径上x/y方向上的速度分量,同时判断下一步是否需要拐弯(在路标处) var previousWaypoint = (this.tracer.waypoint == 0) ? this.waypoints[this.waypoints.length - 1] : this.waypoints[this.tracer.waypoint - 1], dxTotal = previousWaypoint[0] - this.waypoints[this.tracer.waypoint][0], dyTotal = previousWaypoint[1] - this.waypoints[this.tracer.waypoint][1], distanceTotal = Math.sqrt(dxTotal * dxTotal + dyTotal * dyTotal),//该段路径总长度 angle = Math.atan2(this.waypoints[this.tracer.waypoint][1] - this.tracer.y, this.waypoints[this.tracer.waypoint][0] - this.tracer.x), vx = Math.cos(angle) * this.tracer.speed, vy = Math.sin(angle) * this.tracer.speed, dxFuture = previousWaypoint[0] - (this.tracer.x + vx), dyFuture = previousWaypoint[1] - (this.tracer.y + vy), distanceFuture = Math.sqrt(dxFuture * dxFuture + dyFuture * dyFuture);//在该路径上的总移动距离 if (distanceFuture >= distanceTotal) {//已移动距离超过路径长度,则需要拐弯,即更新路标 this.tracer.x = this.waypoints[this.tracer.waypoint][0]; this.tracer.y = this.waypoints[this.tracer.waypoint][1]; this.tracer.waypoint = (this.tracer.waypoint == this.waypoints.length - 1) ? 0 : this.tracer.waypoint + 1; } else {//否则,在当前路径上移动位置 this.tracer.x += vx; this.tracer.y += vy; } }
有了Border对象后,我们来实际使用它。(在C++等面向对象语言中,我们通常称上面定义了一个类Class,在实际使用时,需要创建类的实例,即对象Object)。
var button = $("#your_button_id")[0]; $this = $(button); var border = new Border({ elem: $this, color: $this.data('color'), speed: $this.data('speed') }); $(border.canvas).stop(true).animate({ 'opacity': 1 }, 400); border.loop();
这样我们就实现了一个按钮的绕边动画。
在线实例
你可以通过在线实例自己试试看,还可以基于这个简化版本添加渐变效果和鼠标悬停交互处理,乃至实现不规则形状按钮的描边动画。


最新评论
- 相关文章
微信公众号在线生成二维码带参数怎么搞?
带参数二维码是微信公众号渠道二维码的一种实现
微信的带参数二维码有两种,一种是临时二维码,一种是永久二维码,但是永久二维码的生成是有个数限制的,微...2019年开源WebRTC媒体服务器选型比较
什么是WebRTC服务器?在WebRTC的早期开始,该技术的主要卖点之一是它允许点对点(浏览器到浏览器)通信,几乎没有服务器的干预,服务器通常仅用于信令(比如用于...
2019年NodeJS框架Koa和Express选型比较
Koa和Express都是NodeJS的主流应用开发框架。
Express是一个完整的nodejs应用框架。Koa是由Express团队开发的,但是它有不同的关注点。Koa致力于核心中间件...创建非矩形网页页面元素的常用技术和实例代码
非矩形设计正在变成一种时尚,比如波浪形、菱形、三角形等:而随着技术发展,这种设计在技术实现上也变得更容易。本文以最简单的三角形为例,演示使用5种方法来...
CSS3原生变量(Native Variables)新特性简介
对Web开发者来说,一个盼望已久的特性是CSS终于支持原生变量了!
变量是程序语言中用来解决代码重复和进行表达式计算的关键概念(想想数学方程式中的x)。...React JSX语法简介
JSX是一种类似XML的标签语法,用来简化代码,我们可以不使用JSX,但了解并使用也没什么坏处:)在React中,JSX是一个使用 React.createElement() API的快捷方式...
常见面试题JS语言中四种函数调用方式实例讲解
JS的语言世界中函数(function)是一等公民,函数的调用有多种方法。普通调用这个是最常见和直接的方式:function
Babylon.js入门教程和开发实例
Babylon.js是一款WebGL开发框架。和Three.js类似。主要的技术区别是Three.js还试图回退兼容CSS 3D。Three.js是完全社区推动的,比Babylon.js要成熟些,而Babylon...
学习使用CSS制作进度条
进度条是基础的界面控件,可用于多种场合,比如任务完成进度,手机充电状态等。本文介绍一个简单实用的进度条制作方法。预期效果如下图所示:直观上,我们可以把该进度条控件分为2个部分,外部的边界用来表示固定的目标范围,里面的条形部分用来表示当前进度。外部目标范围元素的CSS代码编写如下:.pb-scope
Blender2.7 快捷键一览表
通用操作
停止当前操作:ESC
快捷搜索:SPACE撤销:ctrl+z重做:ctrl+shift+z渲染:F12
单选:鼠标右键(RMB)全选:A
框选:B
刷选:...WebVR简介和常用资源链接
什么是WebVR这是一个实验性的JavaScript API,提供了在用户网页浏览器中访问虚拟现实设备的统一接口。当前主流VR设备如Oculus Rift DK2、谷歌的CardBoard、三星...
S3TC(S3 Texture Compression)纹理压缩格式详解
使用S3TC格式存储的压缩纹理是以4X4的纹理单元块(texel blocks)为基本单位存储的,每纹理单元块(texel blocks)有64bit或者128bit的纹理单元数据(texel data)。这...
使用top/left/margin和CSS3 translate两种方法实现标题居中的性能差异详解
要实现标题全屏居中(同时在垂直和水平方向居中),有若干种方法,包括使用弹性布局、表格单元、绝对定位、自动外边距和CSS3平移变换等。你可能已经使用了这些方...
div 、section 、article的区别和使用场景
div 、section 、article的区别和使用场景
主要区别,以及适用场合如下:
1、div在html早期版本就支持了,section和article是html5提出的两个雨衣话标... 更多...