скажи заранее
03.06.2020 Добавлены меры предосторожности
Если вам не нужен окончательный эффект, используйте html2canvas для всей статьи, domtoimage не нужен!
текст
Предполагается, что эта статья не предназначена для многих людей, поэтому я не буду представлять, что она делает. ... Сначала я просто хотел использовать domtoimage, чтобы делать скриншоты своих узлов, но, перепробовав бесчисленное количество решений, я, наконец, потерпел поражение.
Есть три причины
- Не использует прокси и не должен быть импортирован локально (конечно, предполагается, что ваш сервер изображений не находится в интрасети, то есть может быть запрошена и внешняя сеть)
- Совместимость со всеми мобильными терминалами (сложность в сафари)
- Вы должны получить base64 изображения png или Jpeg (режим экспорта svg для domtoimage возможен, но если вы не загружаете формат svg, вы никогда не сможете превратить его в png base64, что кажется многообещающим, но на самом деле яма в южной стене)
После полутора дней барабанной игры окончательное решение — dom-to-image и html2canvas для сотрудничества. Конечно, вы думаете, что это нормально, чтобы просто использовать его? Нет! Если вы его не посмотрите, вы все равно умрете в canvas.toDataURL
1. Загрузите и импортируйте
Обратите внимание, что я используюВерсияс тобойсоответствовать
{
"dom-to-image": "^2.6.0",
"html2canvas": "^1.0.0-rc.5"
}
import domtoimage from 'dom-to-image'
import html2canvas from 'html2canvas'
2. Как использовать
1. Определите основную функцию
Основная функция отвечает за вызов, и он нам нужен, чтобы судить о направлении двух моделей
const domToImage = ({ el, android, ios, success, error, handle } = {}) => {
if (!el) {
console.warn('domToImage: 未找到该节点,无法执行后续的截图操作')
return
}
// ios = html2canvas
if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {
IosHandle(el, ios, success, error, handle)
}
// 安卓 || pc = domtoimage
else {
AndroidRender(el, android, success, error, handle)
}
}
}
Разделим гадости, ios идет html2canvas, андроид и ПК идут domtoimage, вводятся параметры:
- el: узел элемента dom
- Android: элемент конфигурации dom-to-image — конкретные параметры можно найти по ссылке на официальном сайте позже.
- ios: элемент конфигурации html2canvas
- успех: обратный вызов после успеха
- ошибка: сбой обратного вызова
- handle: Self-processing callback, здесь мы получим возвращаемое значение callback, Boolean, если оно false, то мы вернем разработчику результат выполнения брошенной библиотеки, а свою последующую обработку выполнять не будем
Зачем отвлекать?
Именно по этой причине используются эти две библиотеки.Помимо вышеперечисленных трех проблем, поддержка domtoimage в сафари очень недружественная
message: "The operation is insecure."(небезопасная эксплуатация)
Может быть, кто-то, кто знает об этом, почувствует то же, что и я.
Уведомление: имя переменной правильное, здесь IosHandle вместо IosRender, потому что нужно сделать еще один уровень обработки
2. Выполнение Android — AndroidRender
Параметры андроида относительно просты и объем кода небольшой, но это не значит, что у него нет проблем
const AndroidRender = (el, options, success, error, handle) => {
domtoimage.toPng(el, {
...options,
quality: 0.95,
})
.then(base64 => {
const isNext = handle && handle(base64)
// 这就是上面所提的,若返回false,则让开发人员执行自己的处理
if (handle === false) {
return
}
try {
success && success(type, base64)
} catch (err) {
error && error(err)
}
})
.catch(err => {
error && error(err)
})
}
Уведомление:toJpegКачество будет немного ниже, ноtoPngКажется, на некоторых моделях есть проблемы с совместимостью с черно-белыми экранами (не уверен).
УведомлениеНе стоит недооценивать параметры качества, если вы их не установите, у вас все равно будет более медленная ситуация, разница в качестве 0,95 - это разрыв в 1 МБ и 200 КБ.
3. Исполнение IOS
3.1 Предварительная обработка — IosHandle
Здесь нам нужно определить две функции для использования
const IosHandle = (el, options, success, error, handle) => {
const imgArr = el.querySelectorAll('img')
let i = 0
if (imgArr[0]) {
let timer = setInterval(() => {
clearInterval(timer)
if (imgArr.length !== i) {
error && error('超时')
}
}, 10000);
[...imgArr].forEach((dom) => {
getUrlBlob(dom.src, ((blob) => {
if (blob !== false) {
dom.src = blob
}
i ++
console.log(i)
// 校验是否全部替换完毕
if ((imgArr.length) === i) {
clearInterval(timer)
IosRender(el, options, success, error, handle)
}
}))
})
return
}
IosRender(el, options, success, error, handle)
}
const getUrlBlob = (url, callback) => {
const str = url.substring(0, 50)
// 避免重复加载
if (str.includes('blob:')) {
return callback(false)
}
// 避免img未有src属性的情况,导致未返回
if (!str) {
return callback(false)
}
let canvas = document.createElement("canvas")
let ctx = canvas.getContext("2d")
let img = new Image
img.crossOrigin = 'Anonymous'
img.src = url
img.onload = function () {
canvas.height = img.height
canvas.width = img.width
ctx.drawImage(img, 0, 0)
try {
canvas.toBlob((blob) => {
callback(URL.createObjectURL(blob))
})
} catch (err) {
callback(img.src)
console.error('转换失败,使用原图', err)
}
canvas = null
}
}
Вышеуказанные два фрагмента кода очень важны — ядро заменяет все изображения и конвертирует их в BLOB-объекты до выполнения html2canvas, этот метод не появится.картинка отсутствуетСлучай
картинка отсутствует: Симптом в том, что при снятии снимка экрана картинка иногда теряется, это связано с тем, что html2Canvas делает еще один запрос на картинку в узле, и этот запрос будет напрямую преобразован в канвас для генерации картинки независимо от того, завершена ли загрузка или нет.Именно в этой ситуации эта карта не появится (первый запрос будет закеширован), а по фиче, что Блоб не будет повторять запрос, нам нужно конвертировать его в Блоб перед IosRender, поэтому мы имеем вышеупомянутый IosHandle
Вопрос второй: Почему вы используете setInterval для оценки тайм-аута, потому что вы хотите избежать существования междоменных изображений и вызвать исключения, которые не могут быть захвачены попыткой
3.2 Исполнение — iOSRender
Это еще не конец, давайте определим еще две функции, мы должныАнти-белый край,Исправлена позиция скриншота
const getOffsetTop = (el) => {
let top = el.offsetTop
let parent = el.offsetParent
while (parent) {
top += parent.offsetTop
parent = parent.offsetParent
}
return top
}
const getOffsetLeft = (el) => {
let left = el.offsetLeft
let parent = el.offsetParent
while (parent) {
left += parent.offsetLeft
parent = parent.offsetParent
}
return left
}
const IosRender = (el, options, success, error, handle) => {
// 脱离下主线程
setTimeout(() => {
html2canvas(el, {
scale: 2,
allowTaint: true,
useCORS: true,
width: el.offsetWidth,
height: el.offsetHeight,
x: getOffsetLeft(el),
y: getOffsetTop(el),
...options,
})
.then(canvas => {
const isNext = handle && handle(canvas)
// 若返回为false,则让开发人员执行自己的处理
if (handle === false) {
return
}
try {
const base64 = canvas.toDataURL('image/png')
success && success(type, canvas, base64)
} catch (err) {
error && error(err)
}
})
.catch(err => {
error && error(err)
})
}, 500)
}
Пока что почти так же.Получается base64 изображения холста,поэтому хочу его скачать,и могу делать что хочу,но есть и минусы.Из-за IosHandle время обработки немного больше, поэтому я должен добавить нагрузку.Интерфейс позволяет пользователям воспринимать
Уведомление: Свойство фильтра ignoreElements в html2canvas не поддерживается в этой версии, вы можете выбрать динамическое добавление "data-html2canvas-ignore”, но с этим будет другая проблема, после того, как он будет скрыт, он не будет соответствовать высоте экспортируемого скриншота, и с этим нужно разбираться вручную.
Уведомление:scale: 2 не для того, чтобы картинка была четче, но если не установить это значение, iphoneX будет просто ждать и плакать
4. Звонок
domToImage({
el: document.querySelector('.snapshot'),
android: {
// options ...
},
ios: {
// options ...
},
handle (data) {
console.log(data)
},
success (val) {
console.log(val)
},
error(err) {
console.error(err)
}
})
Как правило, этого достаточно, если вам нужны более качественные эффекты или обработка (HD, резкость), вы можете использовать опции или справиться самостоятельно.
Tips: Компоненты, используемые для скриншотов, обычно скрыты, родительский узел + непрозрачность или позиционирование + zIndex или покидают видимый диапазон, но обратите внимание на отображение: нет и видимость: скрытый неприемлем.
3. Ненормальная сводка (постоянно обновляется)
Вышеизложенное позволяет избежать подавляющего большинства случаев, но все же есть некоторые проблемы, о которых следует знать.
1) Дом должен установить минимальную высоту minHeight
А еще нужно его немного подкорректировать, особенно для динамически добавляемых элементов, родительских узлов dom высота которых не фиксирована, в этом случае загруженное изображение будет абсолютно центрировано и обрезано.
2) Некоторый расширенный синтаксис CSS3 не поддерживается.
Примером этого является -webkit-background-clip: текст; -webkit-text-fill-color: прозрачный; background-image: -webkit-linear-gradient.
4. Внешние ссылки
Элемент конфигурации html2Canvas Options
Элемент конфигурации параметров dom-to-image
о
сделать: o︻そ╆OVE▅▅▅▆▇◤ (все в небе)
Самородки:Наггетс Талант /user/199636…
чердак:zcxy-gs.lofter.com/
сф:сегмент fault.com/U/столовая Джека Ма…
мерзавец:github.com/gs3170981