经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » React » 查看文章
React + Threejs + Swiper 实现全景图效果的完整代码
来源:jb51  时间:2021/6/28 12:41:46  对本文有异议

  咱先看看全景图实现效果:展示地址
  截图:

全景图

  体验了一下是不是感觉周围环境转了一圈,感觉世界是圆的?😁
  没错!恭喜你答对了!地球就是圆的!👀

全景效果实现

  有了上面的提示,对 threejs 有一点了解的小伙伴可能就猜出来了,这个全景效果其实就是使用一个球体实现的~ 而我们只是在球体表面上贴了一张纹理贴图而已(滚轮向外滚就可以看到这个球体了,看上去像个玻璃球,怪好看的,还有个彩蛋😁(好吧,说出来就不是彩蛋了)):

在这里插入图片描述

  初始时,我们的视角在球体正中心,视角的移动则是依靠 threejs 提供的工具 OrbitControls 来控制。

  那么创建这个球体的代码如下:

  1. const geometry = new THREE.SphereBufferGeometry(500, 32, 32);
  2. geometry.scale(-1, 1, 1); // 将纹理反贴
  3. const material = new THREE.MeshBasicMaterial({
  4. map: new THREE.TextureLoader().load(imglist[0].default) // 传入图片的URL或者路径,也可以是 Data URI.
  5. });
  6. const mesh = new THREE.Mesh(geometry, material);
  7. scene.add(mesh);
  8.  
  9. const controls = new OrbitControls(camera, renderer.domElement);
  10. controls.enablePan = false;
  11. controls.maxDistance = 1000;

不知道 Data URI 是什么的可以看看 MDN 文档

轮播图

  轮播图实现则是使用 swiper 这个库,使用起来非常方便,具体可自行查阅文档。
  在滑动轮播图时,会触发一个 onSliderChange 事件,这个事件传入当前的 swiper 作为参数,我们就可以通过当前激活的元素来获取图片并替换球体的纹理贴图了:

  1. onSliderChange = curSwiper => {
  2. const mesh = this.mesh;
  3. const texture = imglist[curSwiper.activeIndex].default;
  4. mesh.material.map = new THREE.TextureLoader().load(texture);
  5. };

  下面是我的 swiper 设置,其中 SwiperSlider 是一个可滑动的轮播图卡片,EffectCoverflow 是滑动时触发的效果,swiper 中提供了四种可选效果:Fade、Coverflow、Flip 以及 Cube。imglist 则是一组图片,其中 imglist[i].default 属性保存了图片的 base64 编码。

  1. import { Swiper, SwiperSlide } from 'swiper/react';
  2. import SwiperCore, { EffectCoverflow } from 'swiper';
  3. import 'swiper/swiper.min.css';
  4. import 'swiper/components/effect-coverflow/effect-coverflow.min.css';
  5.  
  6. SwiperCore.use([EffectCoverflow]);
  7.  
  8. //....
  9. <Swiper
  10. className='panoramic-imgs'
  11. spaceBetween={50} // 间距
  12. slidesPerView={3} // 轮播图里可预览图片数
  13. onSlideChange={this.onSliderChange} // 滑动时触发的回调
  14. onSwiper={(swiper) => console.log(swiper)} // 初始加载时触发的回调
  15. direction='vertical' // 轮播图方向,默认是水平 horizontal
  16. effect={'coverflow'} // 滑动效果
  17. grabCursor={true} // 鼠标放在轮播图上是否显示拖拽
  18. centeredSlides={true} // 当前处于激活状态的图片是否要居中
  19. coverflowEffect={{ // coverflow 效果参数设置,可自行调整
  20. "rotate": 50,
  21. "stretch": 0,
  22. "depth": 100,
  23. "modifier": 1,
  24. "slideShadows": true
  25. }}
  26. {
  27. imglist.map((img, idx) => {
  28. return <SwiperSlide key={idx}>
  29. <img src={img.default} className='panoramic-img'></img>
  30. </SwiperSlide>
  31. })
  32. }
  33. </Swiper>

  全景效果的实现就说到这了,当然,如果什么地方有疑问可以留言或者参考我的代码(下面贴出来),只要对 threejs 和 react 有一定了解的同学我相信实现这么一个效果并不难,代码量也很小~

