Rysowanie na płótnie jest bardzo powolne

Sep 13 2020

Chcę wyświetlić skalę z oznaczeniami, która działa dobrze. Oprócz tego chcę również wyświetlić położenie myszy na skali z czerwonym wskaźnikiem.

Tak więc rysuję płótno po uruchomieniu aplikacji, a następnie przerysowuję całe płótno, gdy zmienia się lokalizacja myszy.

Jestem nowy w płótnie i nie rozumiem, co jest nie tak w moim kodzie. Próbowałem go rozwiązać, ale bez powodzenia.

Problem może dotyczyć tej funkcji,

 function drawBlackMarkers(y, coordinateMeasurment){
    const markHightY = scaleTextPadding.initial;
    ctxLeft.moveTo(coordinateMeasurment, y + markHightY);
    ctxLeft.lineTo(completeMarkHight, y + markHightY);
  }

Mam dużą pętlę for, która oznacza tyle iteracji do przejścia iw tej pętli wywołuję funkcję drawBlackMarkers tyle razy, co pokazano poniżej.

function setMarkers(initialValY, rangeValY, coordinateMeasurmentr, divisableVal,
    scaleCountStartValueOfY, scaleCountRangeValueOfY) {
    let count = 0;
    // re-modifying scale staring and ending values based on zoom factor
    const scaleInceremnt = scaleIncementValue;
    for (let y = (initialValY), scaleCountY = scaleCountStartValueOfY;
      y <= (rangeValY) && scaleCountY <= scaleCountRangeValueOfY;
      y += scaleInceremnt, scaleCountY += incrementFactor) {


      switch (count) {
        case displayScale.starting:
          coordinateMeasurment = marktype.bigMark; count++;
          const scaleValY = scaleCountY - divisableVal;

          ctxLeft.strokeStyle = colors.black;

          ctxLeft.font = scaleNumberFont;
          const size = ctxLeft.measureText(scaleValY.toString());
          ctxLeft.save();
          const textX = coordinateMeasurment + ((size.width) / 2);
          const textY = y - scaleTextPadding.alignment;
          ctxLeft.translate(textX, textY);
          ctxLeft.rotate(-Math.PI / 2);
          ctxLeft.translate(-textX, -textY);
          ctxLeft.fillText(scaleValY.toString(), coordinateMeasurment, y - scaleTextPadding.complete);
          ctxLeft.restore();
          break;
        case displayScale.middle:
          coordinateMeasurment = marktype.middleMark; count++;
          break;
        case displayScale.end:
          coordinateMeasurment = marktype.smallMark; count = 0;
          break;
        default:
          coordinateMeasurment = marktype.smallMark; count++;
          break;
      }

      // to draw scale lines on canvas
  // drawBlackMarkers(y, coordinateMeasurment);      
    }
  }

Proszę to sprawdzić : http://jsfiddle.net/3v5nt7fe/1/

Problem polega na tym, że jeśli skomentuję wywołanie funkcji drawBlackMarkers , aktualizacja współrzędnych myszy jest bardzo szybka, ale jeśli odkomentuję, aktualizacja lokalizacji zajmuje tak dużo czasu.

Naprawdę potrzebuję pomocy, aby rozwiązać ten problem.

Odpowiedzi

5 SimonSarris Sep 16 2020 at 12:26

To nie jest drawBlackMarkerssamo, to jest to:

