首页 > 解决方案 > 无法使用 paper.js 为“组”使用“符号”

问题描述

我想做一个反应扩散系统的动画paper.js

这是一个只生成一个图像的代码:

<html>
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.2/paper-full.min.js"></script>
    <style>
      canvas {
        width: 400px;
        height: 400px;
        border: black 3px solid;
      }
    </style>
  </head>
  <body>
    <script>
      function subm_index(M, x){
        if (x<0)
          return x+M;
        if(x >= M)
          return x-M;
        return x;
      }
      function update_concentrations(X, L, DA, DB, f, k){ 
        var sum_a, sum_b, x1, y1, t;
        var m = X.A.length;
        var n = X.A[0].length;
        var A = new Array(m);
        var B = new Array(m);
        for(var i = 0; i < m; i++){
          A[i] = new Array(n);
          B[i] = new Array(n);
        }        
        for(var x = 0; x < m; x++) {
          for(var y = 0; y < n; y++){
            sum_a = 0.0;
            sum_b = 0.0;
            for(var i = -1; i <= 1; i++){
              for(var j = -1; j <= 1; j++){
                x1 = subm_index(m, x - i);
                y1 = subm_index(n, y - j);
                sum_a = sum_a + L[i+1][j+1] * X.A[x1][y1];
                sum_b = sum_b + L[i+1][j+1] * X.B[x1][y1];
              }
            }
            t = X.A[x][y]*X.B[x][y]*X.B[x][y];
            A[x][y] = X.A[x][y] + DA*sum_a - t + f*(1-X.A[x][y]);
            B[x][y] = X.B[x][y] + DB*sum_b + t - (k+f)*X.B[x][y];
          }
        }
        return {A: A, B: B};
      }
      function iterate_Gray_Scott(X, L, DA, DB, f, k, n){
        for(var i = 0; i < n; i++){
          X = update_concentrations(X, L, DA, DB, f, k); 
        }
        return X;
      }
      var L = [[0.05, 0.2, 0.05], [0.2, -1, 0.2], [0.05, 0.2, 0.05]];
      var DA = 1;
      var DB = 0.5;
      var f = 0.0545;
      var k = 0.062;
    </script>

    <script type="text/paperscript" canvas="quad">
      var pixels = 200;
      var gridSize = 2;
      var rectSize = 2;

      var A = new Array(pixels);
      var B = new Array(pixels);
      for(var i = 0; i < pixels; i++){
        A[i] = new Array(pixels);
        B[i] = new Array(pixels);
        for(var j = 0; j < pixels; j++){
          A[i][j] = 1;
          B[i][j] = Math.random() < 0.98 ? 0 : 1;
        }
      }
      var X = {A: A, B: B};

      X = iterate_Gray_Scott(X, L, DA, DB, f, k, 1000);
      
      for(var y = 0; y < pixels; y++){
        for(var x = 0; x < pixels; x++){
          var color = {
		        hue: X.B[x][y] * 360,
		        saturation: 1,
		        brightness: 1
	        };
          var path = new Path.Rectangle(new Point(x, y) * gridSize, new Size(rectSize, rectSize));
          path.fillColor = color;
        }
      }
      project.activeLayer.position = view.center;
    </script>
    <canvas id="quad" resize></canvas>
  </body>
</html>

现在,这是一个生成动画的代码:

