Глубокое понимание проблемы размытого рисования холста на мобильной стороне.

JavaScript

Из-за некоторых причин совместимости на мобильной стороне один из наших проектов должен преобразовать PDF-файл в интерфейс, который можно напрямую просматривать на странице мобильной версии. Чтобы облегчить решение, мы используем плагин pdf.js, который может конвертировать pdf в холст и рисовать его на странице. Однако в ходе тестирования выяснилось, что в мобильном браузереОтрисовываемое содержимое очень размыто (как показано на рисунке ниже), после анализа выясняется, что это вызвано высоким разрешением экрана мобильного терминала.Задокументируйте причины и результаты запроса после того, как проблема была решена

移动端显示十分模糊

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

Некоторые базовые знания об экране

  • Физический пиксель (DP)

    Физические пиксели также называют пикселями устройства. Мы часто слышим о разрешении мобильных телефонов как о физических пикселях. Например, физическое разрешение iPhone 7 составляет 750 * 1334. Экран состоит из пикселей, то есть экран имеет 750 пикселей по горизонтали и 1334 пикселя по вертикали.

  • Независимый от устройства пиксель (DIP)

    Также известный как логические пиксели, например, размер Iphone4 и Iphone3GS составляет 3,5 дюйма, физическое разрешение iphone4 составляет 640 * 980, а 3gs - только 320 * 480, если мы нарисуем изображение шириной 320 пикселей в соответствии с реальным макетом, в iphone4 только половина верха имеет контент, а оставшаяся половина пустая.Чтобы избежать этой проблемы, мы ввели логические пиксели и установили логические пиксели обоих мобильных телефонов на 320px, что удобно для рисования.

  • соотношение пикселей устройства(ДНР)

    Вышеупомянутые аппаратно-независимые пиксели предназначены для удобства вычислений. Мы унифицируем логические пиксели устройства, но физические пиксели, представленные каждым логическим пикселем, не являются детерминированными. Чтобы определить взаимосвязь между физическими пикселями и логическими пикселями в случае немасштабированных, мы вводим понятие соотношения пикселей устройства (DPR)

    设备像素比 = 设备像素 / 逻辑像素
    DPR = DP / DIP
    

Выше было сказано много теорий, и ниже прилагается диаграмма, чтобы объяснить

Как видно из приведенного выше рисунка, при одинаковом размере логических пикселей экран высокого разрешения имеет больше физических пикселей. На обычном экране 1 логический пиксель соответствует 1 физическому пикселю, а на экране высокой четкости с dpr = 2 1 логический пиксель состоит из 4 физических пикселей. Это также является причиной того, что экран высокой четкости более тонкий.

Некоторые базовые знания о холсте

  • Холст рисует растровые изображения

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

    По растровым изображениям мы объясним это позже, а сейчас нам нужно только знать, что изображение, нарисованное с помощью canvas, является растровым.

  • свойства ширины и высоты холста

    Новичкам очень легко ошибиться в свойствах ширины и высоты холста. Эти два свойства часто путают со свойствами ширины и высоты в CSS.

    Например, у нас есть следующий код (1):

    <canvas width="600" height="300" style="width: 300px; height: 150px"></canvas>
    
    • Ширина и высота в стиле соответствуют ширине и высоте элемента холста в интерфейсе соответственно.То есть ширина и высота по стилю
    • ширина и высота в атрибутеОн представляет собой ширину и высоту фактического пикселя холста.

    Если вы все еще не можете понять, вы можете представить себе следующий код (2):

    <!-- logo.png的像素为600 * 300  -->
    <img  style="width: 300px; height: 150px" src="logo.png" /> 
    

    Ширина и высота холста по умолчанию 300 * 150. После установки CSS,Холст будет масштабироваться в соответствии с установленной шириной и высотой css (обратите внимание, что он не обрезается), что совпадает с тегом img

    Приведенный выше код (1) на самом деле можно объяснить более популярным способом, то есть 1 логический пиксель фактически заполняется 2 пикселями холста.

Предварительное обсуждение причин нечеткости

Вышеизложенное является кратким введением в необходимые базовые знания, а следующее представляет собой формальное исследование.

Прежде всего, мы упомянули, что использование холста для рисования изображений — это растровое изображение, и jpg и png, которые мы обычно используем, также являются растровыми изображениями. Так что же такое растровое изображение?

Растровая карта, также известная как пиксельная карта или растровая карта, хранит и отображает изображения, записывая цвет, глубину и другую информацию о каждой точке изображения. Говоря более конкретно, вы можете представить растровое изображение как огромную головоломку с бесчисленным количеством частей, каждая из которых представляет собой однотонный пиксель.Теоретически 1 пиксель растрового изображения соответствует 1 физическому пикселю.. Но что, если вы используете экран высокой четкости, такой как экран Apple Retina, для просмотра изображения?

