簡體   English   中英

在碰撞時開始畫布動畫,並使其停止

[英]start canvas animation on collision and ease it to stop

當我遇到一個問題時,我正在通過畫布學習動畫,借助於一個小jQuery,我在畫布上創建了兩個矩形。 一種來自畫布中心,另一種受我的鼠標位置控制。 id想要做的是當鼠標矩形碰到畫布上的第二個矩形時。 我希望第二個矩形沿被擊中的方向移動。

我一直遇到的問題是我希望動畫流暢且容易停止。 在代碼下面,Iv'e讓第二個方塊自動執行ID方式,就像動畫在被擊打時看起來一樣,但是當我將其放入if colliding為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();

這樣的事情怎么樣? 我添加了一些變量(oldMouseX,oldMouseY)來跟蹤鼠標的先前坐標,從而可以計算鼠標的方向。 然后,您的粒子還需要X和Y速度,以使其能夠在這兩個軸上移動……假設您願意。 如果您不希望Y發生變化,那就不要介意。 但看來您的平穩停止動作對我有用。 我不確定我是否完全了解您的需求。 我還添加了一個reset變量,以便在框再次分開之前不會繼續執行共謀。

<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>

輕松功能

您可以使用緩動功能,其中有許多功能。

緩動函數從0到1取一個值,並從0-1返回修改后的值。 輸入值和返回值之間的差異表示輕松程度。

下圖顯示了稍微復雜一些的緩動函數示例,該函數采用稱為p(或冪)的第二個值,該值修改了函數創建的曲線。 它顯示p的3個值,其中1產生線性響應。 Pow可以是0 <p <Infinity的任何值。x是輸入,y是輸出。

在此處輸入圖片說明

抱歉,手邊沒有圖像編輯器,因此示例中未使用該功能

基本多項式緩解函數

最基本的易用性函數(在任何用途中)是多項式(簡單地表示具有冪次函數的函數)。 輸入值被鉗位,輸出是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;

然后使用緩動函數獲得修改后的單位距離

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 = px; p.ly = py; if (p.moveTime > 0) { p.moveTime -= p.moveSpeed; var pos = particleMoveFunction(1 - p.moveTime, p.moveCurve); px = (p.dest.x - p.from.x) * pos + p.from.x; py = (p.dest.y - p.from.y) * pos + p.from.y; } px = px < p.size + edgeDist ? p.size + edgeDist : (px >= canvas.width - p.size - edgeDist ? canvas.width - p.size - edgeDist : px); py = py < p.size + edgeDist ? p.size + edgeDist : (py >= canvas.height - p.size - edgeDist ? canvas.height - p.size - edgeDist : py); var dist = Math.sqrt(Math.pow(mouse.x - px, 2) + Math.pow(mouse.y - py, 2)); if (dist < mouseSize + p.size) { var dx = (px - mouse.x) / dist; var dy = (py - mouse.y) / dist; px = mouse.x + dx * (mouseSize + p.size); // move to edge of mouse py = 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 = px + dx * rd + Math.random() * 5 - 2.5; p.dest.y = py + dy * rd + Math.random() * 5 - 2.5; p.from.x = px; p.from.y = py; p.moveTime = 1; } p.distMoved = Math.sqrt(Math.pow(p.lx - px, 2) + Math.pow(p.ly - py, 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(px, py); 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; mx = e.clientX - m.bounds.left; my = 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") { mw = e.wheelDelta; } else if (t === "DOMMouseScroll") { mw = -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 **/ 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM