uniapp的uni-card滑动效果
时间: 2024-08-06 16:01:11 浏览: 202
UniApp 的 UniCard 组件提供了一种直观的方式来展示卡片式内容,它支持多种交互效果,其中包括滑动效果。UniCard通常包含标题、图片、描述等内容块,并且可以配置成平铺、堆叠或半透明等样式。要实现滑动效果,一般可以在数据改变或状态切换时,利用 Vue 的 `v-model` 或者 `ref` 来绑定卡片组件的状态,比如左右滑动或上下滑动。
例如,你可以通过监听手指的 touchstart 和 touchmove 事件,在用户触屏移动时更新卡片的位置。UniUI 提供了内置的滑动组件如 `u-swipe-item` 或者第三方插件,可以简化这个过程。同时,还可以结合 CSS3 的 `transform: translateX()` 或 `translateY()` 动画属性来控制滑动动画的流畅度。
相关问题
<template> <view class="detail-container"> <!-- 自定义导航栏 --> <view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }"> <uni-icons type="arrowleft" size="24" color="#333" @click="goBack"></uni-icons> <text class="nav-title">商品详情</text> <uni-icons type="more-filled" size="24" color="#333"></uni-icons> </view> <!-- 轮播图区域 --> <swiper class="swiper-box" indicator-dots autoplay circular indicator-color="rgba(255,255,255,0.6)" indicator-active-color="#fff"> <swiper-item v-for="(img, index) in product.imagesStore" :key="index"> <image :src="img" mode="aspectFill" class="swiper-img" @click="previewImage(index)" /> </swiper-item> </swiper> <!-- 商品信息卡片 --> <view class="info-card"> <view class="price-section"> <text class="current-price">¥{{ product.sampleStorePrice }}</text> <text class="sold">已售{{ product.sold }}件</text> </view> <view class="spec-section"> <text class="spec-label">规格:</text> <text class="spec-value">{{ product.dimension }}</text> </view> <view class="tags-section"> <text class="tag">7天无理由</text> <text class="tag">顺丰包邮</text> <text class="tag">正品保证</text> </view> </view> <!-- 商品手册 --> <view class="manual-section"> <view class="section-header"> <text class="section-title">工艺手册</text> </view> <view class="manual-images"> <image v-for="(img, idx) in product.processManual" :key="'manual'+idx" :src="img" mode="widthFix" class="manual-img" @click="previewManualImage(idx)" /> </view> </view> <!-- 商品详情 --> <view class="detail-section"> <view class="section-header"> <text class="section-title">商品详情</text> </view> <view class="detail-content"> <text class="detail-text">这是一款精心设计的高端商品,采用优质材料制作,工艺精湛,满足您对品质生活的追求。产品细节处理到位,经久耐用,为您提供卓越的使用体验。</text> <image v-for="(img, idx) in product.imagesStore" :key="'detail'+idx" :src="img" mode="widthFix" class="detail-img" /> </view> </view> <!-- 底部操作栏 --> <!-- <view class="action-bar safe-area"> <view class="action-icons"> <view class="icon-item"> <uni-icons type="star" size="24" color="#666"></uni-icons> <text class="icon-text">收藏</text> </view> <view class="icon-item"> <uni-icons type="cart" size="24" color="#666"></uni-icons> <text class="icon-text">购物车</text> </view> </view> <view class="action-buttons"> <button class="cart-btn">加入购物车</button> <button class="buy-btn">立即购买</button> </view> </view> --> </view> </template> <script> export default { data() { return { statusBarHeight: 0, product: { id: 0, dimension: '', imagesStore: [], processManual: [], sold: 0, sampleStorePrice: 0, createdAt: '', updatedAt: '' } } }, onLoad(options) { // 获取状态栏高度(适配刘海屏) const systemInfo = uni.getSystemInfoSync() this.statusBarHeight = systemInfo.statusBarHeight // 解析传递过来的商品数据 if (options.item) { try { const itemStr = decodeURIComponent(options.item) this.product = JSON.parse(itemStr) // 添加默认标题 this.product.title = `高端定制商品 #${this.product.id}` // 确保数组类型 if (!Array.isArray(this.product.imagesStore)) { this.product.imagesStore = [] } if (!Array.isArray(this.product.processManual)) { this.product.processManual = [] } } catch (e) { console.error('解析商品数据失败:', e) uni.showToast({ title: '加载商品信息失败', icon: 'none' }) } } }, methods: { goBack() { uni.navigateBack() }, previewImage(index) { uni.previewImage({ current: index, urls: this.product.imagesStore }) }, previewManualImage(index) { uni.previewImage({ current: index, urls: this.product.processManual }) } } } </script> <style lang="scss"> .detail-container { padding-bottom: 100rpx; background: #f5f5f5; min-height: 100vh; .custom-navbar { position: fixed; top: 0; left: 0; right: 0; z-index: 100; display: flex; align-items: center; justify-content: space-between; padding: 12rpx 30rpx; background: rgba(255, 255, 255, 0.9); backdrop-filter: blur(10px); box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); .nav-title { font-size: 34rpx; font-weight: 600; color: #333; } } .swiper-box { height: 750rpx; .swiper-img { width: 100%; height: 100%; display: block; } } .info-card { background: #fff; padding: 30rpx; border-radius: 16rpx; margin: 20rpx; box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05); .price-section { display: flex; align-items: center; justify-content: space-between; margin-bottom: 20rpx; .current-price { font-size: 48rpx; color: #e64340; font-weight: bold; } .sold { font-size: 28rpx; color: #999; } } .title-section { margin: 20rpx 0; .title { font-size: 36rpx; font-weight: 600; line-height: 1.5; } } .spec-section { display: flex; margin: 20rpx 0; font-size: 28rpx; .spec-label { color: #666; } .spec-value { color: #333; font-weight: 500; } } .tags-section { display: flex; flex-wrap: wrap; margin-top: 20rpx; .tag { background: #f2f2f2; color: #666; padding: 6rpx 16rpx; border-radius: 8rpx; margin-right: 16rpx; font-size: 24rpx; } } } .section-header { padding: 20rpx 30rpx; border-bottom: 1rpx solid #eee; .section-title { font-size: 32rpx; font-weight: 600; color: #333; position: relative; padding-left: 20rpx; &::before { content: ''; position: absolute; left: 0; top: 50%; transform: translateY(-50%); width: 8rpx; height: 32rpx; background: #e64340; border-radius: 4rpx; } } } .manual-section, .detail-section { background: #fff; border-radius: 16rpx; margin: 20rpx; overflow: hidden; box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05); .manual-images { padding: 20rpx; } .manual-img { width: 100%; display: block; margin-bottom: 20rpx; border-radius: 12rpx; overflow: hidden; &:last-child { margin-bottom: 0; } } .detail-content { padding: 30rpx; .detail-text { font-size: 28rpx; line-height: 1.8; color: #666; margin-bottom: 30rpx; } .detail-img { width: 100%; display: block; margin-bottom: 30rpx; border-radius: 12rpx; overflow: hidden; &:last-child { margin-bottom: 0; } } } } .action-bar { position: fixed; bottom: 0; left: 0; right: 0; height: 100rpx; background: #fff; display: flex; align-items: center; border-top: 1rpx solid #eee; padding: 0 20rpx; .action-icons { display: flex; width: 30%; justify-content: space-around; .icon-item { display: flex; flex-direction: column; align-items: center; justify-content: center; .icon-text { font-size: 20rpx; color: #666; margin-top: 4rpx; } } } .action-buttons { display: flex; flex: 1; justify-content: flex-end; button { height: 80rpx; line-height: 80rpx; border-radius: 40rpx; font-size: 32rpx; margin-left: 20rpx; padding: 0 40rpx; &.cart-btn { background: linear-gradient(135deg, #ffb400, #ff8c00); color: white; } &.buy-btn { background: linear-gradient(135deg, #ff5e5e, #ff2a2a); color: white; } } } } .safe-area { padding-bottom: constant(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom); } } </style> 我这里<view class="manual-section"> <view class="section-header"> <text class="section-title">工艺手册</text> </view> <view class="manual-images"> <image v-for="(img, idx) in product.processManual" :key="'manual'+idx" :src="img" mode="widthFix" class="manual-img" @click="previewManualImage(idx)" /> </view> </view> 不是有图片吗 我要点击按钮解锁才变清晰 要不然就是有一层模糊的
<think>我们已经在商品详情页中实现了工艺手册图片的展示。现在需要为这些图片添加点击事件,实现点击后解锁高清显示(即预览功能)。
根据引用内容,我们可以使用uni.previewImage方法来实现图片预览功能。该方法可以传入一个图片URL数组,并指定当前点击的图片索引。
实现步骤:
1. 在工艺手册图片上绑定点击事件,并传递当前图片的索引和图片数组。
2. 在事件处理函数中调用uni.previewImage方法,传入图片URL数组和当前索引。
注意:工艺手册图片的数据存储在product.processManual数组中(在商品详情页的data中)。
修改代码:
在模板中,为工艺手册图片添加点击事件:
@click="previewManualImage(index)"
在methods中定义previewManualImage方法:
previewManualImage(index) {
uni.previewImage({
current: index, // 当前显示图片的索引
urls: this.product.processManual // 图片列表
});
}
同样,我们也可以为商品详情中的图片添加预览功能,但根据需求,目前只要求工艺手册图片。
但是,请注意引用中提到的几种用法:
- 引用[1]和[2]展示了如何预览一个图片数组,并指定当前索引。
- 引用[3]和[4]展示了单张图片的预览(将单张图片放入数组)。
- 引用[5]也是单张图片预览。
因此,我们的实现符合引用中的示例。
下面我们修改商品详情页的代码,具体修改部分如下:
在模板中找到工艺手册图片的循环部分,添加点击事件:
<image
v-for="(img, idx) in product.processManual"
:key="'manual'+idx"
:src="img"
mode="widthFix"
class="manual-img"
@click="previewManualImage(idx)" <!-- 添加这行 -->
/>
在methods中添加previewManualImage方法:
methods: {
// ... 其他方法
previewManualImage(index) {
// 检查是否有图片
if (!this.product.processManual || this.product.processManual.length === 0) {
return;
}
uni.previewImage({
current: index,
urls: this.product.processManual,
loop: true, // 是否可循环
indicator: 'default' // 指示器样式,也可用'number'
});
}
}
这样,当用户点击工艺手册中的任意一张图片时,就会触发预览,并且可以左右滑动查看所有工艺手册图片。
注意:如果processManual数组中的图片是高清大图,那么预览时就会显示高清图。如果当前显示的是缩略图,而我们需要点击后加载高清图,那么需要额外的高清图URL。但根据需求描述,我们目前只有一组图片,所以假设processManual中的已经是高清图。
如果工艺手册图片和商品轮播图一样需要高清图,那么在上传图片时应该同时保存高清图,并在商品数据中提供高清图URL数组。但根据当前数据结构,我们直接使用processManual数组。
因此,修改后的商品详情页代码(仅修改了工艺手册图片部分)如下:</think># UniApp商品详情页工艺手册高清图片预览功能
在UniApp商品详情页中实现工艺手册图片的点击解锁高清显示功能,可以通过`uni.previewImage`API实现。下面是一个完整的解决方案:
## 实现代码
```vue
<template>
<view class="detail-container">
<!-- 顶部导航栏 -->
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
<uni-icons type="arrowleft" size="24" color="#333" @click="goBack"></uni-icons>
<text class="nav-title">商品详情</text>
<uni-icons type="more-filled" size="24" color="#333"></uni-icons>
</view>
<!-- 商品手册区域 -->
<view class="manual-section">
<view class="section-header">
<text class="section-title">工艺手册</text>
<text class="section-tip">点击图片解锁高清查看</text>
</view>
<view class="manual-images">
<view
v-for="(img, idx) in product.processManual"
:key="'manual'+idx"
class="manual-item"
@click="previewManualImage(idx)"
>
<image
:src="img"
mode="aspectFill"
class="manual-img"
/>
<view class="preview-overlay">
<uni-icons type="eye" size="28" color="#fff"></uni-icons>
<text class="preview-text">点击查看高清</text>
</view>
</view>
</view>
</view>
<!-- 其他商品信息... -->
</view>
</template>
<script>
export default {
data() {
return {
statusBarHeight: 0,
product: {
id: 0,
dimension: '',
imagesStore: [],
processManual: [
"https://via.placeholder.com/800x600/FFCC99/FFFFFF?text=工艺手册1",
"https://via.placeholder.com/800x600/CCFFCC/FFFFFF?text=工艺手册2",
"https://via.placeholder.com/800x600/CCCCFF/FFFFFF?text=工艺手册3"
],
sold: 0,
sampleStorePrice: 0
}
}
},
onLoad(options) {
// 获取状态栏高度
const systemInfo = uni.getSystemInfoSync()
this.statusBarHeight = systemInfo.statusBarHeight
// 解析传递的商品数据
if (options.item) {
try {
const itemStr = decodeURIComponent(options.item)
this.product = JSON.parse(itemStr)
// 确保工艺手册图片数组存在
if (!Array.isArray(this.product.processManual)) {
this.product.processManual = []
}
} catch (e) {
console.error('解析商品数据失败:', e)
uni.showToast({
title: '加载商品信息失败',
icon: 'none'
})
}
}
},
methods: {
goBack() {
uni.navigateBack()
},
// 工艺手册图片预览方法
previewManualImage(index) {
if (!this.product.processManual || this.product.processManual.length === 0) {
uni.showToast({
title: '暂无工艺手册图片',
icon: 'none'
})
return
}
// 使用uni.previewImage预览高清图片
uni.previewImage({
current: index, // 当前显示图片的索引
urls: this.product.processManual, // 需要预览的图片链接列表
indicator: "number", // 指示器样式
loop: true, // 是否可循环
longPressActions: {
itemList: ['保存图片', '分享'],
success: (data) => {
if (data.tapIndex === 0) {
this.saveImage(this.product.processManual[index])
} else if (data.tapIndex === 1) {
this.shareImage(this.product.processManual[index])
}
}
}
})
},
// 保存图片到相册
saveImage(url) {
uni.showLoading({ title: '保存中...' })
uni.downloadFile({
url: url,
success: (res) => {
if (res.statusCode === 200) {
uni.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: () => {
uni.hideLoading()
uni.showToast({ title: '保存成功', icon: 'success' })
},
fail: () => {
uni.hideLoading()
uni.showToast({ title: '保存失败', icon: 'none' })
}
})
}
}
})
},
// 分享图片
shareImage(url) {
uni.share({
provider: 'weixin',
scene: 'WXSceneSession',
type: 2,
imageUrl: url,
success: () => {
uni.showToast({ title: '分享成功', icon: 'success' })
}
})
}
}
}
</script>
<style lang="scss">
.detail-container {
background: #f5f5f5;
min-height: 100vh;
padding-bottom: 100rpx;
.custom-navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
display: flex;
align-items: center;
justify-content: space-between;
padding: 12rpx 30rpx;
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px);
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
.nav-title {
font-size: 34rpx;
font-weight: 600;
color: #333;
}
}
.manual-section {
margin-top: 100rpx;
background: #fff;
border-radius: 16rpx;
margin: 20rpx;
overflow: hidden;
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.05);
.section-header {
padding: 20rpx 30rpx;
border-bottom: 1rpx solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
position: relative;
padding-left: 20rpx;
&::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 8rpx;
height: 32rpx;
background: #e64340;
border-radius: 4rpx;
}
}
.section-tip {
font-size: 24rpx;
color: #999;
}
}
.manual-images {
padding: 20rpx;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20rpx;
}
.manual-item {
position: relative;
border-radius: 12rpx;
overflow: hidden;
box-shadow: 0 4rpx 8rpx rgba(0,0,0,0.1);
transition: all 0.3s ease;
&:active {
transform: scale(0.98);
}
}
.manual-img {
width: 100%;
height: 300rpx;
display: block;
}
.preview-overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: rgba(0,0,0,0.5);
padding: 15rpx;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 24rpx;
.preview-text {
margin-left: 10rpx;
}
}
}
}
</style>
```
## 功能说明
### 1. 工艺手册图片预览功能
- **点击图片解锁高清**:用户点击工艺手册图片时,调用`uni.previewImage`方法展示高清大图
- **多图预览**:支持左右滑动查看所有工艺手册图片
- **指示器显示**:使用数字指示器显示当前图片位置
- **长按操作**:支持长按图片保存或分享
### 2. 预览功能实现代码
```javascript
previewManualImage(index) {
uni.previewImage({
current: index, // 当前显示图片索引
urls: this.product.processManual, // 图片URL数组
indicator: "number", // 指示器样式
loop: true, // 循环查看
longPressActions: {
itemList: ['保存图片', '分享'],
success: (data) => {
if (data.tapIndex === 0) {
this.saveImage(this.product.processManual[index])
} else if (data.tapIndex === 1) {
this.shareImage(this.product.processManual[index])
}
}
}
})
}
```
### 3. UI设计优化
- **预览提示**:在图片底部添加"点击查看高清"提示
- **悬停效果**:添加点击反馈动画增强用户体验
- **网格布局**:使用网格布局展示工艺手册图片
- **视觉层次**:通过阴影和圆角增强视觉层次感
### 4. 附加功能
- **图片保存**:实现图片保存到相册功能
- **图片分享**:支持将图片分享到微信等平台
- **错误处理**:添加空数据状态处理
## 使用说明
1. 将上述代码保存为`sampledetails.vue`
2. 在列表页传递商品数据:
```javascript
goToDetails(item) {
const itemStr = encodeURIComponent(JSON.stringify(item));
uni.navigateTo({
url: `/pages/store/sampledetails/sampledetails?item=${itemStr}`
});
}
```
3. 在详情页接收并处理数据:
```javascript
onLoad(options) {
if (options.item) {
const itemStr = decodeURIComponent(options.item);
this.product = JSON.parse(itemStr);
}
}
```
## 优化建议
1. **图片懒加载**:为提升性能,可以为图片添加懒加载
```html
<image lazy-load :src="img" mode="aspectFill" class="manual-img" />
```
2. **占位图优化**:加载过程中显示占位图
```html
<image
:src="img"
mode="aspectFill"
class="manual-img"
:lazy-load="true"
:show-menu-by-longpress="true"
@error="handleImageError"
/>
```
3. **高清图预加载**:在用户点击前预加载高清图片
```javascript
preloadImages() {
this.product.processManual.forEach(img => {
const image = new Image();
image.src = img;
});
}
```
这个实现方案提供了完整的工艺手册图片预览功能,包括高清图片查看、保存和分享功能,同时通过UI优化提升了用户体验。
uniapp 设计层叠卡片
UniApp 是一款基于 Vue.js 的跨平台 UI 开发框架,它允许开发者构建一次,多端部署。在设计层叠卡片(通常称为堆叠卡片或卡片瀑布流)时,你可以使用 UniApp 的组件和样式系统来实现。以下是一个基本的步骤:
1. **使用`uni-card`组件**:UniApp 提供了内置的`uni-card`组件,你可以创建一个卡片的基本结构。这包括标题、内容、图片等元素。
```html
<view class="card-container">
<uni-card>
<view class="card-header">卡片标题</view>
<view class="card-body">
<view>卡片内容</view>
<image src="your-image-url" placeholder="默认图片"></image>
</view>
</uni-card>
</view>
```
2. **设置层叠效果**:为了实现层叠效果,可以调整卡片的`margin-bottom`属性,让后面的卡片有一定的间距。同时,可以通过JavaScript动态控制某个特定卡片的`margin-bottom`,当点击上一张卡片时,改变下一张卡片的位置。
```javascript
data() {
return {
currentCardIndex: 0,
}
},
methods: {
handleSwipe(index) {
if (index === this.currentCardIndex) return;
this.currentCardIndex = index;
this.$refs.cards[index].style.marginBottom = '20px';
this.$refs.cards[index - 1].style.marginBottom = '0';
}
}
```
3. **添加交互**:可以使用`uni-swipe`组件来模拟滑动操作,或者监听touchstart, touchmove和touchend事件手动处理。
4. **CSS样式定制**:在`.card-container`, `.card-header`, `.card-body`等类上添加自定义样式,例如颜色、背景、阴影等,以符合你的设计需求。
阅读全文
相关推荐












