javascript - HTML5 canvas 绘制一个带有明胶效果的矩形

标签 javascript css html canvas rectangles

我正在用 javascript 创建一个平台游戏,其中玩家是矩形,他们可以跳上和跳下平台。这很简单,但现在我试图为玩家添加“明胶效果”,以便当他们降落在平台上时,他们会移动一点(如明胶)。我一直在寻找很长一段时间,但我似乎找不到任何关于如何改变矩形形状的好例子。

我想出的只是在css中使用@keyframes,并且我已经尝试实现它,如果我在html元素上使用它就可以了。这是因为使用 DOM 元素,我可以使用 .style 访问 CSSStyleDeclaration,但如果我创建了一个新的播放器对象,我就不能(如下面的代码所示)。

我不确定如何将 css @keyframes 转换为 javascript,或者我正在做的事情是不可能的……或者是否有其他(更好的)方法可以实现我的目标?

var keystate = [];
var players = [];

var jelly = document.getElementById('jelly');
console.log(jelly.style); // shows the CSSStyleDeclarations

document.body.addEventListener("keydown", function(e) {
  keystate[e.keyCode] = true;
});

document.body.addEventListener("keyup", function(e) {
  keystate[e.keyCode] = false;
});

function gelatine(e) {
  if (e.style.webkitAnimationName !== 'gelatine') {
    e.style.webkitAnimationName = 'gelatine';
    e.style.webkitAnimationDuration = '0.5s';
    e.style.display = 'inline-block';

    setTimeout(function() {
      e.style.webkitAnimationName = '';
    }, 1000);
  }
}

var canvas = document.getElementById("canvas");
var context = canvas.getContext('2d');
canvas.width = 500;
canvas.height = 300;

function Player(x, y, width, height) {
  this.x = x;
  this.y = y;
  this.width = width;
  this.height = height;
  this.jumping = false;
  this.velocityY = 0;
  this.gravity = 0.3;
  this.speed = 5;
  this.timer = 0;
  this.delay = 120;
}

Player.prototype.render = function render() {
  context.clearRect(0, 0, canvas.width, canvas.height);
  context.fillStyle = 'blue';
  context.fillRect(this.x, this.y, this.width, this.height);
  context.fillStyle = 'black';
  context.font = '20pt sans-serif';
  context.fillText("I'm not jelly:(", this.x - 160, this.y + 30);
};

Player.prototype.update = function update() {
  // arrow up key to jump with the player
  if (keystate[38]) {
    if (!this.jumping) {
      this.jumping = true;
      this.velocityY = -this.speed * 2;
    }
  }

  this.velocityY += this.gravity;
  this.y += this.velocityY;

  if (this.y >= canvas.height - this.height) {
    this.y = canvas.height - this.height;
    this.jumping = false;
  }

  if (this.timer === 0) {
    gelatine(jelly);
    this.timer = this.delay;
  }
  if (this.timer > 0 && this.timer <= this.delay) {
    this.timer--;
  }
};

players.push(new Player((canvas.width / 2) - 25, (canvas.height / 2), 50, 50));

console.log(players[0].style); // no CSSStyleDeclarations :(
function render() {
  for (var i = 0; i < players.length; i++) {
    players[i].render();
  }
}

function update() {
  for (var i = 0; i < players.length; i++) {
    players[i].update();
  }
}

function tick() {
  update();
  render();

  requestAnimationFrame(tick);
}
tick();
<html>

<head>
  <style>
    #jelly {
      position: absolute;
      margin-left: auto;
      margin-right: auto;
      top: 80px;
      bottom: 0;
      left: 0;
      right: 0;
      display: inline-block;
      width: 100px;
      height: 100px;
      background: blue;
    }
    p {
      font-size: 20px;
      color: white;
    }
    canvas {
      border: 1px solid #000;
      position: absolute;
      margin: auto;
      top: 20px;
      bottom: 0;
      left: 0;
      right: 0;
    }
    @keyframes gelatine {
      25% {
        -webkit-transform: scale(0.9, 1.1);
        transform: scale(0.9, 1.1);
      }
      50% {
        -webkit-transform: scale(1.1, 0.9);
        transform: scale(1.1, 0.9);
      }
      75% {
        -webkit-transform: scale(0.95, 1.05);
        transform: scale(0.95, 1.05);
      }
    }
  </style>
  <title>Jelly rectangle</title>