<html>
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.2/paper-full.min.js"></script>
    <style>
      canvas {
        width: 400px;
        height: 400px;
        border: black 3px solid;
      }
    </style>
  </head>
  <body>
    <script>
      function subm_index(M, x){
        if (x<0)
          return x+M;
        if(x >= M)
          return x-M;
        return x;
      }
      function update_concentrations(X, L, DA, DB, f, k){ 
        var sum_a, sum_b, x1, y1, t;
        var m = X.A.length;
        var n = X.A[0].length;
        var A = new Array(m);
        var B = new Array(m);
        for(var i = 0; i < m; i++){
          A[i] = new Array(n);
          B[i] = new Array(n);
        }        
        for(var x = 0; x < m; x++) {
          for(var y = 0; y < n; y++){
            sum_a = 0.0;
            sum_b = 0.0;
            for(var i = -1; i <= 1; i++){
              for(var j = -1; j <= 1; j++){
                x1 = subm_index(m, x - i);
                y1 = subm_index(n, y - j);
                sum_a = sum_a + L[i+1][j+1] * X.A[x1][y1];
                sum_b = sum_b + L[i+1][j+1] * X.B[x1][y1];
              }
            }
            t = X.A[x][y]*X.B[x][y]*X.B[x][y];
            A[x][y] = X.A[x][y] + DA*sum_a - t + f*(1-X.A[x][y]);
            B[x][y] = X.B[x][y] + DB*sum_b + t - (k+f)*X.B[x][y];
          }
        }
        return {A: A, B: B};
      }
      function iterate_Gray_Scott(X, L, DA, DB, f, k, n){
        for(var i = 0; i < n; i++){
          X = update_concentrations(X, L, DA, DB, f, k); 
        }
        return X;
      }
      var L = [[0.05, 0.2, 0.05], [0.2, -1, 0.2], [0.05, 0.2, 0.05]];
      var DA = 1;
      var DB = 0.5;
      var f = 0.0545;
      var k = 0.062;
    </script>

    <script type="text/paperscript" canvas="quad">
      var pixels = 200;
      var gridSize = 2;
      var rectSize = 2;
      var A = new Array(pixels);
      var B = new Array(pixels);
      var Paths = new Array(pixels);
      for(var i = 0; i < pixels; i++){
        A[i] = new Array(pixels);
        B[i] = new Array(pixels);
        Paths[i] = new Array(pixels);
        for(var j = 0; j < pixels; j++){
          A[i][j] = 1;
          B[i][j] = Math.random() < 0.99 ? 0 : 1;
        }
      }
      var X = {A: A, B: B};

      for(var y = 0; y < pixels; y++){
        for(var x = 0; x < pixels; x++){
          var color = {
		        hue: X.B[x][y] * 360,
		        saturation: 1,
		        brightness: 1
	        };
          Paths[x][y] = new Path.Rectangle(new Point(x, y) * gridSize, new Size(rectSize, rectSize));
          Paths[x][y].fillColor = color;
        }
      }

      var nframes = 100;

      var XX = new Array(nframes);
      XX[0] = X;
      for(var frm = 1; frm < nframes; frm++){
        XX[frm] = iterate_Gray_Scott(XX[frm-1], L, DA, DB, f, k, frm);
      }

      project.activeLayer.position = view.center;

      function onFrame(event){
        console.log(event.count);
        if(event.count < nframes){
          for(var y = 0; y < pixels; y++){
            for(var x = 0; x < pixels; x++){
              var color = {
                hue: XX[event.count].B[x][y] * 360,
                saturation: 1,
                brightness: 1
              };
              Paths[x][y].fillColor = color;
            }
          }
        }
      }
    </script>

    <canvas id="quad" resize></canvas>
  </body>
</html>

它可以工作,但动画不够流畅。这是由于 in 中的双循环onFrame,这会消耗一些时间。

所以我首先尝试创建一个包含nframes Group元素的数组,每个组都包含pixels*pixels Rectangle元素。但这产生了内存不足。

所以我尝试使用 aSymbol来节省一些内存。代码在下面,但它不起作用,没有出现单个图像。

