PIX优化分享生成海报样式(底部LOGO/描述/二维码)
本文是《技术相关(共62篇)》目录的第 62 篇。阅读本文前,建议先阅读本文前3篇文章:
pix主题分享生成海报的JS位于inc/assets/js/poster.js,代码如下:
// 部分代码来自ZIBILL主题作者
// canvas生成海报
const poster = (function() {
const DEBUG = false
const WIDTH = 700
const HEIGHT = 1160
function init(config) {
const $container = document.querySelector(config.selector)
$container.style.border = '1px solid #f0f0f0'
const $wrapper = createDom('div', 'id', 'wrapper')
const $canvas = createDom('canvas', 'id', 'canvas', 'block')
const $day = createDom('canvas', 'id', 'day')
const $date = createDom('canvas', 'id', 'date')
const $title = createDom('canvas', 'id', 'title')
const $content = createDom('canvas', 'id', 'content')
const $logo = createDom('canvas', 'id', 'logo')
const $description = createDom('canvas', 'id', 'description')
appendChilds($wrapper, $canvas, $day, $date, $title, $content, $logo, $description)
$container.appendChild($wrapper)
const date = new Date()
// day
const dayStyle = {
font: 'bold 80px Helvetica',
color: 'rgba(255, 255, 255, 1)',
position: 'right'
}
drawOneline($day, dayStyle, date.getDate());
// date
const dateStyle = {
font: '23px Helvetica',
color: 'rgba(255, 255, 255, 1)',
position: 'right'
}
drawOneline($date, dateStyle, date.getFullYear() + '/' + (date.getMonth() + 1))
// title canvas
const titleStyle = {
font: '30px Helvetica',
lineHeight: 1.5,
color: 'rgba(255, 255, 255, 1)',
length: 2,
position: 'left'
}
titleStyle.font = (config.titleStyle && config.titleStyle.font) || titleStyle.font
titleStyle.color = (config.titleStyle && config.titleStyle.color) || titleStyle.color
titleStyle.position = (config.titleStyle && config.titleStyle.position) || titleStyle.position
drawMoreLines($title, titleStyle, config.title)
// content canvas
const contentStyle = {
font: '22px Helvetica',
lineHeight: 1.5,
position: 'left',
color: 'rgba(236, 241, 255, 1)'
}
contentStyle.font = (config.contentStyle && config.contentStyle.font) || contentStyle.font
contentStyle.color = (config.contentStyle && config.contentStyle.color) || contentStyle.color
contentStyle.lineHeight = (config.contentStyle && config.contentStyle.lineHeight) || contentStyle.lineHeight
contentStyle.position = (config.contentStyle && config.contentStyle.position) || contentStyle.position
drawMoreLines($content, contentStyle, config.content);
// description
const descriptionStyle = {
font: '24px Helvetica',
color: 'rgba(180, 180, 180, 1)',
lineHeight: 1.2,
position: 'left'
}
drawMoreLines($description, descriptionStyle, config.description)
// background image
const image = new Image();
image.crossOrigin = "Anonymous";
//logo
const logo = new Image();
logo.crossOrigin = "Anonymous";
logo.src = config.logo;
//qrcode
const qrcode = new Image();
qrcode.src = config.qrcode;
const onload = function() {
$canvas.width = WIDTH;
$canvas.height = HEIGHT;
image.src = config.banner;
image.onload = function() {
const ctx = $canvas.getContext('2d')
ctx.fillStyle = 'rgba(255, 255, 255, 1)';
ctx.fillRect(0, 0, $canvas.width, $canvas.height);
// banner
imgRect = coverImg($canvas.width - 40, $canvas.height / 1.2 - 40, image.width, image.height);
ctx.drawImage(image, imgRect.sx, imgRect.sy, imgRect.sWidth, imgRect.sHeight, 20, 20, $canvas.width - 40, $canvas.height / 1.2 - 40);
//覆盖层
ctx.fillStyle="rgba(0, 0, 0, 0.3)";
ctx.fillRect(20,20,$canvas.width - 40,$canvas.height / 1.2 - 40);
// 时间
ctx.drawImage($day, -20, 50)
ctx.drawImage($date, -21, 125)
//logo
var logowidth = logo.width;
var logoheight = logo.height;
var scale = logowidth / logoheight;
var logoh = 200;
var cwidth = logoh * scale;
ctx.drawImage(logo, 70, $canvas.height / 1.2 - 20, cwidth, logoh);
ctx.drawImage(qrcode, $canvas.width - 280, $canvas.height / 1.2 - 20, 220, 220);
//标题文字
ctx.drawImage($title, 20, $canvas.height / 2 + 20)
ctx.drawImage($content, 20, $canvas.height / 2 + 120)
ctx.drawImage($description, 20, $canvas.height / 1.2 + 170)
ctx.strokeStyle = 'rgba(122, 122, 122, 0.5)';
const img = new Image();
img.crossOrigin = "Anonymous";
img.src = $canvas.toDataURL('image/png')
const radio = config.radio || 0.7
img.width = WIDTH * radio
img.height = HEIGHT * radio
img.className = 'poster_load';
ctx.clearRect(0, 0, $canvas.width, $canvas.height)
$canvas.style.display = 'none'
if ($container.querySelector('.poster_load')) {
$container.querySelector('.poster_load').src = img.src;
} else {
$container.appendChild(img);
}
$container.appendChild(img);
$container.removeChild($wrapper)
if (config.callback) {
config.callback($container)
}
}
}
onload()
}
//裁切
function containImg(sx, sy, box_w, box_h, source_w, source_h) {
var dx = sx,
dy = sy,
dWidth = box_w,
dHeight = box_h;
if (source_w > source_h || (source_w == source_h && box_w < box_h)) {
dHeight = source_h * dWidth / source_w;
dy = sy + (box_h - dHeight) / 2;
} else if (source_w < source_h || (source_w == source_h && box_w > box_h)) {
dWidth = source_w * dHeight / source_h;
dx = sx + (box_w - dWidth) / 2;
}
return {
dx,
dy,
dWidth,
dHeight
}
}
function coverImg(box_w, box_h, source_w, source_h) {
var sx = 0,
sy = 0,
sWidth = source_w,
sHeight = source_h;
if (source_w > source_h || (source_w == source_h && box_w < box_h)) {
sWidth = box_w * sHeight / box_h;
sx = (source_w - sWidth) / 2;
} else if (source_w < source_h || (source_w == source_h && box_w > box_h)) {
sHeight = box_h * sWidth / box_w;
sy = (source_h - sHeight) / 2;
}
return {
sx,
sy,
sWidth,
sHeight
}
}
function createDom(name, key, value, display = 'none') {
const $dom = document.createElement(name)
$dom.setAttribute(key, value)
$dom.style.display = display
$dom.width = WIDTH
return $dom
}
function appendChilds(parent, ...doms) {
doms.forEach(dom => {
parent.appendChild(dom)
})
}
function drawOneline(canvas, style, content) {
const ctx = canvas.getContext('2d')
canvas.height = parseInt(style.font.match(/\d+/), 10) + 20
ctx.font = style.font
ctx.fillStyle = style.color
ctx.textBaseline = 'top'
let lineWidth = 0
let idx = 0
let truncated = false
for (let i = 0; i < content.length; i++) {
lineWidth += ctx.measureText(content[i]).width;
if (lineWidth > canvas.width - 60) {
truncated = true
idx = i
break
}
}
let padding = 30
if (truncated) {
content = content.substring(0, idx)
padding = canvas.width / 2 - lineWidth / 2
}
if (DEBUG) {
ctx.strokeStyle = "#6fda92";
ctx.strokeRect(0, 0, canvas.width, canvas.height);
}
if (style.position === 'center') {
ctx.textAlign = 'center';
ctx.fillText(content, canvas.width / 2, 0)
} else if (style.position === 'left') {
ctx.fillText(content, padding, 0)
} else {
ctx.textAlign = 'right'
ctx.fillText(content, canvas.width - padding, 0)
}
}
function drawMoreLines(canvas, style, content) {
const ctx = canvas.getContext('2d')
const fontHeight = parseInt(style.font.match(/\d+/), 10)
if (DEBUG) {
ctx.strokeStyle = "#6fda92";
ctx.strokeRect(0, 0, canvas.width, canvas.height);
}
ctx.font = style.font
ctx.fillStyle = style.color
ctx.textBaseline = 'top'
ctx.textAlign = 'center'
let alignX = 0
if (style.position === 'center') {
alignX = canvas.width / 2;
} else if (style.position === 'left') {
ctx.textAlign = 'left'
alignX = 40
} else {
ctx.textAlign = 'right'
alignX = canvas.width - 40
}
let lineWidth = 0
let lastSubStrIndex = 0
let offsetY = 0
for (let i = 0; i < content.length; i++) {
lineWidth += ctx.measureText(content[i]).width;
if (lineWidth > canvas.width - 120) {
ctx.fillText(content.substring(lastSubStrIndex, i), alignX, offsetY);
offsetY += fontHeight * style.lineHeight
lineWidth = 0
lastSubStrIndex = i
}
if (i === content.length - 1) {
ctx.fillText(content.substring(lastSubStrIndex, i + 1), alignX, offsetY);
}
}
}
return {
init
}
})()
//ajax生成文章海报
$('body').on('click', '.cr_poster', function() {
var post_id = $(this).attr('poster-data');
$('.poster_box').remove();
$('#share_modal_'+post_id+' .poster_box_ap').append('<div class="poster_box"></div>');
function Posterdown(e){
if (e == null) {
return;
}
var modal = '#share_modal_'+post_id;
var url = $(''+modal+' .poster_box img').attr('src');
$(''+modal+' .post_share_box').removeClass('hide');
$(''+modal+' .poster_download').attr('href',url).attr('download','poster_' + post_id + '.png');
$
}
$.ajax({
type: "POST",
url:Theme.ajaxurl,
dataType: 'json',
data: {
action: 'pix_create_poster',
post_id: post_id,
},
beforeSend: function () {
cocoMessage.info('海报生成中..');
$('#share_modal_'+post_id+' .poster_box').append('<div class="loading_box"><div uk-spinner></div></div>');
},
success: function(data){
cocoMessage.success('海报已生成');
$('.loading_box').remove();
poster.init({
banner: data.banner,
selector: '.poster_box',
title: data.title,
content: data.content,
logo: data.logo,
qrcode: data.qrcode,
description: data.des,
callback: Posterdown
});
}
});
});

