В этой статье используется лицензионное соглашение «Signature 4.0 International (CC BY 4.0)», добро пожаловать на перепечатку или изменение для использования, но вам необходимо указать источник.Атрибуция 4.0 Международная (CC BY 4.0)
Автор этой статьи: Су Ян
Создано: 28 июля 2019 г. Статистические слова: 5452 слова Время чтения: 11 минут на чтение Ссылка на эту статью:Поиск teay.com/2019/07/28/…
Используйте Node.js для создания изображений, которые легко распространять
В повседневной работе всегда есть какие-то вещи, которые нужно связать с «пакетной генерацией картинок», особенно когда нужно сделатьРаспространение контентаВ сценарии:В конце концов, изображения более интуитивны и эффективны..
- Есть много потребностей в наборе персонала, но количество слов, разрешенных для публикации на платформе, ограничено. нужно учитывать верстку при создании длинной картинки, реализация на чистом коде слишком громоздка.
- После проведения мероприятия лектор должен поделиться контентом с большим количеством людей, сообщить о мероприятии большему количеству людей и распространить слегка оформленную картинку в кругу друзей.В это время нам нужно сделать картинку для распространения, связанную с лектор.
- Я написал блог, но верстка Weibo и других платформ совершенно испорчена, и формат можно сохранить, только перейдя на распространение длинных изображений.
Использование в Интернете часто хвалятnode canvas
/ webgl
/ web canvas
решить проблему. На мой взгляд, не обязательно, на самом деле использование Node.js для написания десятков строк скрипта с безголовым браузером может решить проблему. Итак, давайте поговорим о том, как писать простые и надежные сценарии Node.
написать впереди
Много раз мы одержимы использованием определенного языка и определенной технологии для решения всех проблем.Хотя стоимость обслуживания программы очень низкая, с точки зрения эффективности выполнения это не стоит потерь.
Конечно, если это простой и чистый контент, такой как подписи посетителей и генерация QR-кода, то другое дело, здесь не нужно продумывать сложную верстку и почти не нужно настраивать стиль контента, например, как я уже упоминал ранее. :
- Создайте высокопроизводительный сервис QR-кода с помощью Docker и Nginx.
- Быстро внедрить онлайн-сервис декодирования QRCode с помощью Docker и Node.
Начнем с самого простого, пакетного создания изображений запроса на набор персонала (акцент на верстке).
Пакетная генерация изображений спроса на набор персонала
Изображения, основанные на потребностях в наборе персонала, ориентированы на макет контента, особенно подходящий для написания в Markdown, с помощью инструментов создания статических веб-сайтов, таких как Hugo / Hexo, для создания кратких и красивых страниц, а затем получения желаемых результатов с помощью скриншотов и других методов.
кHugo
Например, после подготовки копии резюме поместите ее вcontent/posts
, структура каталогов выглядит следующим образом:
.
├── archetypes
│ └── default.md
├── config.toml
├── content
│ └── posts
│ ├── 招聘岗位A.md
│ ├── 招聘岗位B.md
│ ├── 招聘岗位C.md
│ ├── 招聘岗位D.md
│ └── 招聘岗位E.md
├── layouts
├── static
└── themes
Затем выполнитеhugo server
, вы увидите вывод журнала, аналогичный следующему:
hugo server
| ZH-CN
+------------------+-------+
Pages | 18
Paginator pages | 0
Non-page files | 0
Static files | 12
Processed images | 0
Aliases | 1
Sitemaps | 1
Cleaned | 0
Total in 24 ms
Watching for changes in /Users/soulteary/work/hugo-jd/jd/{content,data,layouts,static,themes}
Watching for config changes in /Users/soulteary/work/hugo-jd/jd/config.toml
Environment: "development"
Serving pages from memory
Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender
Web Server is available at http://localhost:1313/ (bind address 127.0.0.1)
Press Ctrl+C to stop
Открыть в браузереlocalhost:1313
, вы можете увидеть страницы с приличной версткой.
Затем напишите несколько строкCSS
Закодируйте, выполните адаптацию мобильного терминала, а затем выведите его в виде изображения, и все готово, но если вы хотите получить изображения с хорошим опытом чтения на мобильных устройствах (особенно на экранах с высокими показателями), просто используйте скриншот системы. ярлыки или обычные скриншоты Скриншот программного обеспечения может не соответствовать потребностям, и заинтересованные учащиеся могут узнать о DPR.
Поэтому при создании скриншотов вам необходимо имитировать экранное устройство с высоким разрешением для захвата изображений, например следующий скрипт Node.js с менее чем 20 строками:
'use strict';
const puppeteer = require('puppeteer');
const { 'iPhone X': deviceModel } = require('puppeteer/DeviceDescriptors');
const { readFileSync } = require('fs');
const links = readFileSync('./target.txt', 'utf-8').split('\n').filter(n => n);
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.emulate(deviceModel);
for (let i = 0, j = links.length; i < j; i++) {
await page.goto(links[i]);
await page.screenshot({ path: `./jd-${i}.png`, fullPage: true });
}
await browser.close();
})();
Этот скрипт имитирует ситуацию, когда экран высокого разрешения устройства iPhone X заходит на страницу, а затем передаетpuppeteer
Предоставленная возможность скриншота генерирует нужные нам изображения.
Если вы хотите использовать этот образ для создания скрипта, вам также необходимо подготовитьtarget.txt
файле, пропишите адрес страницы, где изображение нужно сгенерировать построчно в файле:
http://localhost/page/1.html
http://localhost/page/2.html
http://localhost/page/3.html
...
Если вы идете хорошо, выполнитеnode 你的图片脚本.js
Вы можете получить результаты, подобные следующим.
Пакетная генерация графа распространения круга друзей
Просматривая круг друзей, я часто вижу, как некоторые друзья присылают рекламные фотографии мероприятия с небольшим чувством дизайна. На самом деле такое изображение может создаваться партиями, но оно немного отличается от приведенного выше примера, поэтому следует использовать другую стратегию.
Во-первых, копирайтинга для этого вида коммуникативной картинки не так много, и она не нуждается в относительно сложном и едином стилевом оформлении, разница между копирайтингом между картинками и картинками относительно невелика, почти только "имя", "аватарка". ", "короткая копия сообщения" и "подбор цвета". Немного отличается, нужно сгенерировать много картинок. Если вы все-таки заранее подготовите кучу md-документов, вы, вероятно, закостенеете от набора текста на клавиатуре.
Для людей, вовлеченных в изображение, мы можем использовать для описания некоторую структурную грамматику, которая избавит от многих проблем, например следующее: (Конечно, вы также можете найти разделитель, который не конфликтует с контентом для сегментации контента )
[
{ name: '小明', title: '讲师' },
{ name: '小刚', title: '嘉宾' }
]
Со структурированными персональными данными, которыми может манипулировать программа, мы затем «рисуем» картинку с помощью фронтенд-технологии (легендарный вырез). Как упоминалось выше, в этом типе изображений отличается лишь небольшое количество информации, например, различаются только имя и идентификатор, поэтому мы можем описать структуру «изображения» следующим образом. (Здесь поленитесь, замените на псевдокод, не реализуйте стиль.)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<h1>我是040期沙龙$TITLE $NAME</h1>
<p>我来自美团技术团队,2018美团技术沙龙资源合辑奉上。</p>
</body>
</html>
в структуре$TITLE
, $NAME
Это контент, который мы хотим заменить динамически.Если мы откроем шаблон непосредственно в браузере, мы увидим следующие результаты.
Как сделать так, чтобы содержимое шаблона «динамически менялось» по нашему желанию? нужна помощь здесьhttp
Этот модуль выполняет динамическую замену содержимого, когда пользователь получает шаблон. Для простоты я здесьexpress
Например, для решения задачи требуется всего 20-30 строк.
const express = require('express');
const app = express();
const port = 3000;
app.get('/', (req, res) => res.redirect('/0'));
const template = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<h1>我是040期沙龙$TITLE $NAME</h1>
<p>我来自美团技术团队,2018美团技术沙龙资源合辑奉上。</p>
</body>
</html>`;
const personsData = [
{ name: '小明', title: '讲师' },
{ name: '小刚', title: '嘉宾' }
];
app.get('/:id', (req, res) => {
const { id } = req.params;
const { name, title } = personsData[id];
return res.send(template.replace('$NAME', name).replace('$TITLE', title));
})
app.listen(port, () => console.log(`App listening on port ${port}!`));
сохранить код какweb.js
, затем выполнитеnode web.js
, откройте браузер, посетитеlocalhost:3000
,илиlocalhost:3000/0
/localhost:3000/1
Информация шаблона становится динамической.
Наконец, соответствующим образом настроив CSS и обратившись к приведенному выше сценарию для пакетной генерации изображений, вы можете получить вид изображения, показанный в начале этого раздела.
Создание изображений для постов в блоге
Вам может быть интересно, в чем разница между созданием изображения для блога и изображением в первом разделе статьи?
Есть два основных отличия:
- При фактическом перехвате контента некоторые элементы необходимо скрыть или «пропустить», чтобы избежать плохого конечного результата.
- Сообщения в блогах обычно имеют большую длину, поэтому размер сгенерированных изображений обычно относительно велик.Некоторые платформы ограничивают размер одного изображения, а
puppeteer
При создании сверхдлинных изображений оно будет «цветочным».
Как избежать перехвата ненужных элементов
Как и часть, обведенная красным каркасом на картинке выше, я не хочу, чтобы меня «записывали» в процессе создания изображения. Если это в браузере, вы можете выполнить код JavaScript на странице, чтобы удалить эти элементы и решить проблему, например:
const selector = "#J_footer-container,.page-navigation-container,.page-comments-container";
const elements = document.querySelectorAll(selector);
for (let i = 0; i < elements.length; i++) {
elements[i].parentNode.removeChild(elements[i]);
}
Конечно, совмещаяpuppeteer
Требует небольшой доработки:
'use strict';
const puppeteer = require('puppeteer');
const { 'iPhone X': deviceModel } = require('puppeteer/DeviceDescriptors');
const { readFileSync } = require('fs');
const targetLinks = readFileSync('./target.txt', 'utf-8').split('\n').filter(n => n);
const elementsRemoved = "#J_footer-container,.page-navigation-container,.page-comments-container";
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.emulate(deviceModel);
for (let i = 0, j = targetLinks.length; i < j; i++) {
await page.goto(targetLinks[i]);
await page.evaluate((selector) => {
const elements = document.querySelectorAll(selector);
for (let i = 0; i < elements.length; i++) {
elements[i].parentNode.removeChild(elements[i]);
}
}, elementsRemoved)
await page.screenshot({ path: `./blog-${i}.png`, fullPage: true });
}
await browser.close();
})();
сохранить код какblog.js
, затем выполнитеnode blog.js
, если статья не очень длинная, вы можете успешно получить длинную картину статьи блога в начале этого раздела.
Разделяйте длинные изображения, чтобы избежать ошибок генерации изображений
Но если вы хотите сгенерировать изображение с особенно длинной статьей, вы получите следующий результат:Незавершенное изображение.
В апреле я проконсультировался с @鹔大, ошибка на этом снимке экрана на самом деле возникла из официальной заявки Google.
DevTools: capture full page screenshot renders blank page for pages higher than 0x4000px.
Bug: 831773
Change-Id: Ia5dfad17af526b495c38d6827292364a1d505dba
TBR: dgozman
Reviewed-on: https://chromium-review.googlesource.com/1010476
Commit-Queue: Pavel Feldman <pfeldman@chromium.org>
Reviewed-by: Pavel Feldman <pfeldman@chromium.org>
Reviewed-by: Dmitry Gozman <dgozman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#550264}
Как показано на рисунке ниже, официальное ограничение высоты изображения, полученного в полноэкранном режиме страницы, из соображений производительности.Заинтересованные студенты могут посмотреть кодОтправить адрес.
Решение также очень простое: скомпилируйте его самиpuppeteer
И снять ограничение, а еще проще, разрезать изображение на части.
Реализация кода не сложная, достаточно написать еще десять строчек на основе предыдущего кода, и проблема решена.
'use strict';
const puppeteer = require('puppeteer');
const { 'iPhone X': deviceModel } = require('puppeteer/DeviceDescriptors');
const { readFileSync } = require('fs');
const links = readFileSync('./target.txt', 'utf-8').split('\n').filter(n => n);
const elementsRemoved = "#J_footer-container,.page-navigation-container,.page-comments-container";
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.emulate(deviceModel);
for (let i = 0, j = links.length; i < j; i++) {
await page.goto(links[i]);
await page.evaluate((selector) => {
const elements = document.querySelectorAll(selector);
for (let i = 0; i < elements.length; i++) {
elements[i].parentNode.removeChild(elements[i]);
}
}, elementsRemoved);
const { width: viewWidth, height: viewHeight } = page.viewport();
const pageHeight = await page.evaluate(_ => document.body.scrollHeight);
const dpr = await page.evaluate('window.devicePixelRatio');
const maxHeight = viewHeight * 8;
const splitCount = Math.ceil(pageHeight / maxHeight);
const lastViewHeight = pageHeight - ((splitCount - 1) * maxHeight)
for (let i = 1; i <= splitCount; i++) {
await page.screenshot({
clip: {
x: 0, y: maxHeight * (i - 1), width: viewWidth,
height: i !== splitCount ? maxHeight : lastViewHeight
},
path: `./out/split-${i}-@${dpr}x.png`
});
}
}
await browser.close();
})();
Сохраните приведенный выше код какsplit.js
, затем выполнитеnode split.js
Можно получить нормальную картинку.
наконец
Если вы читали другие мои статьи, я пытался использовать короткие коды и простые решения для решения многих, казалось бы, сложных задач, с которыми мы сталкиваемся каждый день.
На самом деле, во многих случаях эти требования не сложны.Пока вы готовы успокоиться и разделить их разумно, и постепенно ломать их простыми и надежными решениями, вам будет сделано.
Однако люди, которые что-то делают, часто попадают в присущие им собственные ловушки знаний, думая, что вещи слишком сложны, а их реализация слишком сложна, так что стоимость добавления последующих проектов слишком высока и сложна в обслуживании.
Если вы видите это, я надеюсь, что в процессе работы вы сможете больше думать о том, есть ли более простой способ решить насущную проблему, вместо того, чтобы слепо следовать «изоморфному, высокоуровневому решению».
взаимное поощрение.
—ЭОФ
Теперь у меня есть небольшая группа по метанию, в которую входят друзья, которые любят метать.
В отсутствие рекламы мы будем вместе болтать о софте, HomeLab и вопросах программирования, а также время от времени будем делиться технической информацией о салоне в группе.
Друзья, которые любят бросать, могут отсканировать код, чтобы добавить друзей. (Пожалуйста, укажите источник и цель, иначе не пройдет проверку)