手机端作品页面样式修改,支持手滑动翻页
This commit is contained in:
@@ -671,47 +671,246 @@ input:checked+.slider:before {
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.artwork-info {
|
||||
padding: var(--spacing-lg);
|
||||
margin: 0;
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
|
||||
.artwork-title {
|
||||
font-size: 1.25rem;
|
||||
max-width: none;
|
||||
white-space: normal;
|
||||
overflow: visible;
|
||||
text-overflow: unset;
|
||||
}
|
||||
|
||||
/* 移动端按钮重新布局 */
|
||||
.artwork-actions {
|
||||
flex-direction: column;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: var(--spacing-md);
|
||||
margin-bottom: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.artwork-actions .btn {
|
||||
min-width: unset;
|
||||
padding: var(--spacing-md);
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
border-radius: var(--radius-lg);
|
||||
height: 44px; /* 符合移动端触摸标准 */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 下载按钮占据两列 */
|
||||
.artwork-actions .btn:first-child {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
/* 如果有删除按钮,下载按钮占一列,删除按钮占一列 */
|
||||
.artwork-actions .btn:first-child:not(:last-child) {
|
||||
grid-column: 1 / 2;
|
||||
}
|
||||
|
||||
.artwork-stats {
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-lg);
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: var(--spacing-md);
|
||||
text-align: center;
|
||||
background: var(--color-bg-secondary);
|
||||
padding: var(--spacing-lg);
|
||||
border-radius: var(--radius-lg);
|
||||
margin-bottom: var(--spacing-xl);
|
||||
}
|
||||
|
||||
.artwork-navigation {
|
||||
.stat {
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-md);
|
||||
gap: var(--spacing-xs);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.stat svg {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* 移动端导航优化 */
|
||||
.artwork-navigation {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: var(--color-bg-primary);
|
||||
border-top: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-lg) var(--radius-lg) 0 0;
|
||||
padding: var(--spacing-md);
|
||||
margin: var(--spacing-xl) calc(-1 * var(--spacing-lg)) 0;
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
gap: var(--spacing-sm);
|
||||
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1);
|
||||
z-index: 2000;
|
||||
}
|
||||
|
||||
.nav-back {
|
||||
order: -1;
|
||||
align-self: flex-start;
|
||||
min-width: 80px;
|
||||
order: 0;
|
||||
min-width: 60px;
|
||||
padding: var(--spacing-sm);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-bg-secondary);
|
||||
border-color: var(--color-border);
|
||||
}
|
||||
|
||||
.nav-back span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav-prev,
|
||||
.nav-next {
|
||||
min-width: auto;
|
||||
min-width: unset;
|
||||
padding: var(--spacing-md) var(--spacing-lg);
|
||||
font-weight: 600;
|
||||
border-radius: var(--radius-lg);
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
.nav-prev {
|
||||
order: 1;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.nav-next {
|
||||
order: 2;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 快速导航提示 */
|
||||
.artwork-navigation::before {
|
||||
content: "← 上一个 | 下一个 →";
|
||||
position: absolute;
|
||||
top: -24px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 0.75rem;
|
||||
color: var(--color-text-tertiary);
|
||||
background: var(--color-bg-secondary);
|
||||
padding: var(--spacing-xs) var(--spacing-sm);
|
||||
border-radius: var(--radius-sm);
|
||||
white-space: nowrap;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.artist-info {
|
||||
padding: var(--spacing-lg);
|
||||
margin-bottom: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.artist-avatar {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
}
|
||||
|
||||
.artist-name {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.artist-account {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.artwork-tags {
|
||||
margin-bottom: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.tags-list {
|
||||
gap: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.artwork-meta {
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-lg);
|
||||
gap: var(--spacing-md);
|
||||
padding-bottom: 80px; /* 为底部导航留出更多空间 */
|
||||
}
|
||||
|
||||
.toggle-container {
|
||||
align-self: flex-end;
|
||||
align-self: stretch;
|
||||
justify-content: space-between;
|
||||
padding: var(--spacing-md);
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.caption-content {
|
||||
max-height: 80px;
|
||||
font-size: 0.75rem;
|
||||
max-height: 100px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.caption-header h3 {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.artwork-description,
|
||||
.artwork-caption {
|
||||
margin-bottom: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.keyboard-hint {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* 更小屏幕的额外优化 */
|
||||
@media (max-width: 480px) {
|
||||
.artwork-info {
|
||||
padding: var(--spacing-md);
|
||||
}
|
||||
|
||||
.artwork-title {
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.artwork-actions .btn {
|
||||
font-size: 0.8rem;
|
||||
height: 40px;
|
||||
padding: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.artwork-stats {
|
||||
padding: var(--spacing-md);
|
||||
}
|
||||
|
||||
.stat {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
.stat svg {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
.artist-info {
|
||||
padding: var(--spacing-md);
|
||||
}
|
||||
|
||||
.artist-avatar {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.nav-prev,
|
||||
.nav-next {
|
||||
padding: var(--spacing-sm) var(--spacing-md);
|
||||
font-size: 0.875rem;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.nav-back {
|
||||
min-width: 50px;
|
||||
padding: var(--spacing-xs);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="artwork-page">
|
||||
<div class="artwork-page" @touchstart="handleTouchStart" @touchmove="handleTouchMove" @touchend="handleTouchEnd">
|
||||
<div class="container">
|
||||
<!-- 收藏错误提示 -->
|
||||
<div v-if="bookmarkError" class="error-section">
|
||||
@@ -15,8 +15,15 @@
|
||||
<LoadingSpinner text="加载中..." />
|
||||
</div>
|
||||
|
||||
<!-- 滑动提示 -->
|
||||
<div v-if="artwork && isMobile" class="swipe-hint" :class="{ 'hint-visible': showSwipeHint }">
|
||||
<div class="hint-content">
|
||||
<span class="hint-text">← 滑动切换作品 →</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 作品内容 -->
|
||||
<div v-if="artwork" class="artwork-content" :class="{ 'content-loading': loading }">
|
||||
<div v-if="artwork" class="artwork-content" :class="{ 'content-loading': loading, 'swiping': isSwipeActive }">
|
||||
<!-- 左侧图片组件 -->
|
||||
<ArtworkGallery :artwork="artwork" :current-page="currentImagePage" :loading="loading"
|
||||
@page-change="currentImagePage = $event" />
|
||||
@@ -98,6 +105,83 @@ const showRecommendations = ref(true);
|
||||
// Caption 显示开关状态
|
||||
const showCaption = ref(false);
|
||||
|
||||
// 触摸滑动相关状态
|
||||
const touchStartX = ref(0);
|
||||
const touchStartY = ref(0);
|
||||
const touchEndX = ref(0);
|
||||
const touchEndY = ref(0);
|
||||
const isSwipeActive = ref(false);
|
||||
const isMobile = ref(false);
|
||||
const showSwipeHint = ref(false);
|
||||
|
||||
// 检测是否为移动设备
|
||||
const checkMobileDevice = () => {
|
||||
isMobile.value = window.innerWidth <= 768 || 'ontouchstart' in window;
|
||||
};
|
||||
|
||||
// 触摸开始事件
|
||||
const handleTouchStart = (event: TouchEvent) => {
|
||||
if (!showNavigation.value || loading.value) return;
|
||||
|
||||
const touch = event.touches[0];
|
||||
touchStartX.value = touch.clientX;
|
||||
touchStartY.value = touch.clientY;
|
||||
isSwipeActive.value = false;
|
||||
};
|
||||
|
||||
// 触摸移动事件
|
||||
const handleTouchMove = (event: TouchEvent) => {
|
||||
if (!showNavigation.value || loading.value) return;
|
||||
|
||||
const touch = event.touches[0];
|
||||
const deltaX = Math.abs(touch.clientX - touchStartX.value);
|
||||
const deltaY = Math.abs(touch.clientY - touchStartY.value);
|
||||
|
||||
// 如果水平滑动距离大于垂直滑动距离,且超过阈值,则激活滑动状态
|
||||
if (deltaX > deltaY && deltaX > 30) {
|
||||
isSwipeActive.value = true;
|
||||
event.preventDefault(); // 阻止页面滚动
|
||||
}
|
||||
};
|
||||
|
||||
// 触摸结束事件
|
||||
const handleTouchEnd = (event: TouchEvent) => {
|
||||
if (!showNavigation.value || loading.value) return;
|
||||
|
||||
const touch = event.changedTouches[0];
|
||||
touchEndX.value = touch.clientX;
|
||||
touchEndY.value = touch.clientY;
|
||||
|
||||
const deltaX = touchEndX.value - touchStartX.value;
|
||||
const deltaY = Math.abs(touchEndY.value - touchStartY.value);
|
||||
|
||||
// 检查是否为有效的水平滑动
|
||||
const minSwipeDistance = 80; // 最小滑动距离
|
||||
const maxVerticalDistance = 100; // 最大垂直偏移
|
||||
|
||||
if (Math.abs(deltaX) > minSwipeDistance && deltaY < maxVerticalDistance) {
|
||||
if (deltaX > 0 && canNavigateToPrevious.value) {
|
||||
// 向右滑动 - 上一个作品
|
||||
navigateToPrevious();
|
||||
} else if (deltaX < 0 && canNavigateToNext.value) {
|
||||
// 向左滑动 - 下一个作品
|
||||
navigateToNext();
|
||||
}
|
||||
}
|
||||
|
||||
isSwipeActive.value = false;
|
||||
};
|
||||
|
||||
// 显示滑动提示
|
||||
const showSwipeHintTemporarily = () => {
|
||||
if (!isMobile.value || !showNavigation.value) return;
|
||||
|
||||
showSwipeHint.value = true;
|
||||
setTimeout(() => {
|
||||
showSwipeHint.value = false;
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
// 初始化推荐开关状态(从localStorage读取)
|
||||
const initializeRecommendationsState = () => {
|
||||
const saved = localStorage.getItem('artwork-show-recommendations');
|
||||
@@ -820,6 +904,12 @@ onMounted(() => {
|
||||
// 确保页面滚动到顶部
|
||||
window.scrollTo(0, 0);
|
||||
|
||||
// 检测移动设备
|
||||
checkMobileDevice();
|
||||
|
||||
// 监听窗口大小变化
|
||||
window.addEventListener('resize', checkMobileDevice);
|
||||
|
||||
fetchArtworkDetail();
|
||||
if (showNavigation.value) {
|
||||
// 从路由查询参数获取页码
|
||||
@@ -837,6 +927,11 @@ onMounted(() => {
|
||||
|
||||
// 初始化 Caption 开关状态
|
||||
initializeCaptionState();
|
||||
|
||||
// 延迟显示滑动提示(仅在移动端且有导航时)
|
||||
setTimeout(() => {
|
||||
showSwipeHintTemporarily();
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
// 组件卸载时移除事件监听
|
||||
@@ -844,6 +939,7 @@ onUnmounted(() => {
|
||||
document.removeEventListener('keydown', handleKeydown);
|
||||
document.removeEventListener('keydown', handleKeyDown);
|
||||
document.removeEventListener('keyup', handleKeyUp);
|
||||
window.removeEventListener('resize', checkMobileDevice);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -897,8 +993,60 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.artwork-page {
|
||||
padding: 1rem 0 0 0; /* 移除底部内边距 */
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 0 1rem;
|
||||
margin-bottom: 0; /* 移除底部外边距 */
|
||||
}
|
||||
|
||||
.artwork-content {
|
||||
gap: 1rem;
|
||||
transition: transform 0.3s ease, opacity 0.3s ease;
|
||||
padding-bottom: 0; /* 确保没有额外的底部内边距 */
|
||||
}
|
||||
|
||||
.artwork-content.swiping {
|
||||
transform: scale(0.98);
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
/* 滑动提示样式 */
|
||||
.swipe-hint {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
color: white;
|
||||
padding: 1rem 2rem;
|
||||
border-radius: 2rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
z-index: 1000;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: all 0.3s ease;
|
||||
pointer-events: none;
|
||||
backdrop-filter: blur(10px);
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.swipe-hint.hint-visible {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.hint-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.hint-text {
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user