Скорость анимации холста

Я создаю симуляцию городской транспортной системы и пытаюсь улучшить свои навыки Javascript и Canvas. Я предоставил версию с голыми костями здесь: https://jsfiddle.net/ftmzm9vp/

Два вопроса:

1) Я хочу, чтобы «стручки» работали с одинаковой скоростью. Сейчас все они прибывают в пункт назначения в одно и то же время, а это значит, что они путешествуют с разной скоростью. Как это исправить?

2) Очевидно, что мне нужно сделать больше — заставить капсулы двигаться по существующим линиям, разработать наилучший путь к месту назначения, увеличить количество линий и станций — все это увеличит вычислительные затраты. Прямо сейчас, когда я хочу использовать 500 стручков, анимация начинает ползать. Я переписал все это, чтобы использовать requestAnimFrame, так как я думал, что это будет быстрее, но, похоже, это не так гладко, как должно быть. Что я могу сделать, чтобы улучшить это? Спасибо!

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<title>Pod Stations Lines Test</title>
<body>
    <canvas id="layer1" style="z-index: 2;  
                position:absolute;
                left:0px;
                top:0px;
                " height="600px" width="1000">This text is displayed if your browser does not support HTML5 Canvas.</canvas>
    <canvas id="layer2" style="z-index: 3;
                position:absolute;
                left:0px;
                top:0px;
                " height="600px" width="1000">This text is displayed if your browser does not support HTML5 Canvas.</canvas>
    <canvas id="layer3" style="z-index: 1;
                position:absolute;
                left:0px;
                top:0px;
                " height="600px" width="1000">This text is displayed if your browser does not support HTML5 Canvas.</canvas>
    <script>
        //Modified Source: http://jsfiddle.net/m1erickson/HAbfm/
        //
        layer1 = document.getElementById("layer1");
        ctx1 = layer1.getContext("2d");
        layer2 = document.getElementById("layer2");
        ctx2 = layer2.getContext("2d");
        layer3 = document.getElementById("layer3");
        ctx3 = layer3.getContext("2d");

        window.requestAnimFrame = (function(callback) {
            return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) {
                window.setTimeout(callback, 1000 / 60);
            };
        })();

        //STATION LIST
        var station = [
            ['A', 100, 50],
            ['B', 300, 50],
            ['C', 200, 150],
            ['D', 100, 250],
            ['E', 300, 250],
            ['F', 400, 250]
        ];


        //DRAW LINES
        function drawLines() {

            ctx1.clearRect(0, 0, layer3.width, layer3.height);

            var linkAB = ctx1.beginPath();
            ctx1.moveTo(station[0][1], station[0][2]);
            ctx1.lineTo(station[1][1], station[1][2]);
            ctx1.stroke();
            var linkBC = ctx1.beginPath();
            ctx1.moveTo(station[1][1], station[1][2]);
            ctx1.lineTo(station[2][1], station[2][2]);
            ctx1.stroke();
            var linkCD = ctx1.beginPath();
            ctx1.moveTo(station[2][1], station[2][2]);
            ctx1.lineTo(station[3][1], station[3][2]);
            ctx1.stroke();
            var linkDE = ctx1.beginPath();
            ctx1.moveTo(station[3][1], station[3][2]);
            ctx1.lineTo(station[4][1], station[4][2]);
            ctx1.stroke();
            var linkCE = ctx1.beginPath();
            ctx1.moveTo(station[2][1], station[2][2]);
            ctx1.lineTo(station[4][1], station[4][2]);
            ctx1.stroke();
            var linkEF = ctx1.beginPath();
            ctx1.moveTo(station[4][1], station[4][2]);
            ctx1.lineTo(station[5][1], station[5][2]);
            ctx1.stroke();

        }


        //CREATE PODS

        var podArray = [];

        function Pod(startX, startY, endX, endY, riders, color) {
            this.startX = startX;
            this.startY = startY;
            this.endX = endX;
            this.endY = endY;
            this.riders = riders;
            this.color = color;
        }

        var colorArray = ["gold", "orange", "red", "green", "blue", "black"];

        function randomPass() {
            occ = 1 + Math.floor(Math.random() * 6);
            return occ;
            console.log("Riders " + occ);
        }


        for (i = 0; i < 500; i++) {
            var origNum = Math.floor(Math.random() * station.length);
            var origin = {
                x: station[origNum][1],
                y: station[origNum][2]
            }
            var destNum = Math.floor(Math.random() * station.length);
            while (origNum == destNum) {
                destNum = Math.floor(Math.random() * station.length);
            }
            var destination = {
                x: station[destNum][1],
                y: station[destNum][2]
            }

            podArray.push(new Pod(
            startX = origin.x,
            startY = origin.y,
            endX = destination.x,
            endY = destination.y,
            riders = randomPass(),
            color = colorArray[riders - 1]

            ));
        }

        var pct = 0.00;
        var fps = 60;

        //CALL DRAWING AND ANIMATION
        drawLines();
        animate();

        function animate() {
            setTimeout(function() {

                if (pct <= 1.00) {
                    requestAnimFrame(animate)
                };

                // increment the percent (from 0.00 to 1.00)
                pct += .01;

                // clear the canvas
                ctx3.clearRect(0, 0, layer3.width, layer3.height);

                // draw all podArray
                for (var i = 0; i < podArray.length; i++) {

                    // get reference to next aPod
                    var aPod = podArray[i];

                    var dx = aPod.endX - aPod.startX;
                    var dy = aPod.endY - aPod.startY;
                    var nextX = aPod.startX + dx * pct;
                    var nextY = aPod.startY + dy * pct;

                    //create pod on screen
                    ctx3.fillStyle = aPod.color;
                    ctx3.beginPath();
                    ctx3.arc(nextX, nextY, 5, 0, Math.PI * 2, true);
                    ctx3.fillStyle = aPod.color;
                    ctx3.fill();
                    ctx3.closePath();

                    //STATION LETTERS

                    for (s = 0; s < station.length; s++) {
                        ctx2.font = '12pt Calibri';
                        ctx2.fillStyle = 'red';
                        ctx2.textAlign = 'center';
                        ctx2.fillText(station[s][0], station[s][1], (station[s][2]) + 4);
                    }

                }

            }, 1000 / fps);
        }
    </script>
