资讯专栏INFORMATION COLUMN

canvas 鼠标点击出现烟花效果

denson / 3682人阅读

摘要:主要想法鼠标点击事件触发圆点出现在鼠标处圆点大小,颜色,数量随机有一个小白圈做为轨迹不断改变半径变大变淡消失圆点向外移动,自身变小,消失。

主要想法:

鼠标点击事件触发圆点出现在鼠标处

圆点大小,颜色,数量随机

有一个小白圈做为轨迹,不断改变半径变大变淡消失

圆点向外移动,自身变小,消失。随着移动的距离远,移动速度跟着变

需要处理的是,从鼠标点击处可以确定圆点的x,y轴,但是动去哪,怎么动是一个问题,
所以先确定圆点的目标坐标,再从圆点的原始坐标(即鼠标点击处)向目标坐标移动,并且x,y 成比例 去移动才不会在x,y移动距离不相等的情况下 不平衡

随机生成圆点目标坐标的函数代码:

</>复制代码

  1. let endpos = (x, y) => {
  2. let angle = random(0, 360) * Math.PI / 180,
  3. value = random(20, 150),
  4. radius = [-1, 1][random(0, 1)] * value;
  5. return {
  6. x: x + radius * Math.cos(angle),
  7. y: y + radius * Math.sin(angle)
  8. }
  9. }

因为需要大量的随机数,所以封装一个生成随机数函数

</>复制代码

  1. let random = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;

圆点的移动函数,我们需要的移动是从原始坐标向目标坐标由快而慢。
生成比例分成3段。那就是原始坐标到第一段坐标为第一段,第一段坐标到第二段坐标为第二段,第二段坐标到目标坐标为第三段。如果当前坐标是处于第一段内,则速度越快,以此类推

现在两种情况,一种是目标坐标大于原始坐标,那么,第一段就为最接近原始坐标的那一段(Math.max(current, ff) == current ? (Math.max(current, mm) == current ? s : m) : f),第二种情况是目标坐标小于原始坐标,那么就反过来,第一段为离原始坐标最远的那一段。

</>复制代码

  1. //根据不同距离段设置前行的步伐,分为3个阶段,离出发点近的那一段为速度最快,中间为速度一般,最远为速度最慢
  2. //区分目标点小于出发点的情况
  3. //ratio为两点之间的距离的行走比例,比例数值越大,行走越慢
  4. moveFun(start, end, current) {
  5. let s = random(26, 35),
  6. m = random(20, 25),
  7. f = random(15, 20),
  8. ff = start.x + ~~((end.x - start.x) / 3),
  9. mm = ff + ~~((end.x - start.x) / 3),
  10. 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)),
  11. mp = {
  12. x: end.x - start.x,
  13. y: end.y - start.y
  14. };
  15. return {
  16. x: Math.abs(mp.x / ratio),
  17. y: Math.abs(mp.y / ratio)
  18. }
  19. }

每一个小圆点做为一个个体,自带移动函数,和移动的目标坐标。每次Animation时,圆点进行重绘更新最新的坐标

小圆点的代码:

</>复制代码

  1. class Circle {
  2. constructor(x, y) {
  3. this.r = random(9, 13)
  4. this.opos = {}
  5. this.x = this.opos.x = x
  6. this.y = this.opos.y = y
  7. this.colors = ["#FF1461", "#18FF92", "#5A87FF", "#FBF38C"]
  8. this.color = this.colors[random(0, this.colors.length)];
  9. this.tpos = endpos(x, y)
  10. }
  11. creatCircle(ctx) {
  12. ctx.beginPath()
  13. ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI)
  14. ctx.closePath()
  15. ctx.fillStyle = this.color
  16. ctx.fill()
  17. }
  18. //根据不同距离段设置前行的步伐,分为3个阶段,离出发点近的那一段为速度最快,中间为速度一般,最远为速度最慢
  19. //区分目标点小于出发点的情况
  20. //ratio为两点之间的距离的行走比例,比例数值越大,行走越慢
  21. moveFun(start, end, current) {
  22. let s = random(26, 35),
  23. m = random(20, 25),
  24. f = random(15, 20),
  25. ff = start.x + ~~((end.x - start.x) / 3),
  26. mm = ff + ~~((end.x - start.x) / 3),
  27. 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)),
  28. mp = {
  29. x: end.x - start.x,
  30. y: end.y - start.y
  31. };
  32. return {
  33. x: Math.abs(mp.x / ratio),
  34. y: Math.abs(mp.y / ratio)
  35. }
  36. }
  37. //根据计算出来的移动值去移动
  38. //如果目标坐标大于原始坐标则向右移动,最大不能超过目标坐标,反之向左移动最小不能小于目标坐标
  39. move() {
  40. var movepos = this.moveFun(this.opos, this.tpos, this.x);
  41. 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)
  42. 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)
  43. this.r = Math.max(Math.abs((this.r - Math.random() / 1.2).toFixed(2)), 0)
  44. }
  45. }

