构建虚拟商店:React 360商品展示与购物车功能

构建虚拟商店:React 360商品展示与购物车功能

【免费下载链接】react-360 Create amazing 360 and VR content using React 【免费下载链接】react-360 项目地址: https://gitcode.com/gh_mirrors/re/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度商品展示和购物车功能。主要实现了以下功能:

  1. 使用Pano组件创建360度全景商店环境
  2. 通过Entity组件实现3D商品模型展示
  3. 使用React Context API管理购物车状态
  4. 设计交互式UI面板,实现商品浏览和购买流程

React 360提供了丰富的组件和API,可以帮助开发者快速构建沉浸式VR体验。更多高级功能和优化可以参考官方文档和示例项目。

高级示例:Samples/MediaAppTemplate/

API参考:docs/runtime.md

【免费下载链接】react-360 Create amazing 360 and VR content using React 【免费下载链接】react-360 项目地址: https://gitcode.com/gh_mirrors/re/react-360

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值