Проблема Canvas putImageData XOR только на iPad

Кто-нибудь знает, что не так со следующим кодом.

Демонстрация: http://jsbin.com/xecicovi/1/

Он отлично работает на Windows и Android, но курсор непоследовательно оставляет некоторые следы на iPad. Я использую getImageData и XOR для битов и putImageData для рисования и стирания прямоугольного блока курсора.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title> New Document </title>
<script src="jquery-1.11.0.js"></script>
<script language="javascript">
var m_ctx;
var m_canvas;
var m_bCursorOn = false;
var m_nXpos = 0;

function DrawCursor()
{
   var nCellY = 10;
   var nCellH = 24;
   var nCellX = m_nXpos;
   var nCellW = 13;

   m_bCursorOn = !m_bCursorOn;

   // XOR the cursor location image to draw/erase the cursor
   var imgData = m_ctx.getImageData( nCellX, nCellY, nCellW, nCellH );
   var data = imgData.data;
   for(var i = 0; i < data.length; i += 4) 
   {
      data[i] ^= 255;
      data[i + 1] ^= 255;
      data[i + 2] ^= 255;      
   }
   m_ctx.putImageData( imgData, nCellX, nCellY );
}

function MoveCursor()
{
   if (m_bCursorOn)
      DrawCursor();

   m_nXpos += 13;
   if ( m_nXpos >1000)
      m_nXpos = 0;

   DrawCursor();
}

window.onload = function() 
{
   m_canvas = $('#myCanvas')[0];
   m_ctx = m_canvas.getContext("2d");
   m_canvas.width  = window.innerWidth;
   m_canvas.height = window.innerHeight;
   m_ctx.fillStyle = "black";
   m_ctx.fillRect( 0, 0, m_canvas.width, m_canvas.height );
   setInterval( function(){ DrawCursor();}, 301 );  // cursor blink
   setInterval( function(){ MoveCursor();}, 501 );
};

</script>
</head>

<body >
<canvas id="myCanvas" width="800" height="600"></canvas>
</body>
</html>

person Rex Hui    schedule 07.04.2014    source источник
comment
Я не уверен, но я думаю, что это может быть проблема субпиксельного сглаживания, в основном вы рисуете свой квадрат на круглых числах, но для браузера это означает, что ему придется рисовать половину здесь и половину там, и когда он удаляет старый курсор он оставляет за этими приближениями. Попробуйте нарисовать свой квадрат на числах 0,5 (например, 200,5, 157,5, ...) и посмотрите, решит ли это   -  person Jonas Grumann    schedule 07.04.2014
comment
Спасибо Джонас за быстрый ответ. Но я думаю, что было бы наоборот, если бы было субпиксельное сглаживание. Я имею в виду, что если я укажу целое число, это должен быть конкретный пиксель. Только если я укажу десятичное число, которое потребуется для того, что вы говорите, верно?   -  person Rex Hui    schedule 07.04.2014
comment
В том то и дело, что в элементе canvas это так не работает.   -  person Jonas Grumann    schedule 07.04.2014
comment
Я ответил на это несколько дней назад: stackoverflow. com/questions/22780675/   -  person Jonas Grumann    schedule 07.04.2014
comment
Благодарю. Я пробовал с 0.5, но все равно: jsbin.com/xecicovi/3/edit   -  person Rex Hui    schedule 07.04.2014
comment
И почему это происходит только на iPad, а не на Android и Windows?   -  person Rex Hui    schedule 08.04.2014
comment
Трюк с 0.5 не работает, если установлен масштаб (обычно window.devicePixelRatio), а не 1.   -  person user13500    schedule 08.04.2014
comment
Не то, чтобы это имело или должно было иметь какой-либо эффект в вашем коде...   -  person user13500    schedule 08.04.2014


Ответы (1)


Скорее всего, это проблема того, как браузер Safari работает с обратным буфером и соотношением сторон в пикселях по сравнению с дробной шириной. Это не связано с субпикселированием, поскольку вы имеете дело непосредственно с пиксельным буфером.

Ваш код правильный сам по себе; обходной путь - принудительно преобразовать значения ширины в целые значения для холста.

Измените эти строки:

m_canvas.width  = window.innerWidth;
m_canvas.height = window.innerHeight;

to

m_canvas.width  = window.innerWidth|0;  // removes fractions
m_canvas.height = window.innerHeight|0;

и проблема должна исчезнуть в Safari (модифицированный jsbin здесь).

Надеюсь это поможет.

Добавлен исходный ответ:

Добавьте эту строку:

m_canvas.style.width  = ((m_canvaswindow.width/13)|0)*13+'px';

в этом месте:

window.onload = function() {
    m_canvas = $('#myCanvas')[0];
    m_ctx = m_canvas.getContext("2d");
    m_canvas.width  = window.innerWidth;
    m_canvas.height = window.innerHeight;

    m_canvas.style.width = ((m_canvas.width/13)|0)*13+'px';
    // possibly you will need this for height as well

    m_ctx.fillStyle = "black";
    m_ctx.fillRect( 0, 0, m_canvas.width, m_canvas.height );
    setInterval( DrawCursor, 301 );
    setInterval( MoveCursor, 501 );
};

Это заставит размер соответствовать размеру курсора (если вы измените размер курсора, то 13 там, конечно, также должны быть обновлены - просто вместо этого используйте динамическое значение).

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

person Community    schedule 07.04.2014
comment
Cryptoburner, большое спасибо за ответ. Хотя ваш ответ, кажется, помогает (проблема возникает реже), он все же не решает проблему полностью. Я обновил демонстрацию событием щелчка мыши для перемещения курсора. Вы должны увидеть проблему после нескольких попыток. jsbin.com/xecicovi/8 - person Rex Hui; 08.04.2014
comment
@RexHui Я добавил обратно в исходный ответ, который я отредактировал (чтобы упростить ответ). Посмотрите, улучшит ли это его. При этом: это явно проблема Safari, и ее должна решить команда Safari. Ответ будет действовать только как временное решение, поскольку сам по себе он не может решить проблему. - person ; 09.04.2014
comment
Хорошо, я ценю вашу помощь. - person Rex Hui; 10.04.2014