【问题标题】:start canvas animation on collision and ease it to stop在碰撞时启动画布动画并使其停止
【发布时间】:2016-09-20 03:41:52
【问题描述】:

当我遇到一点问题时,我正在通过画布学习动画,借助一点 jQuery,我在画布上创建了两个矩形。一个起源于画布的中心,一个由我的鼠标位置控制。 id 喜欢做的是当鼠标矩形碰到画布上的第二个矩形时。我希望第二个矩形朝着它被击中的方向移动。

我一直遇到的问题是我希望动画流畅并轻松地停顿下来。在下面的代码中,我让第二个方块自动执行 id 的方式,就像动画一样,当它被击中时,但是当我把它放在 if colliding is true 语句中时,第二个方块只移动 5px。现在这对我来说很有意义,因为它只被告知如果盒子正在接触,它会移动 5 个像素,我什至尝试在 if 语句中运行一个 for 循环来运行一定次数,并在速度达到长度我希望它移动。然而,这只会导致第二个框跳到最后而不显示动画。很抱歉这个冗长的问题,但如果有人能指出我正确的方向或告诉我如何重构代码以使这项工作我很感激。

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
canvas.width = 1000;
canvas.height = 400;
var width = canvas.width;
var height = canvas.height;
var particles = [];

var mouseSize = 50;
var isColliding = false;
var mouseX;
var mouseY;

function particle() {
  var particle = {
    originX: width / 2,
    originY: height / 2,
    x: width / 2,
    y: height / 2,
    movement: 60, //overall movement wanted when collides
    velocity: 5,
    size: 30,
    draw: function() {
      ctx.fillStyle = "white";
      ctx.fillRect(this.x, this.y, this.size, this.size)


      this.x += this.velocity;
      this.velocity *= .98;

    }
  }
  return particle;
}

function createParticles() {

  particles.push(particle())
}

createParticles();

function draw() {
  ctx.clearRect(0, 0, width, height);

  //console.log(event.pageX+','+event.pageY)
  //mouse rect created here. did not create directly in mouseMove event because I could not
  //properly clear the canvas between each frame and keep all objects on screen.
  ctx.fillStyle = 'white';
  ctx.fillRect(mouseX, mouseY, mouseSize, mouseSize);

  particles[0].draw();

  requestAnimationFrame(draw);
}

$("#canvas").mousemove(function(event) {
  console.log(isColliding);
  mouseX = event.pageX;
  mouseY = event.pageY;

  //if objects are colliding set iscolliding to true, otherwise set it to false;
  if(event.pageX < particles[0].x + particles[0].size &&
    event.pageX + mouseSize > particles[0].x &&
    event.pageY < particles[0].y + particles[0].size &&
    mouseSize + event.pageY > particles[0].y) {
    isColliding = true;
    // console.log("collison detected");    
  } 
  else{isColliding=false;}

})

window.onload = draw();