</head>

<body>
  <div id="jelly">
    <p>&nbsp&nbsp i'm jelly :)</p>
  </div>
  <canvas id='canvas'></canvas>

</body>

</html>

最佳答案

果冻。

为了创建在游戏中使用的果冻效果,我们可以利用基于步骤的开放式效果(即效果长度取决于环境并且没有固定时间)

果冻和大多数液体具有不可压缩的特性。这意味着无论对它们施加什么力,体积都不会改变。对于 2D 游戏,这意味着对于果冻,该区域不会改变。这意味着我们可以压扁高度并知道面积计算宽度。

因此,当物体掉落并撞击地面时,我们可以在高度方向上对物体施加挤压力。模拟阻尼 Spring ,我们可以产生非常逼真的果冻效果。

定义果冻

我有点傻,因为这是季节,所以变量 wobbla 和 bouncy 用 wobbla 定义了阻尼 Spring 。是 Spring 的刚度,从 0 到 1,其中 0.1 非常软,1 非常硬。 flex 是从 0 到 1 的 Spring 阻尼,其中 0 具有 100% 阻尼,而 1 没有阻尼。

我还添加了react这是施加在 Spring 上的力的简单乘数。由于这是一款游戏,因此需要夸大效果。 react使施加在 Spring 上的力成倍增加。小于 1 的值会降低效果,大于 1 的值会增加效果。
hrhd (是的坏名字)是真实高度(显示)和高度增量(每帧高度的变化)。这两个变量控制 Spring 效果。

有一个标志表明果冻在地上,sticky如果为 true,则标记会使果冻粘在地上。

还有控制果冻的三个功能

function createJellyBox(image,x, y, wobbla, bouncy, react){
    return {
        img:image,
        x : x,                 // position
        y : y,
        dx : 0,                // movement deltas
        dy : 0, 
        w : image.width,       // width 
        h : image.height,      // height
        area : image.width * image.height,  // area 
        hr : image.height,     // squashed height chaser
        hd : 0,                // squashed height delta
        wobbla : wobbla,       // higher values make it wobble more
        bouncy : bouncy,       // higher values make it react to change in speed
        react : react,         // additional reaction multiplier. < 1 reduces reaction > 1 increases reaction
        onGround : false,      // true if on the ground
        sticky : true,         // true to stick to the ground. false to let it bounce
        squashed : 1,          // the amount of squashing or stretching along the height
        force : 0,             // the force applied to the jelly when it hits the ground
        draw : drawJelly,      // draw function
        update : updateJelly,  // update function
        reset : resetJelly,    // reset function
    }
}

功能

控制果冻,重置,更新和绘制三个功能。 对不起 答案超过了 30000 个字符的限制,因此请在下面的演示中找到函数。

抽奖

绘制只是绘制果冻。它使用压扁值来计算高度并从中计算宽度,然后简单地绘制具有设置宽度和高度的图像。图像在其中心绘制以使计算更简单。

重置

只需在 x 定义的位置重置果冻和 y您可以通过设置 jelly.hr = jelly.h 对其进行修改以消除任何摆动和 jelly.hd = 0
更新

这就是辛苦的地方。它通过将重力添加到增量 Y 来更新对象位置。它检查果冻是否已经撞到地面,如果有,它会将力施加到 jelly.hr通过添加到 jelly.hd (高度增量)力等于撞击时间的速度 jelly.react
我发现施加的力应该随着时间的推移而增加,因此随着时间的推移施加挤压力时会有一点点(标有注释)。这可以去除,但它只会降低果冻摇晃效果的平滑度。

最后一件事是重新计算高度并移动果冻,使其不会进入地面。

我敢肯定,像泥饼一样清澈。因此,如果需要澄清,请随时提出问题。

演示

如果没有演示,任何好的 SO 答案都会是什么。所以这是一个果冻模拟。单击 Canvas 以放下果冻。左上角的三个 slider 控制值 Wobbla、Bouncy 和 React。使用这些值来改变果冻效果。复选框打开和关闭粘性,但您需要一个长屏幕才能获得反弹所需的影响。删除 cludge 代码以查看粘性的真实用途。

因为我需要一个 UI,所以有很多额外的代码不适用于答案。答案代码清楚地标记,以便于查找。主循环在底部。我还没有在 FF 上测试过它,但它应该可以工作。如果不让我知道,我会修复它。

