200 lines
8.9 KiB
JavaScript
200 lines
8.9 KiB
JavaScript
/**
|
|
* Reference-theme poster sharing runtime.
|
|
*
|
|
* @package AKSRT
|
|
*/
|
|
(function (window, document, $) {
|
|
'use strict';
|
|
|
|
var WIDTH = 768;
|
|
var HEIGHT = 1168;
|
|
var FONT = '-apple-system,BlinkMacSystemFont,"Segoe UI","Microsoft YaHei",Arial,sans-serif';
|
|
|
|
function notice(message, type) {
|
|
if (typeof window.notyf === 'function') window.notyf(message, type || '');
|
|
}
|
|
|
|
function ajaxUrl() {
|
|
return (window.aksrtData && window.aksrtData.ajaxUrl) || (window._win && window._win.ajax_url) || '';
|
|
}
|
|
|
|
function nonce() {
|
|
return (window.aksrtData && window.aksrtData.nonce) || '';
|
|
}
|
|
|
|
function loadImage(sources) {
|
|
sources = sources.filter(Boolean);
|
|
return new Promise(function (resolve, reject) {
|
|
var index = 0;
|
|
function next() {
|
|
if (index >= sources.length) { reject(new Error('image unavailable')); return; }
|
|
var image = new Image();
|
|
image.crossOrigin = 'anonymous';
|
|
image.onload = function () { resolve(image); };
|
|
image.onerror = function () { index += 1; next(); };
|
|
image.src = sources[index];
|
|
}
|
|
next();
|
|
});
|
|
}
|
|
|
|
function qrCodeDataUrl(text) {
|
|
return new Promise(function (resolve) {
|
|
if (!text || typeof window.tbquire !== 'function') { resolve(''); return; }
|
|
window.tbquire(['qrcode'], function () {
|
|
var host = $('<div>').css({ position: 'fixed', left: '-9999px', top: '-9999px' }).appendTo('body');
|
|
try {
|
|
host.qrcode({ width: 160, height: 160, correctLevel: 0, text: text, background: '#fff', foreground: '#333' });
|
|
var canvas = host.find('canvas').get(0);
|
|
resolve(canvas ? canvas.toDataURL('image/png') : '');
|
|
} catch (error) {
|
|
resolve('');
|
|
} finally {
|
|
host.remove();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function coverRect(boxWidth, boxHeight, sourceWidth, sourceHeight) {
|
|
var sx = 0, sy = 0, sw = sourceWidth, sh = sourceHeight;
|
|
if (sourceWidth / sourceHeight > boxWidth / boxHeight) {
|
|
sw = boxWidth * sourceHeight / boxHeight;
|
|
sx = (sourceWidth - sw) / 2;
|
|
} else {
|
|
sh = boxHeight * sourceWidth / boxWidth;
|
|
sy = (sourceHeight - sh) / 2;
|
|
}
|
|
return { sx: sx, sy: sy, sw: sw, sh: sh };
|
|
}
|
|
|
|
function containRect(x, y, boxWidth, boxHeight, sourceWidth, sourceHeight) {
|
|
var ratio = Math.min(boxWidth / sourceWidth, boxHeight / sourceHeight);
|
|
var width = sourceWidth * ratio;
|
|
var height = sourceHeight * ratio;
|
|
return { x: x + (boxWidth - width) / 2, y: y + (boxHeight - height) / 2, width: width, height: height };
|
|
}
|
|
|
|
function drawLines(ctx, text, maxWidth, maxRows, x, y, lineHeight) {
|
|
var chars = String(text || '').replace(/[\r\n]+/g, ' ').split('');
|
|
var rows = [], line = '';
|
|
chars.forEach(function (char) {
|
|
var next = line + char;
|
|
if (line && ctx.measureText(next).width > maxWidth) { rows.push(line); line = char; }
|
|
else line = next;
|
|
});
|
|
if (line) rows.push(line);
|
|
if (rows.length > maxRows) {
|
|
rows = rows.slice(0, maxRows);
|
|
while (rows[maxRows - 1] && ctx.measureText(rows[maxRows - 1] + '...').width > maxWidth) {
|
|
rows[maxRows - 1] = rows[maxRows - 1].slice(0, -1);
|
|
}
|
|
rows[maxRows - 1] += '...';
|
|
}
|
|
rows.forEach(function (row, index) { ctx.fillText(row, x, y + index * lineHeight); });
|
|
}
|
|
|
|
function renderPoster(data) {
|
|
return Promise.all([
|
|
loadImage([data.banner, data.banner_default, data.banner_spare]),
|
|
data.logo ? loadImage([data.logo]).catch(function () { return null; }) : Promise.resolve(null),
|
|
qrCodeDataUrl(data.url).then(function (src) { return src ? loadImage([src]).catch(function () { return null; }) : null; })
|
|
]).then(function (assets) {
|
|
var banner = assets[0], logo = assets[1], qr = assets[2];
|
|
var canvas = document.createElement('canvas');
|
|
canvas.width = WIDTH; canvas.height = HEIGHT;
|
|
var ctx = canvas.getContext('2d');
|
|
var imageHeight = HEIGHT / 2;
|
|
|
|
ctx.fillStyle = '#fff'; ctx.fillRect(0, 0, WIDTH, HEIGHT);
|
|
var crop = coverRect(WIDTH, imageHeight, banner.width, banner.height);
|
|
ctx.drawImage(banner, crop.sx, crop.sy, crop.sw, crop.sh, 0, 0, WIDTH, imageHeight);
|
|
|
|
var now = new Date();
|
|
ctx.fillStyle = '#fff'; ctx.font = '35px ' + FONT; ctx.fillText('周' + '日一二三四五六'.charAt(now.getDay()), 30, 70);
|
|
ctx.font = '28px ' + FONT; ctx.fillText((now.getMonth() + 1) + ' / ' + now.getDate(), 30, 115);
|
|
|
|
ctx.strokeStyle = '#00a1ff'; ctx.lineWidth = 7; ctx.lineCap = 'round'; ctx.beginPath();
|
|
ctx.moveTo(40, imageHeight + 38); ctx.lineTo(40, imageHeight + 74); ctx.stroke();
|
|
|
|
ctx.fillStyle = '#424242'; ctx.font = 'bold 35px ' + FONT;
|
|
drawLines(ctx, data.title, WIDTH - 120, 2, 58, imageHeight + 68, 52);
|
|
ctx.fillStyle = '#fc4e1f'; ctx.font = '24px ' + FONT;
|
|
drawLines(ctx, data.tags, WIDTH - 80, 1, 40, imageHeight + 172, 36);
|
|
ctx.fillStyle = '#585858'; ctx.font = '25px ' + FONT;
|
|
drawLines(ctx, data.content, WIDTH - 80, 4, 40, imageHeight + 232, 39);
|
|
|
|
ctx.strokeStyle = 'rgba(122,122,122,.2)'; ctx.lineWidth = 2; ctx.strokeRect(30, HEIGHT - 230, WIDTH - 60, 200);
|
|
if (logo) {
|
|
var logoRect = containRect(80, HEIGHT - 190, 300, 100, logo.width, logo.height);
|
|
ctx.drawImage(logo, logoRect.x, logoRect.y, logoRect.width, logoRect.height);
|
|
}
|
|
if (qr) ctx.drawImage(qr, WIDTH - 200, HEIGHT - 200, 120, 120);
|
|
ctx.fillStyle = '#b4b4b4'; ctx.font = '22px ' + FONT; ctx.textAlign = 'center';
|
|
ctx.fillText(data.description || '扫一扫 立即查看', WIDTH / 2, HEIGHT - 52);
|
|
return canvas.toDataURL('image/png');
|
|
});
|
|
}
|
|
|
|
function modalId(postId) { return 'modal_poster_' + postId; }
|
|
|
|
function createModal(postId) {
|
|
var id = modalId(postId);
|
|
var html = '<div class="modal fade flex jc" id="' + id + '" tabindex="-1" role="dialog" aria-hidden="true">' +
|
|
'<div class="modal-dialog poster-share hover-show" role="document"><div class="modal-content" style="height:220px;overflow:hidden">' +
|
|
'<div class="poster-imgbox hover-show"></div><div class="flex jc loading-mask absolute main-bg radius8">' +
|
|
'<div class="loading zts fa-2x"></div><div class="muted-2-color box-body">正在生成图片,请稍候...</div></div></div>' +
|
|
'<div class="padding-6 abs-center hover-show-con text-center poster-share-actions" style="top:auto">' +
|
|
'<a href="#" class="but cir jb-blue poster-download hide"><i class="fa fas fa-download" aria-hidden="true"></i></a>' +
|
|
'<button type="button" data-dismiss="modal" class="but cir jb-yellow ml10"><svg aria-hidden="true" class="icon em12"><use xlink:href="#icon-close"></use></svg></button>' +
|
|
'</div></div></div>';
|
|
$('body').append(html);
|
|
return $('#' + id);
|
|
}
|
|
|
|
function showPoster($modal, postId, src) {
|
|
var $box = $modal.find('.poster-imgbox').empty().append($('<img>', { src: src, alt: '分享海报', class: 'loaded-img' })).addClass('loaded');
|
|
$modal.find('.poster-download').attr({ href: src, download: 'poster_' + postId + '.png' }).removeClass('hide');
|
|
$modal.find('.loading-mask').fadeOut(160);
|
|
$modal.find('.modal-content').css({ height: $box.outerHeight(), transition: '.2s' });
|
|
window.setTimeout(function () { $modal.find('.modal-content').css({ height: '', overflow: '', transition: '' }); }, 220);
|
|
}
|
|
|
|
function requestPoster($trigger, postId, $modal) {
|
|
$.ajax({
|
|
type: 'POST', url: ajaxUrl(), dataType: 'json',
|
|
data: { action: 'aksrt_poster_share_data', id: postId, nonce: nonce() }
|
|
}).done(function (response) {
|
|
if (!response || !response.success || !response.data) {
|
|
notice((response && response.data && response.data.message) || '海报数据加载失败', 'danger');
|
|
$modal.modal('hide');
|
|
$modal.remove();
|
|
return;
|
|
}
|
|
renderPoster(response.data).then(function (src) { showPoster($modal, postId, src); }).catch(function () {
|
|
notice('海报加载失败,请检查图片跨域设置', 'danger');
|
|
$modal.modal('hide');
|
|
$modal.remove();
|
|
});
|
|
}).fail(function (xhr) {
|
|
var message = xhr.responseJSON && xhr.responseJSON.data && xhr.responseJSON.data.message;
|
|
notice(message || '海报数据加载失败', 'danger');
|
|
$modal.modal('hide');
|
|
}).always(function () { $trigger.removeAttr('disabled'); });
|
|
}
|
|
|
|
$('body').off('click.aksrtPosterShare', '[poster-share]').on('click.aksrtPosterShare', '[poster-share]', function (event) {
|
|
event.preventDefault();
|
|
var $trigger = $(this);
|
|
var postId = parseInt($trigger.attr('poster-share'), 10) || 0;
|
|
if (!postId || $trigger.attr('disabled')) return;
|
|
var $modal = $('#' + modalId(postId));
|
|
if ($modal.length) { $modal.modal('show'); return; }
|
|
$trigger.attr('disabled', 'disabled');
|
|
$('.modal').modal('hide');
|
|
$modal = createModal(postId);
|
|
$modal.modal('show');
|
|
requestPoster($trigger, postId, $modal);
|
|
});
|
|
})(window, document, window.jQuery);
|