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

внешний интерфейс CSS Canvas

Недавно я хочу сделать h5 для создания плаката.Принцип заключается в использованииcanvasизdrawImageAPI рисует картинку, думая, что она должна быть очень простой, но обнаружил, что в ней есть большая дыра. После засыпки ямы поделюсь решением.В статье основное внимание уделено следующим двум проблемам.

  • Отрисовка картинок будет иметь междоменные проблемы
  • Сгенерированные изображения и текст имеют неправильный размер, размыты и нечетки.

1. Демонстрация первого теста

<style>
.J_ret_poster {
    position: fixed;
    z-index: 999;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
}
</style>

 <canvas hidden id="J_poster">你的浏览器版本较低,请换个浏览器试试</canvas>
 
 <script>
    const $ = q => document.querySelector(q),
        $JPoster = $('#J_poster'),
        $btn = $('J_btn'),
        ctx = $canvas.getContext('2d'),
        $bg = new Image(), // 需要的背景图片(海报底图)
        $retImg = new Image(), // 最终生成的图片
        winW = window.innerWidth,
        winH = window.innerHeight

    // 设置canvas的大小为全屏
    $JPoster.setAttribute('width', winW); 
    $JPoster.setAttribute('height', winH);
    
    // 设置生成的图片相关信息
    $retImg.setAttribute('width', winW);
    $retImg.setAttribute('height', winH);
    $retImg.setAttribute('alt', '海报');
    $retImg.setAttribute('class', 'J_ret_poster');
    
    $bg.src = 'http://canvas-img.oss-cn-shenzhen.aliyuncs.com/normal.jpg';
    $bg.onload = () => {
        ctx.drawImage($bg, 0, 0, winW, winH); // 从窗口的左上角开始画起,铺满整个屏幕
        ctx.font = '14px Arial';
        ctx.fillStyle = '#fff';
        ctx.fillText('这是test测试文字123', 100, 100);
        
        const retUrl = $JPoster.toDataURL('image/png'); // 生成的图片url
        $retImg.setAttribute('src', retUrl);
        $retImg.onload = () => {
            $('body').appendChild($retImg); // 添加到body下
        }
    }
 </script>

ошибка после запуска

1.1 Существует междоменная проблема с изображением

cors-error
сообщил о междоменной ошибке

1.2 Добавьте заголовки междоменных запросов к изображениямaccess-control-allow-origin:*

Поскольку здесь я использую oss Aliyun, я буду использовать диаграмму, чтобы проиллюстрировать, как работать (если нет oss Aliyun, вы также можете использовать узел, чтобы перенаправить его самостоятельно)

ali-set-cors
Потом снова возникла проблема!
toDataURL-error
Вот инструкции

1.3 Добавьте к изображению атрибут crossOrigin

$bg.crossOrigin = ''; // 跨域设置,这里不用设置为`Anonymous`也是可以的

Тогда картинка выйдет

size-big

На данный момент возникла проблема, неправильный размер изображения

2. Размер холста неправильный и размытый

2.1 Ошибка размера

Причина: Размер рисуемой картинки(1242*2208) намного больше, чем размер экрана нашего iphone 6sp414*736, поэтому предполагается, чтоcanvasизcssРазмер задается размером нашего экрана, поэтому рисунок должен занимать всю площадь экрана.

// css定宽高为全屏
$JPoster.style.width = winW + 'px';
$JPoster.style.height = winH + 'px';

Затем в симуляторе хрома размер изображения показывает, что все нормально

pc-display


Однако на мобильном телефоне появилась новая проблема, изображения и текстразмытоиз! ! (Если вы не видите размытия, вы можете сравнить его со следующим изображением)

mobile-display

2.2 Размытие рисунка

потому чтоcanvasНе векторная графика, а в растровом режиме, как картинки. высокоdpiУстройства отображения означают больше пикселей на квадратный дюйм. то естьдвойной экран, браузер отобразит2ширина пикселя для рендеринга пикселя,canvasсуществуетRetinaЭкран эквивалентен занятию2умноженное на пространство, что эквивалентно увеличению изображенияудвоенный, поэтому нарисованные картинки и текст будут размыты. Поэтому делайтеRetinaЭкранная адаптация, главное знать соотношение пикселей устройства на текущем экране, а затемcanvasувеличьте масштаб до соотношения пикселей устройства для рисования, затемcanvasиспользоватьcssУстановите размер экрана для отображения.

Решения: в браузереwindowобъект имеет одинdevicePixelRatioАтрибут, который представляет соотношение пикселей экрана устройства, то есть рендеринг с шириной в несколько пикселей.1пикселей.

аналогично, вcanvas contextСуществует такжеbackingStorePixelRatioатрибут, значение этого атрибута определяет, когда браузер отображаетcanvasРаньше для хранения информации о холсте использовалось несколько пикселей.backingStorePixelRatioАтрибуты получаются по-разному у разных производителей браузеров, поэтому для достижения совместимости необходимо добавлять префиксы браузеров.

Вот источник цитаты

Код:

    let devRatio = window.devicePixelRatio || 1, // 获取设备像素比
        // ctx的像素比
        backingStore = ctx.backingStorePixelRatio ||
        ctx.webkitBackingStorePixelRatio ||
        ctx.mozBackingStorePixelRatio ||
        ctx.msBackingStorePixelRatio ||
        ctx.oBackingStorePixelRatio ||
        ctx.backingStorePixelRatio || 1;
  
    const ratio = devRatio / backingStore;
    // canvas放大像素比倍
    $JPoster.setAttribute('width', winW * ratio);  
    $JPoster.setAttribute('height', winH * ratio);
    // canvas 放大后,相应的绘制图片也要放大
    ctx.scale(ratio, ratio);

Тогда наша картинка, наконец, нормально отрисовывалась и четко отображалась на мобильном телефоне.

draw-right

3. Другие моменты, на которые следует обратить внимание

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