主要想法:html
鼠标点击事件触发圆点出如今鼠标处web
圆点大小,颜色,数量随机canvas
有一个小白圈作为轨迹,不断改变半径变大变淡消失dom
圆点向外移动,自身变小,消失。随着移动的距离远,移动速度跟着变函数
须要处理的是,从鼠标点击处能够肯定圆点的x,y轴,可是动去哪,怎么动是一个问题,
因此先肯定圆点的目标坐标,再从圆点的原始坐标(即鼠标点击处)向目标坐标移动,而且x,y 成比例 去移动才不会在x,y移动距离不相等的状况下 不平衡动画
随机生成圆点目标坐标的函数代码:this
let endpos = (x, y) => { let angle = random(0, 360) * Math.PI / 180, value = random(20, 150), radius = [-1, 1][random(0, 1)] * value; return { x: x + radius * Math.cos(angle), y: y + radius * Math.sin(angle) } }
由于须要大量的随机数,因此封装一个生成随机数函数spa
let random = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
圆点的移动函数,咱们须要的移动是从原始坐标向目标坐标由快而慢。
生成比例分红3段。那就是原始坐标到第一段坐标为第一段,第一段坐标到第二段坐标为第二段,第二段坐标到目标坐标为第三段。若是当前坐标是处于第一段内,则速度越快,以此类推code
如今两种状况,一种是目标坐标大于原始坐标,那么,第一段就为最接近原始坐标的那一段(Math.max(current, ff) == current ? (Math.max(current, mm) == current ? s : m) : f)
,第二种状况是目标坐标小于原始坐标,那么就反过来,第一段为离原始坐标最远的那一段。htm
//根据不一样距离段设置前行的步伐,分为3个阶段,离出发点近的那一段为速度最快,中间为速度通常,最远为速度最慢 //区分目标点小于出发点的状况 //ratio为两点之间的距离的行走比例,比例数值越大,行走越慢 moveFun(start, end, current) { let s = random(26, 35), m = random(20, 25), f = random(15, 20), ff = start.x + ~~((end.x - start.x) / 3), mm = ff + ~~((end.x - start.x) / 3), ratio = end.x >= start.x ? (Math.max(current, ff) == current ? (Math.max(current, mm) == current ? s : m) : f) : (Math.max(current, ff) == current ? f : (Math.max(current, mm) == current ? m : s)), mp = { x: end.x - start.x, y: end.y - start.y }; return { x: Math.abs(mp.x / ratio), y: Math.abs(mp.y / ratio) } }
每个小圆点作为一个个体,自带移动函数,和移动的目标坐标。每次Animation时,圆点进行重绘更新最新的坐标
小圆点的代码:
class Circle { constructor(x, y) { this.r = random(9, 13) this.opos = {} this.x = this.opos.x = x this.y = this.opos.y = y this.colors = ['#FF1461', '#18FF92', '#5A87FF', '#FBF38C'] this.color = this.colors[random(0, this.colors.length)]; this.tpos = endpos(x, y) } creatCircle(ctx) { ctx.beginPath() ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI) ctx.closePath() ctx.fillStyle = this.color ctx.fill() } //根据不一样距离段设置前行的步伐,分为3个阶段,离出发点近的那一段为速度最快,中间为速度通常,最远为速度最慢 //区分目标点小于出发点的状况 //ratio为两点之间的距离的行走比例,比例数值越大,行走越慢 moveFun(start, end, current) { let s = random(26, 35), m = random(20, 25), f = random(15, 20), ff = start.x + ~~((end.x - start.x) / 3), mm = ff + ~~((end.x - start.x) / 3), ratio = end.x >= start.x ? (Math.max(current, ff) == current ? (Math.max(current, mm) == current ? s : m) : f) : (Math.max(current, ff) == current ? f : (Math.max(current, mm) == current ? m : s)), mp = { x: end.x - start.x, y: end.y - start.y }; return { x: Math.abs(mp.x / ratio), y: Math.abs(mp.y / ratio) } } //根据计算出来的移动值去移动 //若是目标坐标大于原始坐标则向右移动,最大不能超过目标坐标,反之向左移动最小不能小于目标坐标 move() { var movepos = this.moveFun(this.opos, this.tpos, this.x); this.x = (this.opos.x > this.tpos.x) ? Math.max(this.x - movepos.x, this.tpos.x) : Math.min(this.x + movepos.x, this.tpos.x) this.y = this.opos.y > this.tpos.y ? Math.max(this.y - movepos.y, this.tpos.y) : Math.min(this.y + movepos.y, this.tpos.y) this.r = Math.max(Math.abs((this.r - Math.random() / 1.2).toFixed(2)), 0) } }
大圆则是从鼠标点击处建立的一个圆,并不断改变其半径向外扩展,而且愈来愈透明,当它已经很大而且很透明时意味着咱们的动画要结束了,因此以这个为标准,中止动画,而且清空屏幕
大圆代码:
class BigCircle { constructor(x, y) { this.bR = random(16, 32) this.overR = random(60, 100) this.x = x this.y = y this.op = 1 } creatBigCircle(ctx) { ctx.beginPath() ctx.arc(this.x, this.y, this.bR, 0, 2 * Math.PI) ctx.closePath() ctx.strokeStyle = 'rgba(128, 128, 128, ' + this.op + ')' ctx.stroke() } changeR() { this.bR = Math.min(this.bR += random(1, 4), this.overR); this.op = Math.max((this.op - Math.random() / 12).toFixed(2), 0) } //检查是否运行完毕,以大圆为标准清空屏幕 complete() { return this.bR >= this.overR && this.op <= 0; } }
所有代码:
//canvas鼠标点击烟花特效 let endpos = (x, y) => { let angle = random(0, 360) * Math.PI / 180, value = random(20, 150), radius = [-1, 1][random(0, 1)] * value; return { x: x + radius * Math.cos(angle), y: y + radius * Math.sin(angle) } } let random = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min; class Circle { constructor(x, y) { this.r = random(9, 13) this.opos = {} this.x = this.opos.x = x this.y = this.opos.y = y this.colors = ['#FF1461', '#18FF92', '#5A87FF', '#FBF38C'] this.color = this.colors[random(0, this.colors.length)]; this.tpos = endpos(x, y) } creatCircle(ctx) { ctx.beginPath() ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI) ctx.closePath() ctx.fillStyle = this.color ctx.fill() } //根据不一样距离段设置前行的步伐,分为3个阶段,离出发点近的那一段为速度最快,中间为速度通常,最远为速度最慢 //区分目标点小于出发点的状况 //ratio为两点之间的距离的行走比例,比例数值越大,行走越慢 moveFun(start, end, current) { let s = random(26, 35), m = random(20, 25), f = random(15, 20), ff = start.x + ~~((end.x - start.x) / 3), mm = ff + ~~((end.x - start.x) / 3), ratio = end.x >= start.x ? (Math.max(current, ff) == current ? (Math.max(current, mm) == current ? s : m) : f) : (Math.max(current, ff) == current ? f : (Math.max(current, mm) == current ? m : s)), mp = { x: end.x - start.x, y: end.y - start.y }; return { x: Math.abs(mp.x / ratio), y: Math.abs(mp.y / ratio) } } //根据计算出来的移动值去移动 //若是目标坐标大于原始坐标则向右移动,最大不能超过目标坐标,反之向左移动最小不能小于目标坐标 move() { var movepos = this.moveFun(this.opos, this.tpos, this.x); this.x = (this.opos.x > this.tpos.x) ? Math.max(this.x - movepos.x, this.tpos.x) : Math.min(this.x + movepos.x, this.tpos.x) this.y = this.opos.y > this.tpos.y ? Math.max(this.y - movepos.y, this.tpos.y) : Math.min(this.y + movepos.y, this.tpos.y) this.r = Math.max(Math.abs((this.r - Math.random() / 1.2).toFixed(2)), 0) } } class BigCircle { constructor(x, y) { this.bR = random(16, 32) this.overR = random(60, 100) this.x = x this.y = y this.op = 1 } creatBigCircle(ctx) { ctx.beginPath() ctx.arc(this.x, this.y, this.bR, 0, 2 * Math.PI) ctx.closePath() ctx.strokeStyle = 'rgba(128, 128, 128, ' + this.op + ')' ctx.stroke() } changeR() { this.bR = Math.min(this.bR += random(1, 4), this.overR); this.op = Math.max((this.op - Math.random() / 12).toFixed(2), 0) } //检查是否运行完毕,以大圆为标准清空屏幕 complete() { return this.bR >= this.overR && this.op <= 0; } } window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; window.clearRequestTimeout = window.cancelAnimationFrame || window.mozCancelRequestAnimationFrame || window.webkitCancelRequestAnimationFrame || window.msCancelRequestAnimationFrame; let c = document.getElementById("fireworks"), w = c.width = c.offsetWidth, h = c.height = c.offsetHeight, ctx = c.getContext("2d"), nums = 40, circles = [], bCircle = null, animationId = false; let int = function(x, y) { circles = [] if (animationId) clearRequestTimeout(animationId) for (let i = nums; i-- > 0;) { circles.push(new Circle(x, y)) } bCircle = new BigCircle(x, y) creat() } let creat = function() { ctx.clearRect(0, 0, w, h); circles.forEach(function(v) { v.move(); v.creatCircle(ctx) }) bCircle.changeR() bCircle.creatBigCircle(ctx) animationId = requestAnimationFrame(creat) if (bCircle.complete()) { //以大圆为标准,清空屏幕中止动画 ctx.clearRect(0, 0, w, h); clearRequestTimeout(animationId) } } c.onclick = function(e) { e = e || window.event; int(e.clientX, e.clientY) }
html为
<canvas id="fireworks" width="500px" height="500px"></canvas>