图片的上半部分没有什么问题,在于调整底部LOGO、描述和二维码的样式。
一、LOGO 的尺寸由 canvas.drawImage() 方法的参数直接定义,代码位置在 init 函数的 image.onload 回调中(绘制 LOGO 的核心逻辑),具体代码段如下:
var logowidth = logo.width;
var logoheight = logo.height;
var scale = logowidth / logoheight;
var logoh = 60;
var cwidth = logoh * scale;
ctx.drawImage(logo, 60, $canvas.height / 1.2 + 30, cwidth, logoh);
ctx.drawImage(qrcode, $canvas.width - 160, $canvas.height / 1.2 + 20, 120, 120);
修改方式:
调整 LOGO 高度:直接修改 var logoh = 60; 中的 60(单位为 px),例如改为 80 会让 LOGO 整体变大,改为 40 会变小。
调整 LOGO 宽度:无需手动改 cwidth,因为它是通过 logoh * scale 自动计算的(保证 LOGO 不拉伸变形);若想强制改宽度,可直接替换 cwidth 为固定值(如 100),但可能导致 LOGO 变形。
调整二维码高度和宽度:最后一行括号内参数依次为「图片对象、x轴位置、y轴位置、宽度、高度」,其中最后两个参数 120, 120 就是控制二维码大小的关键,改为 150, 150 会让二维码变大,改为 100, 100 会让二维码变小。
二、控制 LOGO 与描述(description)的上下左右位置、二维码上下左右位置。
上段代码中:
ctx.drawImage(logo, 60, $canvas.height / 1.2 + 30, cwidth, logoh);
ctx.drawImage(logo, x, y, 宽, 高) 中的第2个参数60是LOGO的X坐标(数值越大,LOGO 越靠右),第3个参数height / 1.2 + 30是LOGO的Y轴坐标。$canvas.height 是海报画布的总高度(默认 1160px,在文件代码开头位置设置),$canvas.height / 1.2 是固定基准位置,+30 是基准位置的偏移量(数值越大,LOGO 越靠下)。
上段代码中:
ctx.drawImage(qrcode, $canvas.width - 160, $canvas.height / 1.2 + 20, 120, 120);
$canvas.width - 160是X轴坐标,$canvas.height / 1.2 + 20是Y轴坐标,+20 是偏移量(数值越大,二维码越靠下)。
本段代码中:
//标题文字
ctx.drawImage($title, 20, $canvas.height / 2 + 20)
ctx.drawImage($content, 20, $canvas.height / 2 + 120)
ctx.drawImage($description, 20, $canvas.height / 1.2 + 110)
ctx.strokeStyle = 'rgba(122, 122, 122, 0.5)';
ctx.drawImage($description, x, y)中的第2个参数20是描述文本的X坐标(数值越大,描述文本越靠右),第3个参数$canvas.height / 1.2 + 110是描述文本的Y轴坐标。同理,$canvas.height / 1.2 是基准位置,+110 是偏移量(数值越大,描述文本越靠下)。
以本站为例,最终修改代码如下:
//logo和二维码
var logowidth = logo.width;
var logoheight = logo.height;
var scale = logowidth / logoheight;
var logoh = 200;
var cwidth = logoh * scale;
ctx.drawImage(logo, 70, $canvas.height / 1.2 - 20, cwidth, logoh);
ctx.drawImage(qrcode, $canvas.width - 280, $canvas.height / 1.2 - 20, 220, 220);
//标题文字
ctx.drawImage($title, 20, $canvas.height / 2 + 20)
ctx.drawImage($content, 20, $canvas.height / 2 + 120)
ctx.drawImage($description, 20, $canvas.height / 1.2 + 170)
ctx.strokeStyle = 'rgba(122, 122, 122, 0.5)';
最终效果如下:

李的日志
好功能,学习一下(tp系统这种功能太少了
似水流年
要不转wp吧!🤭
ymz316
随着博主技术的进步,博客功能是越来越丰富了😀
似水流年
看见值得学习的就想抄一下。🤭
彬红茶
厉害!和子比真的非常像!
似水流年
主题作者说源代码就有一部分来自子比。
obaby
这个配图感觉不高级 有些乱
是不是优化下配色?
似水流年
回头我让AI优化一下。