• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    详解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;
    }

    看了代码估计也只是心里炫了一下,也没有让你想把这个东西做出来的欲望,为此我知道必需得让你眼睛心服口服才行。在线 DEMO: 动感的粒子示例。

    人无完人,代码也一样。看起来运行顺畅的代码也或多或少有一些瑕疵,日前这个效果还只支持中文。英文的话,我得再努力一把,不管怎么样,英文后面肯定是会加入来的,只是时间问题了。还有代码中用于标记是否可再次执行生成文字的 属性:this.isPlay ,还是一点瑕疵,this.isPlay 的状态更改没有准确的在粒子归位的那一瞬间更改,而是提前更改了状态。但这个状态不会影响本例子效果的完整实现。

    这个例子中用到了 dynamics.js 库,主要是用到它里面的一些运动函数,让粒子动起来更感人一些,仅此而已。

    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

     
    上一篇:详解HTML5 canvas绘图基本使用方法
    下一篇:HTML5 Canvas图像模糊完美解决办法
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯

    时间:9:00-21:00 (节假日不休)

    地址:江苏信息产业基地11号楼四层

    《增值电信业务经营许可证》 苏B2-20120278

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