Недавно я хочу сделать h5 для создания плаката.Принцип заключается в использованииcanvas
изdrawImage
API рисует картинку, думая, что она должна быть очень простой, но обнаружил, что в ней есть большая дыра. После засыпки ямы поделюсь решением.В статье основное внимание уделено следующим двум проблемам.
- Отрисовка картинок будет иметь междоменные проблемы
- Сгенерированные изображения и текст имеют неправильный размер, размыты и нечетки.
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 Существует междоменная проблема с изображением
сообщил о междоменной ошибке1.2 Добавьте заголовки междоменных запросов к изображениямaccess-control-allow-origin:*
Поскольку здесь я использую oss Aliyun, я буду использовать диаграмму, чтобы проиллюстрировать, как работать (если нет oss Aliyun, вы также можете использовать узел, чтобы перенаправить его самостоятельно)
Потом снова возникла проблема!Вот инструкции
1.3 Добавьте к изображению атрибут crossOrigin
$bg.crossOrigin = ''; // 跨域设置,这里不用设置为`Anonymous`也是可以的
Тогда картинка выйдет
На данный момент возникла проблема, неправильный размер изображения
2. Размер холста неправильный и размытый
2.1 Ошибка размера
Причина: Размер рисуемой картинки(
1242*2208
) намного больше, чем размер экрана нашего iphone 6sp414*736
, поэтому предполагается, чтоcanvas
изcss
Размер задается размером нашего экрана, поэтому рисунок должен занимать всю площадь экрана.
// css定宽高为全屏
$JPoster.style.width = winW + 'px';
$JPoster.style.height = winH + 'px';
Затем в симуляторе хрома размер изображения показывает, что все нормально
Однако на мобильном телефоне появилась новая проблема, изображения и текстразмытоиз! ! (Если вы не видите размытия, вы можете сравнить его со следующим изображением)
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);
Тогда наша картинка, наконец, нормально отрисовывалась и четко отображалась на мобильном телефоне.
3. Другие моменты, на которые следует обратить внимание
Во-первых, размер рисунка, который мы рисуем, должен хорошо контролироваться, и если он слишком велик, сжать его, иначе сгенерированный base64 будет слишком большим, время рисования будет долгим, и могут возникнуть ошибки. Во-вторых, строка заголовка в браузере будет занимать высоту, что приведет к неправильному соотношению размеров окна, и сгенерированное изображение будет деформировано.