#javascript #google-chrome #canvas
#javascript #google-chrome #холст
Вопрос:
Вступление
Я создаю страницу, которая содержит «строки» информации в соответствии со следующим:
- Каждая строка содержит один холст
- Все строки должны иметь одинаковую ширину: ширину самого широкого холста
- Если ширина самого широкого холста меньше ширины окна просмотра, используется ширина окна просмотра.
Решение простое и простое: использование таблицы (с отдельными столбцами) и стиля min-width: 100%;
. Это не проблема.
Проблема
Все, что нарисовано на холстах, имеет эффект размытия.
Это (по-видимому) происходит только в (последних версиях) Chrome.
Воспроизводимость
Чтобы воспроизвести проблему, я создал эту скрипку.
В примере я создал две строки с разным цветом фона каждая, на первом canvas отображается текст, на втором — графический контент. Для графического содержимого я использую следующее изображение:
Пожалуйста, обратите внимание, что изображение содержит только два цвета: чистый красный и чистый зеленый. Его размер составляет 100×100. На втором холсте изображение отображается 10 раз, одно рядом с другим, поэтому ширина холста составляет 1000×100.
Компьютер, на котором я тестирую, — это Linux с Chrome 87.0.4280.66 (2020-11-17), последней стабильной версией на момент написания этого вопроса.
Я протестировал его на других компьютерах и браузерах со следующими результатами:
- Тот же компьютер Linux, (старый) Chrome: ОК
- Тот же компьютер Linux, Mozilla: ОК
- Другой компьютер Linux, (недавний) Chrome: ПРОБЛЕМА
- Другой компьютер с Linux, Mozilla: ОК
- MacBook, (недавний) Chrome: ПРОБЛЕМА
- MacBook, Mozilla: ОК
- MacBook, Safari. ОК
- Планшет Android, (старый) Chrome: ОК
Если вы видите, что все в порядке, и вам интересно, как выглядит проблема, вот скриншот. (Возможно, вам придется щелкнуть, чтобы увидеть его на 100%, в скорректированном размере трудно увидеть проблему):
Обратите внимание, что:
- Изображение выглядит размытым, смешивая красный и зеленый цвета (в результате получается бордовый). Это особенно печально известно вокруг 5th. image.
- Проблема также влияет на текст. Если вы внимательно посмотрите, текст выше 5-го. квадрат немного более размытый, чем в начале.
Эффект аналогичен тому, который достигается, если вы рисуете на изображении (или на внеэкранном холсте), а затем рисуете его содержимое на холсте, используя другой масштабный коэффициент (т. Е. Рисуете изображение размером 100×100 в пространстве 99×99), потому что эффект напоминает типичный результат повторной выборки.
То, что я пробовал, безуспешно
context.resetTransform()
перед рисованием.context.imageSmoothingEnabled = false
. Даже если это сработало, это решило проблему для изображения, но не для текста.context.drawImage()
сdWidth
dHeight
параметрами и, чтобы принудительно отрисовывать цель размером 100×100.#canvas { image-rendering: pixelated; }
- Chrome -> Настройки -> Внешний вид -> Использовать аппаратное ускорение, когда доступно -> (отключение)
- Использование других методов для макета. Я пытался использовать DIVs вместо таблицы.
- Согласно <canvas>: графический элемент холста:
Изменение размера холста с помощью CSS по сравнению с HTML
Отображаемый размер canvas можно изменить с помощью CSS, но если вы сделаете это, изображение будет масштабироваться во время рендеринга в соответствии с размером стилизованного изображения, что может привести к искажению конечного графического рендеринга.
Лучше указать размеры холста, установив атрибуты width и height непосредственно на элементах, либо непосредственно в HTML, либо с помощью JavaScript.
Все материалы canvas выполняются с помощью Javascript, без CSS, без HTML.
Вы как-то экспериментировали с этим эффектом раньше? Вы решили это? Как?
Есть идеи для решения?
Если вы экспериментируете с проблемой, и у вас браузер, отличный от Chrome, пожалуйста, прокомментируйте, чтобы исключить проблему как специфичную для Chrome.
Любая помощь будет высоко оценена.
Комментарии:
1. Учтите тот факт, что (0,0) индексирует верхний левый угол первого пикселя. Вертикальная линия шириной 1 пиксель, проведенная отсюда, даст полную интенсивность только первой 1/2 пикселя. При рисовании линий шириной 1 пиксель вы получаете гораздо более чистые результаты, если считать (0.5,0.5) центром пикселя. На самом деле вы были почти там. В скрипте раскомментируйте
context.imageSmoothingEnabled = false;
и добавьте 0,5 к координатам X и Y. И вуаля — неизмененные копии изображения. 🙂2. Спасибо @enhzflep за ваш комментарий. К сожалению, из-за этого все 10 изображений выглядят одинаково, но эти копии определенно отличаются от исходного изображения. С уважением
3. Что означает
console.log( devicePixelRatio )
вывод? Делает ли эта скрипка лучше: Странно, что у вас все в порядке с другими браузерами… Может ли в вашем браузере быть применено некоторое масштабирование?4. Привет, @Kaiido.
devicePixelRatio
сообщает о странном значении 1.001312255859375. Я убедился, что масштабирование не используется. Ваша скрипка выглядит идеально!!! , я не знал о существованииdevicePixelRatio
. Я немедленно попробую это сделать на своей странице. Большое спасибо5. Привет @Kaiido, ваше решение работает как шарм. Я предлагаю вам записать его в качестве ответа, чтобы я мог оценить его как правильный. С уважением
Ответ №1:
Решение требует (как отметил @Kaiido) использования Window.devicePixelRatio
:
devicePixelRatio оконного интерфейса возвращает отношение разрешения в физических пикселях к разрешению в пикселях CSS для текущего устройства отображения. Это значение также можно интерпретировать как отношение размеров пикселей: размер одного пикселя CSS к размеру одного физического пикселя. Проще говоря, это сообщает браузеру, сколько фактических пикселей экрана следует использовать для рисования одного пикселя CSS.
Код, который решает проблему:
if (devicePixelRatio != 1.0) {
context.canvas.style.width = (context.canvas.width / devicePixelRatio) "px";
context.canvas.style.height = (context.canvas.height / devicePixelRatio) "px";
}
Это также работает в браузерах, отличных от Chrome, они (в моем случае) возвращают значение 1 для Window.devicePixelRatio
, оставляя предыдущую функциональность нетронутой.