大圆则是从鼠标点击处创建的一个圆,并不断改变其半径向外扩展,并且越来越透明,当它已经很大并且很透明时意味着我们的动画要结束了,所以以这个为标准,停止动画,并且清空屏幕

大圆代码:

</>复制代码

  1. class BigCircle {
  2. constructor(x, y) {
  3. this.bR = random(16, 32)
  4. this.overR = random(60, 100)
  5. this.x = x
  6. this.y = y
  7. this.op = 1
  8. }
  9. creatBigCircle(ctx) {
  10. ctx.beginPath()
  11. ctx.arc(this.x, this.y, this.bR, 0, 2 * Math.PI)
  12. ctx.closePath()
  13. ctx.strokeStyle = "rgba(128, 128, 128, " + this.op + ")"
  14. ctx.stroke()
  15. }
  16. changeR() {
  17. this.bR = Math.min(this.bR += random(1, 4), this.overR);
  18. this.op = Math.max((this.op - Math.random() / 12).toFixed(2), 0)
  19. }
  20. //检查是否运行完毕,以大圆为标准清空屏幕
  21. complete() {
  22. return this.bR >= this.overR && this.op <= 0;
  23. }
  24. }

全部代码:

</>复制代码

  1. //canvas鼠标点击烟花特效
  2. let endpos = (x, y) => {
  3. let angle = random(0, 360) * Math.PI / 180,
  4. value = random(20, 150),
  5. radius = [-1, 1][random(0, 1)] * value;
  6. return {
  7. x: x + radius * Math.cos(angle),
  8. y: y + radius * Math.sin(angle)
  9. }
  10. }
  11. let random = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
  12. class Circle {
  13. constructor(x, y) {
  14. this.r = random(9, 13)
  15. this.opos = {}
  16. this.x = this.opos.x = x
  17. this.y = this.opos.y = y
  18. this.colors = ["#FF1461", "#18FF92", "#5A87FF", "#FBF38C"]
  19. this.color = this.colors[random(0, this.colors.length)];
  20. this.tpos = endpos(x, y)
  21. }
  22. creatCircle(ctx) {
  23. ctx.beginPath()
  24. ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI)
  25. ctx.closePath()
  26. ctx.fillStyle = this.color
  27. ctx.fill()
  28. }
  29. //根据不同距离段设置前行的步伐,分为3个阶段,离出发点近的那一段为速度最快,中间为速度一般,最远为速度最慢
  30. //区分目标点小于出发点的情况
  31. //ratio为两点之间的距离的行走比例,比例数值越大,行走越慢
  32. moveFun(start, end, current) {
  33. let s = random(26, 35),
  34. m = random(20, 25),
  35. f = random(15, 20),
  36. ff = start.x + ~~((end.x - start.x) / 3),
  37. mm = ff + ~~((end.x - start.x) / 3),
  38. 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)),
  39. mp = {
  40. x: end.x - start.x,
  41. y: end.y - start.y
  42. };
  43. return {
  44. x: Math.abs(mp.x / ratio),
  45. y: Math.abs(mp.y / ratio)
  46. }
  47. }
  48. //根据计算出来的移动值去移动
  49. //如果目标坐标大于原始坐标则向右移动,最大不能超过目标坐标,反之向左移动最小不能小于目标坐标
  50. move() {
  51. var movepos = this.moveFun(this.opos, this.tpos, this.x);
  52. 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)
  53. 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)
  54. this.r = Math.max(Math.abs((this.r - Math.random() / 1.2).toFixed(2)), 0)
  55. }
  56. }
  57. class BigCircle {
  58. constructor(x, y) {
  59. this.bR = random(16, 32)
  60. this.overR = random(60, 100)
  61. this.x = x
  62. this.y = y
  63. this.op = 1
  64. }
  65. creatBigCircle(ctx) {
  66. ctx.beginPath()
  67. ctx.arc(this.x, this.y, this.bR, 0, 2 * Math.PI)
  68. ctx.closePath()
  69. ctx.strokeStyle = "rgba(128, 128, 128, " + this.op + ")"
  70. ctx.stroke()
  71. }
  72. changeR() {
  73. this.bR = Math.min(this.bR += random(1, 4), this.overR);
  74. this.op = Math.max((this.op - Math.random() / 12).toFixed(2), 0)
  75. }
  76. //检查是否运行完毕,以大圆为标准清空屏幕
  77. complete() {
  78. return this.bR >= this.overR && this.op <= 0;
  79. }
  80. }
  81. window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
  82. window.clearRequestTimeout = window.cancelAnimationFrame || window.mozCancelRequestAnimationFrame || window.webkitCancelRequestAnimationFrame || window.msCancelRequestAnimationFrame;
  83. let c = document.getElementById("fireworks"),
  84. w = c.width = c.offsetWidth,
  85. h = c.height = c.offsetHeight,
  86. ctx = c.getContext("2d"),
  87. nums = 40,
  88. circles = [],
  89. bCircle = null,
  90. animationId = false;
  91. let int = function(x, y) {
  92. circles = []
  93. if (animationId) clearRequestTimeout(animationId)
  94. for (let i = nums; i-- > 0;) {
  95. circles.push(new Circle(x, y))
  96. }
  97. bCircle = new BigCircle(x, y)
  98. creat()
  99. }
  100. let creat = function() {
  101. ctx.clearRect(0, 0, w, h);
  102. circles.forEach(function(v) {
  103. v.move();
  104. v.creatCircle(ctx)
  105. })
  106. bCircle.changeR()
  107. bCircle.creatBigCircle(ctx)
  108. animationId = requestAnimationFrame(creat)
  109. if (bCircle.complete()) {
  110. //以大圆为标准,清空屏幕停止动画
  111. ctx.clearRect(0, 0, w, h);
  112. clearRequestTimeout(animationId)
  113. }
  114. }
  115. c.onclick = function(e) {
  116. e = e || window.event;
  117. int(e.clientX, e.clientY)
  118. }

