使用requestAnimationFrame导致vue堆栈超出的问题

报错信息:
[Vue warn]: Error in mounted hook: "RangeError: Maximum call stack size exceeded"

例子是参考张鑫旭老师的博客的一个例子canvas图形绘制之星空、噪点与烟雾效果想在vue里面使用

这是组件代码index.vue直接引入你的vue项目即可:

<template>
  <canvas id="canvas"></canvas>
</template>

<script>
import Star from './star'
export default {
  name: "StarrySky",
  data() {
    return {
      stars: {},
      starsIndex: 0,
      density: 400,
      context: null
    }
  },
  computed: {
    WIDTH() {
      return window.innerWidth
    },
    HEIGHT() {
      return window.innerHeight
    }
  },
  mounted() {
    const canvas = document.getElementById('canvas')
    this.context = canvas.getContext('2d')

    canvas.width = this.WIDTH
    canvas.height = this.HEIGHT

    this.init()
  },
  methods: {
    init () {
      this.context.clearRect(0, 0, this.WIDTH, this.HEIGHT)
      this.context.fillStyle = '#000'
      this.context.fillRect(0, 0, this.WIDTH, this.HEIGHT)
      const length = 400

      if (Object.keys(this.stars).length > length) {
        this.density = 0
      }

      for (let i = 0; i < this.density; i++) {
        if (Math.random() > 0.97) {
          this.starsIndex++
          this.stars[this.starsIndex] = new Star()
        }
      }
      // 星星移动
      for (let i in this.stars) {
        this.stars[i].draw(this.context)
      }
      requestAnimationFrame(this.init())
    }
  }
}
</script>

star.js

const WIDTH = window.innerWidth
const HEIGHT = window.innerHeight
const settings = {
  r: 1400, // 圆形轨迹半径
  height: 260, // 围绕旋转的圆露出的圆弧的高度
  alpha: 0.0, // 当前透明度
  maxAlpha: 1 // 最大透明度
}

function Star () {
  // 圆的轨迹方程式为:(x-a)²+(y-b)²=r²
  // 因此,已知x, 则y = Math.sqrt(r² - (x-a)²) + b
  // 其中,圆心是(a, b)
  // 在本例子中
  // 圆心坐标是(WIDTH/2, HEIGHT - 600 + r)
  var a = WIDTH/2, b = HEIGHT - settings.height + settings.r;
  // 因此,已知横坐标随机
  this.x = Math.floor(Math.random() * WIDTH);
  // 纵坐标需要在圆弧以上
  // 越往上,越稀疏
  this.offsety = getMinRandom() * (HEIGHT - settings.height);
  this.y = b - Math.sqrt(settings.r * settings.r - (this.x - a) * (this.x - a)) - this.offsety;

  this.vx = Math.random() * 0.05 + 0.05;    // 水平偏移,也是移动速度

  // 星星的尺寸
  this.particleSize = 0.5 + (Math.random() + 0.1 / 4);
  this.alpha = 0.0;
  this.maxAlpha = 0.2 + (this.y/HEIGHT) * Math.random() * 0.8;
  this.alphaAction = 1;
}

Star.prototype.draw = function (context) {
  // 横坐标移动
  this.x += this.vx;
  // 根据切线方向进行偏移
  // y坐标
  this.y = HEIGHT - settings.height + settings.r - Math.sqrt(settings.r * settings.r - (this.x - WIDTH/2) * (this.x - WIDTH/2)) - this.offsety;
  // 透明度慢慢起来
  if (this.alphaAction == 1) {
    if (this.alpha < this.maxAlpha ) {
      this.alpha += 0.005;
    } else {
      this.alphaAction = -1;
    }
  } else {
    if (this.alpha > 0.2 ) {
      this.alpha -= 0.002;
    } else {
      this.alphaAction = 1;
    }
  }
  if (this.x + (this.particleSize*2) >= WIDTH) {
    // x到左侧
    this.x = this.x - WIDTH;
  }
  // 绘制星星
  context.beginPath();
  context.fillStyle="rgba(255,255,255," + this.alpha.toString() + ")";
  context.arc(this.x, this.y, this.particleSize, 0, Math.PI * 2, true); 
  context.closePath();
  context.fill();
}

// 挑选一个随机数
function getMinRandom () {
  const rand = Math.random()
  // step的大小决定了星星靠近地球的聚拢程度,
  // step = Math.ceil(2 / (1 - rand))就聚拢很明显
  const step = Math.ceil(1 / (1 - rand))
  let rands = []
  for (let i = 0; i < step; i++) {
    rands.push(Math.random())
  }

  return Math.min.apply(null, rands)
}

export default Star

这是我修改张鑫旭老师的代码,运行起来没有影响:

<!DOCTYPE html>
<html>
<head>
  <title>canvas 粒子</title>
  <style type="text/css">
    *{
      padding: 0;
      margin:0;
    }
  </style>
</head>

