mousemove事件无法像Javascript中预期的那样工作

mousemove event not working like expected in Javascript

本文关键字:工作 事件 Javascript mousemove      更新时间:2023-09-26

下面有一些代码,用于开始我正在使用HTML5画布制作的蛇游戏。出于某种原因,我暂时用来代表我的蛇的红圈不断地沿着鼠标移动的路径绘制。它以食物为起点。在浏览器中查看它,因为它真的很难描述。我只想让圆圈跟随鼠标,留下一条小轨迹,结束后不会停留在画布上。我该怎么做呢。提前感谢!

<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>Snake 2.0</title>
</head>
<style>
</style>
<body>
    <div>
        <canvas id="canvas" width=500 height=500></canvas>
    </div>
<script type="text/javascript">
    var canvas = document.getElementById("canvas");
    var context = canvas.getContext("2d");
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    makeFood();
    function makeFood() {
        foods = [];
        for (var i = 0; i < 1; i++){
            foods.push(new Food());
        }
    }
    function Food() {
        this.x = Math.random() * canvas.width;
        this.y = Math.random() * canvas.height;
        this.radius = 10;
    }
    function drawFood() {
        for (var i = 0; i < 1; i++){
            foods.push(new Food());
        }

        for (var i = 0; i < foods.length; i++){     
            var f = foods[i];
            context.beginPath();
            var grd = context.createRadialGradient(f.x, f.y, (f.radius - (f.radius - 1)), f.x + 1, f.y + 1, (f.radius));
            grd.addColorStop(0, 'red');
            grd.addColorStop(1, 'blue');
            context.arc(f.x, f.y, f.radius, 0, 2 * Math.PI, true);
            context.fillStyle = grd;
            context.fill();
        }
    }
    function makePower() {
        powers = [];
        for (var i = 0; i < 1; i++){
            powers.push(new Power());
        }
    }   
    function Power() {
        this.x = Math.random() * canvas.width;
        this.y = Math.random() * canvas.height;
        this.radius = 8;
    }
    function drawPower() {

        for (var i = 0; i < powers.length; i++){        
            var p = powers[i];
            context.beginPath();
            var grd = context.createRadialGradient(p.x, p.y, (p.radius - (p.radius - 1)), p.x + 1, p.y + 1, (p.radius));
            grd.addColorStop(0, 'green');
            grd.addColorStop(1, 'yellow');
            context.arc(p.x, p.y, p.radius, 0, 2 * Math.PI, true);
            context.fillStyle = grd;
            context.fill();

        }
    }
    canvas.addEventListener("mousemove", function(event) {
        move(event);
        }); 
    function move(e) {
        context.fillStyle = "black";
        context.fillRect(0, 0, canvas.width, canvas.height);    
        var a = e.clientX;
        var b = e.clientY;
        context.arc(a, b, 20, 0, 2 * Math.PI, true);
        context.fillStyle = "red";
        context.fill();
    }

    context.fillStyle = "black";
    context.fillRect(0, 0, canvas.width, canvas.height);    
    var functions = [drawFood];

    var timer = setInterval(function(){
                drawFood();
            }, 5000);

    function stop() {
        clearInterval(timer);
    }

    canvas.addEventListener("click", stop);
    //timer = setInterval(start, 1000);
    //timer = setInterval(start, 5000);
</script>
</body>
</html>

您可以在编辑器的第102-103行"context.arc(a,b,20,0,2*Math.PI,true);"之前的"move"函数中添加"context.beginPath();"。

function move(e) {
    context.fillStyle = "black";
    context.fillRect(0, 0, canvas.width, canvas.height);
    var a = e.clientX;
    var b = e.clientY;
    context.beginPath();
    context.arc(a, b, 20, 0, 2 * Math.PI, true);
    context.fillStyle = "red";
    context.fill();
}

这是小提琴:http://jsfiddle.net/sd5hh57b/1/

您应该将移动的位置存储在数组中。然后,一个新的计时器应该重新访问这些光盘,并在每次滴答作响时将其重新绘制成更褪色的颜色,直到光盘变黑。然后应该将其从该数组中删除。

这是小提琴。

