基于ArkTS打造的电商应用界面设计与实现
在移动应用开发领域,ArkTS作为HarmonyOS生态的重要开发语言,凭借其简洁的语法和强大的组件化能力,正成为构建高性能应用的首选。本文将深入解析一个基于ArkTS开发的电商应用界面代码,从架构设计到交互实现,全方位展现如何利用ArkTS打造流畅且美观的电商购物体验。
数据模型设计:清晰的业务实体抽象
在电商应用中,数据模型是构建整个应用的基础。代码中定义了两个核心类,分别用于表示轮播图数据和商品信息,这种清晰的实体抽象为后续开发奠定了坚实基础。
BannerClass:轮播图数据模型
class BannerClass {
id: string;
imagePath: string;
title: string;
constructor(id: string, imagePath: string, title: string) {
this.id = id;
this.imagePath = imagePath;
this.title = title;
}
}
该模型包含三个基本属性:唯一标识id、图片路径imagePath和标题title。在实际应用中,轮播图通常用于展示促销活动或热门商品,通过这种简洁的模型设计,能够轻松实现轮播图数据的管理和展示。
ShopClass:商品信息模型
class ShopClass {
id: string;
imagePath: string;
title: string;
price: string;
constructor(id: string, imagePath: string, title: string, price: string) {
this.id = id;
this.imagePath = imagePath;
this.title = title;
this.price = price;
}
}
商品模型在电商应用中至关重要,此模型包含商品id、图片路径、商品名称和价格信息。值得注意的是,价格字段采用字符串类型,这在实际开发中较为常见,便于处理价格前缀(如"¥")或其他格式化需求。
首页设计:从布局到交互的完整实现
首页作为电商应用的门户,直接影响用户体验。代码中的Index组件采用了"轮播图+瀑布流"的经典布局模式,同时融入了丰富的交互效果。
轮播图组件:动态展示与视觉引导
Swiper() {
ForEach(this.SliderImage, (item: BannerClass) => {
Image($r(item.imagePath))
.width('100%')
.height(200)
.objectFit(ImageFit.Cover)
.borderRadius(20)
.margin({ bottom: 10 })
});
}
.autoPlay(true)
.interval(3000)
.indicatorStyle({ color: '#ffffff' })
轮播图通过Swiper组件实现,设置了自动播放(autoPlay(true))和3秒的切换间隔(interval(3000)),白色指示器(indicatorStyle)在视觉上与背景形成对比,提升了用户体验。每张图片采用Cover模式填充,确保图片完整显示且不失真,20px的圆角设计则符合现代UI的审美趋势。
瀑布流布局:高效的商品展示方案
WaterFlow() {
ForEach(this.TutorialArray, (item: ShopClass) => {
FlowItem() {
this.badList(item)
}
.onClick(() => {
this.mainStarIndex.pushPathByName('shopCartView', item);
})
});
}
.columnsTemplate('1fr 1fr')
.columnsGap(15)
.rowsGap(15)
.padding({ left: 12, right: 12, top: 5, bottom: 10 })
.backgroundColor('#f5f5f5')
瀑布流布局使用WaterFlow组件实现,采用两列布局(columnsTemplate('1fr 1fr')),行列间距均为15px,确保商品卡片之间有合适的留白。点击商品卡片时,通过mainStarIndex.pushPathByName导航到商品详情页,实现了从列表到详情的交互流程。
商品卡片组件:细节之处见真章
@Builder
badList(item: ShopClass) {
Column({ space: 10 }) {
// 商品图片 - 添加悬停效果
Image($r(item.imagePath))
.width('100%')
.height(150)
.objectFit(ImageFit.Cover)
.borderRadius(12)
.transition({ type: TransitionType.All, scale: { x: 0.95, y: 0.95 } })
// 商品标题
Text(item.title)
.width('100%')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.textAlign(TextAlign.Start)
.fontFamily('HarmonyOS Sans')
// 评分和价格
Row() {
// 评分部分
Row({ space: 5 }) {
Image($r('app.media.star'))
.width(18)
Text('4.5')
.fontSize(15)
.fontColor('#666666')
}
// 价格
Text(`¥${item.price}`)
.fontSize(18)
.fontColor('#e6393f')
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
.textAlign(TextAlign.End)
}
.width('100%')
.alignItems(VerticalAlign.Center)
}
.width('100%')
.padding({ bottom: 15 })
.backgroundColor('#FFFFFF')
.borderRadius(15)
.shadow({ color: '#dcdcdc', radius: 6, offsetX: 0, offsetY: 2 })
.margin({ bottom: 10 })
.hoverEffect(HoverEffect.Scale) // 添加悬停缩放效果
}
商品卡片通过@Builder装饰器封装为可复用组件,包含图片、标题、评分和价格信息。图片添加了悬停缩放过渡效果(transition),当用户手指悬停时,图片会轻微缩小,提供直观的交互反馈。价格使用红色(#e6393f)和加粗样式突出显示,符合电商应用的设计惯例。卡片整体采用白色背景、15px圆角和阴影效果,营造出层次感和立体感。
商品详情页:沉浸式购物体验构建
商品详情页是促成交易的关键环节,代码中的ShopsView组件实现了从轮播图展示到购买按钮的完整流程,注重细节设计和用户引导。
顶部轮播与导航:品牌与功能的双重展示
Swiper() {
ForEach(this.SliderArray, (item: BannerClass) => {
Image($r(item.imagePath))
.width('100%')
.height(300)
.objectFit(ImageFit.Cover)
.transition({ type: TransitionType.All, opacity: 0.8, scale: { x: 0.95, y: 0.95 } })
});
}
.autoPlay(true)
.interval(3000)
.borderRadius(20)
.margin({ bottom: 15 })
// 返回按钮和标题栏
Row() {
Image($r('app.media.black'))
.width(30)
.height(30)
.onClick(() => {
this.mainStarIndex.pop();
})
.margin({ right: 10 })
Text('商品详情')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontFamily('HarmonyOS Sans')
.layoutWeight(1)
.textAlign(TextAlign.Center)
.fontColor(Color.Red)
}
.width('100%')
.height(60)
.alignItems(VerticalAlign.Bottom)
.justifyContent(FlexAlign.Start)
.padding({ top: 0, left: 15, right: 15 })
.position({ top: 0 })
详情页顶部的轮播图高度增加到300px,提供更沉浸式的图片展示体验,同时添加了透明度和缩放的过渡效果,增强视觉吸引力。自定义的返回按钮和标题栏采用红色文字突出显示,与白色背景形成鲜明对比,便于用户识别和操作。
商品信息展示:从内容到交互的完整链路
// 商品名称
Text(this.article.title)
.fontSize(26)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
.textAlign(TextAlign.Center)
.width('100%')
.margin({ bottom: 10 })
// 价格标签
Row() {
Text(`¥${this.article.price.split('¥')[1]}`)
.fontSize(24)
.fontColor('#e6393f')
.fontWeight(FontWeight.Bold)
Text('已售 1000+')
.fontSize(14)
.fontColor('#999999')
.margin({ left: 10 })
}
.width('100%')
.padding({ left: 20, right: 20 })
// 商品描述
Text('这里是商品的详细描述,可以包含材质、尺寸、产地等重要信息。您可以通过滚动查看完整的商品介绍。')
.fontSize(16)
.fontColor('#555555')
.lineHeight(26)
.textAlign(TextAlign.Start)
.width('100%')
.padding({ left: 20, right: 20 })
商品名称使用26px的大号字体和粗体样式,确保用户一眼就能看到商品信息。价格部分通过split('¥')[1]处理,提取实际价格数值并以红色突出显示,旁边的销售数据("已售1000+")则增强了商品的可信度。商品描述采用较大的行高(26),提升长文本的可读性,为实际应用中的详细说明预留了空间。
购物引导:从信任到行动的转化设计
// 商品特色图标
Row({ space: 30 }) {
Column() {
Image($r('app.media.background'))
.width(25)
.height(25)
Text('快递包邮')
.fontSize(14)
.fontColor('#666666')
}
Column() {
Image($r('app.media.background'))
.width(25)
.height(25)
Text('7天退换')
.fontSize(14)
.fontColor('#666666')
}
Column() {
Image($r('app.media.background'))
.width(25)
.height(25)
Text('正品保障')
.fontSize(14)
.fontColor('#666666')
}
}
.width('100%')
.justifyContent(FlexAlign.SpaceEvenly)
.padding({ left: 20, right: 20 })
// 加入购物车按钮
Button('加入购物车')
.onClick(() => {
// 这里可以添加购物车逻辑
})
.width(240)
.height(50)
.fontSize(18)
.backgroundColor('#ff4444')
.borderRadius(30)
.margin({ top: 25 })
.shadow({ color: '#ff6666', radius: 10, offsetX: 0, offsetY: 4 })
.hoverEffect(HoverEffect.Scale)
特色服务图标通过图标+文字的形式展示"快递包邮"、"7天退换"和"正品保障"等信息,增强用户对商品的信任感。加入购物车按钮采用醒目的红色(#ff4444),搭配阴影效果和悬停缩放动画,吸引用户点击。240px的宽度和50px的高度确保按钮在各种设备上都易于点击,符合移动端交互设计原则。
架构设计:Navigation与组件化思想
整个应用采用了Navigation导航框架,通过NavPathStack管理页面栈,实现了首页与详情页之间的平滑切换。@Provide和@Consume装饰器的使用,实现了组件间的数据传递,体现了ArkTS的状态管理思想。
@Provide('mainStarIndex') mainStarIndex: NavPathStack = new NavPathStack();
@Builder
shopPage(name: string, params: ShopClass | string) {
if (name === 'shopCartView') {
ShopsView({
article: params as ShopClass
});
}
}
build() {
Navigation(this.mainStarIndex) {
// 页面内容
}
.navDestination(this.shopPage)
}
在详情页中,通过@Consume接收来自首页的导航参数:
@Component
export struct ShopsView {
@Consume('mainStarIndex') mainStarIndex: NavPathStack;
@Prop article: ShopClass;
// 页面内容
}
这种架构设计使得页面导航和数据传递变得清晰可控,为后续扩展更多页面和功能奠定了良好的基础。
附:代码
class BannerClass {
id: string;
imagePath: string;
title: string;
constructor(id: string, imagePath: string, title: string) {
this.id = id;
this.imagePath = imagePath;
this.title = title;
}
}
class ShopClass {
id: string;
imagePath: string;
title: string;
price: string;
constructor(id: string, imagePath: string, title: string, price: string) {
this.id = id;
this.imagePath = imagePath;
this.title = title;
this.price = price;
}
}
@Entry
@Component
struct Index {
private SliderImage: Array<BannerClass> = [
new BannerClass('1', 'app.media.shop_swiper1', ''),
new BannerClass('2', 'app.media.shop_swiper1', ''),
new BannerClass('3', 'app.media.shop_swiper1', ''),
new BannerClass('4', 'app.media.shop_swiper1', ''),
];
private TutorialArray: Array<ShopClass> = [
new ShopClass('1', 'app.media.shop_01', '商品1', '¥10'),
new ShopClass('2', 'app.media.shop_02', '商品2', '¥10'),
new ShopClass('3', 'app.media.shop_03', '商品3', '¥10'),
new ShopClass('4', 'app.media.shop_04', '商品4', '¥10'),
new ShopClass('5', 'app.media.shop_05', '商品5', '¥10'),
new ShopClass('6', 'app.media.shop_06', '商品6', '¥10'),
new ShopClass('7', 'app.media.shop_07', '商品7', '¥10'),
new ShopClass('8', 'app.media.shop_08', '商品8', '¥10'),
new ShopClass('9', 'app.media.shop_09', '商品9', '¥10'),
new ShopClass('10', 'app.media.shop_10', '商品10', '¥10'),
new ShopClass('11', 'app.media.shop_11', '商品11', '¥10'),
new ShopClass('12', 'app.media.shop_12', '商品12', '¥10'),
];
@Provide('mainStarIndex') mainStarIndex: NavPathStack = new NavPathStack();
@Builder
shopPage(name: string, params: ShopClass | string) {
if (name === 'shopCartView') {
ShopsView({
article: params as ShopClass
});
}
}
build() {
Navigation(this.mainStarIndex) {
Column({ space: 15 }) {
// 轮播图组件 - 添加自动播放和指示器
Swiper() {
ForEach(this.SliderImage, (item: BannerClass) => {
Image($r(item.imagePath))
.width('100%')
.height(200)
.objectFit(ImageFit.Cover)
.borderRadius(20)
.margin({ bottom: 10 })
});
}
.autoPlay(true)
.interval(3000)
.indicatorStyle({ color: '#ffffff' })
// 瀑布流布局 - 优化样式和间距
WaterFlow() {
ForEach(this.TutorialArray, (item: ShopClass) => {
FlowItem() {
this.badList(item)
}
.onClick(() => {
this.mainStarIndex.pushPathByName('shopCartView', item);
})
});
}
.columnsTemplate('1fr 1fr')
.columnsGap(15)
.rowsGap(15)
.padding({ left: 12, right: 12, top: 5, bottom: 10 })
.backgroundColor('#f5f5f5')
}
.width('100%')
}
.hideTitleBar(true)
.mode(NavigationMode.Stack)
.navDestination(this.shopPage)
}
@Builder
badList(item: ShopClass) {
Column({ space: 10 }) {
// 商品图片 - 添加悬停效果
Image($r(item.imagePath))
.width('100%')
.height(150)
.objectFit(ImageFit.Cover)
.borderRadius(12)
.transition({ type: TransitionType.All, scale: { x: 0.95, y: 0.95 } })
// 商品标题
Text(item.title)
.width('100%')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.textAlign(TextAlign.Start)
.fontFamily('HarmonyOS Sans')
// 评分和价格
Row() {
// 评分部分
Row({ space: 5 }) {
Image($r('app.media.star'))
.width(18)
// .tintColor('#FFD700')
Text('4.5')
.fontSize(15)
.fontColor('#666666')
}
// 价格
Text(`¥${item.price}`)
.fontSize(18)
.fontColor('#e6393f')
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
.textAlign(TextAlign.End)
}
.width('100%')
.alignItems(VerticalAlign.Center)
}
.width('100%')
.padding({ bottom: 15 })
.backgroundColor('#FFFFFF')
.borderRadius(15)
.shadow({ color: '#dcdcdc', radius: 6, offsetX: 0, offsetY: 2 })
.margin({ bottom: 10 })
.hoverEffect(HoverEffect.Scale) // 添加悬停缩放效果
}
}
// 商品详细页面
@Component
export struct ShopsView {
@Consume('mainStarIndex') mainStarIndex: NavPathStack;
@Prop article: ShopClass;
private SliderArray: Array<BannerClass> = [
new BannerClass('', `${this.article.imagePath}`, ''),
new BannerClass('', `${this.article.imagePath}`, ''),
new BannerClass('', `${this.article.imagePath}`, ''),
new BannerClass('', `${this.article.imagePath}`, ''),
new BannerClass('', `${this.article.imagePath}`, ''),
];
build() {
NavDestination() {
Stack() {
// 轮播图 - 添加指示器和过渡效果
Swiper() {
ForEach(this.SliderArray, (item: BannerClass) => {
Image($r(item.imagePath))
.width('100%')
.height(300)
.objectFit(ImageFit.Cover)
.transition({ type: TransitionType.All, opacity: 0.8, scale: { x: 0.95, y: 0.95 } })
});
}
.autoPlay(true)
.interval(3000)
.borderRadius(20)
.margin({ bottom: 15 })
// 返回按钮和标题栏
Row() {
Image($r('app.media.black'))
.width(30)
.height(30)
.onClick(() => {
this.mainStarIndex.pop();
})
.margin({ right: 10 })
Text('商品详情')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontFamily('HarmonyOS Sans')
.layoutWeight(1)
.textAlign(TextAlign.Center)
.fontColor(Color.Red)
}
.width('100%')
.height(60)
.alignItems(VerticalAlign.Bottom)
.justifyContent(FlexAlign.Start)
.padding({ top: 0,left: 15, right: 15})
.position({ top: 0})
}
.width('100%')
// 商品信息展示区
Column({ space: 25 }) {
// 商品名称
Text(this.article.title)
.fontSize(26)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
.textAlign(TextAlign.Center)
.width('100%')
.margin({ bottom: 10 })
// 价格标签
Row() {
Text(`¥${this.article.price.split('¥')[1]}`)
.fontSize(24)
.fontColor('#e6393f')
.fontWeight(FontWeight.Bold)
Text('已售 1000+')
.fontSize(14)
.fontColor('#999999')
.margin({left: 10})
}
.width('100%')
.padding({ left: 20, right: 20 })
// 商品描述
Text('这里是商品的详细描述,可以包含材质、尺寸、产地等重要信息。您可以通过滚动查看完整的商品介绍。')
.fontSize(16)
.fontColor('#555555')
.lineHeight(26)
.textAlign(TextAlign.Start)
.width('100%')
.padding({ left: 20, right: 20 })
// 分割线
Divider()
.strokeWidth(1)
.color('#eeeeee')
.margin({ top: 15, bottom: 15 })
// 商品特色图标
Row({ space: 30 }) {
Column() {
Image($r('app.media.background'))
.width(25)
.height(25)
Text('快递包邮')
.fontSize(14)
.fontColor('#666666')
}
Column() {
Image($r('app.media.background'))
.width(25)
.height(25)
Text('7天退换')
.fontSize(14)
.fontColor('#666666')
}
Column() {
Image($r('app.media.background'))
.width(25)
.height(25)
Text('正品保障')
.fontSize(14)
.fontColor('#666666')
}
}
.width('100%')
.justifyContent(FlexAlign.SpaceEvenly)
.padding({ left: 20, right: 20 })
// 加入购物车按钮
Button('加入购物车')
.width(20)
.height(20)
.margin({ right: 10 })
.onClick(() => {
// 这里可以添加购物车逻辑
})
.width(240)
.height(50)
.fontSize(18)
.backgroundColor('#ff4444')
.borderRadius(30)
.margin({ top: 25 })
.shadow({ color: '#ff6666', radius: 10, offsetX: 0, offsetY: 4 })
.hoverEffect(HoverEffect.Scale)
}
.width('100%')
.alignItems(HorizontalAlign.Center)
// .padding({ top:10, bottom: 30 })
.backgroundColor('#f9f9f9')
}
.hideTitleBar(true)
.onBackPressed(() => {
// 自定义返回逻辑
this.mainStarIndex.pop();
return true;
});
}
}