适配日记簿
@@ -0,0 +1,36 @@
|
||||
/* 瞬间标签与评论间距 */
|
||||
.moment_tag_gap {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
/* 灯箱视频格式 */
|
||||
.fancybox-slide--video .fancybox-content {
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.fancybox-slide--video video.fancybox-video {
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
max-height: 75dvh !important;
|
||||
max-width: 75dvw !important;
|
||||
object-fit: contain;
|
||||
}
|
||||
/* 片刻视频容器鼠标样式 */
|
||||
.fancybox.mo_img {
|
||||
cursor: zoom-in;
|
||||
}
|
||||
/* 图库视频图标hover放大 */
|
||||
.video-fancybox .play-icon {
|
||||
transition: all 0.4s ease !important;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
.video-fancybox:hover .play-icon {
|
||||
transform: scale(1.3) !important;
|
||||
opacity: 1;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 216 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 151 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 217 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 229 KiB |
@@ -0,0 +1,64 @@
|
||||
// 接管手机端底部音乐按钮
|
||||
function modify_m_bottom_music_button_link() {
|
||||
const music = document.querySelector('a[href*="#m_bottom_music_button"]');
|
||||
if (!music) return;
|
||||
music.addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
mu_box_show();
|
||||
});
|
||||
}
|
||||
|
||||
// 视频默认正方形,播放恢复比例
|
||||
function modify_moment_video_size() {
|
||||
const videos = document.querySelectorAll('.pix_video');
|
||||
videos.forEach(video => {
|
||||
if (!video.hasAttribute('data-video-events-bound')) {
|
||||
video.addEventListener('play', () => {
|
||||
video.classList.add('is-playing');
|
||||
});
|
||||
video.addEventListener('ended', () => {
|
||||
video.classList.remove('is-playing');
|
||||
});
|
||||
video.setAttribute('data-video-events-bound', 'true');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// = = = = = = = = = = = = = = = = 触发器 = = = = = = = = = = = = = = = =
|
||||
|
||||
// 集合:PJAX(初始化执行+PJAX执行)
|
||||
function init_anian_pjax() {
|
||||
modify_moment_video_size();
|
||||
}
|
||||
|
||||
// 集合:初始化(仅初始化执行一次)
|
||||
function init_anian_inport() {
|
||||
init_anian_pjax();
|
||||
modify_m_bottom_music_button_link();
|
||||
}
|
||||
|
||||
// PJAX监听
|
||||
document.addEventListener('pjax:complete', function () {
|
||||
init_anian_pjax();
|
||||
})
|
||||
|
||||
// 入口
|
||||
init_anian_inport();
|
||||
@@ -1,217 +1,217 @@
|
||||
var pix = {
|
||||
|
||||
|
||||
/**
|
||||
* 页面運行時間
|
||||
*/
|
||||
addRuntime: function () {
|
||||
const $runtimeCount = document.getElementById('runtimeshow');
|
||||
if ($runtimeCount) {
|
||||
var s1 = $runtimeCount.innerText;//建站时间
|
||||
if (s1) {
|
||||
s1 = new Date(s1.replace(/-/g, "/"));
|
||||
s2 = new Date();
|
||||
var days = s2.getTime() - s1.getTime();
|
||||
var number_of_days = parseInt(days / (1000 * 60 * 60 * 24));
|
||||
$runtimeCount.innerText = number_of_days + '天';
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
loadLightbox: function () {
|
||||
/**
|
||||
* fancybox
|
||||
*/
|
||||
const addFancybox = function (ele) {
|
||||
const runFancybox = (ele) => {
|
||||
ele.each(function (i, o) {
|
||||
const $this = $(o)
|
||||
const lazyloadSrc = $this.attr('data-src') || $this.attr('src')
|
||||
const dataCaption = $this.attr('alt') || ''
|
||||
$this.wrap(`<a href="${lazyloadSrc}" data-fancybox="images" class="fancybox" data-srcset="${lazyloadSrc}"></a>`)
|
||||
})
|
||||
}
|
||||
|
||||
runFancybox($(ele))
|
||||
}
|
||||
const $fancyboxEle = document.querySelectorAll('.single-content :not(a) > img,.single-content > img')
|
||||
if ($fancyboxEle.length > 0) {
|
||||
addFancybox($fancyboxEle)
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
initTopBar: function () {
|
||||
let topBar = $('#masthead .top_bar');
|
||||
var placeholder = document.querySelector('#masthead .uk-sticky-placeholder');
|
||||
var width = $('#masthead').width();
|
||||
var height = '71.9979px;'
|
||||
if (window.innerWidth < 960) {
|
||||
if (window.innerWidth <= 540) {
|
||||
height = '63.9957px'
|
||||
}
|
||||
topBar.addClass('uk-sticky-fixed').css({ "width": width, "top": '0px', "position": "fixed" });
|
||||
$('#masthead .uk-sticky-placeholder').css({ "width": width, "height": height, "margin": "0px" });
|
||||
placeholder.hidden = false;
|
||||
} else {
|
||||
topBar.removeAttr("style");
|
||||
$('#masthead .uk-sticky-placeholder').css({ "width": width, "height": '71.9979px;', "margin": "0px" });
|
||||
placeholder.hidden = true;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
getMsg: function () {
|
||||
var com_msg_btn = $('.com_msg_btn')
|
||||
var msg_modal_inner = $('.msg_modal_inner')
|
||||
let username = Theme.username
|
||||
var f_unread_num = 0;
|
||||
if (com_msg_btn.length > 0) {
|
||||
$.ajax({
|
||||
type: "get",
|
||||
url: `/apis/api.notification.halo.run/v1alpha1/userspaces/${username}/notifications?fieldSelector=spec.unread=true`,
|
||||
success: function (res) {
|
||||
if (res.total == 0) {
|
||||
$('.f_unread_num').remove();
|
||||
}else {
|
||||
f_unread_num = res.total
|
||||
com_msg_btn.append(`<small class="f_unread_num">${res.total}</small>`);
|
||||
}
|
||||
if (msg_modal_inner.length > 0) {
|
||||
$.ajax({
|
||||
type: "get",
|
||||
url: `/apis/api.notification.halo.run/v1alpha1/userspaces/${username}/notifications?page=1&size=10`,
|
||||
success: function (res) {
|
||||
var data = res.items
|
||||
if (data.length > 0) {
|
||||
var unread_box = `
|
||||
<div class="unread_box">
|
||||
<div class="unread_tip"># 您有${f_unread_num}条未读消息 #
|
||||
<a href="/uc/notifications" target="_blank">查看全部</a>
|
||||
</div>
|
||||
`
|
||||
var read_box = `
|
||||
<div class="read_box">
|
||||
`
|
||||
var unreadSum = 0
|
||||
data.forEach((e => {
|
||||
if (e.spec.unread) {
|
||||
unreadSum = unreadSum + 1;
|
||||
unread_box += `
|
||||
<div class="vi_reply_item">
|
||||
<div class="box" msg-name="${e.metadata.name}">
|
||||
<span class="mark_read">标记为已读</span>
|
||||
<div class="avatar_top"><img alt=""
|
||||
src="https://cravatar.cn/avatar/?d=mp"
|
||||
class="avatar avatar-50 photo" height="50" width="50" decoding="async"></div>
|
||||
${e.spec.htmlContent}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
} else {
|
||||
read_box += `
|
||||
<div class="vi_reply_item">
|
||||
<div class="box">
|
||||
<div class="avatar_top"><img alt=""
|
||||
src="https://cravatar.cn/avatar/?d=mp"
|
||||
class="avatar avatar-50 photo" height="50" width="50" decoding="async"></div>
|
||||
${e.spec.htmlContent}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
}
|
||||
))
|
||||
unread_box += `</div>`
|
||||
read_box += `</div><p class="msg_limit">只显示最新10条未读和已读信息</p> `
|
||||
msg_modal_inner.html(unread_box + read_box)
|
||||
|
||||
} else {
|
||||
msg_modal_inner.html(`
|
||||
<p class="no_posts"><small># 暂无消息 #</small><img class="s_nodata"
|
||||
src="/themes/theme-pix/assets/img/nodata.png"></p>
|
||||
<p class="msg_limit">只显示最新10条未读和已读信息</p>
|
||||
`)
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
msg_modal_inner.html(`
|
||||
<p class="no_posts"><small># 暂无消息 #</small><img class="s_nodata"
|
||||
src="/themes/theme-pix/assets/img/nodata.png"></p>
|
||||
<p class="msg_limit">只显示最新10条未读和已读信息</p>
|
||||
`)
|
||||
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
|
||||
initGalleryPhotos: function () {
|
||||
var galleryPhotos = document.querySelectorAll('.gallery-photos .gallery-photo');
|
||||
if (galleryPhotos.length > 0) {
|
||||
// 瀑布流排版
|
||||
imgStatus.watch('.photo-img', () => {
|
||||
waterfall('.gallery-photos');
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
topCategoriesBarScroll: function () {
|
||||
if (document.getElementById("cat_nav_items")) {
|
||||
let xscroll = document.getElementById("cat_nav_items");
|
||||
xscroll.addEventListener("mousewheel", function (e) {
|
||||
//计算鼠标滚轮滚动的距离
|
||||
let v = -e.wheelDelta / 2;
|
||||
xscroll.scrollLeft += v;
|
||||
//阻止浏览器默认方法
|
||||
e.preventDefault();
|
||||
}, false);
|
||||
}
|
||||
},
|
||||
|
||||
topTableBarScroll: function () {
|
||||
var tables = document.querySelectorAll('.single-content table')
|
||||
if (tables.length>0) {
|
||||
tables.forEach(function (xscroll) {
|
||||
xscroll.addEventListener("mousewheel", function (e) {
|
||||
//计算鼠标滚轮滚动的距离
|
||||
let v = -e.wheelDelta / 2;
|
||||
xscroll.scrollLeft += v;
|
||||
//阻止浏览器默认方法
|
||||
e.preventDefault();
|
||||
}, false);
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
//处理瞬间权限
|
||||
roleMoments: async function () {
|
||||
const response = await fetch(`/apis/api.console.halo.run/v1alpha1/users/-`);
|
||||
const data = await response.json();
|
||||
var roles = data.roles;
|
||||
|
||||
if(roles.length>0) {
|
||||
var name = roles[0].metadata.name
|
||||
if(name != 'super-role') {
|
||||
let dependencies = roles[0].metadata.annotations["rbac.authorization.halo.run/dependencies"]
|
||||
if(dependencies != undefined) {
|
||||
var targetRoles = ["role-template-uc-moments-approved","role-template-uc-moments-publish"]
|
||||
const result = targetRoles.some(r => dependencies.includes(r));
|
||||
if(!result) {
|
||||
$('.normal_edit').remove();
|
||||
}
|
||||
}else {
|
||||
$('.normal_edit').remove();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
var pix = {
|
||||
|
||||
|
||||
/**
|
||||
* 页面運行時間
|
||||
*/
|
||||
addRuntime: function () {
|
||||
const $runtimeCount = document.getElementById('runtimeshow');
|
||||
if ($runtimeCount) {
|
||||
var s1 = $runtimeCount.innerText;//建站时间
|
||||
if (s1) {
|
||||
s1 = new Date(s1.replace(/-/g, "/"));
|
||||
s2 = new Date();
|
||||
var days = s2.getTime() - s1.getTime();
|
||||
var number_of_days = parseInt(days / (1000 * 60 * 60 * 24));
|
||||
$runtimeCount.innerText = number_of_days + '天';
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
loadLightbox: function () {
|
||||
/**
|
||||
* fancybox
|
||||
*/
|
||||
const addFancybox = function (ele) {
|
||||
const runFancybox = (ele) => {
|
||||
ele.each(function (i, o) {
|
||||
const $this = $(o)
|
||||
const lazyloadSrc = $this.attr('data-src') || $this.attr('src')
|
||||
const dataCaption = $this.attr('alt') || ''
|
||||
$this.wrap(`<a href="${lazyloadSrc}" data-fancybox="images" class="fancybox" data-srcset="${lazyloadSrc}"></a>`)
|
||||
})
|
||||
}
|
||||
|
||||
runFancybox($(ele))
|
||||
}
|
||||
const $fancyboxEle = document.querySelectorAll('.single-content :not(a) > img,.single-content > img')
|
||||
if ($fancyboxEle.length > 0) {
|
||||
addFancybox($fancyboxEle)
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
initTopBar: function () {
|
||||
let topBar = $('#masthead .top_bar');
|
||||
var placeholder = document.querySelector('#masthead .uk-sticky-placeholder');
|
||||
var width = $('#masthead').width();
|
||||
var height = '71.9979px;'
|
||||
if (window.innerWidth < 960) {
|
||||
if (window.innerWidth <= 540) {
|
||||
height = '63.9957px'
|
||||
}
|
||||
topBar.addClass('uk-sticky-fixed').css({ "width": width, "top": '0px', "position": "fixed" });
|
||||
$('#masthead .uk-sticky-placeholder').css({ "width": width, "height": height, "margin": "0px" });
|
||||
placeholder.hidden = false;
|
||||
} else {
|
||||
topBar.removeAttr("style");
|
||||
$('#masthead .uk-sticky-placeholder').css({ "width": width, "height": '71.9979px;', "margin": "0px" });
|
||||
placeholder.hidden = true;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
getMsg: function () {
|
||||
var com_msg_btn = $('.com_msg_btn')
|
||||
var msg_modal_inner = $('.msg_modal_inner')
|
||||
let username = Theme.username
|
||||
var f_unread_num = 0;
|
||||
if (com_msg_btn.length > 0) {
|
||||
$.ajax({
|
||||
type: "get",
|
||||
url: `/apis/api.notification.halo.run/v1alpha1/userspaces/${username}/notifications?fieldSelector=spec.unread=true`,
|
||||
success: function (res) {
|
||||
if (res.total == 0) {
|
||||
$('.f_unread_num').remove();
|
||||
}else {
|
||||
f_unread_num = res.total
|
||||
com_msg_btn.append(`<small class="f_unread_num">${res.total}</small>`);
|
||||
}
|
||||
if (msg_modal_inner.length > 0) {
|
||||
$.ajax({
|
||||
type: "get",
|
||||
url: `/apis/api.notification.halo.run/v1alpha1/userspaces/${username}/notifications?page=1&size=10`,
|
||||
success: function (res) {
|
||||
var data = res.items
|
||||
if (data.length > 0) {
|
||||
var unread_box = `
|
||||
<div class="unread_box">
|
||||
<div class="unread_tip"># 您有${f_unread_num}条未读消息 #
|
||||
<a href="/uc/notifications" target="_blank">查看全部</a>
|
||||
</div>
|
||||
`
|
||||
var read_box = `
|
||||
<div class="read_box">
|
||||
`
|
||||
var unreadSum = 0
|
||||
data.forEach((e => {
|
||||
if (e.spec.unread) {
|
||||
unreadSum = unreadSum + 1;
|
||||
unread_box += `
|
||||
<div class="vi_reply_item">
|
||||
<div class="box" msg-name="${e.metadata.name}">
|
||||
<span class="mark_read">标记为已读</span>
|
||||
<div class="avatar_top"><img alt=""
|
||||
src="https://cravatar.cn/avatar/?d=mp"
|
||||
class="avatar avatar-50 photo" height="50" width="50" decoding="async"></div>
|
||||
${e.spec.htmlContent}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
} else {
|
||||
read_box += `
|
||||
<div class="vi_reply_item">
|
||||
<div class="box">
|
||||
<div class="avatar_top"><img alt=""
|
||||
src="https://cravatar.cn/avatar/?d=mp"
|
||||
class="avatar avatar-50 photo" height="50" width="50" decoding="async"></div>
|
||||
${e.spec.htmlContent}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
}
|
||||
))
|
||||
unread_box += `</div>`
|
||||
read_box += `</div><p class="msg_limit">只显示最新10条未读和已读信息</p> `
|
||||
msg_modal_inner.html(unread_box + read_box)
|
||||
|
||||
} else {
|
||||
msg_modal_inner.html(`
|
||||
<p class="no_posts"><small># 暂无消息 #</small><img class="s_nodata"
|
||||
src="/upload/nodata.png"></p>
|
||||
<p class="msg_limit">只显示最新10条未读和已读信息</p>
|
||||
`)
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
msg_modal_inner.html(`
|
||||
<p class="no_posts"><small># 暂无消息 #</small><img class="s_nodata"
|
||||
src="/upload/nodata.png"></p>
|
||||
<p class="msg_limit">只显示最新10条未读和已读信息</p>
|
||||
`)
|
||||
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
|
||||
initGalleryPhotos: function () {
|
||||
var galleryPhotos = document.querySelectorAll('.gallery-photos .gallery-photo');
|
||||
if (galleryPhotos.length > 0) {
|
||||
// 瀑布流排版
|
||||
imgStatus.watch('.photo-img', () => {
|
||||
waterfall('.gallery-photos');
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
topCategoriesBarScroll: function () {
|
||||
if (document.getElementById("cat_nav_items")) {
|
||||
let xscroll = document.getElementById("cat_nav_items");
|
||||
xscroll.addEventListener("mousewheel", function (e) {
|
||||
//计算鼠标滚轮滚动的距离
|
||||
let v = -e.wheelDelta / 2;
|
||||
xscroll.scrollLeft += v;
|
||||
//阻止浏览器默认方法
|
||||
e.preventDefault();
|
||||
}, false);
|
||||
}
|
||||
},
|
||||
|
||||
topTableBarScroll: function () {
|
||||
var tables = document.querySelectorAll('.single-content table')
|
||||
if (tables.length>0) {
|
||||
tables.forEach(function (xscroll) {
|
||||
xscroll.addEventListener("mousewheel", function (e) {
|
||||
//计算鼠标滚轮滚动的距离
|
||||
let v = -e.wheelDelta / 2;
|
||||
xscroll.scrollLeft += v;
|
||||
//阻止浏览器默认方法
|
||||
e.preventDefault();
|
||||
}, false);
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
//处理瞬间权限
|
||||
roleMoments: async function () {
|
||||
const response = await fetch(`/apis/api.console.halo.run/v1alpha1/users/-`);
|
||||
const data = await response.json();
|
||||
var roles = data.roles;
|
||||
|
||||
if(roles.length>0) {
|
||||
var name = roles[0].metadata.name
|
||||
if(name != 'super-role') {
|
||||
let dependencies = roles[0].metadata.annotations["rbac.authorization.halo.run/dependencies"]
|
||||
if(dependencies != undefined) {
|
||||
var targetRoles = ["role-template-uc-moments-approved","role-template-uc-moments-publish"]
|
||||
const result = targetRoles.some(r => dependencies.includes(r));
|
||||
if(!result) {
|
||||
$('.normal_edit').remove();
|
||||
}
|
||||
}else {
|
||||
$('.normal_edit').remove();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1 +1,74 @@
|
||||
@-webkit-keyframes nprogress-spinner{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(360deg)}}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(360deg)}}#nprogress{pointer-events:none}#nprogress .bar{background:#172eff;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;-webkit-transform:rotate(3deg) translate(0,-4px);-ms-transform:rotate(3deg) translate(0,-4px);transform:rotate(3deg) translate(0,-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:solid 2px transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;-webkit-animation:nprogress-spinner 400ms linear infinite;animation:nprogress-spinner 400ms linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}
|
||||
@-webkit-keyframes nprogress-spinner {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
-webkit-transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes nprogress-spinner {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
#nprogress {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#nprogress .bar {
|
||||
background: #22bb6d; /* ? 修改后的横条颜色 */
|
||||
position: fixed;
|
||||
z-index: 1031;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
#nprogress .peg {
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: 100px;
|
||||
height: 100%;
|
||||
box-shadow: 0 0 10px #22bb6d, 0 0 5px #22bb6d;
|
||||
opacity: 1;
|
||||
transform: rotate(3deg) translate(0, -4px);
|
||||
}
|
||||
|
||||
/* ? 修改 Spinner -> 使用自定义图片且居中 */
|
||||
#nprogress .spinner {
|
||||
display: block;
|
||||
position: fixed;
|
||||
z-index: 1031;
|
||||
top: 15px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
/* ? 不再使用旋转圆形,换成图片 */
|
||||
#nprogress .spinner-icon {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url("/upload/jiazai.gif");
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.nprogress-custom-parent {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nprogress-custom-parent #nprogress .bar,
|
||||
.nprogress-custom-parent #nprogress .spinner {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||