</body>

person Tyler330    schedule 17.08.2015    source источник


Ответы (2)


Все ваши транспортные средства достигают пункта назначения одновременно, потому что вы меняете их положение в процентах. Таким образом, когда pct==1.00, все транспортные средства одновременно прибывают в свои конечные точки, независимо от расстояния, которое им нужно преодолеть, чтобы добраться туда.

// increment the percent (from 0.00 to 1.00)
pct += .01;

Чтобы обеспечить прибытие транспортного средства в зависимости от пройденного расстояния

Вопрос № 1: Вы можете рассчитать каждую путевую точку (путевая точка == уникальный пиксель), которую транспортное средство должно пройти, чтобы завершить свой маршрут. Продвигайте транспортное средство к следующей путевой точке с каждым новым кадром анимации. Это приводит к тому, что каждое транспортное средство прибывает в зависимости от длины их маршрута, а не от единого процента.

Вопрос № 2: Для каждого транспортного средства, если вы предварительно рассчитаете и сохраните его путевые точки в массиве, вы можете легко получить 500 транспортных средств, отрисованных на холсте во время каждого кадра анимации.

Вот аннотации и демонстрация:

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;

ctx.lineWidth=2;

// define routes
var routes=[];
routes.push({
  points:linePoints({x:10,y:10},{x:150,y:10}),
  currentPoint:0,
  color:'red',
});
routes.push({
  points:linePoints({x:10,y:50},{x:250,y:65}),
  currentPoint:0,
  color:'green',
});
routes.push({
  points:linePoints({x:10,y:90},{x:325,y:105}),
  currentPoint:0,
  color:'blue',
});

// animation related vars
var lastTime=0;
var delay=1000/60*5;

