给博客的图片添加一个漂亮的图片描述
于发布于分类: 博客
效果
实现步骤
实现内容
- 等待DOM完全加载
- 获取所有的IMG标签
- 展示所有的IMG描述内容
我美化了什么
- 鼠标移上显示,避免遮挡图片内容,同样避免破坏版面
- 给文字背景毛玻璃效果
- 给毛玻璃动态背景颜色,颜色提取图片主色作为背景颜色
- 给字体一点描边,增加极端背景色下的可读性
实现代码
document.addEventListener('DOMContentLoaded', function () {
const mainContent = document.querySelector('.post-content, .article-content');
if (!mainContent) {
return;
}
mainContent.querySelectorAll('img').forEach(img => {
const title = img.getAttribute('title');
const alt = img.getAttribute('alt');
if (!title && !alt) {
return;
}
img.crossOrigin = 'anonymous';
const customElement = document.createElement('div');
customElement.setAttribute('class', 'article-image-wrapper');
customElement.style.display = 'none';
const figcaption = document.createElement('figcaption');
figcaption.setAttribute('class', 'image-info-overlay');
let infoHTML = '';
if (alt) {
infoHTML += `<p class="image-alt-text">${alt}</p>`;
}
if (title) {
infoHTML += `<p class="image-title-text">${title}</p>`;
}
figcaption.innerHTML = infoHTML;
img.parentNode.insertBefore(customElement, img);
customElement.appendChild(img);
customElement.appendChild(figcaption);
customElement.addEventListener('mouseover', () => {
customElement.classList.add('is-hovering');
});
customElement.addEventListener('mouseout', () => {
customElement.classList.remove('is-hovering');
});
img.addEventListener('load', function () {
customElement.style.display = 'inline-block';
if (isSameOrigin(img.src)) {
const canvas = document.createElement('canvas');
const rgbColor = getImageAverageColor(canvas, img);
figcaption.style.backgroundColor = rgbColor;
} else {
figcaption.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
}
});
img.addEventListener('error', function () {
customElement.remove();
});
});
function isSameOrigin(url) {
try {
const imgUrl = new URL(url);
return imgUrl.origin === window.location.origin;
} catch (e) {
return true;
}
}
function getImageAverageColor(canvas, img) {
try {
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
const context = canvas.getContext("2d");
context.drawImage(img, 0, 0, canvas.width, canvas.height);
const data = context.getImageData(0, 0, canvas.width, canvas.height).data;
let r = 0, g = 0, b = 0;
const pixelCount = data.length / 4;
for (let i = 0; i < data.length; i += 4) {
r += data[i];
g += data[i + 1];
b += data[i + 2];
}
r = Math.round(r / pixelCount);
g = Math.round(g / pixelCount);
b = Math.round(b / pixelCount);
return `rgba(${r}, ${g}, ${b}, 0.3)`;
} catch (e) {
return 'rgba(0, 0, 0, 0.7)';
}
}
});
# css部分
.article-image-wrapper {
position: relative;
display: inline-block;
overflow: hidden
}
.article-image-wrapper img {
display: block;
max-width: 100%;
height: auto
}
.image-info-overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding-left: 10px;
padding-bottom: 2px;
padding-top: 2px;
border-radius: 0px 0px 4px 4px;
color: white;
backdrop-filter: blur(5px);
box-sizing: border-box;
transform: translateY(100%);
transition: opacity .3s ease-out, transform .3s ease-out
}
.article-image-wrapper.is-hovering .image-info-overlay {
opacity: 1;
transform: translateY(0)
}
.image-info-overlay p {
margin: 5px 0;
line-height: 1;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
color: #ffffff;
}
.image-alt-text{
font-size: 0.8rem;
}
.image-title-text{
font-size: 1.1rem;
}下面是比较详细的解释
代码在 DOM 内容完全加载后执行,它遍历文章中的所有 <img> 标签,并对那些设置了 title 或 alt 属性的图片执行以下操作:
- 图片包装与结构化:
- 将图片用一个新的
<div>元素(类名为article-image-wrapper)包裹起来,并将其初始设置为隐藏 (display: 'none')。 - 在包裹元素内,为图片添加一个
figcaption元素(类名为image-info-overlay),用于显示图片的alt和title信息。
- 将图片用一个新的
- 信息显示(Alt/Title):
- 从
<img>标签中获取alt和title属性值,并将它们格式化为<p>标签,放入figcaption元素中。
- 从
- 交互效果:
- 为图片包裹元素添加鼠标悬停监听器,在悬停时添加/移除
is-hovering类,以便通过 CSS 实现悬停时的视觉效果。
- 为图片包裹元素添加鼠标悬停监听器,在悬停时添加/移除
- 图片加载成功后的处理:
- 当图片加载成功后,将包裹元素显示出来 (
display: 'inline-block')。 - 尝试获取图片的平均颜色:
- 如果是同源图片,则使用
<canvas>元素计算图片的平均 RGB 颜色,并将此颜色设置为figcaption的背景色(带有 $0.3$ 的透明度)。 - 如果是跨域或计算失败(包括同源计算失败),则设置默认的深色背景(
rgba(0, 0, 0, 0.7))。
- 如果是同源图片,则使用
- 当图片加载成功后,将包裹元素显示出来 (
- 跨域设置与错误处理:
- 将图片元素的
crossOrigin属性设置为'anonymous',以支持跨域图片在<canvas>上进行颜色计算。 - 如果图片加载失败,则移除整个图片包裹元素。
- 将图片元素的
fancybox
另外点击图片是有灯箱效果的,我使用的是fancybox3,首先你需要引入fancybox的css和js文件,然后在你的JS中添加如下代码:
'use strict';
const cheerio = require('cheerio');
hexo.extend.filter.register('after_post_render', function(data) {
if (!data.content) return data;
const $ = cheerio.load(data.content, {
decodeEntities: false
});
$('img:not(a img)').each(function() {
const $img = $(this);
const src = $img.attr('src');
const alt = $img.attr('alt');
if (!src) return;
const $a = $('<a>')
.attr('href', src)
.attr('data-fancybox', 'gallery')
.attr('data-caption', alt || '');
$img.wrap($a);
});
data.content = $.html();
return data;
});因为博客的图片我基本都从原图(jpg)格式转换为了avif格式。所以如果你希望点击图片展示的灯箱为原图(大尺寸原图),你可以使用以下的代码,把原图路径替换以下
$('img:not(a img)').each(function() {
const $img = $(this);
const originalSrc = $img.attr('src');
const newSrc = originalSrc.replace(/\.avif$/, '.jpg');
console.log(newSrc);
const alt = $img.attr('alt');
if (!newSrc) return;
const $a = $('<a>')
.attr('href', newSrc)
.attr('data-fancybox', 'gallery')
.attr('data-caption', alt || '');
$img.wrap($a);
});