当前位置: 移动技术网 > IT编程>网页制作>Html5 > 详解Canvas 实现炫丽的粒子运动效果(粒子生成文字)

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

2019年07月25日  | 移动技术网IT编程  | 我要评论

买来的皇后,中国雅虎网站,蕴奇支付

没有最好,只有更好,如题所示,这篇文章只要是分享一个用 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 库,主要是用到它里面的一些运动函数,让粒子动起来更感人一些,仅此而已。

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

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网