代码的更改从canvas.addEventListener("mousemove",...开始,如下所示:

canvas.addEventListener("mousemove", function(event) {
    // Replaced move function by drawDisc function, 
    // which needs coordinates and color intensity
    drawDisc(event.clientX, event.clientY, 0xF);
}); 
// Array to keep track of previous positions, i.e. the trail
var trail = [];
function drawDisc(x, y, red) {
    context.beginPath();
    context.arc(x, y, 20, 0, 2 * Math.PI, true);
    context.fillStyle = '#' + red.toString(16) + '00000';
    context.fill();
    // If disc is not completely faded out, push it in the trail list
    if (red) {
        trail.push({x: x, y: y, red: red});
    }
}
// New function to regularly redraw the trail
function fadeTrail() {
    var discs = trail.length;
    // If there is only one disc in the trail, leave it as-is, 
    // it represents the current position.
    if (discs > 1) {
        for (var i = discs; i; i--) {
           // take "oldest" disc out of the array:
           disc = trail.shift();
           // and draw it with a more faded color, unless it is
           // the current disc, which keeps its color
           drawDisc(disc.x, disc.y, disc.red - (i === 1 ? 0 : 1));
        }
    }
}
// New timer to fade the trail
var timerFade = setInterval(function(){
    fadeTrail();
}, 10);

我认为这些评论将清楚地表明这一点。请注意,光盘的颜色从0xF00000到0xE00000、0xD00000、…、,0x000000。除当前光盘外,该光盘始终保持其0xF00000颜色。

其他答案是正确的:

  • 在每个新的arc()使用beginPath()来创建新的路径,而避免context.fill()将整个路径视为单个路径
  • 使用轨迹阵列存储您最后绘制轨迹的位置

但是,应避免使用setTimeoutsetInterval(甚至避免使用多个)
现代浏览器确实支持requestAnimationFrame计时方法,对于older(基本上是IE9),您可以很容易地找到polyfill。它有很多优点,我不会在这里列举,请阅读文档。

这是您的代码的修改版本,它使用了requestAnimationFrame循环。我还创建了两个屏幕外画布来更新foodspowers,这样它们就不会在每次绘制时消失。两者都将在绘制功能中绘制。

我更改了鼠标移动处理程序,使其只更新轨迹数组,将绘图部分留在绘图循环中。在每次调用时,它都会设置一个moving标志,让我们的draw函数知道我们正在移动鼠标。否则,它将开始从阵列中删除旧的轨迹弧。

    var canvas = document.getElementById("canvas");
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    var context = canvas.getContext("2d");
     // create other contexts (layer like) for your food and powers
    var foodContext = canvas.cloneNode(true).getContext('2d');
    var pwrContext = canvas.cloneNode(true).getContext('2d');
     // a global to tell weither we are moving or not
    var moving;
     // a global to store our animation requests and to allow us to pause it
    var raf;
     // an array to store our trail position
    var trail = [];
     // here we can determine how much of the last position we'll keep at max (can then be updated if we ate some food)
    var trailLength = 10;
     // your array for the foods
    var foods = [];
     // a global to store the last time we drawn the food, no more setInterval
    var lastDrawnFood = 0;
     // start the game
    draw();
    function makeFood() {
      foods.push(new Food());
    }
    function Food() {
      this.x = Math.random() * canvas.width;
      this.y = Math.random() * canvas.height;
      this.radius = 10;
    }
    function drawFood() {
        // clear the food Canvas (this could be done only if we ate some, avoiding the loop through all our foods at each call of this method)
        foodContext.clearRect(0, 0, canvas.width, canvas.height);
        foods.push(new Food());
        for (var i = 0; i < foods.length; i++) {
          var f = foods[i];
          // draw on the food context
          foodContext.beginPath();
          foodContext.arc(f.x, f.y, f.radius, 0, 2 * Math.PI, true);
          var foodGrd = foodContext.createRadialGradient(f.x, f.y, (f.radius - (f.radius - 1)), f.x + 1, f.y + 1, (f.radius));
          foodGrd.addColorStop(0, 'red');
          foodGrd.addColorStop(1, 'blue');
          foodContext.fillStyle = foodGrd;
          foodContext.fill();
        }
      }
      // I'll let you update this one
    function makePower() {
      powers = [];
      for (var i = 0; i < 1; i++) {
        powers.push(new Power());
      }
    }
    function Power() {
      this.x = Math.random() * canvas.width;
      this.y = Math.random() * canvas.height;
      this.radius = 8;
    }
    function drawPower() {
        pwrContext.clearRect(0, 0, canvas.width, canvas.height);
        for (var i = 0; i < powers.length; i++) {
          var p = powers[i];
          var pwrGrd = pwrContext.createRadialGradient(p.x, p.y, (p.radius - (p.radius - 1)), p.x + 1, p.y + 1, (p.radius));
          pwrGrd.addColorStop(0, 'green');
          pwrGrd.addColorStop(1, 'yellow');
          pwrContext.beginPath();
          pwrContext.arc(p.x, p.y, p.radius, 0, 2 * Math.PI, true);
          pwrContext.fillStyle = pwrGrd;
          pwrContext.fill();
        }
      }
      // the event object is already passed, no need for an anonymous function here
    canvas.addEventListener("mousemove", move);
    function move(e) {
      // we paused the game, don't update our position
      if (!raf) return;
      // update the snake
      var a = e.clientX - canvas.offsetLeft;
      var b = e.clientY - canvas.offsetTop;
      trail.splice(0, 0, {
        x: a,
        y: b
      });
      // tell our draw function that we moved
      moving = true;
    }
    function draw(time) {
      // our food timer
      if (time - lastDrawnFood > 5000) {
        lastDrawnFood = time;
        drawFood();
      }
      // clear the canvas
      context.fillStyle = "black";
      context.fillRect(0, 0, canvas.width, canvas.height);
      // draw the food
      context.drawImage(foodContext.canvas, 0, 0);
      // draw the power
      context.drawImage(pwrContext.canvas, 0, 0);
      //draw the snake
      for (var i = 0; i < trail.length; i++) {
        // decrease the opacity
        opacity = 1 - (i / trail.length);
        context.fillStyle = "rgba(255, 0,0," + opacity + ")";
        // don't forget to create a new Path for each circle
        context.beginPath();
        context.arc(trail[i].x, trail[i].y, 20, 0, 2 * Math.PI, true);
        context.fill();
      }
      // if we're not moving or if our trail is too long
      if ((!moving || trail.length > trailLength) && trail.length > 1)
      // remove the oldest trail circle
        trail.pop();
      // we're not moving anymore
      moving = false;
      // update the animation request
      raf = requestAnimationFrame(draw);
    }
    context.fillStyle = "black";
    context.fillRect(0, 0, canvas.width, canvas.height);
    function toggleStop() {
      if (!raf) {
        // restart the animation
        raf = window.requestAnimationFrame(draw);
      } else {
        // cancel the next call
        cancelAnimationFrame(raf);
        raf = 0;
      }
    }
    canvas.addEventListener("click", toggleStop);
html, body{margin:0;}
<canvas id="canvas" width=500 height=500></canvas>