1. Спрос
В последнее время руководитель продукта Dr. Предыдущие изображения были сгенерированы серверной частью и кэшированы в течение трех дней, что привело к несвоевременному обновлению информации. Выполнение этого на переднем конце позволяет избежать этих проблем.
Как только я это услышал, то легко сказать, а не просто ли это функция для сохранения картинок, достаточно посмотреть требования:
- Улучшите информацию о карте, и информация будет более трехмерной при обмене
- Изменить запись профиля
- сохранить запись изображения
- Это может решить проблему времени кеша визитной карточки врача
- Выглядит так ⬇
Проанализируйте две точки
- html отображает информацию о пользователе в режиме реального времени
- Нажмите «Сохранить», чтобы сохранить текущую страницу как изображение локально и не содержит функциональных кнопок.
2. Схема
Потому что я слышал раньше, что есть библиотека, которая может конвертировать HTML в холст, а потом я услышал, что холст можно преобразовать в картинки, а потом я услышал, что картинки можно скачать.... (Разработка в основном зависит от слуха (поиск ), это бред )
Тогда мой основной план:
html -> canvas -> image -> a[download]
- html2canvas.js: преобразование htmldom в элемент холста.портал
- canvasAPI: toDataUrl() может конвертировать холст в формат base64
- Создайте тег [download], чтобы инициировать событие клика для загрузки.
3. Производительность ямы
Теперь, когда план определен, давайте начнем выходить на пит-лейн и выступать, 👏
3.1 Принцип
Официальное введение выглядит следующим образом:
js будет проходить через узел DOM загруженной страницы, собирать информацию обо всех элементах, а затем использовать эту информацию для отображения страницы. Другими словами, эта библиотека на самом деле не делает скриншот страницы, а рисует холст по крупицам на основе элементов и атрибутов, считанных из DOM. В результате он правильно отображает только те элементы и свойства, которые понимает, а это означает, что многие свойства CSS не работают.
// v0.4.1
html2canvas(element, {
onrendered: function(canvas) {
// 现在你已经拿到了canvas DOM元素
}
});
// v0.5.0
html2canvas(element, options).then(canvas => {
// 现在你已经拿到了canvas DOM元素
});
Итак, в принципе можно догадаться, что весь рабочий процесс должен быть:
- Рекурсивно обработайте каждый узел и запишите, как он должен быть нарисован. (Например, div рисует границы и фон, text рисует текст и т. д.)
- Рассмотрим иерархию узлов. Например, влияние многих атрибутов стиля, связанных с макетом: z-index, float, position и т. д.
- Картина с низкого уровня до холста и нарисовать. Высокий уровень высокого покрытия низкий (процесс рендеринга самого браузера очень похож).
3.2 Яма 💀
В настоящее время существует много официальных версий, официальная версияv0.4.1 - 7.9.2013
, последняя версияv0.5.0-beta4
, то для нашего развития, если мы не играемся с новыми фичами что ли, мы обычно выбираем официальную версию.В итоге мы попали в первую яму и долго лезли.
3.2.1 Изображение размыто
Потому что во время разработки для создания холста использовался симулятор хрома, и размытие не было обнаружено, но когда ПК использовался для запроса ресурсов разработки с мобильного телефона, было обнаружено, что размытие экрана было очень очевидным.
Как показано на рисунке:
Легко подумать, что это может быть проблема расчета плотности пикселей на мобильном терминале.
Соотношение пикселей устройства (называемое dpr) определяет соответствие между физическими пикселями и независимыми от устройства пикселями.Его значение можно получить по следующей формуле:
设备像素比 = 物理像素 / 设备独立像素 // 在某一方向上,x方向或者y方向
Знать это бесполезно, потому что в документации вообще нет места для настройки соотношения пикселей. .
Однако в ходе исследований выясняется, что официальные документы на самом деле до сих пор0.4.1
да, из0.5.0
Фактически, с самого начала версии он тайно поддерживает пользовательский холст в качестве элемента конфигурации, и он начнет рисовать на основе холста, который мы передали. Поэтому, когда мы вызываем html2canvas, мы можем сначала создать холст подходящего размера, а затем передать его.
Без лишних слов сначала обновите библиотеку до0.5.0
,Потом:
/**
* 根据window.devicePixelRatio获取像素比
*/
function DPR() {
if (window.devicePixelRatio && window.devicePixelRatio > 1) {
return window.devicePixelRatio;
}
return 1;
}
/**
* 将传入值转为整数
*/
function parseValue(value) {
return parseInt(value, 10);
};
/**
* 绘制canvas
*/
async function drawCanvas(selector) {
// 获取想要转换的 DOM 节点
const dom = document.querySelector(selector);
const box = window.getComputedStyle(dom);
// DOM 节点计算后宽高
const width = parseValue(box.width);
const height = parseValue(box.height);
// 获取像素比
const scaleBy = DPR();
// 创建自定义 canvas 元素
const canvas = document.createElement('canvas');
// 设定 canvas 元素属性宽高为 DOM 节点宽高 * 像素比
canvas.width = width * scaleBy;
canvas.height = height * scaleBy;
// 设定 canvas css宽高为 DOM 节点宽高
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
// 获取画笔
const context = canvas.getContext('2d');
// 将所有绘制内容放大像素比倍
context.scale(scaleBy, scaleBy);
// 将自定义 canvas 作为配置项传入,开始绘制
return await html2canvas(dom, {canvas});
}
Приведенный выше код сначала получает соотношение пикселей устройства и создает холст большего размера в соответствии с соотношением. Например, двойной экран - это двойной, тройной экран - это тройной, восемь раз зеркало - это восемь раз...
Скриншот на мобильном телефоне такой же, как эффект отображения html, и разница практически незаметна.
3.2.2 Почему картинка отсутствует?
Скриншот с ПК:
Причины могут быть разные.После расследования выясняется, что картинки в канве являются междоменными.объяснил здесь
В общем, так оно и есть: вы можете рисовать междоменные картинки в канвасе, но в это время канвас находится в «загрязненном» состоянии, а загрязненный канвас вызовет проблемы при использовании таких API, как toDataUrl().
Итак, теперь нам нужно сделать две вещи:
- установить элемент img
crossOrigin
свойство, значениеanonymous
- Настройки сервера изображений разрешают междоменное использование (возврат заголовка CORS)
Первое сделать несложно, т.к. сам html2canvas поддерживает настройкуuseCORS: true
;
А вот второе зависит от ситуации. Когда изображение размещается на собственном сервере, достаточно попросить администратора изменить конфигурацию. Но когда изображение помещается в CDN... Что ж, для более быстрого ответа многие CDN кэшируют возвращаемое значение изображения, а кэшированное значение не имеет заголовка CORS. Поскольку заголовок CORS отсутствует, запрос js будет перехвачен. В настоящее время мы можем использовать переадресацию сервера с заголовками CORS при пересылке. (Хорошим решением является использование среднего уровня узла на внешнем интерфейсе для переадресации сервера, что будет обсуждаться отдельно в следующий раз)
В ПОРЯДКЕ. Используя приведенную выше схему, протестируем ее.
Сторона ПК открывается, отлично.
WeChat, эй, все еще не работает.
Позже нашел, используя html2canvas0.5.0
С версией проблем нет, но она используется при разработке0.4.1
Рисование на холсте все равно приведет к потере изображения. Предположение, потому что html2canvas имеет что-то неописуемое, когда он предварительно загружает изображения и рисует изображения. Чтобы решить эту проблему, мы используем очень жесткое решение: используем js, чтобы получить изображение, получить его base64, поместить его обратно в src img, а затем отрисовать.
/**
* 图片转base64格式
*/
img2base64(url, crossOrigin) {
return new Promise(resolve => {
const img = new Image();
img.onload = () => {
const c = document.createElement('canvas');
c.width = img.naturalWidth;
c.height = img.naturalHeight;
const cxt = c.getContext('2d');
cxt.drawImage(img, 0, 0);
// 得到图片的base64编码数据
resolve(c.toDataURL('image/png'));
};
crossOrigin && img.setAttribute('crossOrigin', crossOrigin);
img.src = url;
});
}
В эту яму наконец-то наткнулись.
3.2.3 Фаска
border-radius
Должна быть ≤ половины длины короткой стороны и иметь конкретное значение, иначе могут возникнуть фантастические эффекты.
Кроме того, использование псевдоэлементов для достижения границ в 0,5 пикселя также может иметь прекрасные эффекты, рекомендуется использовать их напрямую.border
Атрибуты
0.4.1
В версии круглое изображение можно установить только как фоновое изображение, img не поддерживает отрисовку border-radius,0.5.0
Нет такого ограничения в
3.2.4 Пунктирная линия
Как упоминалось ранее, html2canvas поддерживает не все свойства css. использоватьborder-style: dashed/dotted
Недействительная, все еще большая сплошная линия. Вырезанное изображение допустимо на ПК, но в WeChat при попытке использовать вырезанное изображение для рендеринга пунктирной линии может по-прежнему сообщатьсяSecurityError, The operation is insecure.
Ошибка, приводящая к сбою преобразования в base64
3.3 Сохранить
идеально:
/**
* 在本地进行文件保存
* @param {String} data 要保存到本地的图片数据
* @param {String} filename 文件名
*/
saveFile(data, filename) {
const save_link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
save_link.href = data;
save_link.download = filename;
const event = document.createEvent('MouseEvents');
event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
save_link.dispatchEvent(event);
}
Реальность:
ПК: Отлично.Босс WeChat: Извините, что вы сказали? Я не слышу? !
Ну, в WeChat вообще ничего не происходит. ПроверятьWeChat SDKВыяснил:
-
downloadImage
поддерживается толькоuploadImage
Изображение, загруженное интерфейсом. -
uploadImage
Интерфейс поддерживает толькоchooseImage
Картинка, выбранная интерфейсным альбомом. -
chooseImage
Интерфейс предназначен для выбора изображений из локального альбома. - Так вот вопрос, фотки все в альбоме, что еще надо сделать?
- ....
4. Доставить (с болью и компромиссом)
Окончательное решение:
- Пользователь заходит на эту страницу
- Получите всю информацию о текущем пользователе, аватаре, QR-коде и т. д.
- Преобразовать все изображения в base64
- оказывать
html
- рисовать
canvas
- сохранить холст как base64
- заменять
html
заimg
,src
как base64 - После завершения преобразования страницы в изображение пользователи WeChat могут долго нажимать на страницу, чтобы вызвать actionSheet для идентификации или сохранения изображения.
То есть, когда пользователь впервые заходит на страницу, отображается html. После выполнения js удалите исходный html и замените его картинками.
Вернемся к нашим потребностям:
- html отображать информацию о пользователе в режиме реального времени
- Нажмите «Сохранить», чтобы локально сохранить текущую страницу в виде изображения.
На самом деле окончательно реализован только первый пункт, а второй пункт фактически реализован наполовину.Хотя картинка сгенерирована, функция сохранения по-прежнему требует от пользователя долгого нажатия на картинку и вызова встроенного меню WeChat для завершения . При разработке H5 после рассмотрения WeChat могут возникнуть некоторые проблемы и ограничения, которые ранее не учитывались, и продакт-менеджеры и программисты должны знать об этом как можно больше. Знайте, что можно и чего нельзя делать в WeChat, снижая затраты на разработку и повторное общение.
Надеюсь, что приведенный выше контент может быть полезен для вашего будущего развития.
Автор: Сиреневый сад f2e - Гу Чунси