【问题讨论】:

    标签: javascript animation html5-canvas collision-detection


    【解决方案1】:

    这样的事情怎么样?我添加了一些变量(oldMouseX、oldMouseY)来跟踪鼠标以前的坐标,这样就可以计算出它的方向。然后你的粒子还需要 X 和 Y 速度来允许它在这两个轴上移动......假设你想要那个。如果您不希望 Y 发生变化,那么请不要介意。但看起来你的平稳停止动作对我有用。我不确定我是否完全理解你想要什么。我还添加了一个重置​​变量,这样它就不会继续执行共谋,直到盒子再次分开。

    <html>
    <body style="background-color:black">
    <canvas id="canvas"></canvas>
    <script>
    var canvas = document.getElementById('canvas');
    var ctx = canvas.getContext('2d');
    canvas.width = 1000;
    canvas.height = 400;
    var width = canvas.width;
    var height = canvas.height;
    var particles = [];
    
    var mouseSize = 50;
    var isColliding = false;
    var mouseX;
    var mouseY;
    var oldMouseX;
    var oldMouseY;
    var reset = true;
    
    function particle() {
        var particle = {
        originX: width / 2,
        originY: height / 2,
        x: width / 2,
        y: height / 2,
        movement: 60,
        velocityX: 0,
        velocityY: 0,
        size: 30,
        draw: function() {
          ctx.fillStyle = "white";
          ctx.fillRect(this.x, this.y, this.size, this.size)
    
    
          this.x += this.velocityX;
          this.y += this.velocityY;
          this.velocityX *= .98;
          this.velocityY *= .98;
    
        }
      }
      return particle;
    }
    
    function createParticles() {
    
      particles.push(particle())
    }
    
    createParticles();
    
    function draw() {
      ctx.clearRect(0, 0, width, height);
    
      //console.log(event.pageX+','+event.pageY)
      //mouse rect created here. did not create directly in mouseMove event because i could not
      //properly clear the canvas between each frame and keep all objects on screen.
      ctx.fillStyle = 'white';
      ctx.fillRect(mouseX, mouseY, mouseSize, mouseSize);
    
      particles[0].draw();
    
      requestAnimationFrame(draw);
    }
    
    canvas.addEventListener('mousemove', function(event) {
      console.log(isColliding);
      oldMouseX = mouseX;
      oldMouseY = mouseY;
      mouseX = event.pageX;
      mouseY = event.pageY;
    
      //if objects are colliding set iscolliding to true, otherwise set it to false;
      if(event.pageX < particles[0].x + particles[0].size &&
        event.pageX + mouseSize > particles[0].x &&
        event.pageY < particles[0].y + particles[0].size &&
        mouseSize + event.pageY > particles[0].y) {
        isColliding = true;
        // console.log("collison detected");
      } 
      else{isColliding=false; reset=true;}
    
      if (isColliding && reset) {
        particles[0].velocityX = mouseX - oldMouseX;
        particles[0].velocityY = mouseY - oldMouseY;
        reset = false;
        }
    
    
    })
    
    window.onload = draw();
    </script>
    </body>
    </html>
    

    【讨论】:

    • 哦,对不起,我可能应该更具体。无论是否有任何碰撞,我要移动的方块当前都会自动移动并轻松停止。我正在寻找的是如何在检测到碰撞后使该动画完全实现,然后将添加额外的代码以考虑碰撞方向。
    • 它会自动移动,因为你在创建粒子时给了它 5 的速度。如果您希望它在共谋之后发生,那么只需在检测到共谋后将其速度设置为 5。顺便说一句,我编辑了我的答案以实现重置变量并修复方向,所以现在更自然了。
    • 这样一个简单的解决方案可以解决一个让我非常费劲的问题。我现在可以吻你了,哈哈。谢谢。
    • 没问题,我喜欢在画布上制作这样的东西。
    【解决方案2】:

    简易功能

    您可以使用缓动功能,其中有很多。

    Ease 函数取一个从 0 到 1 的值,并返回一个从 0-1 修改的值。输入值与返回值之差代表轻松。

    下图显示了一个稍微复杂一些的缓动函数示例,它采用称为 p(或幂)的第二个值来修改函数创建的曲线。它显示了 p 的 3 个值,其中值为 1 会产生线性响应。 Pow 可以是 0

    对不起,手头没有图像编辑器,所以函数不是示例中使用的那个

    基本多项式缓动函数

    最基本的缓动函数(任何用途)是多项式(仅表示具有某种幂的函数)。输入值是钳位的,输出是 p 次方的值。

    function easeInOut(v,p){  // p < 1 is rush out ease in , 
                              //  p > 1 is ease out rush in
                              // p = 1 and the function is linear
        v = v < 0 ? 0 : v > 1 ? 1 : v;  // clamp input also clamps output
        return Math.pow(v,p); // return value
    }
    

    使用缓动功能

    要使用缓动功能,您需要两个位置来缓动对象之间的位置

    var fromX = 10;
    var fromY = 10;
    var toX = 100;
    var toY = 100;
    

    然后得到它们之间的区别

    var dx = toX - fromX;
    var dy = toY - fromY;
    

    然后使用ease函数得到一个修改后的单位距离

    var uDist = 0.5; // half way along
    var uDistE = easeInOut(moveD,0.2);
    
    // the position eased
    var x = fromX + dx * uDistE;
    var y = fromY + dy * uDistE;
    

    随着时间的推移逐渐缓解

    随着时间的推移,您可以将值 uDist 从开头的 0 更改为结尾的 1。如果您希望这是一个固定时间,则记录开始时间和移动时间并将其标准化。

    var startTime = performance.now(); // do this when you start movement
    
    
    const timeToMove = 2000; // time to move in ms (2000 = 2 seconds)
    
    var uDist = (performance.now()-startTime) / timeToMove; // ease function clamps 
                                                            // values so no need
                                                            // to worry about being 
                                                           // out of range
    

    使用缓动功能的演示

    这个例子展示了使用缓动函数来移动粒子。在演示中,有 4 种运动类型,每种运动具有不同的缓动功能。较小的圆圈易于冲出,大颗粒则易于冲入。

    一半的代码是样板,与答案相关的东西在顶部。

    最佳浏览整页。

    /** SimpleFullCanvasMouse.js begin **/
    // Note set window.onResize (note capital R) to get the debounced resize call
    // ie onResize = function(){ // blah blah };
    // Note. mouse, canvas, ctx are set for you. Mouse is a simple mouse with
    // x,y,w (wheel) alt,shift,ctrl, and buttonRaw as a bit field bits 0,1,2 for buttons left, mid, right
    // ie if(mouse.buttonRaw & 1) { //is left button down (othe buttons may be down also
    // ie if(mouse.buttonRaw === 1) { //is only left button down
    // set debounced resize event function to put new particles on canvas
    onResize = function(){ particles.length = 0; };
    const pSize = 5; // particle size (radius)
    var particles = []; // array of things
    const mouseSize = 30; // Size of mouse (radius)
    const mouseCol = "red"; // guess what this holds???
    const runDist = 300; // How far a particl will move when touched
    const moveSpeed = 0.01; // speed to move (will move 1 unit dist so 1 / MoveSpeed == number frames to move)
    const edgeDist = 10; // distance particles will stay away from the canvas edge.
    const MAX_PARTICLES = 200; // the max number oof you know whats
    
    // ease function return unit value clamped from 0-1 based on the input value clamped 0-1
    // most functions take a second argument that is a curve modifier. Usualy the power
    const EASE_FUNC = {
        linear : function (val) {
            val = val < 0 ? 0 : val > 1 ? 1 : val;
            return val;
        },
        easePow : function (val, pow) { // for pow > 1 this is ease out rush in
            // for pow > 0 & pow < 1 this is rush out ease in
            val = val < 0 ? 0 : val > 1 ? 1 : val;
            return Math.pow(val, pow);
        },
    }
    const particleMoveFunction = EASE_FUNC.easePow; // ease function to use
    
    
    // creates a particle at x,y
    function createParticle(x, y) {
        var typesC = "#F80,#8F0,#0D0,#00F".split(",");
        var curves = [0.2,0.5,1.2,2];
        var sizes = [1,1.2,1.6,2.2];
        var type = Math.floor(4-Math.sqrt(Math.random() * 16)); // more small fast
        
        return {
            x : x,
            y : y,
            lx : x,
            ly : y,
            dest : {
                x : x,
                y : y,
            },
            from : {
                x : x,
                y : y,
            },
            size : pSize * sizes[type],
            col : typesC[type] ,
            moveTime : 0,
            moveSpeed : moveSpeed,
            moveCurve : curves[type],
        }
    }
    
    // Move particles.
    // checks for mouse interaction
    // checks for edge distance
    // moves particles is particle moveTime > 0
    function updateAllParticles() {
        var i;
        for (i = 0; i < particles.length; i++) {
            var p = particles[i];
            p.lx = p.x;
            p.ly = p.y;
            if (p.moveTime > 0) {
                p.moveTime -= p.moveSpeed;
                var pos = particleMoveFunction(1 - p.moveTime, p.moveCurve);
                p.x = (p.dest.x - p.from.x) * pos + p.from.x;
                p.y = (p.dest.y - p.from.y) * pos + p.from.y;
            }
            p.x = p.x < p.size + edgeDist ? p.size + edgeDist : (p.x >= canvas.width - p.size - edgeDist ? canvas.width - p.size - edgeDist : p.x);
            p.y = p.y < p.size + edgeDist ? p.size + edgeDist : (p.y >= canvas.height - p.size - edgeDist ? canvas.height - p.size - edgeDist : p.y);
    
            var dist = Math.sqrt(Math.pow(mouse.x - p.x, 2) + Math.pow(mouse.y - p.y, 2));
            if (dist < mouseSize + p.size) {
                var dx = (p.x - mouse.x) / dist;
                var dy = (p.y - mouse.y) / dist;
                p.x = mouse.x + dx * (mouseSize + p.size); // move to edge of mouse
                p.y = mouse.y + dy * (mouseSize + p.size);
                // random is to stop them grouping to much
                var rd = runDist / 2 + Math.random() * runDist / 2;
                p.dest.x = p.x + dx * rd + Math.random() * 5 - 2.5;
                p.dest.y = p.y + dy * rd + Math.random() * 5 - 2.5;
                p.from.x = p.x;
                p.from.y = p.y;
                p.moveTime = 1;
            }
            p.distMoved = Math.sqrt(Math.pow(p.lx - p.x, 2) + Math.pow(p.ly - p.y, 2));
        }
    }
    
    // draw particles
    function drawAllParticles() {
        var i;
        ctx.lineCap = "round";
        for (i = 0; i < particles.length; i++) {
            var p = particles[i];
            ctx.strokeStyle = p.col;
            ctx.lineWidth = p.size * 2;
            ctx.beginPath();
            ctx.moveTo(p.lx, p.ly);
            ctx.lineTo(p.x, p.y);
            ctx.stroke();
        }
    }
    
    // draws the mouse object
    function drawMouse() {
        ctx.fillStyle = mouseCol;
        ctx.beginPath();
        ctx.moveTo(mouse.x + mouseSize, mouse.y);
        ctx.arc(mouse.x, mouse.y, mouseSize, 0, Math.PI * 2);
        ctx.fill();
    }
    
    // Main display function
    function display() {
        ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
        ctx.globalAlpha = 0.33; // fade background
        ctx.fillStyle = "white";
        ctx.fillRect(0, 0, w, h);
    
    
        ctx.globalAlpha = 1; // reset alpha
        if (particles.length < MAX_PARTICLES) {
            particles.push(createParticle(
                    Math.random() * (canvas.width - pSize * 2) + pSize,
                    Math.random() * (canvas.height - pSize * 2) + pSize));
        }
        updateAllParticles();
        drawAllParticles();
        drawMouse();
    }
    
    //==================================================================================================
    // Boilerplate code not part of answer
    //
    //
    // The following code is support code that provides me with a standard interface to various forums.
    // It provides a mouse interface, a full screen canvas, and some global often used variable
    // like canvas, ctx, mouse, w, h (width and height), globalTime
    // This code is not intended to be part of the answer unless specified and has been formated to reduce
    // display size. It should not be used as an example of how to write a canvas interface.
    // By Blindman67
    if (typeof onResize === "undefined") {
        window["onResize"] = undefined; // create without the JS parser knowing it exists.
        // this allows for it to be declared in an outside
        // modal.
    }
    const RESIZE_DEBOUNCE_TIME = 100;
    var w, h, cw, ch, canvas, ctx, mouse, createCanvas, resizeCanvas, setGlobals, globalTime = 0, resizeCount = 0;
    createCanvas = function () {
        var c,
        cs;
        cs = (c = document.createElement("canvas")).style;
        cs.position = "absolute";
        cs.top = cs.left = "0px";
        cs.zIndex = 1000;
        document.body.appendChild(c);
        return c;
    }
    resizeCanvas = function () {
        if (canvas === undefined) {
            canvas = createCanvas();
        }
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
        ctx = canvas.getContext("2d");
        if (typeof setGlobals === "function") {
            setGlobals();
        }
        if (typeof onResize === "function") {
            resizeCount += 1;
            setTimeout(debounceResize, RESIZE_DEBOUNCE_TIME);
        }
    }
    function debounceResize() {
        resizeCount -= 1;
        if (resizeCount <= 0) {
            onResize();
        }
    }
    setGlobals = function () {
        cw = (w = canvas.width) / 2;
        ch = (h = canvas.height) / 2;
        mouse.updateBounds();
    }
    mouse = (function () {
        function preventDefault(e) {
            e.preventDefault();
        }
        var mouse = {
            x : 0,
            y : 0,
            w : 0,
            alt : false,
            shift : false,
            ctrl : false,
            buttonRaw : 0,
            over : false,
            bm : [1, 2, 4, 6, 5, 3],
            active : false,
            bounds : null,
            mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",")
        };
        var m = mouse;
        function mouseMove(e) {
            var t = e.type;
            m.x = e.clientX - m.bounds.left;
            m.y = e.clientY - m.bounds.top;
            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 (m.callbacks) {
                m.callbacks.forEach(c => c(e));
            }
            e.preventDefault();
        }
        m.updateBounds = function () {
            if (m.active) {
                m.bounds = m.element.getBoundingClientRect();
            }
        }
        m.addCallback = function (callback) {
            if (typeof callback === "function") {
                if (m.callbacks === undefined) {
                    m.callbacks = [callback];
                } else {
                    m.callbacks.push(callback);
                }
            } else {
                throw new TypeError("mouse.addCallback argument must be a function");
            }
        }
        m.start = function (element, blockContextMenu) {
            if (m.element !== undefined) {
                m.removeMouse();
            }
            m.element = element === undefined ? document : element;
            m.blockContextMenu = blockContextMenu === undefined ? false : blockContextMenu;
            m.mouseEvents.forEach(n => {
                m.element.addEventListener(n, mouseMove);
            });
            if (m.blockContextMenu === true) {
                m.element.addEventListener("contextmenu", preventDefault, false);
            }
            m.active = true;
            m.updateBounds();
        }
        m.remove = function () {
            if (m.element !== undefined) {
                m.mouseEvents.forEach(n => {
                    m.element.removeEventListener(n, mouseMove);
                });
                if (m.contextMenuBlocked === true) {
                    m.element.removeEventListener("contextmenu", preventDefault);
                }
                m.element = m.callbacks = m.contextMenuBlocked = undefined;
                m.active = false;
            }
        }
        return mouse;
    })();
    resizeCanvas();
    mouse.start(canvas, true);
    window.addEventListener("resize", resizeCanvas);
    function update(timer) { // Main update loop
        globalTime = timer;
        display(); // call demo code
        requestAnimationFrame(update);
    }
    requestAnimationFrame(update);
    /** SimpleFullCanvasMouse.js end **/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-09-04
      • 2017-11-07
      • 2013-04-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多