<body>

  <canvas id="starCanvas" width="1920" height="1000"></canvas> 

  <script>
    (function() {
      var canvas = document.querySelector("#starCanvas");
      var context = canvas.getContext("2d");
      var stars = {},
      particleIndex = 0, // 粒子索引
      settings = {
        r: 1400, // 根据是设计稿确定的轨迹半径
        height: 260, // 露出的圆弧的高度
        density: 300, // 密度
        maxLife: 100,
        leftWall: 0,
        rightWall: canvas.width, // 右边界限值
        groundLevel: canvas.height,
        alpha: 0.0, // 当前透明度
        maxAlpha: 1 // 最大透明度
      };

      var getMinRandom = function() {
        var rand = Math.random();
        // step的大小决定了星星靠近地球的聚拢程度,
        // step = Math.ceil(2 / (1 - rand))就聚拢很明显
        var step = Math.ceil(1 / (1 - rand));
        var arr = [];
        for (var i=0; i<step; i++) {
          arr.push(Math.random());
        }

        return Math.min.apply(null, arr);       
      };

      function resizeCanvas() {
        canvas.width = 1920;
        canvas.height = 800;
        settings.rightWall = canvas.width;
        settings.height = 260 + (canvas.height - 800) / 2;
        redraw();
      }
      resizeCanvas();
      window.addEventListener('resize', resizeCanvas);

      function redraw() {
        context.clearRect(0, 0, canvas.width, canvas.height);
        context.fillStyle = "#000";
        context.fillRect(0, 0, canvas.width, canvas.height);
      }

      function Star() {
        // 圆的轨迹方程式为:(x-a)²+(y-b)²=r²
        // 因此,已知x, 则y = Math.sqrt(r² - (x-a)²) + b;
        // 其中,圆心是(a, b)
        // 在本例子中
        // 圆心坐标是(canvas.width/2, canvas.height - 600 + r);
        var a = canvas.width/2, b = canvas.height - settings.height + settings.r;
        // 因此,已知横坐标随机
        this.x = Math.floor(Math.random() * canvas.width);
        // 纵坐标需要在圆弧以上
        // 越往上,越稀疏
        this.offsety = getMinRandom() * (canvas.height - settings.height);
        this.y = b - Math.sqrt(settings.r * settings.r - (this.x - a) * (this.x - a)) - this.offsety;

        this.vx = Math.random() * 0.05 + 1.00;    // 水平偏移,也是移动速度

        // 星星的尺寸
        this.particleSize = 0.5 + (Math.random() + 0.1 / 4);
        particleIndex++;
        stars[particleIndex] = this;
        this.alpha = 0.0;
        this.maxAlpha = 0.2 + (this.y/canvas.height) * Math.random() * 0.8;
        this.alphaAction = 1;
      }

      Star.prototype.draw = function() {
        // 横坐标移动
        this.x += this.vx;
        // 根据切线方向进行偏移
        // y坐标
        this.y = canvas.height - settings.height + settings.r - Math.sqrt(settings.r * settings.r - (this.x - canvas.width/2) * (this.x - canvas.width/2)) - this.offsety;

        // 透明度慢慢起来
        if (this.alphaAction == 1) {
          if (this.alpha < this.maxAlpha ) {
            this.alpha += 0.005;
          } else {
            this.alphaAction = -1;
          }
        } else {
          if (this.alpha > 0.2 ) {
            this.alpha -= 0.002;
          } else {
            this.alphaAction = 1;
          }
        }

        if ( this.x + (this.particleSize*2) >= settings.rightWall ) {
            // x到左侧
            this.x = this.x - settings.rightWall;
          }

        // 绘制星星
        context.beginPath();
        context.fillStyle="rgba(255,255,255," + this.alpha.toString() + ")";
        context.arc(this.x, this.y, this.particleSize, 0, Math.PI*2, true); 
        context.closePath();
        context.fill();
      }

      function render() {

        redraw();
        // 星星的数目
        // IE下CUP性能有限,数目小
        var length = 400;
        if (!history.pushState) {
      // IE9
            length = 200;
        } else if (document.msHidden != undefined) {
      // IE10+
            length = 300;
        }

        if ( Object.keys(stars).length > length ) {
            settings.density = 0;
          console.log('length', length, settings.density)
        }

        for ( let i = 0; i < settings.density; i++ ) {
          console.log('length1111')
            if ( Math.random() > 0.97 ) {
                new Star();
            }
        }

        // 星星实时移动
        for ( let i in stars ) {
          stars[i].draw();
        }

        requestAnimationFrame(render);
      }
    if (!window.requestAnimationFrame) {
        window.requestAnimationFrame = function(fn) {
            setTimeout(fn, 17);
        };
    }

      render();

    })();
  </script>
</body>
</html>

请求大佬指点一下!

阅读 5.3k
2 个回答
requestAnimationFrame(this.init)

无限循环了
requestAnimationFrame提出去

image.png

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题