feat: 实现图片合成对比效果,左半部分显示原图左半,右半部分显示效果图右半

- 重新设计图片对比逻辑,实现真正的图片合成对比效果
- 左半部分显示原图的左半部分,右半部分显示效果图的右半部分
- 使用CSS transform实现图片的精确裁剪和定位
- 左图不偏移显示左半部分,右图向左偏移50%显示右半部分
- 优化标签布局,左右标签分别覆盖对应区域底部
- 保持中央分割线和交互手柄的视觉引导效果
- 实现真正的before/after对比效果,用户可直观看到处理差异
This commit is contained in:
杨明明 2025-09-03 17:34:54 +08:00
parent e37e7334cd
commit e683bcdcb9
2 changed files with 62 additions and 42 deletions

View File

@ -20,20 +20,20 @@
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.15); box-shadow: 0 8px 30px rgba(0, 0, 0, 0.15);
} }
/* 左右对比区域 */ /* 合成对比图片区域 */
.image-comparison { .image-comparison {
position: relative; position: relative;
padding: 16px; padding: 16px;
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
} }
.split-container { .merged-image-container {
position: relative; position: relative;
display: flex;
border-radius: 12px; border-radius: 12px;
overflow: hidden; overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
height: 180px; height: 180px;
display: flex;
} }
.image-half { .image-half {
@ -42,16 +42,8 @@
overflow: hidden; overflow: hidden;
} }
.left-half {
border-right: 1px solid rgba(255, 255, 255, 0.3);
}
.right-half {
border-left: 1px solid rgba(255, 255, 255, 0.3);
}
.comparison-image { .comparison-image {
width: 100%; width: 200%; /* 图片宽度设为容器的2倍 */
height: 100%; height: 100%;
object-fit: cover; object-fit: cover;
display: block; display: block;
@ -59,28 +51,49 @@
transition: opacity 0.3s ease; transition: opacity 0.3s ease;
} }
/* 左半部分显示原图的左半部分 */
.left-image {
transform: translateX(0); /* 不偏移,显示图片左半部分 */
}
/* 右半部分显示效果图的右半部分 */
.right-image {
transform: translateX(-50%); /* 向左偏移50%,显示图片右半部分 */
}
.comparison-image:not([src]) { .comparison-image:not([src]) {
opacity: 0.5; opacity: 0.5;
} }
.image-label { /* 标签容器 */
.image-labels {
position: absolute; position: absolute;
bottom: 8px; bottom: 0;
left: 50%; left: 0;
transform: translateX(-50%); right: 0;
padding: 4px 12px; display: flex;
border-radius: 16px; z-index: 2;
}
.label-left,
.label-right {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
padding: 8px 4px;
backdrop-filter: blur(10px); backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
white-space: nowrap;
} }
.input-label { .label-left {
background: rgba(52, 152, 219, 0.9); background: rgba(52, 152, 219, 0.9);
border-radius: 0 0 0 12px;
} }
.output-label { .label-right {
background: rgba(46, 204, 113, 0.9); background: rgba(46, 204, 113, 0.9);
border-radius: 0 0 12px 0;
} }
.label-text { .label-text {
@ -90,12 +103,12 @@
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
} }
/* 分割线和拖拽手柄 */ /* 分割线和手柄 */
.split-line { .split-line {
position: absolute; position: absolute;
left: 50%; left: 50%;
top: 16px; top: 0;
bottom: 16px; bottom: 0;
width: 2px; width: 2px;
background: linear-gradient(to bottom, background: linear-gradient(to bottom,
rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0.8) 0%,
@ -110,19 +123,19 @@
top: 50%; top: 50%;
left: 50%; left: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
width: 32px; width: 28px;
height: 32px; height: 28px;
background: rgba(255, 255, 255, 0.95); background: rgba(255, 255, 255, 0.95);
border-radius: 50%; border-radius: 50%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
border: 2px solid rgba(0, 122, 255, 0.2); border: 2px solid rgba(0, 122, 255, 0.3);
} }
.split-icon { .split-icon {
font-size: 14px; font-size: 12px;
color: #007AFF; color: #007AFF;
font-weight: bold; font-weight: bold;
} }

View File

@ -14,39 +14,46 @@ export default function TemplateCard({ template, onClick }: TemplateCardProps) {
return ( return (
<View className='template-card' onClick={handleClick}> <View className='template-card' onClick={handleClick}>
{/* 左右对比区域 */} {/* 合成对比图片区域 */}
<View className='image-comparison'> <View className='image-comparison'>
<View className='split-container'> <View className='merged-image-container'>
{/* 左半部分 - 原图的左半部分 */}
<View className='image-half left-half'> <View className='image-half left-half'>
<Image <Image
className='comparison-image' className='comparison-image left-image'
src={template.input} src={template.input}
mode='aspectFill' mode='aspectFill'
lazyLoad lazyLoad
/> />
<View className='image-label input-label'>
<Text className='label-text'></Text>
</View>
</View> </View>
{/* 右半部分 - 效果图的右半部分 */}
<View className='image-half right-half'> <View className='image-half right-half'>
<Image <Image
className='comparison-image' className='comparison-image right-image'
src={template.output} src={template.output}
mode='aspectFill' mode='aspectFill'
lazyLoad lazyLoad
/> />
<View className='image-label output-label'>
<Text className='label-text'></Text>
</View>
</View>
</View> </View>
{/* 中央分割线 */}
<View className='split-line'> <View className='split-line'>
<View className='split-handle'> <View className='split-handle'>
<Text className='split-icon'></Text> <Text className='split-icon'></Text>
</View> </View>
</View> </View>
{/* 标签 */}
<View className='image-labels'>
<View className='label-left input-label'>
<Text className='label-text'></Text>
</View>
<View className='label-right output-label'>
<Text className='label-text'></Text>
</View>
</View>
</View>
</View> </View>
{/* 模板信息 */} {/* 模板信息 */}