html为

</>复制代码

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/83391.html

相关文章

  • canvas 简易时钟

    摘要:简易版时钟时钟清除画布,每次执行重新绘图,解决时钟模糊,边框有锯齿。 canvas 简易版时钟 showImg(https://segmentfault.com/img/bVDNx7?w=405&h=370); 时钟 *{ margin:0; padding:0; } bod...

    高胜山 评论0 收藏0
  • canvas 简易时钟

    摘要:简易版时钟时钟清除画布,每次执行重新绘图,解决时钟模糊,边框有锯齿。 canvas 简易版时钟 showImg(https://segmentfault.com/img/bVDNx7?w=405&h=370); 时钟 *{ margin:0; padding:0; } bod...

    sugarmo 评论0 收藏0
  • 每周一点canvas动画——《支付宝价格拖动选择》

    摘要:主要功能就是拖动标尺变动价格。整个标尺的长度为,步长为元。金额显示首先,增加一个输入框,然后获取它。输入金额移动标尺标尺的移动除了拖动以外,我们也希望通过金额输入框来达到。 效果源码 终于到年底了,再过两天我也要回家过年了,想想就激动呢!今天给大家带来一个基于移动端的canvas价格选择效果。 showImg(https://segmentfault.com/img/bV3QGd?w...

    snifes 评论0 收藏0

发表评论

0条评论

denson

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<