<html>
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.2/paper-full.min.js"></script>
    <style>
      canvas {
        width: 400px;
        height: 400px;
        border: black 3px solid;
      }
    </style>
  </head>
  <body>
    <script>
      function subm_index(M, x){
        if (x<0)
          return x+M;
        if(x >= M)
          return x-M;
        return x;
      }
      function update_concentrations(X, L, DA, DB, f, k){ 
        var sum_a, sum_b, x1, y1, t;
        var m = X.A.length;
        var n = X.A[0].length;
        var A = new Array(m);
        var B = new Array(m);
        for(var i = 0; i < m; i++){
          A[i] = new Array(n);
          B[i] = new Array(n);
        }        
        for(var x = 0; x < m; x++) {
          for(var y = 0; y < n; y++){
            sum_a = 0.0;
            sum_b = 0.0;
            for(var i = -1; i <= 1; i++){
              for(var j = -1; j <= 1; j++){
                x1 = subm_index(m, x - i);
                y1 = subm_index(n, y - j);
                sum_a = sum_a + L[i+1][j+1] * X.A[x1][y1];
                sum_b = sum_b + L[i+1][j+1] * X.B[x1][y1];
              }
            }
            t = X.A[x][y]*X.B[x][y]*X.B[x][y];
            A[x][y] = X.A[x][y] + DA*sum_a - t + f*(1-X.A[x][y]);
            B[x][y] = X.B[x][y] + DB*sum_b + t - (k+f)*X.B[x][y];
          }
        }
        return {A: A, B: B};
      }
      function iterate_Gray_Scott(X, L, DA, DB, f, k, n){
        for(var i = 0; i < n; i++){
          X = update_concentrations(X, L, DA, DB, f, k); 
        }
        return X;
      }
      var L = [[0.05, 0.2, 0.05], [0.2, -1, 0.2], [0.05, 0.2, 0.05]];
      var DA = 1;
      var DB = 0.5;
      var f = 0.0545;
      var k = 0.062;
    </script>

    <script type="text/paperscript" canvas="quad">
      var pixels = 50;
      var gridSize = 2;
      var rectSize = 2;
      var A = new Array(pixels);
      var B = new Array(pixels);
      var Paths = new Array(pixels);
      for(var i = 0; i < pixels; i++){
        A[i] = new Array(pixels);
        B[i] = new Array(pixels);
        Paths[i] = new Array(pixels);
        for(var j = 0; j < pixels; j++){
          A[i][j] = 1;
          B[i][j] = Math.random() < 0.99 ? 0 : 1;
        }
      }
      var X = {A: A, B: B};

      var nframes = 50;

      var XX = new Array(nframes);
      XX[0] = X;
      for(var frm = 1; frm < nframes; frm++){
        XX[frm] = iterate_Gray_Scott(XX[frm-1], L, DA, DB, f, k, frm);
      }
      
      var Rects = [];
      for(var x = 0; x < pixels; x++){
        for(var y = 0; y < pixels; y++){
          var rect = new Path.Rectangle(new Point(x, y) * gridSize, new Size(rectSize, rectSize));
          var color = {
            hue: 1,
            saturation: 1,
            brightness: 1
          };
          rect.fillColor = color;
          rect.visible = false;
          Rects.push(rect);
        }
      }

      group = new Group(Rects);

      symbolGroup = new Symbol(group);

      var Groups = new Array(nframes);
      for(var frm = 0; frm < nframes; frm++){
        Groups[frm] = symbolGroup.place(view.center);
        var k = 0;
        for(var x = 0; x < pixels; x++){
          for(var y = 0; y < pixels; y++){
            Groups[frm].definition.definition.children[k].fillColor = {
              hue: XX[frm].B[x][y] * 360,
              saturation: 1,
              brightness: 1
            };
            k = k+1;
          }
        } 
        XX[frm] = null; // to free some memory
      } 

      project.activeLayer.position = view.center;

      function onFrame(event){
        if(event.count < nframes){
          console.log(event.count);
          Groups[event.count].visible = true;
          if(event.count > 0){
            Groups[event.count-1].visible = false; // to free some memory
          }
        }
      }
    </script>

    <canvas id="quad" resize></canvas>
  </body>
</html>

你能帮我修复这个最后的代码吗?

标签: javascriptpaperjs

解决方案


如果您的目标是性能,我认为在这种情况下,您应该摆脱 Paper.js,这会增加渲染部分的开销。
作为替代,您可以直接使用 Canvas API 来渲染您的绘图。
这是一个基于您的代码的快速示例,展示了如何使用它。

<html>
<head>
    <style>
        canvas {
            width  : 400px;
            height : 400px;
        }
    </style>
</head>
<body>
<canvas></canvas>

