详解Canvas 实现炫丽的粒子运动效果(粒子生成文字)分享!

没有最好,只有更好,如题所示,这篇文章只要是分享一个用 Canvas 来实现的粒子运动效果。感觉有点标题党了,但换个角度,勉勉强强算是炫丽吧,虽然色彩上与炫丽无关,但运动效果上还是算得上有点点炫的。不管怎么样,我们还是开始这个所谓的炫丽效果吧!

直接上代码 ,不懂可以看代码注释。估计就会看明白大概的思路了。

html 代码

  <!DOCTYPE html>  <html lang="en">  <head>  <meta charset="UTF-8">  <title>Canvas 实现炫丽的粒子运动效果-云库前端</title>  <style>  * {      margin: 0;      padding: 0;  }  html,  body {      width: 100%;      height: 100%;  }  canvas {      display: block;      background: #000;  }  body::-webkit-scrollbar{      display: none;  }  .operator-box{      position: fixed;      top: 0;      left: 50%;      border: 1px solid #fff;      background: rgba(255,255,255,0.5);      padding: 20px 10px;      -webkit-transform: translateX(-50%);      transform: translateX(-50%);  }  .back-type,.back-animate{      margin-right: 20px;  }  .flex-box{      display: flex;      justify-content: center;      align-items: center;  }  #input-text{      line-height: 35px;      width: 260px;      height: 35px;      background: rgba(0, 0, 0,0.7);      color: #fff;      font-size: 16px;      border: none;      outline: none;      text-indent: 12px;      box-shadow: inset 0 0 12px 1px rgba(0,0,0,0.7);  }  #input-text::placeholder{      color: #ccc;      line-height: 55px;      height: 55px;  }  select{      -webkit-appearance: none;      -moz-appearance: none;      appearance: none;      border: none;      padding: 0px 20px 0px 6px;      height: 35px;      color: #fff;      text-align: left;      background: rgba(0, 0, 0,0.7) url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAICAYAAAAx8TU7AAAAOUlEQ…R4gPgWEIMAiOYBCS4C8ZDAIrBq4gigNkztQEFMi6AuQHESAPMeXiEMiWfpAAAAAElFTkSuQmCC) no-repeat 190px 12px;      background-size: 5px 8px;      box-shadow: inset 0 0 12px 1px rgba(0,0,0,0.7);  }  </style>  </head>  <body>  <div class="operator-box">  <div class="flex-box">      <div class="back-type">散开类型:          <select name="" id="selectType">              <option value="back">归位</option>              <option value="auto">随机</option>          </select>      </div>      <div class="back-animate">散开效果(对归位有效):         <select class="back-dynamics" id="selectDynamics">             <option value="spring">dynamics.spring</option>             <option value="bounce">dynamics.bounce</option>             <option value="forceWithGravity">dynamics.forceWithGravity</option>             <option value="gravity">dynamics.gravity</option>             <option value="easeInOut">dynamics.easeInOut</option>             <option value="easeIn">dynamics.easeIn</option>             <option value="easeOut">dynamics.easeOut</option>             <option value="linear">dynamics.linear</option>         </select>      </div>      <div class="input-box"><input type="text" placeholder="输入汉字后回车" id="input-text"></div>  </div>  </div>  <script src="dynamics.min.js"></script>  <script src="index.js"></script>  <script>  var iCircle = new Circle();  </script>  </body>  </html>  

HTML 代码不多,只要是几个操作元素。这里一看就明白。不费过多口舌。我们来看看本文的主角 JavaScript 代码,不过,在看代码前,我们不妨先听听实现这个效果的思路:

  1. 首先,我们得先生成一堆群众演员(粒子);
  2. 把每个粒子的相关参数挂到自身的一些属性上,因为第个粒子都会有自己的运动轨迹;
  3. 接着得让它们各自运动起来。运动有两种(自由运动和生成文字的运动);

JavaScript 代码中使用了三个 Canvas 画布,this.iCanvas(主场)、this.iCanvasCalculate(用来计算文字宽度)、this.iCanvasPixel(用于画出文字,并从中得到文字对应的像素点的位置坐标)。

this.iCanvasCalculate 和 this.iCanvasPixel 这两个无需在页面中显示出来,只是辅助作用。

下面就献上棒棒的 JS 实现代码

  function Circle() {      var This = this;      this.init();      this.generalRandomParam();      this.drawCircles();      this.ballAnimate();      this.getUserText();      // 窗口改变大小后,生计算并获取画面      window.onresize = function(){          This.stateW = document.body.offsetWidth;          This.stateH = document.body.offsetHeight;          This.iCanvasW = This.iCanvas.width = This.stateW;          This.iCanvasH = This.iCanvas.height = This.stateH;          This.ctx = This.iCanvas.getContext("2d");      }  }  // 初始化  Circle.prototype.init = function(){      //父元素宽高      this.stateW = document.body.offsetWidth;      this.stateH = document.body.offsetHeight;      this.iCanvas = document.createElement("canvas");      // 设置Canvas 与父元素同宽高      this.iCanvasW = this.iCanvas.width = this.stateW;      this.iCanvasH = this.iCanvas.height = this.stateH;      // 获取 2d 绘画环境      this.ctx = this.iCanvas.getContext("2d");      // 插入到 body 元素中      document.body.appendChild(this.iCanvas);      this.iCanvasCalculate = document.createElement("canvas");      // 用于保存计算文字宽度的画布      this.mCtx =  this.iCanvasCalculate.getContext("2d");      this.mCtx.font = "128px 微软雅黑";      this.iCanvasPixel = document.createElement("canvas");      this.iCanvasPixel.setAttribute("style","position:absolute;top:0;left:0;");      this.pCtx = null; // 用于绘画文字的画布      // 随机生成圆的数量      this.ballNumber = ramdomNumber(1000, 2000);      // 保存所有小球的数组      this.balls = [];      // 保存动画中最后一个停止运动的小球      this.animte = null;      this.imageData = null;      this.textWidth = 0; // 保存生成文字的宽度      this.textHeight = 150; // 保存生成文字的高度      this.inputText = ""; // 保存用户输入的内容      this.actionCount = 0;      this.ballActor = []; // 保存生成文字的粒子      this.actorNumber = 0; // 保存生成文字的粒子数量      this.backType = "back"; // 归位      this.backDynamics = ""; // 动画效果      this.isPlay = false; // 标识(在生成文字过程中,不能再生成)  }  // 渲染出所有圆  Circle.prototype.drawCircles = function () {      for(var i=0;i<this.ballNumber;i++){          this.renderBall(this.balls[0]);      }  }  // 获取用户输入文字  Circle.prototype.getUserText = function(){      This = this; // 保存 this 指向      ipu = document.getElementById("input-text");      ipu.addEventListener("keydown",function(event){          if(event.which === 13){ // 如果是回车键              ipu.value = ipu.value.trim(); // 去头尾空格              var pat = /[u4e00-u9fa5]/; // 中文判断              var isChinese = pat.test(ipu.value);              if(ipu.value.length !=0 && isChinese){                  This.inputText = ipu.value;              }else{                  alert("请输入汉字");                  return;              }              if(This.isPlay){                  return              }              This.getAnimateType();              This.getTextPixel();              This.isPlay = true;          }      });  }  // 计算文字的宽  Circle.prototype.calculateTextWidth = function () {      this.textWidth = this.mCtx.measureText(this.inputText).width;  }  // 获取文字像素点  Circle.prototype.getTextPixel = function () {      if(this.pCtx){          this.pCtx.clearRect(0,0,this.textWidth,this.textHeight);      }      this.calculateTextWidth(this.inputText);      this.iCanvasPixel.width = this.textWidth;      this.iCanvasPixel.height = this.textHeight;      this.pCtx =  this.iCanvasPixel.getContext("2d");      this.pCtx.font = "128px 微软雅黑";      this.pCtx.fillStyle = "#FF0000";      this.pCtx.textBaseline = "botom";      this.pCtx.fillText(this.inputText,0,110);      this.imageData = this.pCtx.getImageData(0,0,this.textWidth,this.textHeight).data;      this.getTextPixelPosition(this.textWidth,this.textHeight);  }  // 获取文字粒子像素点位置  Circle.prototype.getTextPixelPosition = function (width,height) {      var left = (this.iCanvasW - width)/2;      var top = (this.iCanvasH - height)/2;      var space = 4;      this.actionCount = 0;      for(var i=0;i<this.textHeight;i+=space){          for(var j=0;j<this.textWidth;j+=space){              var index = j*space+i*this.textWidth*4;              if(this.imageData[index] == 255){                  if(this.actionCount<this.ballNumber){                      this.balls[this.actionCount].status = 1;                      this.balls[this.actionCount].targetX = left+j;                      this.balls[this.actionCount].targetY = top+i;                      this.balls[this.actionCount].backX = this.balls[this.actionCount].x;                      this.balls[this.actionCount].backY = this.balls[this.actionCount].y;                      this.ballActor.push(this.balls[this.actionCount]);                      this.actionCount++;                  }              }          }          this.actorNumber = this.ballActor.length;      }      this.animateToText();  }  // 粒子运动到指定位置  Circle.prototype.animateToText = function(){      for(var i=0;i<This.actorNumber;i++){          dynamics.animate(This.ballActor[i], {            x: this.ballActor[i].targetX,            y: this.ballActor[i].targetY          },{              type: dynamics.easeIn,              duration: 1024,          });      }      setTimeout(function(){          This.ballbackType();      },3000);  }  // 粒子原路返回  Circle.prototype.ballBackPosition = function(){      for(var i=0;i<This.actorNumber;i++){          var ball = This.ballActor[i];          dynamics.animate(ball, {            x: ball.backX,            y: ball.backY          },{              type: dynamics[this.backDynamics],              duration: 991,              complete:this.changeStatus(ball)          });      }  }  // 获取类型|动画效果  Circle.prototype.getAnimateType = function() {      var selectType = document.getElementById("selectType");      var selectDynamics = document.getElementById("selectDynamics");      this.backType = selectType.options[selectType.options.selectedIndex].value;      this.backDynamics = selectDynamics.options[selectDynamics.options.selectedIndex].value;  }  // 复位散开  Circle.prototype.ballbackType = function(){      if(this.backType == "back"){          this.ballBackPosition();      }else{          this.ballAutoPosition();      }      this.ballActor = [];  }  // 随机散开  Circle.prototype.ballAutoPosition = function(ball){      for(var i=0;i<this.actorNumber;i++){          this.changeStatus(this.ballActor[i])      }  }  // 更改小球状态  Circle.prototype.changeStatus = function(ball){      ball.status = 0;      if(this.isPlay == true){          this.isPlay = false;      }  }  // 随机生成每个圆的相关参数  Circle.prototype.generalRandomParam = function(){      for(var i=0;i<this.ballNumber;i++){          var ball = {};          ball.size = 1; // 随机生成圆半径          // 随机生成圆心 x 坐标          ball.x = ramdomNumber(0+ball.size, this.iCanvasW-ball.size);          ball.y = ramdomNumber(0+ball.size, this.iCanvasH-ball.size);          ball.speedX = ramdomNumber(-1, 1);          ball.speedY = ramdomNumber(-1, 1);          this.balls.push(ball);          ball.status = 0;          ball.targetX = 0;          ball.targetY = 0;          ball.backX = 0;          ball.backY = 0;      }  }  // 改变圆的位置  Circle.prototype.changeposition = function(){      for(var i=0;i<this.ballNumber;i++){          if( this.balls[i].status == 0){              this.balls[i].x += this.balls[i].speedX;              this.balls[i].y += this.balls[i].speedY;          }      }  }  // 画圆  Circle.prototype.renderBall = function(ball){      this.ctx.fillStyle = "#fff";      this.ctx.beginPath(); // 这个一定要加      this.ctx.arc(ball.x, ball.y, ball.size, 0, 2 * Math.PI);      this.ctx.closePath(); // 这个一定要加      this.ctx.fill();  }  // 小球碰撞判断  Circle.prototype.collision = function(ball){      for(var i=0;i<this.ballNumber;i++){         if(ball.x>this.iCanvasW-ball.size || ball.x<ball.size){              if(ball.x>this.iCanvasW-ball.size){                  ball.x = this.iCanvasW-ball.size;              }else{                  ball.x = ball.size;              }              ball.speedX = - ball.speedX;         }         if(ball.y>this.iCanvasH-ball.size || ball.y<ball.size){              if(ball.y>this.iCanvasH-ball.size){                  ball.y = this.iCanvasH-ball.size;              }else{                  ball.y = ball.size;              }              ball.speedY = - ball.speedY;         }      }  }  // 开始动画  Circle.prototype.ballAnimate = function(){      var This = this;      var animateFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;      (function move(){          animte = animateFrame(move);          This.ctx.clearRect(0, 0, This.iCanvasW, This.iCanvasH);          This.changeposition();          for(var i=0;i<This.ballNumber;i++){             This.collision(This.balls[i]);             This.renderBall(This.balls[i]);          }      })();  }  // 生成一个随机数  function ramdomNumber(min, max) {      return Math.random() * (max - min) + min;  }

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/htm5ctutorials/472244.html

(0)
上一篇 2020年10月26日
下一篇 2020年10月26日

精彩推荐