From a0efbc2cbd99eb5968d2a7c605ebfe2c3e19c59a Mon Sep 17 00:00:00 2001 From: imeepos Date: Wed, 3 Sep 2025 17:41:35 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E5=8F=AF=E6=8B=96?= =?UTF-8?q?=E6=8B=BD=E5=88=86=E5=89=B2=E7=BA=BF=E5=8A=9F=E8=83=BD=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=8A=A8=E6=80=81=E8=B0=83=E6=95=B4=E5=AF=B9?= =?UTF-8?q?=E6=AF=94=E6=AF=94=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit � 核心功能: - 添加可拖拽分割线,用户可左右拖动调整原图和效果图显示比例 - 初始50/50比例,支持10%-90%范围内自由调整 - 实时响应拖拽操作,提供流畅交互体验 � 交互优化: - 增大拖拽区域(4px宽度)便于操作 - 拖拽时手柄放大并增强阴影效果 - 使用touch-action: none防止页面滚动干扰 - 延迟重置拖拽状态避免误触发卡片点击 � 动态效果: - 左右区域宽度随拖拽实时调整 - 标签宽度动态跟随区域变化 - 平滑过渡动画(0.1s ease-out) - 分割线位置流畅跟随手指移动 � 技术实现: - React useState管理分割线位置状态 - getBoundingClientRect()获取精确容器位置 - 动态计算触摸点位置并转换为百分比 - CSS动态样式与React状态完美结合 - 触摸事件阻止冒泡避免干扰其他交互 � 更新设计文档,添加交互使用说明 --- DESIGN_PREVIEW.md | 38 +++++++++---- src/components/TemplateCard/index.css | 29 +++++++--- src/components/TemplateCard/index.tsx | 81 ++++++++++++++++++++++++--- 3 files changed, 120 insertions(+), 28 deletions(-) diff --git a/DESIGN_PREVIEW.md b/DESIGN_PREVIEW.md index 59253e0..76283eb 100644 --- a/DESIGN_PREVIEW.md +++ b/DESIGN_PREVIEW.md @@ -11,12 +11,13 @@ - **响应式设计**:在更大屏幕上自动调整为3列 - **自适应高度**:每个卡片根据内容自动调整高度 -### 2. 左右对比效果 ⭐ **最新更新** -- **左右分屏**:原图展示左半边,效果图展示右半边,各占50%宽度 -- **视觉分割线**:中间添加优雅的白色分割线,带有圆形交互手柄 -- **标签标识**:底部居中显示标签,原图蓝色,效果图绿色 -- **毛玻璃效果**:标签使用backdrop-filter实现现代毛玻璃效果 -- **完美对比**:统一高度180px,确保视觉平衡和对比效果 +### 2. 可拖拽对比效果 🚀 **最新功能** +- **可拖拽分割线**:用户可以左右拖动分割线,动态调整对比比例 +- **实时响应**:拖拽过程中实时看到两张图片的显示比例变化 +- **智能边界**:限制拖拽范围在10%-90%之间,确保两侧都有内容显示 +- **触摸优化**:增大拖拽区域,优化触摸体验,防止误触 +- **视觉反馈**:拖拽时手柄会放大,提供清晰的交互反馈 +- **动态标签**:左右标签宽度随分割线位置动态调整 ### 3. 卡片设计 - **渐变背景**:图片区域使用渐变背景增加层次感 @@ -38,10 +39,10 @@ Home页面 │ └── 副标题:"选择模板,一键生成精美效果" └── 瀑布流网格 └── 模板卡片 × 6 - ├── 左右对比区域 ⭐ **新设计** - │ ├── 左半边:原图(底部蓝色"原图"标签) - │ ├── 中央分割线:白色线条 + 圆形手柄(⟷图标) - │ └── 右半边:效果图(底部绿色"效果"标签) + ├── 可拖拽对比区域 🚀 **交互功能** + │ ├── 左半边:原图左半部分(动态宽度,蓝色标签) + │ ├── 可拖拽分割线:白色线条 + 可拖拽手柄(⟷图标) + │ └── 右半边:效果图右半部分(动态宽度,绿色标签) └── 信息区域 ├── 模板名称 ├── 功能描述 @@ -86,11 +87,28 @@ Home页面 - CSS动画使用transform提升性能 - 合理的图片尺寸设置 +## 🎮 交互使用说明 + +### 拖拽对比功能 +1. **初始状态**:分割线位于中央(50/50比例) +2. **开始拖拽**:用手指按住分割线上的圆形手柄 +3. **左右拖动**: + - 向左拖动:原图区域变小,效果图区域变大 + - 向右拖动:原图区域变大,效果图区域变小 +4. **实时预览**:拖拽过程中可以实时看到对比效果 +5. **智能限制**:拖拽范围限制在10%-90%之间 + +### 视觉反馈 +- **拖拽时**:手柄会放大并增强阴影效果 +- **动态标签**:左右标签宽度随分割线实时调整 +- **平滑过渡**:所有动画都有平滑的过渡效果 + ## 🚀 使用方法 1. 启动开发服务器:`npm run dev:weapp` 2. 在微信开发者工具中打开项目 3. 查看home页面的瀑布流效果 +4. 尝试拖拽分割线体验对比功能 ## 📝 后续优化建议 diff --git a/src/components/TemplateCard/index.css b/src/components/TemplateCard/index.css index c42d716..30b5ed9 100644 --- a/src/components/TemplateCard/index.css +++ b/src/components/TemplateCard/index.css @@ -38,8 +38,8 @@ .image-half { position: relative; - flex: 1; overflow: hidden; + transition: width 0.1s ease-out; } .comparison-image { @@ -77,13 +77,14 @@ .label-left, .label-right { - flex: 1; display: flex; justify-content: center; align-items: center; padding: 8px 4px; backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); + transition: width 0.1s ease-out; + min-width: 40px; /* 确保标签有最小宽度 */ } .label-left { @@ -103,19 +104,21 @@ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); } -/* 分割线和手柄 */ +/* 可拖拽分割线 */ .split-line { position: absolute; - left: 50%; top: 0; bottom: 0; - width: 2px; + width: 4px; /* 增加宽度便于拖拽 */ background: linear-gradient(to bottom, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0.9) 50%, rgba(255, 255, 255, 0.8) 100%); transform: translateX(-50%); z-index: 3; + cursor: col-resize; + touch-action: none; /* 防止页面滚动 */ + transition: left 0.1s ease-out; } .split-handle { @@ -123,15 +126,23 @@ top: 50%; left: 50%; transform: translate(-50%, -50%); - width: 28px; - height: 28px; + width: 32px; + height: 32px; background: rgba(255, 255, 255, 0.95); border-radius: 50%; display: flex; align-items: center; justify-content: center; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); - border: 2px solid rgba(0, 122, 255, 0.3); + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.25); + border: 2px solid rgba(0, 122, 255, 0.4); + cursor: col-resize; + touch-action: none; + transition: all 0.2s ease; +} + +.split-handle:active { + transform: translate(-50%, -50%) scale(1.1); + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3); } .split-icon { diff --git a/src/components/TemplateCard/index.tsx b/src/components/TemplateCard/index.tsx index 4e98f7f..6210c1b 100644 --- a/src/components/TemplateCard/index.tsx +++ b/src/components/TemplateCard/index.tsx @@ -1,4 +1,5 @@ import { View, Text, Image } from '@tarojs/components' +import { useState, useRef } from 'react' import { Template } from '../../store/types' import './index.css' @@ -8,17 +9,64 @@ interface TemplateCardProps { } export default function TemplateCard({ template, onClick }: TemplateCardProps) { + const [splitPosition, setSplitPosition] = useState(50) // 分割线位置百分比 + const [isDragging, setIsDragging] = useState(false) + const containerRef = useRef(null) + const handleClick = () => { - onClick(template) + if (!isDragging) { + onClick(template) + } + } + + // 处理触摸开始 + const handleTouchStart = (e: any) => { + e.stopPropagation() + setIsDragging(true) + } + + // 处理触摸移动 + const handleTouchMove = (e: any) => { + if (!isDragging) return + e.stopPropagation() + + const touch = e.touches[0] + const container = containerRef.current + if (!container || !touch) return + + // 获取容器的位置信息 + const rect = container.getBoundingClientRect() + const containerLeft = rect.left + const containerWidth = rect.width + + // 计算触摸点相对于容器的位置 + const touchX = touch.clientX - containerLeft + const percentage = Math.max(10, Math.min(90, (touchX / containerWidth) * 100)) + + setSplitPosition(percentage) + } + + // 处理触摸结束 + const handleTouchEnd = (e: any) => { + e.stopPropagation() + setTimeout(() => setIsDragging(false), 100) // 延迟重置,避免触发点击 } return ( {/* 合成对比图片区域 */} - + {/* 左半部分 - 原图的左半部分 */} - + {/* 右半部分 - 效果图的右半部分 */} - + - {/* 中央分割线 */} - + {/* 可拖拽的分割线 */} + - {/* 标签 */} + {/* 动态标签 */} - + 原图 - + 效果