完整代码

  1. import React, { Component } from 'react';
  2.  
  3. import Layout from '@theme/Layout';
  4. import Head from '@docusaurus/Head';
  5.  
  6. import * as THREE from 'three';
  7. import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
  8. import * as _ from 'underscore';
  9. import { message } from 'antd';
  10.  
  11. import { Swiper, SwiperSlide } from 'swiper/react';
  12. import SwiperCore, { EffectCoverflow } from 'swiper';
  13. import 'swiper/swiper.min.css';
  14. import 'swiper/components/effect-coverflow/effect-coverflow.min.css';
  15.  
  16. import './index.css';
  17. import imgs from './imgs.json';
  18.  
  19. SwiperCore.use([EffectCoverflow]);
  20.  
  21. const imglist = imgs.map(img => {
  22. return require('../../../static/img/panoramic/' + img.name);
  23. });
  24.  
  25. export default class Panormatic extends Component {
  26. constructor() {
  27. super();
  28. this.renderer = null;
  29. this.camera = null;
  30. this.scene = null;
  31. this.container = null;
  32. this.controls = null;
  33. this.showMessage = true; // 彩蛋提示
  34. }
  35.  
  36. componentDidMount() {
  37. const container = document.getElementById('panoramic-canvas-container');
  38. const canvas = document.getElementById('panoramic-canvas');
  39. const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
  40.  
  41. renderer.setClearColor(0xffffff); // b2e0df 绿豆沙色
  42. renderer.setPixelRatio( window.devicePixelRatio );
  43. const height = container.clientHeight;
  44. const width = container.clientWidth;
  45. renderer.setSize(width, height);
  46. const camera = new THREE.PerspectiveCamera(60, width / height, 1, 30000);
  47. camera.position.set(0, 0, 1);
  48. camera.center = new THREE.Vector3(0, 0, 0);
  49.  
  50. const scene = new THREE.Scene();
  51.  
  52. const geometry = new THREE.SphereBufferGeometry(500, 32, 32);
  53. geometry.scale(-1, 1, 1); // 将纹理反贴
  54. const material = new THREE.MeshBasicMaterial({
  55. map: new THREE.TextureLoader().load(imglist[0].default)
  56. });
  57. const mesh = new THREE.Mesh(geometry, material);
  58. scene.add(mesh);
  59.  
  60. const controls = new OrbitControls(camera, renderer.domElement);
  61. // controls.enableZoom = false;
  62. controls.enablePan = false;
  63. controls.maxDistance = 1000;
  64.  
  65. this.renderer = renderer;
  66. this.camera = camera;
  67. this.scene = scene;
  68. this.container = container;
  69. this.controls = controls;
  70. this.mesh = mesh;
  71.  
  72. // 设置提示框的全局配置
  73. message.config({
  74. top: 100,
  75. duration: 3.5,
  76. maxCount: 1,
  77. });
  78.  
  79. this.onControlsChange = _.throttle(this.onChange, 100);
  80. controls.addEventListener('change', this.onControlsChange);
  81. window.addEventListener('resize', this.onWindowResize);
  82. this.renderLoop();
  83. }
  84.  
  85. componentWillUnmount() {
  86. const mesh = this.mesh;
  87. mesh.material.dispose();
  88. mesh.geometry.dispose();
  89. this.scene.remove(mesh);
  90. window.removeEventListener('resize', this.onWindowResize);
  91. this.controls.removeEventListener('change', this.onControlsChange);
  92. message.destroy();
  93. }
  94.  
  95. onChange = (e) => {
  96. const camera = this.camera;
  97. if (camera.position.distanceTo(camera.center) >= 700) {
  98. if (this.showMessage) {
  99. message.success('🎊恭喜你发现了全景效果的小秘密~🎉');
  100. this.showMessage = false;
  101. }
  102. } else {
  103. this.showMessage = true;
  104. }
  105. }
  106.  
  107. onSliderChange = (curSwiper) => {
  108. const mesh = this.mesh;
  109. const texture = imglist[curSwiper.activeIndex].default;
  110. mesh.material.map = new THREE.TextureLoader().load(texture);
  111. };
  112.  
  113. onWindowResize = () => {
  114. const camera = this.camera;
  115. const renderer = this.renderer;
  116. const width = this.container.clientWidth;
  117. const height = this.container.clientHeight;
  118. camera.aspect = width / height;
  119. camera.updateProjectionMatrix();
  120. renderer.setSize(width, height);
  121. };
  122.  
  123. renderLoop = () => {
  124. this.renderer.render(this.scene, this.camera);
  125. requestAnimationFrame(this.renderLoop);
  126. };
  127.  
  128. render() {
  129. return (
  130. <Layout>
  131. <Head>
  132. <title>全景图 | Yle</title>
  133. </Head>
  134. <div id='panoramic-container'>
  135. <Swiper
  136. className='panoramic-imgs'
  137. spaceBetween={50}
  138. slidesPerView={3}
  139. onSlideChange={this.onSliderChange}
  140. onSwiper={(swiper) => console.log(swiper)}
  141. direction='vertical'
  142. effect={'coverflow'}
  143. grabCursor={true}
  144. centeredSlides={true}
  145. coverflowEffect={{
  146. "rotate": 50,
  147. "stretch": 0,
  148. "depth": 100,
  149. "modifier": 1,
  150. "slideShadows": true
  151. }}
  152. >
  153. {
  154. imglist.map((img, idx) => {
  155. return <SwiperSlide key={idx}>
  156. <img src={img.default} className='panoramic-img'></img>
  157. </SwiperSlide>
  158. })
  159. }
  160. </Swiper>
  161. <div id='panoramic-canvas-container'>
  162. <canvas id='panoramic-canvas'></canvas>
  163. </div>
  164. </div>
  165. </Layout>
  166. );
  167. }
  168. }

到此这篇关于React + Threejs + Swiper 实现全景图效果的完整代码的文章就介绍到这了,更多相关React全景图内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号