WEB烟花效果——Canvas实现
摘要
本文主要介绍一种WEB形式的烟花(fireworks)效果(图1所示),该效果基于Canvas实现,巧妙地运用了canvas绘图的特性,并加入了物理力作用的模拟,使整体效果非常绚丽、逼真。本文从本质上介绍了其实现原理,便于其他可视化爱好者能快速上手。本文从视觉渲染和运动轨迹模拟两个方面详细描述了该效果的实现原理及细节。
图1 - Canvas烟花效果截图
引言
“东风夜放花千树。更吹落、星如雨。”——青玉案·元夕。烟花的耀眼只是一瞬,但人们追求美的心却是永恒。本文所要介绍的效果,是我所见过的最美丽、最符合人的精神图像的作品。
网上还有一种类似于火星迸发的线条很细的模拟烟花效果,我觉得比本文这一款差多了。如果作为烟花爆竹出售,我感觉它们是一块钱和十块钱效果的差别,这是一个不恰当的比喻。
烟花效果的逼真在于其颜色的多彩变幻和运行轨迹的合理性。从现象上看,一道光柱逐渐上升,到达一定高度时停止,同时出现多条弧形的流星效果并逐渐下降。随着烟花绽放的时间增加,其颜色逐渐暗淡,最终消逝不见。从本质上来看,它是一系列头部颜色最亮、尾部越来越暗,且轨迹越来淡的线条。从外表上来看,它爆炸前是一条直线,爆炸后变成多条曲线。
本文所描述的实现方式巧妙地利用了Canvas绘图的技巧,通过合理的重绘和清除策略,完美地模拟了烟花绽放。
正文
一、颜色渲染
虽然其实质是线条的渲染,但对于烟花来讲,我们最直观的感觉是有一个实物飞上天,然后有很多小的实物因爆炸而散落下降。因此我们可在画布上根据轨迹不断绘制小圆点,圆点因连续运动而形成线条。再通过不断重绘画布,用带有一定透明度的背景色来渲染画布整个区域,并且保证新绘制的和原来绘制的图形都存在,即可达到轨迹颜色逐渐暗淡的效果。
为了清晰描述这一过程,此处把该部分功能独立出来,我们单独来看看这个线条效果是怎么来的。
如图2所示,为了方便查看,我们把线条放大,节奏放慢。可点击链接到Codepen查看源码。
图2 - 线条渲染
//// In loop update
context.fillStyle = "rgba(0, 0, 0, 0.05)";
context.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); //// draw on canvas
this.render = function(){
if(this.end)return;
var c = context;
c.save();
c.globalCompositeOperation = 'lighter'; var x = this.pos.x, y = this.pos.y, r = this.size;
var gradient = c.createRadialGradient(x, y, 0.1, x, y, r);
gradient.addColorStop(0.1, "rgba(255, 255, 255 ," + 1 + ")");
gradient.addColorStop(1, "rgba(0, 0, 0, " + 1 + ")"); c.fillStyle = gradient;
c.beginPath();
c.arc(x, y, r, 0, Math.PI * 2, true);
c.closePath();
c.fill(); c.restore();
};
如代码标注部分(行2、10、13、19)所示,有四点主要内容:画圆、颜色径向渐变、绘图方式、重绘。
1、画圆。 x, y代表运行轨迹的当前坐标。控制运动的代码部分只需要负责计算并更新x和y的值。渲染的代码只负责在该坐标点画圆并填充。
2、颜色径向渐变。圆点的填充颜色,其中 createRadialGradient() 函数的第1、2个参数与第4、5个参数分别表示渐变开始和渐变结束的圆心坐标。在这里设置为相同的值,使颜色过渡效果更自然真实。
3、绘图方式。Canvas的 globalCompositeOperation 属性决定下一次绘图时如何将一个源(新的)图像绘制到目标(已有)的图像上。此处设置为 'lighter' 使同时显示源图像和目标图像,否则会影响到循环中的画布重绘。
4、重绘。使用 fillRect() 函数对整个画布进行清除,同时设置 fillStyle 属性为带一定不透明度的背景色。如此每一次清除画布将使原有的图形变得暗淡,随着时间流逝,而最终消失在夜幕背景下,或被最新的烟花轨迹覆盖。
以上几点是此次烟花渲染的核心之处,它巧妙运用了Canvas绘图的特点,完美地用动画仿真了烟花的绽放。
二、轨迹模拟
烟花轨迹模拟的核心是模拟物体在物理力作用下的运行状态。在此我们主要考虑方向、速度、重力的影响。烟花在爆炸时向四面八方散开,体现在2D画布上就是0~360度随机方向。我们假想在爆炸的一瞬间冲击力最大,速度最大,然后受阻力影响速度逐渐减小。此外我们需要考虑重力的影响,使其最终大致呈自由落体运动。
为了纯粹演示运动轨迹,我们把该部分逻辑独立出来,用d3.js模拟整个爆炸过程。
如图3所示,可点击链接到Codepen查看源码。
图3 - 轨迹模拟
图4 - 轨迹瞬间
接下来我们直接看核心源码:
//// 创建模型列表
function create(){
//// 初始化
circles = [];
for (var i = 0; i < count; i++) {
var particle = new Circle(objs[i], pos[0], pos[1]);
var angle = Math.random() * Math.PI * 2; var speed = Math.cos(Math.random() * Math.PI / 2) * 15; particle.vel.x = Math.cos(angle) * speed;
particle.vel.y = Math.sin(angle) * speed; particle.size = 10; particle.gravity = 0.2;
particle.resistance = 0.92;
particle.shrink = Math.random() * 0.05 + 0.93; circles.push(particle);
}
} //// In Model Class
//// 模型参数更新
this.update = function(){
this.vel.x *= this.resistance;
this.vel.y *= this.resistance; // gravity down
this.vel.y += this.gravity; this.pos.x += this.vel.x;
this.pos.y += this.vel.y; // shrink
this.size *= this.shrink; this.move();
};
//// 实体位置移动
this.move = function(){
this.object.attr('cx', this.pos.x)
.attr('cy', this.pos.y)
.attr('r', this.size)
;
}
我们抽象出一个物体模型,它的主要属性是pos(当前位置)、vel(方向参数)、resistance(阻力作用参数)、gravity(重力系数),主要方法是update() 和 move()。可在Codepen查看完整源码。
以上代码中, create() 是一个初始化所有爆炸物的方法,总数量count设置为80左右的随机数,objs是用d3.js添加的svg上的元素集合(在此我们使用圆形circle),模型类的每一个实例的‘object‘属性对应一个画布上的元素,在此根据分层思想把update()和move()分开,前者负责逻辑,后者负责效果。
在create里初始化了模型的一系列参数,其中重要的是移动方向,我们逐条来分析:
var angle = Math.random() * Math.PI * 2; 不难理解,就是角度取 0 ~ 360 度的随机一个方向。取值在0 ~ 6.28之间。
var speed = Math.cos(Math.random() * Math.PI / 2) * 15; 其中括号内的取值在 0 ~ π/2 之间,我们知道在此区间上cos函数的值为正,值域在0 ~ 1之间,故speed的取值在0 ~ 15之间。
particle.vel.x = Math.cos(angle) * speed; 区间0 ~ 2π上,cos函数的取值是-1 ~ 1之间,正负概率均等,参考图5。
particle.vel.y = Math.sin(angle) * speed; 区间0 ~ 2π上,sin函数的取值是-1 ~ 1之间,正负概率均等。
因此,方向偏移变量vel分布在二维坐标轴的四个象限,各爆炸物的初始坐标离爆炸点的横纵距离随机分布在0 ~ 15之间。
图5 - 三角函数曲线
在update()函数里,修改方向偏移变量,使力度逐渐衰减,并加入重力影响,然后修改物体的当前位置坐标,以及逐渐减小物体在视觉范围内的尺寸。
this.vel.x *= this.resistance; resistance 的赋值是0.92,故偏移量每次以92%的比例衰减。因此在效果图中我们看到,物体完全自由落体前移动速度有越来越慢的缓冲效果,类似于cubicOut缓冲。
this.vel.y *= this.resistance;
this.vel.y += this.gravity; gravity是重力系数,所以运动时需要每次沿y轴下方适当偏移。当vel变量的衰减殆尽时,只受重力作用影响,就呈直线下降状态。(此处物理专业的朋友禁止较真!)
this.pos.x += this.vel.x; 物体位置信息更新。
this.pos.y += this.vel.y;
this.size *= this.shrink; 物体尺寸大小衰减。
this.move(); 视图层移动。
在这个简单的轨迹模拟程序里,我们直接使用 setInterval() 函数来实现动画刷新,也可以使用 requestAnimationFrame() 函数显得更正式些。
至此,我们的物体物理运动轨迹模拟完成,如图3所示,尽管没有什么渲染的成分,看起来依然有烟花绽放的感觉。
总结
本文介绍了一种基于Canvas的烟花效果实现方式,该方式巧妙地利用了Canvas渲染的特性,其动画受浏览器性能的影响较小,并加入物理力作用效果,使动画整体看起来很逼真,效果很绚丽。本文详细介绍了其原理及实现细节,有助于其他有相似需求的开发者能迅速使用并改进,实现更完美的作品。
其它
在此声明,本文所述的方法非本人原创,也没有找到原作者,在此鸣谢!
基于以上内容的介绍,有兴趣的开发者可以使用d3js框架实现整套的完整效果。
若您发现本文所述有失偏驳之处,或有待改进之处,或您有其它想法、意见及建议,请在评论区留言,谢谢!
WEB烟花效果——Canvas实现的更多相关文章
- 还在用canvas画格子吗?文字烟花效果更不错噢
大家好,我是小丞同学,一名前端爱好者 欢迎访问博主的个人网站:一口奶盖 "在人间贩卖声音 等凑够满天星辰 放烟花给你看" 上次的烟花有些许平淡,这次来放大招了,让你的名字在天空绽放 ...
- Android开发——为EditText添加烟花效果的实现
)什么时候发射烟花:监听EditText的文字改变,获取文字数量的变化以确定风的方向,还有获取光标的位置确定爆炸的位置.光标的位置没有具体的方法确定坐标,要通过反射自己计算. 2. 主要实现类 库里 ...
- 外媒速递:十大最佳心理学概念助你提升Web设计效果
外媒速递是核子可乐精选的近日国外媒体的精彩文章推荐,希望大家喜欢! 本期给大家推荐的是帮助你提升Web设计效果的十大最佳心理学概念.改善企业云环境协作效率的九款卓越工具.选择移动应用开发工具时要考虑的 ...
- >炫酷的计时器效果Canvas绘图与动画<
>炫丽的计时器效果Canvas绘图与动画< 虽然我是学习java的,但是因为最近使用html5的关系,多学习了一下前端知识. 现在,我要介绍的计时器是十分炫酷的,使用画布完成. 喜欢htm ...
- 『HTML5梦幻之旅』-缤纷多姿的烟花效果
天花无数月中开,五采祥云绕绛台.堕地忽惊星彩散,飞空旋作雨声来.怒撞玉斗翻晴雪,勇踏金轮起疾雷.更漏已深人渐散,闹竿挑得彩灯回. ——明·瞿佑·<烟火戏> 记得每年过春节的那段时间,除了欣 ...
- 6 cocos2dx粒子效果,类图关系,系统原生粒子和自己定义粒子效果,粒子编译器软件,爆炸粒子效果,烟花效果,火焰效果,流星效果,漩涡粒子效果,雪花效果,烟雾效果,太阳效果,下雨效果
1 粒子 演示样例 2 类图关系 3 系统原生粒子 CCParticleSystem 全部粒子系统的父类 CCParticleSystemPoint. CCParticleSystemQuad ...
- 【技术博客】在Unity3d中实现烟花效果
在游戏开发中,我们经常需要用到类似烟花的效果.在Unity3d中,实现烟花效果的方法不止一种,我选用了Unity3d中新添加的粒子特效工具--visual effect graph来进行实现. 实现过 ...
- canvas制作的烟花效果
最近感觉canvas挺有意思的,在业余时间没事研究了一下,参考过网上一些思路,话不多说,开始啦. github地址:https://github.com/aWhiteBear/fireworks 演示 ...
- canvas 实现烟花效果
一:创建画布 <canvas width="600" height="600" id="canvas" style="bor ...
随机推荐
- YPbPr 和 YCbCr的区别 .
这几天在做分量视频输入,涉及分量视频表示,接触到YPbPr和YCbCr的概念,发现不光自己的项目上,对这两个概念错乱,就是网上也充斥着大量错误的说法. 分量接口有两种名称YPbPr和YCbCr,这是两 ...
- Windows控制台下绘制简单图形
最近接触到一个很有意思的问题,如何在Windows控制台下画图,翻遍了C的头文件也没找到画图的函数,好吧,那就用Windows提供的API函数吧,看来想移植是没戏了.先画一个简单的图,类似心电图那种吧 ...
- (二十二)java小练习三
练习七:计算1-100的累加和 package demo; /** * 计算1-100的累加和 * @author tuzongxun */ public class Test16 { pu ...
- ONCOCNV软件思路分析之control处理
进行数据初步处理(perl) 统计amplicon的RC(read counts),并且相互overlap大于75%的amplicon合并起来 统计每个amplicon的GC含量,均值, 性别识别并校 ...
- java访问权限修饰符
作用域 当前类 同一package 子孙类 其他package public √ √ √ √ protected √ √ √ × friendly √ √ × × private √ × × × ja ...
- Echarts自动刷新数据
1.Echarts自动刷新数据 1.Echarts柱状图的正常配置 注:声明了 myChart.test这两个都有用 官方示例中myChart是声明在 function(ec)里面的 <scri ...
- 关于margin-top的一些特别问题
当给子元素添加了margin-top的数值,浏览器解析的时候默认添加到父元素上解决的方法: 1 给父元素添加一个上边框border-top. 2 或者给子元素加个浮动. 3 给父元素添加overfl ...
- 【BZOJ2132】圈地计划(最小割)
[BZOJ2132]圈地计划(最小割) 题面 BZOJ 题解 对我而言,不可做!!! 所以我膜烂了ZSY大佬 他的博客写了怎么做... 这,,...太强啦!! 完全想不到黑白染色之后反着连边 然后强行 ...
- 第三方工具 - 关于echarts下钻功能的一些总结.js
废话:好久没有写博客了,每每看着自己的'战绩'都有点愧疚,但是这段时间确实学习了不少东西,待我慢慢地一 一梳理,将之消化并分享. ---------------------------$O_O$--- ...
- TP5 模型类和Db类的使用区别
原文:http://www.upwqy.com/details/3.html 总结 在控制器中 模型操作 get() 和 all() 只能单独使用来查询数据 想要链式操作查询数据 需要使用f ...