适配日记簿
This commit is contained in:
+520
-418
@@ -1,418 +1,520 @@
|
||||
// 部分代码来自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 = 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);
|
||||
|
||||
//标题文字
|
||||
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)';
|
||||
|
||||
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');
|
||||
var banner = $(this).attr('banner');
|
||||
var single_content = $('.single-content')
|
||||
var t_content = $('#post-'+post_id+' .t_content')
|
||||
var content = single_content.length>0 ? single_content[0].innerText : t_content[0].innerText
|
||||
var content_length = content.length
|
||||
var permalink = t_content.length>0 ? window.location.origin + '/moments/'+post_id : window.location.origin + window.location.pathname;
|
||||
var title = t_content.length>0 ? Theme.site_title+'-瞬间' : document.title;
|
||||
new QRious({ element: document.getElementById("twoCode"), value: permalink, size: 260, });
|
||||
$('.poster_box').remove();
|
||||
$('#share_modal_' + post_id + ' .poster_box_ap').append('<div class="poster_box"></div>');
|
||||
|
||||
if (content_length > 120) {
|
||||
content = content.substring(0, 80) + '...'
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
$('#share_modal_' + post_id + ' .poster_box').append('<div class="loading_box"><div uk-spinner></div></div>');
|
||||
$('.loading_box').remove();
|
||||
poster.init({
|
||||
banner: banner,
|
||||
selector: '.poster_box',
|
||||
title: title,
|
||||
content: content,
|
||||
logo: Theme.site_logo,
|
||||
qrcode: $('#twoCode').attr('src'),
|
||||
description: Theme.admin_des,
|
||||
callback: Posterdown
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
function convertImgToBase64(url, callback) {
|
||||
var canvas = document.createElement('canvas');
|
||||
var img = new Image();
|
||||
img.crossOrigin = 'Anonymous';
|
||||
img.onload = function () {
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(img, 0, 0);
|
||||
var ext = img.src.substring(img.src.lastIndexOf('.') + 1).toLowerCase();
|
||||
var dataURL = canvas.toDataURL('image/' + ext);
|
||||
callback(dataURL);
|
||||
canvas = null;
|
||||
};
|
||||
img.src = url;
|
||||
}
|
||||
|
||||
|
||||
function handleShare(e, pic, title,pathname) {
|
||||
var permalink = window.location.origin + pathname;
|
||||
switch (e) {
|
||||
case 'wb':
|
||||
var url = `https://service.weibo.com/share/share.php?url=${permalink}&type=button&language=zh_cn&pic=${pic}&title=${title}`
|
||||
window.open(url);
|
||||
break
|
||||
case 'qzone':
|
||||
var url = `https://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url=${permalink}&title=${title}&pics=${pic}`
|
||||
window.open(url);
|
||||
break
|
||||
case "qq":
|
||||
var url = `http://connect.qq.com/widget/shareqq/index.html?url=${permalink}&title=${title}&pics=${pic}`
|
||||
window.open(url);
|
||||
break;
|
||||
case "copy":
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(permalink).then(function() {
|
||||
cocoMessage.success("链接已复制到剪贴板!");
|
||||
}, function(err) {
|
||||
cocoMessage.error("复制失败,请手动复制链接");
|
||||
});
|
||||
} else {
|
||||
// 兼容旧浏览器
|
||||
var tempInput = document.createElement('input');
|
||||
tempInput.value = permalink;
|
||||
document.body.appendChild(tempInput);
|
||||
tempInput.select();
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
cocoMessage.success("链接已复制到剪贴板!");
|
||||
} catch (err) {
|
||||
cocoMessage.error("复制失败,请手动复制链接");
|
||||
}
|
||||
document.body.removeChild(tempInput);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 部分代码来自ZIBILL主题作者
|
||||
// canvas生成海报
|
||||
const poster = (function() {
|
||||
|
||||
const DEBUG = false
|
||||
|
||||
const WIDTH = 700
|
||||
const HEIGHT = 1160
|
||||
|
||||
/**
|
||||
* 将图片URL加载为Image对象,并返回Promise
|
||||
* @param {string} url - 图片URL
|
||||
* @returns {Promise<Image>} - 加载完成的Image对象
|
||||
*/
|
||||
function loadImage(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.crossOrigin = "Anonymous";
|
||||
img.onload = () => resolve(img);
|
||||
img.onerror = () => reject(new Error('Failed to load image: ' + url));
|
||||
img.src = url;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 核心初始化函数
|
||||
* @param {object} config - 配置对象
|
||||
*/
|
||||
function init(config) {
|
||||
const $container = document.querySelector(config.selector)
|
||||
// ------------------ 【修复点 2:清理容器,避免PJAX残余】 ------------------
|
||||
// 在重新绘制前,确保清空容器,移除旧的 img 和 wrapper
|
||||
$container.innerHTML = '';
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
$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 canvas
|
||||
const dayStyle = {
|
||||
font: 'bold 80px Helvetica',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
position: 'right'
|
||||
}
|
||||
drawOneline($day, dayStyle, date.getDate());
|
||||
|
||||
// date canvas
|
||||
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 canvas
|
||||
const descriptionStyle = {
|
||||
font: '24px Helvetica',
|
||||
color: 'rgba(180, 180, 180, 1)',
|
||||
lineHeight: 1.2,
|
||||
position: 'left'
|
||||
}
|
||||
drawMoreLines($description, descriptionStyle, config.description)
|
||||
|
||||
|
||||
// ------------------ 【修复点 1:使用 Promise.all 等待所有图片加载】 ------------------
|
||||
Promise.all([
|
||||
loadImage(config.banner),
|
||||
loadImage(config.logo),
|
||||
loadImage(config.qrcode)
|
||||
]).then(([image, logo, qrcode]) => {
|
||||
|
||||
$canvas.width = WIDTH;
|
||||
$canvas.height = HEIGHT;
|
||||
const ctx = $canvas.getContext('2d')
|
||||
|
||||
// 1. 绘制背景
|
||||
ctx.fillStyle = 'rgba(255, 255, 255, 1)';
|
||||
ctx.fillRect(0, 0, $canvas.width, $canvas.height);
|
||||
|
||||
// 2. 绘制 Banner
|
||||
const 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);
|
||||
|
||||
// 3. 绘制覆盖层
|
||||
ctx.fillStyle="rgba(0, 0, 0, 0.3)";
|
||||
ctx.fillRect(20, 20, $canvas.width - 40, $canvas.height / 1.2 - 40);
|
||||
|
||||
// 4. 绘制时间 (使用预先绘制好的 Canvas)
|
||||
ctx.drawImage($day, -20, 50)
|
||||
ctx.drawImage($date, -21, 125)
|
||||
|
||||
// 5. 绘制 Logo
|
||||
var logoh = 60;
|
||||
var cwidth = logoh * (logo.width / logo.height);
|
||||
ctx.drawImage(logo, 60, $canvas.height / 1.2 + 30, cwidth, logoh);
|
||||
|
||||
// 6. 绘制二维码
|
||||
ctx.drawImage(qrcode, $canvas.width - 160, $canvas.height / 1.2 + 20, 120, 120);
|
||||
|
||||
// 7. 绘制文字 (使用预先绘制好的 Canvas)
|
||||
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)';
|
||||
|
||||
// 8. 转换并显示最终图片
|
||||
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';
|
||||
|
||||
// 清理 Canvas 避免在屏幕上停留
|
||||
ctx.clearRect(0, 0, $canvas.width, $canvas.height)
|
||||
$canvas.style.display = 'none'
|
||||
|
||||
// 移除旧的 wrapper 并添加最终海报图
|
||||
$container.appendChild(img);
|
||||
// 必须先移除再调用 callback,以确保 DOM 结构干净
|
||||
$container.removeChild($wrapper)
|
||||
|
||||
if (config.callback) {
|
||||
config.callback($container)
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('海报图片加载失败:', error);
|
||||
// 可在此处添加错误处理,例如显示一个错误信息
|
||||
$container.removeChild($wrapper);
|
||||
if (config.callback) {
|
||||
// 即使失败也调用回调,传入 null 或错误信息,以便上层逻辑处理
|
||||
config.callback(null);
|
||||
}
|
||||
});
|
||||
// -----------------------------------------------------------------------
|
||||
}
|
||||
|
||||
// 裁剪函数(不变)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// 创建 DOM 元素(不变)
|
||||
function createDom(name, key, value, display = 'none') {
|
||||
const $dom = document.createElement(name)
|
||||
$dom.setAttribute(key, value)
|
||||
$dom.style.display = display
|
||||
$dom.width = WIDTH
|
||||
$dom.height = HEIGHT // 新增:确保 canvas 都有默认高度,尽管后续会重设
|
||||
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
|
||||
lineWidth = ctx.measureText(content).width;
|
||||
if (style.position === 'center') {
|
||||
padding = canvas.width / 2 - lineWidth / 2;
|
||||
} else if (style.position === 'left') {
|
||||
padding = 30; // 保持左侧间距
|
||||
} else {
|
||||
padding = canvas.width - 30; // 保持右侧间距
|
||||
}
|
||||
}
|
||||
|
||||
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.textAlign = '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)
|
||||
let totalLines = 0;
|
||||
|
||||
// 预计算行高并设置 Canvas 高度
|
||||
let lineWidth = 0
|
||||
let currentLine = 0
|
||||
for (let i = 0; i < content.length; i++) {
|
||||
lineWidth += ctx.measureText(content[i]).width;
|
||||
if (lineWidth > canvas.width - 120) {
|
||||
currentLine++;
|
||||
lineWidth = 0
|
||||
}
|
||||
if (i === content.length - 1) {
|
||||
currentLine++;
|
||||
}
|
||||
}
|
||||
totalLines = currentLine;
|
||||
canvas.height = (fontHeight * style.lineHeight) * totalLines + 20; // 留出一些底部边距
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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生成文章海报
|
||||
// 此处调用部分需要确保在 PJAX 环境下,该事件监听器能够重新绑定(如果使用传统的 document.ready,在 PJAX 中需要改为监听 pjax:success 等事件)
|
||||
$('body').on('click', '.cr_poster', function () {
|
||||
var post_id = $(this).attr('poster-data');
|
||||
var banner = $(this).attr('banner');
|
||||
var single_content = $('.single-content');
|
||||
var t_content = $('#post-'+post_id+' .t_content');
|
||||
|
||||
// 确保 content 区域存在
|
||||
var content_element = single_content.length > 0 ? single_content[0] : (t_content.length > 0 ? t_content[0] : null);
|
||||
|
||||
if (!content_element) {
|
||||
// 如果没有内容元素,则不执行后续操作或给出提示
|
||||
console.error('无法获取文章内容元素。');
|
||||
return;
|
||||
}
|
||||
|
||||
var content = content_element.innerText;
|
||||
var content_length = content.length;
|
||||
var permalink = t_content.length > 0 ? window.location.origin + '/moments/' + post_id : window.location.origin + window.location.pathname;
|
||||
var title = t_content.length > 0 ? Theme.site_title + '-瞬间' : document.title;
|
||||
|
||||
// 确保 QRious 库可用
|
||||
if (typeof QRious === 'undefined') {
|
||||
console.error('QRious 库未加载。');
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查 #twoCode 是否存在,如果不存在需要动态创建,否则 QRious 会失败
|
||||
let twoCodeCanvas = document.getElementById("twoCode");
|
||||
if (!twoCodeCanvas) {
|
||||
twoCodeCanvas = document.createElement('canvas');
|
||||
twoCodeCanvas.id = 'twoCode';
|
||||
twoCodeCanvas.style.display = 'none';
|
||||
document.body.appendChild(twoCodeCanvas); // 插入到 DOM 中
|
||||
}
|
||||
|
||||
new QRious({ element: twoCodeCanvas, value: permalink, size: 260, });
|
||||
|
||||
var poster_box_selector = '#share_modal_' + post_id + ' .poster_box';
|
||||
|
||||
// ------------------ 【修复点 2:清理容器】 ------------------
|
||||
// 先移除旧的 poster_box
|
||||
$('.poster_box').remove();
|
||||
// 重新创建并插入新的 poster_box
|
||||
$('#share_modal_' + post_id + ' .poster_box_ap').append('<div class="poster_box"></div>');
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
if (content_length > 120) {
|
||||
content = content.substring(0, 80) + '...'
|
||||
}
|
||||
|
||||
// Posterdown 函数中不再需要移除 loading_box,因为 init 会清空容器
|
||||
function Posterdown(e) {
|
||||
var modal = '#share_modal_' + post_id;
|
||||
|
||||
// 检查海报是否成功生成
|
||||
if (e && $(modal + ' .poster_box img').length > 0) {
|
||||
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');
|
||||
} else {
|
||||
// 错误处理,如果海报生成失败
|
||||
cocoMessage.error("海报生成失败,请检查图片链接!");
|
||||
}
|
||||
}
|
||||
|
||||
// 移除 loading box 的时机提前,或者在 init 开始前完成
|
||||
// $('.loading_box').remove(); // 原始代码中这行在创建 loading_box 之后,但其作用是移除**所有** .loading_box,可能存在问题
|
||||
|
||||
// 重新添加 loading 状态,并在 init 中等待
|
||||
$(poster_box_selector).append('<div class="loading_box"><div uk-spinner></div></div>');
|
||||
|
||||
poster.init({
|
||||
banner: banner,
|
||||
selector: poster_box_selector, // 传入完整的选择器
|
||||
title: title,
|
||||
content: content,
|
||||
logo: Theme.site_logo,
|
||||
qrcode: $('#twoCode').attr('src'),
|
||||
description: Theme.admin_des,
|
||||
callback: Posterdown
|
||||
});
|
||||
|
||||
// 移除 loading 动画
|
||||
$(poster_box_selector + ' .loading_box').remove();
|
||||
});
|
||||
|
||||
|
||||
function convertImgToBase64(url, callback) {
|
||||
var canvas = document.createElement('canvas');
|
||||
var img = new Image();
|
||||
img.crossOrigin = 'Anonymous';
|
||||
img.onload = function () {
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(img, 0, 0);
|
||||
var ext = img.src.substring(img.src.lastIndexOf('.') + 1).toLowerCase();
|
||||
// 修正:确保 toDataURL 使用正确的 MIME type,如果是 jpg/jpeg 应使用 image/jpeg
|
||||
var mimeType = (ext === 'jpg' || ext === 'jpeg') ? 'image/jpeg' : 'image/png';
|
||||
var dataURL = canvas.toDataURL(mimeType);
|
||||
callback(dataURL);
|
||||
canvas = null;
|
||||
};
|
||||
img.onerror = function() {
|
||||
console.error('convertImgToBase64: 无法加载图片 ' + url);
|
||||
callback(null);
|
||||
}
|
||||
img.src = url;
|
||||
}
|
||||
|
||||
|
||||
function handleShare(e, pic, title, pathname) {
|
||||
var permalink = window.location.origin + pathname;
|
||||
|
||||
// 确保 cocoMessage 库可用
|
||||
if (typeof cocoMessage === 'undefined') {
|
||||
console.error('cocoMessage 库未加载。');
|
||||
// 如果 cocoMessage 不可用,可以替换为原生的 alert
|
||||
if (e === 'copy') {
|
||||
alert("链接已复制到剪贴板!");
|
||||
}
|
||||
}
|
||||
|
||||
switch (e) {
|
||||
case 'wb':
|
||||
var url = `https://service.weibo.com/share/share.php?url=${encodeURIComponent(permalink)}&type=button&language=zh_cn&pic=${encodeURIComponent(pic)}&title=${encodeURIComponent(title)}`
|
||||
window.open(url);
|
||||
break
|
||||
case 'qzone':
|
||||
var url = `https://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url=${encodeURIComponent(permalink)}&title=${encodeURIComponent(title)}&pics=${encodeURIComponent(pic)}`
|
||||
window.open(url);
|
||||
break
|
||||
case "qq":
|
||||
var url = `http://connect.qq.com/widget/shareqq/index.html?url=${encodeURIComponent(permalink)}&title=${encodeURIComponent(title)}&pics=${encodeURIComponent(pic)}`
|
||||
window.open(url);
|
||||
break;
|
||||
case "copy":
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(permalink).then(function() {
|
||||
cocoMessage.success("链接已复制到剪贴板!");
|
||||
}, function(err) {
|
||||
cocoMessage.error("复制失败,请手动复制链接");
|
||||
});
|
||||
} else {
|
||||
// 兼容旧浏览器
|
||||
var tempInput = document.createElement('input');
|
||||
tempInput.value = permalink;
|
||||
document.body.appendChild(tempInput);
|
||||
tempInput.select();
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
cocoMessage.success("链接已复制到剪贴板!");
|
||||
} catch (err) {
|
||||
cocoMessage.error("复制失败,请手动复制链接");
|
||||
}
|
||||
document.body.removeChild(tempInput);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user