#javascript #html #canvas #html5-canvas
#javascript #HTML #холст #html5-canvas
Вопрос:
Я хочу отобразить масштаб с разметкой, который работает нормально. Кроме того, я также хочу отобразить местоположение мыши в масштабе с красным индикатором.
Итак, я рисую холст при запуске приложения, а затем перерисовываю весь холст при изменении местоположения мыши.
Я новичок в canvas и не понимаю, что не так в моем коде. Я пытался решить эту проблему, но безуспешно.
Проблема может быть в этой функции,
function drawBlackMarkers(y, coordinateMeasurment){
const markHightY = scaleTextPadding.initial;
ctxLeft.moveTo(coordinateMeasurment, y markHightY);
ctxLeft.lineTo(completeMarkHight, y markHightY);
}
У меня большой цикл for, что означает выполнение стольких итераций, и в этом цикле я вызываю функцию drawBlackMarkers столько раз, как показано ниже.
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) amp;amp; 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);
}
}
Пожалуйста, проверьте это:http://jsfiddle.net/3v5nt7fe/1 /
Проблема в том, что если я прокомментирую вызов функции drawBlackMarkers, обновление координат мыши происходит очень быстро, но если я раскомментирую, обновление местоположения займет слишком много времени.
Мне действительно нужна помощь для решения этой проблемы.
Комментарии:
1. Чтобы получить максимальную помощь, вам следует добавить функцию, в которой возникла проблема
drawBlackMarkers
, а не только код, вызывающий функцию. Мы не можем использовать код, на который вы ссылаетесь (ссылки являются временными), а некоторые, как я, просто не могут перейти по ссылке, мы просто переходим к следующему вопросу.2. @Blindman67 я полностью согласен. Я обновляю свой вопрос прямо сейчас в соответствии с вашим комментарием.
3. @Blindman67 Спасибо, что хотя бы заглянули в него. Опять же, я полностью согласен с вами. Но размер составляет 64000 пикселей, это потому, что у нас есть механизм панорамирования. Это означает, что вы можете прокручивать / перемещать холст (влево, вправо, сверху, снизу), который на самом деле имеет 8000 пикселей, и, кроме того, у нас есть функции увеличения и уменьшения масштаба, которые делают холст маленьким и большим, и, соответственно, мы должны изменить разметку на линейке / шкале. Вот почему необходимо иметь такой большой размер. Я могу переосмыслить эту часть, но для ее рефакторинга потребуется много усилий.
4. В демонстрационном приложении я отображаю только вертикальный масштаб, но в моем реальном приложении у нас также реализована горизонтальная линейка / масштаб. Теперь проблема в том, что горизонтальные масштабы с одинаковым размером (64000) работают без каких-либо задержек, но вертикальный масштаб требует времени. В это было бы трудно поверить, но это реальность.
5. Посмотрите на карты Google, там есть огромная виртуальная карта, которую можно увеличивать и уменьшать, панорамировать и т.д., Но холст никогда не становится больше, чем разрешение дисплея устройства. Слишком большой размер холста заставит графический процессор заменять оперативную память при рендеринге разных частей холста. Как только вы превысите лимит оперативной памяти, рендеринг даже одного пикселя будет очень медленным. Простой факт заключается в том, что вы не можете иметь холст намного больше, чем разрешение дисплея, и ожидать производительности 60 кадров в секунду. Кстати, вы находитесь прямо на краю максимальной области холста, см. Ссылку developer.mozilla.org/en-US/docs/Web/HTML/Element /…
Ответ №1:
Это не drawBlackMarkers
само по себе, это:
for (let y = (initialValY), scaleCountY = scaleCountStartValueOfY;
y <= (rangeValY) amp;amp; scaleCountY <= scaleCountRangeValueOfY;
y = scaleInceremnt, scaleCountY = incrementFactor) {
Это постоянно увеличивается и происходит 640 000 раз. Вы можете сказать, что это так, написав:
// to draw scale lines on canvas
// drawBlackMarkers(y, coordinateMeasurment);
console.log(y);
и вижу результат консоли.
Так что цикл for делает очень мало, потому что большая его часть находится за оператором switch, и когда он делает даже это простое drawBlackMarkers
, за пределами его отображения истинная стоимость этого цикла. rangeValY
равно 640 000, что означает, что путь, который должен построить контекст холста, огромен.
Поэтому, чтобы исправить это, вы должны найти способ улучшить эту проблему.
Комментарии:
1. Да, я полностью согласен, что у него много итераций, но без
drawBlackMarkers
это быстро, потому что он не рисует 2D-графику на холсте, но сdrawBlackMarkers
это делает. Я работаю над этим, чтобы уменьшить количество итераций. Огромное спасибо за ваш вклад.
Ответ №2:
Это делает много ненужной работы
Высота экрана не составляет 64000 пикселей. Вы хотите вычислить окно просмотра и рисовать только то, что находится в окне просмотра.
Ваша функция drawBlackMarkers не является виновником. До этого система работала очень медленно, просто добавляя еще одну вещь для рисования. Это была соломинка, которая сломала спину верблюду.
Уменьшая длину того, что вы рисуете, вы можете очень легко избежать ненужных циклов процессора.
В этой версии все, что я сделал, это повторно включил drawBlackMarkers и уменьшил холст.
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) amp;amp; 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>