Предположим, у нас есть следующий код, который будет отображаться на экране retina iphone4:

<canvas width="320" height="150" style="width: 320px; height: 150px"></canvas>

Физический пиксель самого iphone4 составляет 640 * 980, в то время как независимый от устройства пиксель составляет 320 * 480, что означает, что 1 пиксель CSS фактически состоит из 4 физических пикселей, пиксель холста составляет 320 * 150, а его пиксель CSS составляет 320. * 150, Это означает, что 1 пиксель css будет состоять из 1 элемента холста, поэтому для преобразованияПод экраном Retina 1 пиксель холста (или 1 пиксель растрового изображения) будет заполнен физическими пикселями 4. Поскольку один пиксель растрового изображения не может быть далее разделен, можно выбрать только ближайший цвет, что приводит к размытым изображениям..

Если есть сомнения, то следующую картинку можно проиллюстрировать на растровом изображении экрана сетчатки, как заполнить:

Левая часть приведенного выше изображения - это правило отображения под обычным экраном, видно, что имеется 4 растровых пикселя, а правая сторона экрана высокой четкости имеет 16 пикселей. Поскольку пиксели нельзя обрезать, цвет меняется.

Но есть еще один момент, который четко не объяснен, поэтому изображение будет принимать ближайший цвет вместо того, чтобы напрямую принимать исходное значение, которое также является вдохновителем размытия.

За кулисами --- технология сглаживания

Один из моих одноклассников объяснил мне следующее: мы только что сказали, что каждый элемент растрового изображения на самом деле является пикселем сплошного цвета. Теперь предположим, что нам нужно нарисовать число «0» на обычном экране с размером css 4px * 4px и dpr, равным 1, тогда то, что мы нарисуем, должно выглядеть так, как показано ниже, где 1 представляет черные пиксели, а 0 — белые пиксели.

Видно, что когда dpr относительно небольшой, наш паттерн "0" все же более очевиден. Теперь, если наш размер css останется неизменным, но мы изменим изображение, которое будет отрисовываться под экран retina, какой будет эффект ?

Мы знаем, что под экраном сетчатки один пиксель css представляет собой физические пиксели 4. Если мы не будем выполнять никакой обработки и расположим матрицу непосредственно в соответствии с вышеизложенным и расширим матрицу, мы обнаружим, что под экраном сетчатки наш шаблон имеет очень очевидная зубчатость.Изображению заметно не хватает намека на оттенок.

Если мы немного изменим изображение, изменим его на следующее изображение

Изображение мгновенно смягчается,Но место, которое должно было быть заполнено 4 0, стало 3 1 плюс 1 0. На самом деле это так называемый процесс сглаживания изображения. Чтобы устранить ощущение неровности, исходный цвет изменяется. На изображении, полном большего количества цветов, соединение изображения становится похожим по цвету для большей естественности, что объясняет, почему Когда заполнив цвет выше, используйте приблизительный цвет вместо естественного цвета.

Сводка причин

На основе приведенного выше объяснения давайте теперь подведем итоги: теперь, когда экраны высокой четкости популярны на мобильных терминалах, 1 пиксель CSS-пикселя фактически соответствует 4 или более физическим пикселям. Однако из-за нашей проблемы с кодом мы рисуем знак равенства между 1 пикселем css и 1 пикселем холста, что приводит к тому, что 1 пиксель холста фактически должен быть заполнен 4 или более физическими пикселями.Чтобы обеспечить плавную обработку изображения, заполните Остальные физические пиксели являются аппроксимацией исходного цвета, что приводит к размытому изображению.

Решения

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

Под объектом окна высокоуровневых браузеров висит свойство devicePixelRatio, которое и является упомянутым выше dpr.

В случае, когда ширина и высота элемента холста css определены, мы можем сделать это

let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
let dpr = window.devicePixelRatio; // 假设dpr为2
// 获取css的宽高
let { width: cssWidth, height: cssHeight } = canvas.getBoundingClientRect();
// 根据dpr,扩大canvas画布的像素,使1个canvas像素和1个物理像素相等
canvas.width = dpr * cssWidth;
canvas.height = dpr * cssHeight;
// 由于画布扩大,canvas的坐标系也跟着扩大,如果按照原先的坐标系绘图内容会缩小
// 所以需要将绘制比例放大
ctx.scale(dpr,dpr);

Сводка опыта

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