图片缩放
方法一:medium-zoom
安装依赖
sh
pnpm add medium-zoom -D导入
ts
import { onMounted, watch, nextTick } from 'vue';
import { useRoute } from 'vitepress';
import DefaultTheme from 'vitepress/theme';
import mediumZoom from 'medium-zoom';
import './styles/index.less';
export default {
extends: DefaultTheme,
setup() {
const route = useRoute();
const initZoom = () => {
mediumZoom('.main img', { background: 'rgba(0,0,0,0.2)' });
};
onMounted(() => {
initZoom();
});
watch(
() => route.path,
() => nextTick(() => initZoom())
);
}
};样式
css
.medium-zoom-overlay {
z-index: 30;
}
.medium-zoom-image {
border-radius: 10px;
z-index: 31;
}方法二:fancybox
警告
如果使用原文的代码,需要安装老版本的 Fancybox v5 @fancyapps/ui@5
以下代码参考原文修改后兼容 Fancybox v6,安装最新版即可
安装依赖
sh
pnpm add @fancyapps/ui -D导入
ts
import { nextTick } from 'vue';
import '@fancyapps/ui/dist/fancybox/fancybox.css';
// 查找图像之前最近的标题
const findNearestHeading = (imgElement) => {
// 获取 img 元素的父节点
let currentElement = imgElement;
// 循环向上查找
while (currentElement && currentElement !== document.body) {
// 在当前元素的前一个兄弟节点中查找 h1-h6 标签
let previousSibling = currentElement.previousElementSibling;
while (previousSibling) {
if (previousSibling.tagName.match(/^H[1-6]$/)) {
return previousSibling.textContent.replace(/\u200B/g, '').trim(); // 返回找到的标题内容
}
previousSibling = previousSibling.previousElementSibling;
}
// 如果没有找到,继续向上一级父节点查找
currentElement = currentElement.parentElement;
}
return '';
};
export const bindFancybox = () => {
nextTick(async () => {
const { Fancybox, PanzoomAction } = await import('@fancyapps/ui'); // 采用这种导入方式是为了避免构建报错问题
const imgs = document.querySelectorAll('.vp-doc img');
imgs.forEach((img) => {
const image = img as HTMLImageElement;
if (!image.hasAttribute('data-fancybox')) {
image.setAttribute('data-fancybox', 'gallery');
}
// 赋予 alt 属性
if (!image.hasAttribute('alt') || image.getAttribute('alt') === '') {
const heading = findNearestHeading(image);
image.setAttribute('alt', heading);
}
// 赋予 data-caption 属性以便显示图片标题
const altString = image.getAttribute('alt') || '';
image.setAttribute('data-caption', altString);
});
Fancybox.bind('[data-fancybox="gallery"]', {
Hash: false,
caption: false,
zoomEffect: false,
Carousel: {
Zoomable: {
Panzoom: {
clickAction: 'iterateZoom',
maxScale: 2,
minScale: 0.8
}
},
Toolbar: {
display: {
left: ['counter'],
middle: ['zoomIn', 'zoomOut', 'toggle1to1', 'rotateCCW', 'rotateCW', 'flipX', 'flipY', 'reset'],
right: ['autoplay', 'thumbs', 'close']
}
},
Thumbs: {
type: 'classic',
showOnStart: false
}
}
});
});
};
export const destroyFancybox = async () => {
const { Fancybox } = await import('@fancyapps/ui');
Fancybox.destroy();
};ts
import { onMounted, onUnmounted } from 'vue';
import { EnhanceAppContext, inBrowser } from 'vitepress';
import DefaultTheme from 'vitepress/theme';
import { bindFancybox, destroyFancybox } from 'src/utils/fancybox';
import 'src/styles/fancybox.less';
export default {
extends: DefaultTheme,
enhanceApp({ app, router }: EnhanceAppContext) {
if (inBrowser) {
BProgress.configure({ showSpinner: false });
router.onBeforeRouteChange = () => {
BProgress.start();
destroyFancybox(); // 销毁图片查看器
};
router.onAfterRouteChange = () => {
BProgress.done();
bindFancybox(); // 绑定图片查看器
};
}
},
setup() {
onMounted(() => {
bindFancybox();
});
onUnmounted(() => {
destroyFancybox();
});
}
};样式
less
:root {
--vp-c-bg-rgb: 255, 255, 255;
--vp-c-bg-reverse-rgb: 27, 27, 31;
}
.dark {
--vp-c-bg-rgb: 27, 27, 31;
--vp-c-bg-reverse-rgb: 255, 255, 255;
}
.fancybox__container {
--fancybox-backdrop-bg: none;
.f-carousel__toolbar {
--f-button-bg: none;
--f-button-hover-bg: rgba(var(--vp-c-bg-reverse-rgb), 0.1);
--f-button-color: rgba(var(--vp-c-bg-reverse-rgb), 1);
--f-button-hover-color: rgba(var(--vp-c-bg-reverse-rgb), 1);
--f-button-svg-disabled-opacity: 0.2;
background: rgba(var(--vp-c-bg-rgb), 0.2);
}
&::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
background: rgba(var(--vp-c-bg-rgb), 0.5);
backdrop-filter: blur(10px);
}
.f-panzoom__content {
object-fit: initial;
border: solid 1px rgba(var(--vp-c-bg-reverse-rgb), 0.5);
border-radius: 10px;
}
.is-classic .is-nav-selected .f-thumbs__slide__button::after {
border-color: var(--vp-c-brand-1);
}
.f-caption {
color: var(--vp-c-text-1);
}
}