// start animating
requestAnimationFrame(animate);

function animate(time){
  // return if the desired time hasn't elapsed
  if(time<lastTime){requestAnimationFrame(animate);return;}
  // redraw each route
  ctx.clearRect(0,0,cw,ch);
  // var used to stop animating if all routes are completed
  var isComplete=true;
  for(var i=0;i<routes.length;i++){
    var r=routes[i];
    // increase the currentPoint, but not beyond points.length-1
    if((r.currentPoint+1)<r.points.length-1){
      isComplete=false;
      r.currentPoint++;
    }
    // draw the route to its current point
    ctx.strokeStyle=r.color;
    ctx.beginPath();
    ctx.moveTo(r.points[0].x,r.points[0].y);
    ctx.lineTo(r.points[r.currentPoint].x,r.points[r.currentPoint].y);
    ctx.stroke();
    ctx.fillStyle=r.color;
    ctx.beginPath();
    ctx.arc(r.points[r.currentPoint].x,r.points[r.currentPoint].y,5,0,Math.PI*2);
    ctx.fill();
  }
  // request another frame unless all routes are completed
  if(!isComplete){
    requestAnimationFrame(animate);
  }
}

function linePoints(p1,p2){
  // start building a points array with the starting point
  var points=[p1];
  var dx=p2.x-p1.x;
  var dy=p2.y-p1.y;
  var count=Math.sqrt(dx*dx+dy*dy)*3;
  for(var pct=0;pct<count;pct++){
    // calc next waypoint on the line
    var x=p1.x+dx*pct/count;
    var y=p1.y+dy*pct/count;
    var lastPt=points[points.length-1];
    // add new waypoint if the its integer pixel value has
    // changed from last waypoint
    if(parseInt(x)!==parseInt(lastPt.x) || parseInt(y)!==parseInt(lastPt.y)){
      points.push({x:x,y:y});
    }
  }
  // force the last point to be the ending point
  points[points.length-1]=p2;
  // return a unique points[] forming a line from p1 to p2
  return(points);
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=350 height=300></canvas>

person markE    schedule 17.08.2015
comment
Все еще пытаюсь понять логику холста. Спасибо тебе за это! - person Tyler330; 20.08.2015
comment
Я пытался адаптировать ваш код, но не могу заставить его рисовать что-либо на экране. Что я пропустил? [ссылка]( jsfiddle.net/yL8udg9r) - person Tyler330; 03.09.2015

Чтобы ваши капсулы двигались с одинаковой скоростью, вы, вероятно, захотите использовать теорему Пифагора. Гипотенуза — это расстояние, которое вы хотите, чтобы узел проходил каждый раз, когда rAF проходит. Затем вычислите ваши x и y из этого.

Я не совсем уверен, понимаю ли я, что делает pct.

Чтобы ускорить RAF, вам нужно:

  1. убить установленный тайм-аут
  2. предварительный рендеринг

Предварительная визуализация — это немного больше работы, но вместо того, чтобы рисовать каждый круг каждый раз, когда у вас есть холсты, которых нет в DOM, на который вы рисуете. Затем вы, по сути, кладете «скрытый» холст, где хотите, поверх видимого холста DOM. Таким образом, рисунок сохраняется в памяти.

Вы также рисуете каждый круг на холсте в конце цикла for. Вытащите метод fill из него, чтобы холст мог рисовать пакетно вместо множества маленьких вызовов (это действительно может снизить производительность).

Настройка шрифта каждый раз может быть удалена.

Canvas отлично подходит для производительности, но вам просто нужно быть осторожным, потому что одна маленькая ошибка может привести к огромному узкому горлышку.

Это хорошая статья: http://www.html5rocks.com/en/tutorials/canvas/performance/

Позвольте мне знать, если у вас есть еще вопросы.

person 9TrSl9IAlaLfDmffagsw    schedule 17.08.2015
comment
Я посмотрю на это. Я видел это раньше, но не пытался реализовать. - person Tyler330; 20.08.2015