<script>
    function subm_index(M, x) {
        if (x < 0) {
            return x + M;
        }
        if (x >= M) {
            return x - M;
        }
        return x;
    }

    function update_concentrations(X, L, DA, DB, f, k) {
        var sum_a, sum_b, x1, y1, t;
        var m = X.A.length;
        var n = X.A[0].length;
        var A = new Array(m);
        var B = new Array(m);
        for (var i = 0; i < m; i++) {
            A[i] = new Array(n);
            B[i] = new Array(n);
        }
        for (var x = 0; x < m; x++) {
            for (var y = 0; y < n; y++) {
                sum_a = 0.0;
                sum_b = 0.0;
                for (var i = -1; i <= 1; i++) {
                    for (var j = -1; j <= 1; j++) {
                        x1 = subm_index(m, x - i);
                        y1 = subm_index(n, y - j);
                        sum_a = sum_a + L[i + 1][j + 1] * X.A[x1][y1];
                        sum_b = sum_b + L[i + 1][j + 1] * X.B[x1][y1];
                    }
                }
                t = X.A[x][y] * X.B[x][y] * X.B[x][y];
                A[x][y] = X.A[x][y] + DA * sum_a - t + f * (1 - X.A[x][y]);
                B[x][y] = X.B[x][y] + DB * sum_b + t - (k + f) * X.B[x][y];
            }
        }
        return { A: A, B: B };
    }

    function iterate_Gray_Scott(X, L, DA, DB, f, k, n) {
        for (var i = 0; i < n; i++) {
            X = update_concentrations(X, L, DA, DB, f, k);
        }
        return X;
    }

    var L = [[0.05, 0.2, 0.05], [0.2, -1, 0.2], [0.05, 0.2, 0.05]];
    var DA = 1;
    var DB = 0.5;
    var f = 0.0545;
    var k = 0.062;


    var pixels = 200;
    var gridSize = 2;
    var rectSize = 2;
    var A = new Array(pixels);
    var B = new Array(pixels);
    var Paths = new Array(pixels);
    for (var i = 0; i < pixels; i++) {
        A[i] = new Array(pixels);
        B[i] = new Array(pixels);
        Paths[i] = new Array(pixels);
        for (var j = 0; j < pixels; j++) {
            A[i][j] = 1;
            B[i][j] = Math.random() < 0.99 ? 0 : 1;
        }
    }
    var X = { A: A, B: B };

    var nframes = 50;

    var XX = new Array(nframes);
    XX[0] = X;
    for (var frm = 1; frm < nframes; frm++) {
        XX[frm] = iterate_Gray_Scott(XX[frm - 1], L, DA, DB, f, k, frm);
    }



    //
    // New code
    //

    // Get a reference to the canvas element. 
    const canvas = document.querySelector('canvas');
    // Make sure that canvas internal size fits its display size. 
    canvas.width = 400;
    canvas.height = 400;
    // Get canvas context to be able to draw directly on it. 
    const ctx = canvas.getContext('2d');

    // Counter used to swicth between frames.
    let currentFrame = 0;

    // Launch the animation.
    animate();

    function animate() {
        // Draw current frame.
        draw();

        // Update frame counter (make it loop after last frame).
        currentFrame = currentFrame < nframes - 1 ? currentFrame + 1 : 0;

        // Do a recursive call to render next frame.
        requestAnimationFrame(animate);
    }

    function draw() {
        // For each pixel...
        for (var y = 0; y < pixels; y++) {
            for (var x = 0; x < pixels; x++) {
                // ...get the color...
                const hue = Math.round(XX[currentFrame].B[x][y] * 360);
                const color = `hsl(${hue}, 100%, 50%)`;
                // ...use it as canvas fill color...
                ctx.fillStyle = color;
                // ...and draw a scaled rectangle.
                ctx.fillRect(x * gridSize, y * gridSize, gridSize, gridSize);
            }
        }
    }
</script>

</body>
</html>

另一个改进想法可能是将当前数据源映射到准备好的ImageData数组,稍后将其加载到画布中,而不是循环遍历像素。


推荐阅读