for (let y = (initialValY), scaleCountY = scaleCountStartValueOfY;
  y <= (rangeValY) && scaleCountY <= scaleCountRangeValueOfY;
  y += scaleInceremnt, scaleCountY += incrementFactor) {

To stale rośnie i dzieje się 640 000 razy. Możesz powiedzieć, że tak jest, pisząc:

  // to draw scale lines on canvas
  // drawBlackMarkers(y, coordinateMeasurment);
  console.log(y);

i widząc wynik konsoli.

Więc pętla for robi bardzo niewiele, ponieważ większość z nich znajduje się za instrukcją switch, a kiedy robi to nawet drawBlackMarkerspoza tym, pokazuje prawdziwy koszt tej pętli. rangeValYwynosi 640 000, co oznacza, że ​​ścieżka, którą musi skonstruować kontekst kanwy, jest ogromna.

Aby to naprawić, musisz znaleźć sposób na rozwiązanie tego problemu.

1 speciesUnknown Sep 18 2020 at 20:31

Robi to dużo niepotrzebnej pracy

Ekran nie ma 64000 pikseli wysokości. Chcesz obliczyć rzutnię i narysować tylko to, co jest w rzutni.

Twoja funkcja drawBlackMarkers nie jest winowajcą. Wcześniej system jest bardzo powolny, po prostu dodaje jeszcze jedną rzecz do narysowania. To była słoma, która złamała grzbiet wielbłąda.

Zmniejszając długość rysowanego elementu, można bardzo łatwo uniknąć zmarnowanych cykli procesora.

W tej wersji wszystko, co zrobiłem, to ponownie włączyć drawBlackMarkers i zmniejszyć płótno.

const CANVAS_WIDTH = 2000;
const CANVAS_HEIGHT = 50;
const completeMarkHight = 15;
const divisibleValue = 0;
const scaleIncementValue = 10;
const scaleTextPadding = { initial: 0, middle: 5, end: 10, complete: 15, alignment: 18 };
const displayScale = { starting: 0, middle: 5, end: 9 };
const colors = { red: '#FF0000', white: '#D5D6D7', black: '#181c21' };
const marktype = { bigMark: 0, middleMark: 5, smallMark: 10 };
const startingInitialOrigin = { x: 0, y: 0 };
const scaleNumberFont = '10px Titillium Web Regular';
const defaultZoomLevel = 100;
const markingGap = {level1: 400, level2: 200, level3: 100, level4: 50, level5: 20, level6: 10 };
const zoomScaleLevel = {level0: 0, level1: 25, level2: 50, level3: 100, level4: 200, level5: 500, level6: 1000};



var $canvas = $('#canvas');
var ctxLeft = $canvas[0].getContext('2d'); var mousePositionCoordinates; var pagePositions = { x: 100, y:0 }; var remainderX; var remainderY; var scaleCountRemainderX; var scaleCountRemainderY; var zoomFactor; var zoomScale; var zoomLevel; var multiplyFactor; var incrementFactor; var markingDistance; var timetaken=0; ctxLeft.fillStyle = colors.white; function render() { clear(); ctxLeft.beginPath(); zoomScale = 1000; zoomLevel = 1000; zoomFactor = zoomLevel / defaultZoomLevel; markingDistance = markingGap.level6; multiplyFactor = markingDistance / defaultZoomLevel; incrementFactor = markingDistance / scaleIncementValue; renderVerticalRuler(startingInitialOrigin.y); } function renderVerticalRuler(posY) { const initialValY = - posY / multiplyFactor; const rangeValY = (CANVAS_WIDTH - posY) / multiplyFactor; const initialValOfYwithMultiplyFactor = -posY; const rangeValOfYwithMultiplyFactor = (CANVAS_WIDTH - posY); // to adjust scale count get remainder value based on marking gap scaleCountRemainderY = initialValOfYwithMultiplyFactor % markingDistance; const scaleCountStartValueOfY = initialValOfYwithMultiplyFactor - scaleCountRemainderY; const scaleCountRangeValueOfY = rangeValOfYwithMultiplyFactor - scaleCountRemainderY; // to get orgin(0,0) values remainderY = initialValY % 100; const translateY = (posY / multiplyFactor) - remainderY; ctxLeft.translate(origin.x, translateY); // x,y const coordinateMeasurment = 0; const t0 = performance.now(); setMarkers(initialValY, rangeValY, coordinateMeasurment, divisibleValue, scaleCountStartValueOfY, scaleCountRangeValueOfY); const t1 = performance.now() console.log("it took " + (t1 - t0) + " milliseconds."); ctxLeft.stroke(); ctxLeft.closePath(); } function setMarkers(initialValY, rangeValY, coordinateMeasurmentr, divisableVal, scaleCountStartValueOfY, scaleCountRangeValueOfY) { let count = 0; // re-modifying scale staring and ending values based on zoom factor const scaleInceremnt = scaleIncementValue; for (let y = (initialValY), scaleCountY = scaleCountStartValueOfY; y <= (rangeValY) && scaleCountY <= scaleCountRangeValueOfY; y += scaleInceremnt, scaleCountY += incrementFactor) { switch (count) { case displayScale.starting: coordinateMeasurment = marktype.bigMark; count++; const scaleValY = scaleCountY - divisableVal; ctxLeft.strokeStyle = colors.black; ctxLeft.font = scaleNumberFont; const size = ctxLeft.measureText(scaleValY.toString()); ctxLeft.save(); const textX = coordinateMeasurment + ((size.width) / 2); const textY = y - scaleTextPadding.alignment; ctxLeft.translate(textX, textY); ctxLeft.rotate(-Math.PI / 2); ctxLeft.translate(-textX, -textY); ctxLeft.fillText(scaleValY.toString(), coordinateMeasurment, y - scaleTextPadding.complete); ctxLeft.restore(); break; case displayScale.middle: coordinateMeasurment = marktype.middleMark; count++; break; case displayScale.end: coordinateMeasurment = marktype.smallMark; count = 0; break; default: coordinateMeasurment = marktype.smallMark; count++; break; } // to draw scale lines on canvas drawBlackMarkers(y, coordinateMeasurment); } } function drawBlackMarkers(y, coordinateMeasurment){ const markHightY = scaleTextPadding.initial; ctxLeft.moveTo(coordinateMeasurment, y + markHightY); ctxLeft.lineTo(completeMarkHight, y + markHightY); } function clear() { ctxLeft.resetTransform(); ctxLeft.clearRect(origin.x, origin.y, CANVAS_HEIGHT, CANVAS_WIDTH); } render(); $('.canvas-container').mousemove(function(e) {
   
    
    mousePositionCoordinates = {x:e.clientX, y:e.clientY};
             
        render();
        
        // SHOW RED INDICATOR 
        ctxLeft.beginPath();
        ctxLeft.strokeStyle = colors.red;  // show mouse indicator
        ctxLeft.lineWidth = 2;

        // to display purple indicator based on zoom level
        const mouseX = mousePositionCoordinates.x * zoomFactor;
        const mouseY = mousePositionCoordinates.y * zoomFactor;
        const markHightY =scaleTextPadding.initial + this.remainderY;
        ctxLeft.moveTo(marktype.bigMark, e.clientY );
        ctxLeft.lineTo(completeMarkHight, e.clientY);
        ctxLeft.stroke();
        $('.mouselocation').text(`${mousePositionCoordinates.x},${mousePositionCoordinates.y}`);
   
});
body, html{
  width: 100000px;
  height:100000px;
}
.canvas-container{
    width:100%;
    height:100%;
}

.canvasLeft {
    position: absolute;
    border:1px solid black;
    background: grey;
    border-top: none;
    z-index: 1;
    top:0
}


.mouselocation{
  position: fixed;
    right: 0px;
    top: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<div class="canvas-container">
  <canvas id="canvas" class="canvasLeft" width="30" height="2000"></canvas>
</div>


<div class="mouselocation">
   
</div>