var STOP = false; // stops the app
var jellyApp = function(){

    /** Compiled by GROOVER Quick run 9:43pm DEC-22-2015 **/   
    /** fullScreenCanvas.js begin **/
    var canvas = (function(){
        var canvas = document.getElementById("canv");
        if(canvas !== null){
            document.body.removeChild(canvas);
        }
        // creates a blank image with 2d context
        canvas = document.createElement("canvas"); 
        canvas.id = "canv";    
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight; 
        canvas.style.position = "absolute";
        canvas.style.top = "0px";
        canvas.style.left = "0px";
        canvas.style.zIndex = 1000;
        canvas.ctx = canvas.getContext("2d"); 
        document.body.appendChild(canvas);
        return canvas;
    })();
    var ctx = canvas.ctx;
    /** fullScreenCanvas.js end **/
    /** MouseFull.js begin **/
    var canvasMouseCallBack = undefined;  // if needed
    var mouse = (function(){
        var mouse = {
            x : 0, y : 0, w : 0, alt : false, shift : false, ctrl : false,
            interfaceId : 0, buttonLastRaw : 0,  buttonRaw : 0,
            over : false,  // mouse is over the element
            bm : [1, 2, 4, 6, 5, 3], // masks for setting and clearing button raw bits;
            getInterfaceId : function () { return this.interfaceId++; }, // For UI functions
            startMouse:undefined,
        };
        function mouseMove(e) {
            var t = e.type, m = mouse;
            m.x = e.offsetX; m.y = e.offsetY;
            if (m.x === undefined) { m.x = e.clientX; m.y = e.clientY; }
            m.alt = e.altKey;m.shift = e.shiftKey;m.ctrl = e.ctrlKey;
            if (t === "mousedown") { m.buttonRaw |= m.bm[e.which-1];
            } else if (t === "mouseup") { m.buttonRaw &= m.bm[e.which + 2];
            } else if (t === "mouseout") { m.buttonRaw = 0; m.over = false;
            } else if (t === "mouseover") { m.over = true;
            } else if (t === "mousewheel") { m.w = e.wheelDelta;
            } else if (t === "DOMMouseScroll") { m.w = -e.detail;}
            if (canvasMouseCallBack) { canvasMouseCallBack(m.x, m.y); }
            e.preventDefault();
        }
        function startMouse(element){
            if(element === undefined){
                element = document;
            }
            "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",").forEach(
            function(n){element.addEventListener(n, mouseMove);});
        }
        mouse.mouseStart = startMouse;
        return mouse;
    })();
    if(typeof canvas === "undefined"){
        mouse.mouseStart(canvas);
    }else{
        mouse.mouseStart();
    }
    /** MouseFull.js end **/





    // unit size for rendering scale
    var ep = canvas.height / 72;
    // font constants
    const FONT = "px Arial Black";
    const FONT_SIZE = Math.ceil(3 * ep);

    const GRAVITY = ep * (1/5);
    const GROUND_AT = canvas.height - canvas.height * (1/20);


    // Answer code.
    //-----------------------------------------------------------------------        // draw the jelly
    function drawJelly(ctx){
        var w,h;
        w = this.w,
        h = this.h;
        h *= this.squashed;
        // the width is ajusted so that the area of the rectagle remains constant
        w = this.area / h; // to keep the area constant
        ctx.drawImage(this.img,this.x - w / 2, this.y - h / 2, w, h);
    }
    function resetJelly(x,y){  // reset the jelly position 
        this.x = x;
        this.y = y;
        this.onGround = false;
        this.dx = 0;
        this.dy = 0;
    }
    // do the jelly math
    function updateJelly(){
        var h;                          // temp height
        var hitG = false;               // flag that the ground has just been hit
        h = this.h * this.squashed;     // get the height from squashed
        if(!this.onGround){             // if not on the ground add grav
            this.dy += GRAVITY; 
        }else{                          // if on the ground move it so it touches correctly
            this.dy = 0;                
            this.y = GROUND_AT - h / 2;
        }
        // update the position
        this.x += this.dx;
        this.y += this.dy;

        // check if it has hit the ground
        if(this.y + h / 2 >= GROUND_AT && this.dy >= 0){
            this.hd += this.dy * this.react;  // add the hit speed to the height delta
                                              // multiply with react to inhance or reduce effect
            this.force += this.dy * this.react;
            hitG = true;
            this.onGround = true;             // flag that jelly is on the ground
        }
        if(this.force > 0){
            this.hd += this.force;
            this.force *= this.wobbla;
        }
        this.hd += (this.h - this.hr) * this.wobbla;  // add wobbla to delta height
        this.hd *= this.bouncy;                       // reduce bounce
        this.hr += this.hd;                           // set the real height
        this.squashed = this.h / this.hr;             // calculate the new squashed amount
        h = this.h * this.squashed;                   // recalculate hieght to make sure
                                                      // the jelly does not overlap the ground
        // do the finnal position ajustment to avoid overlapping the ground                                              
        if(this.y + h / 2 >= GROUND_AT || hitG || (this.sticky && this.onGround)){
            this.y = GROUND_AT - h / 2;
        }
    }

    // create a jelly box
    function createJellyBox(image,x, y, wobbla, bouncy, react){
        return {
            img:image,
            x : x,                 // position
            y : y,
            dx : 0,                // movement deltas
            dy : 0, 
            w : image.width,       // width 
            h : image.height,      // height
            area : image.width * image.height,  // area 
            hr : image.height,     // squashed height chaser
            hd : 0,                // squashed height delta
            wobbla : wobbla,       // higher values make it wobble more
            bouncy : bouncy,       // higher values make it react to change in speed
            react : react,         // additional reaction multiplier. < 1 reduces reaction > 1 increases reaction
            onGround : false,      // true if on the ground
            sticky : true,         // true to stick to the groun. false to let it bounce
            squashed : 1,          // the amount of squashing or streaching along the height
            force : 0,
            draw : drawJelly,      // draw function
            update : updateJelly,  // update function
            reset : resetJelly,    // reset function
        }
    }

    // --------------------------------------------------------------------------------------------
    // END OF ANSWER CODE.
    // FIND the usage at the bottom inside the main loop function.
    // --------------------------------------------------------------------------------------------




    // The following code is just helpers and UI stuff and are not related to the answer.

    var createImage = function (w, h ){
        var image = document.createElement("canvas");
        image.width = w;
        image.height = h;
        image.ctx = image.getContext("2d");
        return image;
    }

    var drawSky = function (img, col1, col2){
        var c, w, h;
        c = img.ctx;
        w = img.width;
        h = img.height;
        var g = c.createRadialGradient(w * (1 / 2), h * (3 / 2), h * (1 / 2) +w * (1 / 4), w * (1 / 2), h * (3 / 2), h * (3 / 2) + w * ( 1 / 4));
        g.addColorStop(0,col1);
        g.addColorStop(1,col2);
        c.fillStyle = g;
        c.fillRect(0, 0, w, h);
        return img;
    }
    var drawGround = function (img, col1, col2,lineColour, lineWidth) {
        var c, w, h, lw;
        c = img.ctx;
        w = img.width;
        h = img.height;
        lw = lineWidth;
        var g = c.createLinearGradient(0, 0, 0, h + lineWidth);
        g.addColorStop(0, col1);
        g.addColorStop(1, col2);
        c.fillStyle = g;
        c.lineWidth = lw;
        c.strokeStyle = lineColour;
        c.strokeRect(-lw * 2, lw / 2, w + lw * 4, h + lw * 4);
        c.fillRect(-lw * 2, lw / 2, w + lw * 4, h + lw * 4);
        return img;
    }
    /** CanvasUI.js begin **/
    var drawRoundedBox = function (img, colour, rounding, lineColour, lineWidth) {
        var c, x, y, w, h, r, p
        p = Math.PI/2; // 90 deg
        c = img.ctx;
        w = img.width;
        h = img.height;
        lw = lineWidth;
        r = rounding ;
        c.lineWidth = lineWidth;
        c.fillStyle = colour;
        c.strokeStyle = lineColour;
        c.beginPath();
        c.arc(w - r - lw / 2, h - r - lw / 2, r, 0, p);
        c.lineTo(r + lw / 2, h - lw / 2);
        c.arc(r + lw / 2, h - r - lw / 2, r, p, p * 2);
        c.lineTo(lw / 2, h - r - lw / 2);
        c.arc(r + lw / 2, r + lw / 2, r, p * 2, p * 3);
        c.lineTo(w-r - lw / 2,   lw / 2);
        c.arc(w - r - lw / 2, r + lw / 2, r, p * 3, p * 4);
        c.closePath();
        c.stroke();
        c.fill();
        return img;
    }
    var drawTick = function (img , col, lineColour, lineWidth){
        var c, w, h, lw, m, l;
        m = function (x, y) {c.moveTo(lw / 2 + w * x, lw / 2 + h * y);};
        l = function (x, y) {c.lineTo(lw / 2 + w * x, lw / 2 + h * y);};
        lw = lineWidth;
        c = img.ctx;
        w = img.width - lw;
        h = img.height - lw;
        c.fillStyle = col;
        c.strokeStyle = lineColour;
        c.lineWidth = lw;
        c.beginPath();
        m(1, 0);
        l(5 / 8, 1);
        l(0, 3 / 4);
        l(1 / 4, 2 / 4);
        l(2 / 4, 3 / 4);
        l(1, 0);
        c.stroke();
        c.fill();
        return img;
    }
    var setFont = function(ctx,font,align){
        ctx.font = font;
        ctx.textAlign = align;
    }
    var measureText = function(ctx,text){
        return ctx.measureText(text).width;
    }
    var drawText = function(ctx,text,x,y,col,col1){
        var of;
        of = Math.floor(FONT_SIZE/10);
        ctx.fillStyle = col1;
        ctx.fillText(text,x+of,y+of);
        ctx.fillStyle = col;
        ctx.fillText(text,x,y);
    }
    var drawSlider = function(ctx){
        var x,y;
        x = this.owner.x;
        y = this.owner.y;
        ctx.drawImage(this.image, this.x + x, this.y + y);
        ctx.drawImage(this.nob, this.nx + x, this.ny + y);
    }
    var updateSlider = function(mouse){
        var mx, my;
        mx = mouse.x - this.owner.x;
        my = mouse.y - this.owner.y;
        this.cursor = "";
        
        if(this.owner.dragging === -1 || this.owner.dragging === this.id ){
            if(mx >= this.x && mx < this.x + this.w &&
                my >= this.y && my <= this.y + this.h){
                this.mouseOver = true;       
                this.cursor = "pointer"
            }else{
                this.mouseOver = false;
            }
            if(mx >= this.nx && mx < this.nx + this.nw &&
                my >= this.ny && my <= this.ny + this.nh){
                this.mouseOverNob = true;       
                this.cursor = "ew-resize"
            }else{
                this.mouseOverNob = false;
            }
            if((mouse.buttonRaw&1) === 1 && (this.mouseOver||this.mouseOverNob) && !this.dragging){
                this.owner.dragging = this.id;
                this.dragging = true;
                this.cursor = "ew-resize"
            }else
            if(this.dragging){
                this.cursor = "ew-resize"
                if((mouse.buttonRaw & 1)=== 0){
                    this.dragging = false;
                    this.owner.dragging = -1;
                    this.cursor = "pointer";
                }
                var p = mx- (this.x+this.nw/2);
                p /= (this.w-this.nw);
                p *= this.range;
                p += this.min;
                this.value = Math.min(this.max, Math.max(this.min, p));
            }
            if(this.mouseOver || this.mouseOverNob || this.dragging){
                this.owner.toolTip = this.toolTip.replace("##",this.value.toFixed(this.decimals));
            }

        }

        this.nx = (this.value - this.min) / this.range * (this.w - this.nw) + this.x;
    }
    var createSlider = function(image,nobImage, x, y, value, min, max, toolTip)   {
        var decimals = 0;
        if(toolTip.indexOf("#.") > -1){
            if(toolTip.indexOf(".DDD") > -1){
                decimals = 3;    
                toolTip = toolTip.replace("#.DDD","##");
            }else
            if(toolTip.indexOf(".DD") > -1){
                decimals = 2;    
                toolTip = toolTip.replace("#.DD","##");
            }else
            if(toolTip.indexOf(".D") > -1){
                decimals = 1;    
                toolTip = toolTip.replace("#.D","##");
            }else{
                toolTip = toolTip.replace("#.","##");
            }
        }
        return {
            id : undefined,
            image : image,
            nob : nobImage,
            min : min,
            max : max,
            x : x,
            y : y + (nobImage.height - image.height)/2,
            ny : y  ,
            nx : ((value - min) / (max - min)) * (image.width - nobImage.width) + x,
            range : max - min,
            w : image.width,
            h : image.height,
            nw : nobImage.width,
            nh : nobImage.height,
            value : value,
            maxH : Math.max( image.height, nobImage.height),
            mouseOver : false,
            mouseOverNob : false,
            toolTip:toolTip,
            decimals:decimals,
            dragging : false,
            update : updateSlider,
            draw : drawSlider,
            position : function (x, y){
                this.x += x;
                this.y += y;
                this.nx += x;
                this.ny += y;
            },
        }
    }
    var drawTickCont = function(ctx){
        var x,y, ofx, ofy;
        x = this.owner.x;
        y = this.owner.y;
        ctx.drawImage(this.image, this.x + x, this.y + y);
        ofy = this.h / 2 - this.textImage.height / 2;
        ofx = this.w / 2;
        ctx.drawImage(this.textImage, this.x + x + this.w + ofx, this.y + y + ofy);    
        if(this.value){
            x -= this.tickImage.width * ( 1/ 4);
            y -= this.tickImage.height * ( 2/  5);
            ctx.drawImage(this.tickImage, this.x + x, this.y + y);
        }
    }
    var updateTick = function(mouse){
        var mx, my;
        mx = mouse.x - this.owner.x;
        my = mouse.y - this.owner.y;
        this.cursor = "";
        if(this.owner.dragging === -1 || this.owner.dragging === this.id ){
            if(mx >= this.x && mx < this.x + this.w &&
                my >= this.y && my <= this.y + this.h){
                this.mouseOver = true;       
                this.cursor = "pointer"
            }else{
                this.mouseOver = false;
            }
            if((mouse.buttonRaw&1) === 1 && this.mouseOver && !this.dragging){
                this.owner.dragging = this.id;
                this.dragging = true;
            }else
            if(this.dragging){
                if((mouse.buttonRaw & 1)=== 0){
                    if(this.mouseOver){
                        this.value = ! this.value;
                    }
                    this.dragging = false;
                    this.owner.dragging = -1;
                    this.cursor = "pointer";
                }
            }
            if(this.mouseOver || this.dragging){
                this.owner.toolTip = this.toolTip;
            }        
        }
    }
    var createTick= function(image,tickImage,textImage, x, y, value, toolTip)   {
        return {
            id : undefined,
            image : image,
            tickImage : tickImage,
            textImage : textImage,
            x : x,
            y : y,
            w : image.width,
            h : image.height,
            value : value,
            maxH : Math.max( image.height, tickImage.height),
            mouseOver : false,
            mouseOverNob : false,
            toolTip:toolTip,
            dragging : false,
            update : updateTick,
            draw : drawTickCont,
            position : function (x, y){
                this.x += x;
                this.y += y;
            },
        }
    }
    function UI(ctx, mouse, x, y){
        this.dragging = -1;
        var ids = 0;
        var controls = [];
        var length = 0;
        this.x = x;
        this.y = y;
        var posX = 0;
        var posY = 0;
        this.addControl = function (control, name) {
            control.id = ids ++;
            control.owner = this;
            control.position(posX, posY);
            posY += control.maxH + ep;
            length = controls.push(control)
            this[name] = control;
        }
        this.update = function(){
            var i, cursor, c;
            cursor = "";
            this.toolTip = "";
            for(i = 0; i < length; i ++){
                c = controls[i];
                c.update(mouse);
                if(c.cursor !== ""){
                    cursor = c.cursor;
                }
                c.draw(ctx);
            }
            if(cursor === ""){
                ctx.canvas.style.cursor = "default";
            }else{
                ctx.canvas.style.cursor = cursor;
            }
            if(this.toolTip !== ""){
                if(mouse.y - FONT_SIZE * (5 / 3) < 0){
                    drawText(ctx, this.toolTip, mouse.x, mouse.y + FONT_SIZE * (4 / 3), "#FD4", "#000");
                }else{
                    drawText(ctx, this.toolTip, mouse.x, mouse.y - FONT_SIZE * (2 / 3), "#FD4", "#000");
                }
            }
        }
    }
    /** CanvasUI.js end **/

    //-----------------------------
    // create UI
    //-----------------------------

    setFont(ctx, FONT_SIZE + FONT, "left");
    // images for UI
    var tickBox = drawRoundedBox(createImage(4 * ep, 4 * ep), "#666", (3 / 2) * ep, "#000", (1 / 2) * ep);
    var tickBoxTick = drawTick(createImage(6 * ep, 6 * ep), "#0D0", "#000", (1 / 2) * ep);
    var w = measureText(ctx, "Sticky");
    var tickBoxText = createImage(w, FONT_SIZE);
    setFont(tickBoxText.ctx, FONT_SIZE + FONT, "left");
    drawText(tickBoxText.ctx, "Sticky", 0, FONT_SIZE * (3 / 4), "white", "black");

    var sliderBar = drawRoundedBox(createImage(20 * ep, 2 * ep), "#666", ep * 0.9, "#000", (1 / 2) * ep);
    var sliderNob = drawRoundedBox(createImage(3 * ep, 4 * ep), "#AAA", ep, "#000", (1 / 2) * ep);
    // UI control
    var controls = new UI(ctx, mouse, 10, 10);
    controls.addControl(createSlider(sliderBar, sliderNob, 0, 0, 0.3, 0, 1, "Wobbla #.DD"), "wobbla");
    controls.addControl(createSlider(sliderBar, sliderNob, 0, 0, 0.8, 0, 1, "Bouncy #.DD"), "bouncy");
    controls.addControl(createSlider(sliderBar, sliderNob, 0, 0, 1.5, 0, 2, "React #.DD"), "react");
    controls.addControl(createTick(tickBox, tickBoxTick, tickBoxText, 0, 0, true, "Activate / Deactivate sticky option."), "sticky");
    //-----------------------------
    // create playfield 
    //-----------------------------
    var skyImage = drawSky(createImage(32, 32), "#9EF", "#48D");
    var groundImage = drawGround(createImage(100, canvas.height - GROUND_AT), "#5F5", "#5A5", "#181", 4);

    //-----------------------------
    // create jelly 
    //-----------------------------
    var jellyImage = drawRoundedBox(createImage(ep * 20, ep * 20), "#FA4", ep * 2, "black", ep * (3 / 5));
    var jelly = createJellyBox(
            jellyImage,
            canvas.width / 2,
            100,
            0.1,
            0.8,
            1.2
    );

    // some more settings
    ctx.imageSmoothingEnabled = true;
    setFont(ctx, FONT_SIZE + FONT, "left");

    //-----------------------------
    // Main animtion loop
    //-----------------------------
    function updateAnim(){
        // draw the background and ground
        ctx.drawImage(skyImage, 0, 0, canvas.width, canvas.height)
        ctx.drawImage(groundImage, 0, GROUND_AT, canvas.width, canvas.height - GROUND_AT)
        // update and draw jelly
        jelly.update();
        jelly.draw(ctx);
        // update and draw controls
        controls.update();
        // update jelly setting from controls.
        jelly.wobbla = controls.wobbla.value;
        jelly.bouncy = controls.bouncy.value;
        jelly.react = controls.react.value;
        jelly.sticky = controls.sticky.value;

        // if the mouse is not busy then use left mouse click and drag to position jellu
        if(controls.dragging < 0 && mouse.buttonRaw === 1){
            controls.dragging = -2;
            jelly.reset(mouse.x,mouse.y);
        }else
        if(controls.dragging === -2){   // release mouse
            controls.dragging = -1;
        }
        if(!STOP){
            requestAnimationFrame(updateAnim);
        }else{
          STOP = false;
        }
    }
    updateAnim();
};
function resizeEvent(){
    var waitForStopped = function(){
        if(!STOP){  // wait for stop to return to false
            jellyApp();
            return;
        }
        setTimeout(waitForStopped,200);
    }
    STOP = true;
    setTimeout(waitForStopped,100);
}
window.addEventListener("resize",resizeEvent);
jellyApp();

关于javascript - HTML5 canvas 绘制一个带有明胶效果的矩形,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34399243/

相关文章:

javascript - 验证 angularjs 中的路由

javascript - 如何将 div View 的动画滚动到特定的 x 和 y 坐标

javascript - 突出显示菜单项及其子菜单项

javascript - 如何使用 uib 分页检查和取消检查当前页面中的所有行?

php - 选择多个单选按钮(通过 jquery、javascript?)

javascript - 如何在滚动页面每200px时发出警报;

css - 单击链接后我的悬停已停用

css - 虚拟化 : Change background color after click on v-card

javascript - jQuery - Div 跟随 touchmove/mousemove + autoalign

html - 使用 flex 方向列时如何将 div 水平居中对齐