构建虚拟商店:React 360商品展示与购物车功能
项目概述
React 360是一个用于创建沉浸式360度和VR内容的开源框架,基于React构建。通过React 360,开发者可以使用熟悉的React组件模型来构建交互式的虚拟现实体验。本教程将展示如何利用React 360构建一个虚拟商店,实现商品360度展示和购物车功能。
官方文档:docs/what-is.md
项目结构概览:README.md
开发环境搭建
安装React 360 CLI
首先,需要安装React 360 CLI工具来创建和管理项目:
npm install -g react-360-cli
创建新项目:
react-360 init VirtualStore
cd VirtualStore
npm start
CLI工具源码:addons/react-360-cli/
项目基础结构
创建完成后,项目将包含以下核心文件和目录:
- index.js: 应用入口点
- client.js: 客户端配置
- index.html: HTML页面模板
- src/: 源代码目录
- static_assets/: 静态资源目录
基础应用模板:Samples/BasicAppTemplate/
360度商品展示实现
全景环境设置
使用Pano组件创建360度全景商店环境:
import { Pano } from 'react-360';
export default class StoreEnvironment extends React.Component {
render() {
return (
<Pano source={asset('store_panorama.jpg')} />
);
}
}
Pano组件源码:Libraries/Pano/Pano.js
资产加载工具:Libraries/Utilities/asset.js
商品展示组件
创建一个可交互的360度商品展示组件:
import React from 'react';
import { View, Entity, VrButton } from 'react-360';
import { StyleSheet } from 'react-360';
export default class ProductDisplay extends React.Component {
state = {
rotation: [0, 0, 0],
scale: 1.0
};
rotateProduct = (axis, amount) => {
const newRotation = [...this.state.rotation];
newRotation[axis] += amount;
this.setState({ rotation: newRotation });
};
zoomProduct = (amount) => {
this.setState({ scale: Math.max(0.5, Math.min(2.0, this.state.scale + amount)) });
};
render() {
const { product } = this.props;
return (
<View style={styles.productContainer}>
<Entity
source={{ obj: asset(product.modelPath), mtl: asset(product.materialPath) }}
style={{
transform: [
{ translate: [0, 0, -5] },
{ rotateX: this.state.rotation[0] },
{ rotateY: this.state.rotation[1] },
{ rotateZ: this.state.rotation[2] },
{ scale: this.state.scale }
]
}}
/>
<View style={styles.controls}>
<VrButton onClick={() => this.rotateProduct(1, 15)} style={styles.button}>
<View style={styles.buttonLabel}>⟲</View>
</VrButton>
<VrButton onClick={() => this.rotateProduct(1, -15)} style={styles.button}>
<View style={styles.buttonLabel}>⟳</View>
</VrButton>
<VrButton onClick={() => this.zoomProduct(0.1)} style={styles.button}>
<View style={styles.buttonLabel}>+</View>
</VrButton>
<VrButton onClick={() => this.zoomProduct(-0.1)} style={styles.button}>
<View style={styles.buttonLabel}>-</View>
</VrButton>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
productContainer: {
width: 600,
height: 400,
alignItems: 'center',
justifyContent: 'center',
},
controls: {
flexDirection: 'row',
position: 'absolute',
bottom: 20,
},
button: {
width: 60,
height: 60,
backgroundColor: 'rgba(255, 255, 255, 0.7)',
margin: 10,
borderRadius: 30,
alignItems: 'center',
justifyContent: 'center',
},
buttonLabel: {
fontSize: 30,
color: '#333',
}
});
实体组件源码:Libraries/Mesh/Entity.js
VR按钮组件:Libraries/VrButton/VrButton.js
商品数据模型
创建商品数据模型和产品列表组件:
// src/data/products.js
export const products = [
{
id: 1,
name: "智能手表",
price: 199.99,
description: "多功能智能手表,支持心率监测和GPS定位",
modelPath: "watch.obj",
materialPath: "watch.mtl",
thumbnail: "watch_thumb.jpg"
},
{
id: 2,
name: "无线耳机",
price: 149.99,
description: "高品质无线耳机,主动降噪",
modelPath: "headphones.obj",
materialPath: "headphones.mtl",
thumbnail: "headphones_thumb.jpg"
},
// 更多商品...
];
// src/components/ProductList.js
import React from 'react';
import { View, Image, Text, VrButton, StyleSheet } from 'react-360';
import { asset } from 'react-360';
export default class ProductList extends React.Component {
render() {
const { products, onSelectProduct } = this.props;
return (
<View style={styles.container}>
<Text style={styles.title}>商品列表</Text>
<View style={styles.productGrid}>
{products.map(product => (
<VrButton
key={product.id}
style={styles.productCard}
onClick={() => onSelectProduct(product)}
>
<Image
source={asset(product.thumbnail)}
style={styles.productImage}
/>
<Text style={styles.productName}>{product.name}</Text>
<Text style={styles.productPrice}>${product.price.toFixed(2)}</Text>
</VrButton>
))}
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
width: 800,
padding: 20,
backgroundColor: 'rgba(255, 255, 255, 0.8)',
borderRadius: 10,
},
title: {
fontSize: 36,
color: '#333',
marginBottom: 20,
textAlign: 'center',
},
productGrid: {
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'center',
},
productCard: {
width: 200,
height: 250,
margin: 10,
backgroundColor: 'white',
borderRadius: 8,
padding: 10,
alignItems: 'center',
},
productImage: {
width: 150,
height: 150,
marginBottom: 10,
},
productName: {
fontSize: 24,
color: '#333',
},
productPrice: {
fontSize: 20,
color: '#e74c3c',
marginTop: 5,
},
});
图片组件源码:Libraries/VRReactOverrides/Image.vr.js
样式表工具:Libraries/StyleSheet/LayoutPropTypes.vr.js
购物车功能实现
购物车状态管理
使用React的Context API创建购物车状态管理:
// src/context/CartContext.js
import React from 'react';
export const CartContext = React.createContext();
export class CartProvider extends React.Component {
state = {
items: [],
total: 0,
};
addToCart = (product) => {
this.setState(prevState => {
const existingItem = prevState.items.find(item => item.id === product.id);
let updatedItems;
if (existingItem) {
updatedItems = prevState.items.map(item =>
item.id === product.id
? { ...item, quantity: item.quantity + 1 }
: item
);
} else {
updatedItems = [...prevState.items, { ...product, quantity: 1 }];
}
const total = updatedItems.reduce(
(sum, item) => sum + item.price * item.quantity,
0
);
return { items: updatedItems, total };
});
};
removeFromCart = (productId) => {
this.setState(prevState => {
const updatedItems = prevState.items.filter(item => item.id !== productId);
const total = updatedItems.reduce(
(sum, item) => sum + item.price * item.quantity,
0
);
return { items: updatedItems, total };
});
};
render() {
return (
<CartContext.Provider value={{
cart: this.state,
addToCart: this.addToCart,
removeFromCart: this.removeFromCart
}}>
{this.props.children}
</CartContext.Provider>
);
}
}
购物车组件
创建购物车组件,显示已选商品和总价:
// src/components/ShoppingCart.js
import React from 'react';
import { View, Text, VrButton, StyleSheet, ScrollView } from 'react-360';
import { CartContext } from '../context/CartContext';
export default class ShoppingCart extends React.Component {
static contextType = CartContext;
render() {
const { cart, removeFromCart } = this.context;
return (
<View style={styles.container}>
<Text style={styles.title}>购物车</Text>
{cart.items.length === 0 ? (
<Text style={styles.emptyMessage}>购物车为空</Text>
) : (
<>
<ScrollView style={styles.itemsContainer}>
{cart.items.map(item => (
<View key={item.id} style={styles.cartItem}>
<Text style={styles.itemName}>{item.name}</Text>
<Text style={styles.itemDetails}>
${item.price.toFixed(2)} x {item.quantity}
</Text>
<VrButton
style={styles.removeButton}
onClick={() => removeFromCart(item.id)}
>
<Text style={styles.removeButtonText}>×</Text>
</VrButton>
</View>
))}
</ScrollView>
<View style={styles.totalContainer}>
<Text style={styles.totalText}>总计: ${cart.total.toFixed(2)}</Text>
<VrButton style={styles.checkoutButton}>
<Text style={styles.checkoutText}>结算</Text>
</VrButton>
</View>
</>
)}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
width: 600,
height: 500,
backgroundColor: 'rgba(255, 255, 255, 0.9)',
borderRadius: 10,
padding: 20,
},
title: {
fontSize: 36,
color: '#333',
marginBottom: 20,
textAlign: 'center',
},
emptyMessage: {
fontSize: 24,
color: '#777',
textAlign: 'center',
marginTop: 50,
},
itemsContainer: {
height: 350,
},
cartItem: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
padding: 10,
borderBottomWidth: 1,
borderBottomColor: '#eee',
},
itemName: {
fontSize: 22,
color: '#333',
width: 250,
},
itemDetails: {
fontSize: 20,
color: '#666',
width: 150,
textAlign: 'right',
},
removeButton: {
width: 40,
height: 40,
backgroundColor: '#e74c3c',
borderRadius: 20,
alignItems: 'center',
justifyContent: 'center',
},
removeButtonText: {
fontSize: 24,
color: 'white',
},
totalContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginTop: 15,
paddingTop: 15,
borderTopWidth: 2,
borderTopColor: '#3498db',
},
totalText: {
fontSize: 28,
color: '#333',
fontWeight: 'bold',
},
checkoutButton: {
backgroundColor: '#2ecc71',
padding: 10,
borderRadius: 5,
},
checkoutText: {
fontSize: 24,
color: 'white',
},
});
滚动视图组件参考:Libraries/Components/View/
整合与交互设计
主应用组件
将所有组件整合到主应用中:
// index.js
import React from 'react';
import { AppRegistry, View, asset, VrButton, Text, StyleSheet } from 'react-360';
import { CartProvider } from './src/context/CartContext';
import StoreEnvironment from './src/components/StoreEnvironment';
import ProductList from './src/components/ProductList';
import ProductDisplay from './src/components/ProductDisplay';
import ShoppingCart from './src/components/ShoppingCart';
import { products } from './src/data/products';
export default class VirtualStore extends React.Component {
state = {
selectedProduct: null,
showCart: false,
};
selectProduct = (product) => {
this.setState({ selectedProduct: product, showCart: false });
};
toggleCart = () => {
this.setState(prevState => ({ showCart: !prevState.showCart }));
};
render() {
const { selectedProduct, showCart } = this.state;
return (
<CartProvider>
<StoreEnvironment />
{/* 商品列表面板 */}
<View style={styles.productListPanel}>
<ProductList
products={products}
onSelectProduct={this.selectProduct}
/>
</View>
{/* 商品详情面板 */}
{selectedProduct && (
<View style={styles.productDisplayPanel}>
<ProductDisplay product={selectedProduct} />
<CartContext.Consumer>
{({ addToCart }) => (
<VrButton
style={styles.addToCartButton}
onClick={() => addToCart(selectedProduct)}
>
<Text style={styles.addToCartText}>加入购物车</Text>
</VrButton>
)}
</CartContext.Consumer>
</View>
)}
{/* 购物车按钮 */}
<VrButton
style={styles.cartButton}
onClick={this.toggleCart}
>
<Text style={styles.cartButtonText}>🛒</Text>
</VrButton>
{/* 购物车面板 */}
{showCart && (
<View style={styles.cartPanel}>
<ShoppingCart />
</View>
)}
</CartProvider>
);
}
}
const styles = StyleSheet.create({
productListPanel: {
transform: [{ translate: [-800, 0, -2000] }, { rotateY: 20 }],
},
productDisplayPanel: {
transform: [{ translate: [800, 0, -2000] }, { rotateY: -20 }],
alignItems: 'center',
},
addToCartButton: {
marginTop: 30,
width: 200,
height: 60,
backgroundColor: '#3498db',
borderRadius: 30,
alignItems: 'center',
justifyContent: 'center',
},
addToCartText: {
fontSize: 24,
color: 'white',
},
cartButton: {
position: 'absolute',
top: 50,
right: 50,
width: 80,
height: 80,
backgroundColor: 'rgba(255, 255, 255, 0.8)',
borderRadius: 40,
alignItems: 'center',
justifyContent: 'center',
},
cartButtonText: {
fontSize: 36,
},
cartPanel: {
transform: [{ translate: [0, 0, -1500] }],
},
});
AppRegistry.registerComponent('VirtualStore', () => VirtualStore);
多面板布局示例:Samples/MultiRoot/
交互优化
使用VrButton组件和光线投射实现交互优化:
// src/components/InteractiveEntity.js
import React from 'react';
import { Entity, VrButton, Text, View, StyleSheet } from 'react-360';
import { asset } from 'react-360';
export default class InteractiveEntity extends React.Component {
state = {
highlighted: false,
};
render() {
const {
modelPath,
materialPath,
position,
scale,
onClick,
label
} = this.props;
const { highlighted } = this.state;
return (
<VrButton
onEnter={() => this.setState({ highlighted: true })}
onExit={() => this.setState({ highlighted: false })}
onClick={onClick}
style={styles.button}
>
<Entity
source={{ obj: asset(modelPath), mtl: asset(materialPath) }}
style={{
transform: [
{ translate: position },
{ scale: highlighted ? [scale[0] * 1.1, scale[1] * 1.1, scale[2] * 1.1] : scale },
],
}}
/>
{highlighted && (
<View style={styles.labelContainer}>
<Text style={styles.labelText}>{label}</Text>
</View>
)}
</VrButton>
);
}
}
const styles = StyleSheet.create({
button: {
position: 'absolute',
},
labelContainer: {
position: 'absolute',
top: -100,
backgroundColor: 'rgba(0, 0, 0, 0.7)',
padding: 10,
borderRadius: 5,
},
labelText: {
fontSize: 24,
color: 'white',
},
});
交互处理模块:Libraries/Events/
控制器支持:Libraries/VRModules/ControllerInfo.js
测试与部署
本地测试
使用开发服务器进行本地测试:
npm start
打开浏览器访问http://localhost:8081即可查看虚拟商店效果。
测试工具:render-tests/
构建与部署
构建生产版本:
npm run bundle
构建脚本:scripts/bundle.js
部署文件将生成在build/目录下,可以将该目录部署到任何静态网站托管服务。
部署指南:docs/publish.md
总结
通过本教程,我们学习了如何使用React 360构建虚拟商店,包括360度商品展示和购物车功能。主要实现了以下功能:
- 使用Pano组件创建360度全景商店环境
- 通过Entity组件实现3D商品模型展示
- 使用React Context API管理购物车状态
- 设计交互式UI面板,实现商品浏览和购买流程
React 360提供了丰富的组件和API,可以帮助开发者快速构建沉浸式VR体验。更多高级功能和优化可以参考官方文档和示例项目。
高级示例:Samples/MediaAppTemplate/
API参考:docs/runtime.md
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



