当前位置:   article > 正文

【js&threeJS】入门three,并实现3D汽车展示厅,附带全码_thress 实现3d展厅

thress 实现3d展厅

首先放个最终效果图:

 

三维(3D)概念:

三维(3D)是一个描述物体在三个空间坐标轴上的位置和形态的概念。相比于二维(2D)只有长度和宽度的平面,三维增加了高度或深度这一维度

三维空间中,我们使用三个独立的坐标轴来描述物体的位置。通常使用笛卡尔坐标系,即X、Y和Z轴。其中,X轴表示横向,Y轴表示纵向,Z轴表示纵深或垂直方向。通过在这些轴上的不同值组合,可以确定一个点或对象在三维空间中的位置

大家可以three编辑器中感受一下三维:three.js editor

ps:默认英文,可以切换中文语言

three前提概念

以舞台展示为例:

  • 场景 Sence 相当于一个舞台,在这里是布置场景物品和表演者表演的地方
  • 相机 Carma 相当于观众的眼睛去观看
  • 几何体 Geometry 相当于舞台的表演者
  • 灯光 light 相当于舞台灯光照射控制 
  • Controls 相当于这出舞台剧的总导演

创建场景与相机

  1. <html>
  2. <head>
  3. <meta charset="utf-8">
  4. <title>My first three.js app</title>
  5. </head>
  6. <body>
  7. <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
  8. <script type="importmap">
  9. {
  10. "imports": {
  11. "three": "./three.module.js"
  12. }
  13. }
  14. </script>
  15. <script type="module">
  16. import { Scene, WebGLRenderer, PerspectiveCamera } from 'three'
  17. let scene,renderer,camera
  18. //创建场景
  19. const setScene = () => {
  20. scene = new Scene()
  21. renderer = new WebGLRenderer()
  22. //调用 setSize() 方法设置渲染器的大小为当前窗口的宽度和高度
  23. renderer.setSize(window.innerWidth, window.innerHeight)
  24. //将渲染器的 DOM 元素添加到页面的 <body> 元素中
  25. document.body.appendChild(renderer.domElement)
  26. }
  27. //相机的默认坐标
  28. const defaultMap = {
  29. x: 0,
  30. y: 10,
  31. z: 20,
  32. }
  33. //创建相机
  34. const setCamera = () => {
  35. const { x, y, z } = defaultMap
  36. //创建一个 PerspectiveCamera 对象,并传入参数来设置透视相机的属性:视野角度为 45 度,宽高比为窗口的宽高比,近裁剪面为 1,远裁剪面为 1000
  37. camera = new PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000)
  38. //用 position.set() 方法将相机的位置设置为之前从 defaultMap 中提取的坐标
  39. camera.position.set(x, y, z)
  40. }
  41. (function () {
  42. setScene()
  43. setCamera()
  44. })()
  45. </script>
  46. </body>
  47. </html>

PerspectiveCamera的详细说明:

new THREE.PerspectiveCamera构造函数用来创建透视投影相机,该构造函数总共有四个参数,分别是fov,aspect,near,far 。

fov表示摄像机视锥体垂直视野角度,最小值为0,最大值为180,默认值为50,实际项目中一般都定义45,因为45最接近人正常睁眼角度;aspect表示摄像机视锥体长宽比,默认长宽比为1,即表示看到的是正方形,实际项目中使用的是屏幕的宽高比;near表示摄像机视锥体近端面,这个值默认为0.1,实际项目中都会设置为1;far表示摄像机视锥体远端面,默认为2000,这个值可以是无限的,说的简单点就是我们视觉所能看到的最远距离。

引入模型

国外一个3d模型下载网站,里面有很多免费的模型下载  点击红框处下载

Log in to your Sketchfab account - Sketchfab

  1. <html>
  2. <head>
  3. <meta charset="utf-8">
  4. <title>My first three.js app</title>
  5. </head>
  6. <body>
  7. <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
  8. <script type="importmap">
  9. {
  10. "imports": {
  11. "three": "./three.module.js"
  12. }
  13. }
  14. </script>
  15. <script type="module">
  16. import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
  17. import { Scene, WebGLRenderer, PerspectiveCamera } from 'three'
  18. let scene, renderer, camera, directionalLight, dhelper
  19. let isLoading = true
  20. let loadingWidth = 0
  21. //创建场景
  22. const setScene = () => {
  23. scene = new Scene()
  24. renderer = new WebGLRenderer()
  25. renderer.setSize(window.innerWidth, window.innerHeight)
  26. document.body.appendChild(renderer.domElement)
  27. }
  28. //相机的默认坐标
  29. const defaultMap = {
  30. x: 0,
  31. y: 10,
  32. z: 20,
  33. }
  34. //创建相机
  35. const setCamera = () => {
  36. const { x, y, z } = defaultMap
  37. camera = new PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000)
  38. camera.position.set(x, y, z)
  39. }
  40. //通过Promise处理一下loadfile函数
  41. const loader = new GLTFLoader() //引入模型的loader实例
  42. const loadFile = (url) => {
  43. return new Promise(((resolve, reject) => {
  44. loader.load(url,
  45. (gltf) => {
  46. resolve(gltf)
  47. }, ({ loaded, total }) => {
  48. let load = Math.abs(loaded / total * 100)
  49. loadingWidth = load
  50. if (load >= 100) {
  51. setTimeout(() => {
  52. isLoading = false
  53. }, 1000)
  54. }
  55. console.log((loaded / total * 100) + '% loaded')
  56. },
  57. (err) => {
  58. reject(err)
  59. }
  60. )
  61. }))
  62. }
  63. (async function () {
  64. const gltf = await loadFile('./assets/scene.gltf')
  65. setScene()
  66. setCamera()
  67. scene.add(gltf.scene)
  68. })()
  69. </script>
  70. </body>
  71. </html>

加载模型代码讲解:

loader.load 用来加载和解析 glTF 文件,接受四个参数:

  • 第一个参数 url 是要加载的 glTF 模型文件的路径。
  • 第二个参数是一个回调函数,当模型加载成功时会被调用
  • 第三个参数是一个回调函数,用于跟踪加载进度。回调函数的 { loaded, total } 参数表示已加载和总共需要加载的文件数量,通过计算百分比可以得到当前加载进度。
  • 第四个参数是一个回调函数,当加载出错时会被调用。

创建灯光

  1. <html>
  2. <head>
  3. <meta charset="utf-8">
  4. <title>My first three.js app</title>
  5. </head>
  6. <body>
  7. <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
  8. <script type="importmap">
  9. {
  10. "imports": {
  11. "three": "./three.module.js"
  12. }
  13. }
  14. </script>
  15. <script type="module">
  16. import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
  17. import { Scene, WebGLRenderer, PerspectiveCamera } from 'three'
  18. let scene, renderer, camera, directionalLight, dhelper
  19. let isLoading = true
  20. let loadingWidth = 0
  21. //创建场景
  22. const setScene = () => {
  23. scene = new Scene()
  24. renderer = new WebGLRenderer()
  25. renderer.setSize(window.innerWidth, window.innerHeight)
  26. document.body.appendChild(renderer.domElement)
  27. }
  28. //相机的默认坐标
  29. const defaultMap = {
  30. x: 0,
  31. y: 10,
  32. z: 20,
  33. }
  34. //创建相机
  35. const setCamera = () => {
  36. const { x, y, z } = defaultMap
  37. camera = new PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000)
  38. camera.position.set(x, y, z)
  39. }
  40. // 设置灯光
  41. const setLight = () => {
  42. // 创建一个颜色为白色(0xffffff),强度为 0.5 的平行光对象
  43. directionalLight = new DirectionalLight(0xffffff, 0.5)
  44. //设置平行光的位置,这里将其放置在三维坐标 (-4, 8, 4) 的位置
  45. directionalLight.position.set(-4, 8, 4)
  46. //创建一个平行光辅助对象,用于可视化平行光的方向和强度
  47. dhelper = new DirectionalLightHelper(directionalLight, 5, 0xff0000)
  48. //创建一个颜色为白色(0xffffff),半球颜色为白色(0xffffff),强度为 0.4 的半球光对象
  49. hemisphereLight = new HemisphereLight(0xffffff, 0xffffff, 0.4)
  50. hemisphereLight.position.set(0, 8, 0)
  51. //创建一个半球光辅助对象,用于可视化半球光的方向和强度
  52. hHelper = new HemisphereLightHelper(hemisphereLight, 5)
  53. //添加到场景
  54. scene.add(directionalLight)
  55. //添加到场景
  56. scene.add(hemisphereLight)
  57. }
  58. //使场景、照相机、模型不停调用
  59. const loop = () => {
  60. //requestAnimationFrame(loop) 是浏览器提供的方法,用于在下一次重绘页面之前调用回调函数 loop。这样可以创建一个循环,使场景、相机和模型不断被渲染更新
  61. requestAnimationFrame(loop)
  62. //使用渲染器 renderer 渲染场景 scene 中的模型,使用相机 camera 进行投影
  63. renderer.render(scene, camera)
  64. }
  65. //通过Promise处理一下loadfile函数
  66. const loader = new GLTFLoader() //引入模型的loader实例
  67. const loadFile = (url) => {
  68. return new Promise(((resolve, reject) => {
  69. // loader.load 用来加载和解析 glTF 文件
  70. loader.load(url,
  71. (gltf) => {
  72. resolve(gltf)
  73. }, ({ loaded, total }) => {
  74. let load = Math.abs(loaded / total * 100)
  75. loadingWidth = load
  76. if (load >= 100) {
  77. setTimeout(() => {
  78. isLoading = false
  79. }, 1000)
  80. }
  81. console.log((loaded / total * 100) + '% loaded')
  82. },
  83. (err) => {
  84. reject(err)
  85. }
  86. )
  87. }))
  88. }
  89. (async function () {
  90. const gltf = await loadFile('./assets/scene.gltf')
  91. setScene()
  92. setCamera()
  93. setLight()
  94. scene.add(gltf.scene)
  95. loop()
  96. })()
  97. </script>
  98. </body>
  99. </html>

DirectionalLight 和 HemisphereLight 是 Three.js 中的两种灯光类型,分别表示平行光和半球光。它们用于模拟现实世界中的光照效果

此刻模型已经可以看见了,如何你只能看见黑黑的一片,无法看到模型,一般两个原因:

  • 模型是否加载成功
  1. try {
  2. gltf = await loadFile('./assets/scene.gltf');
  3. console.log('Model loading completed:', gltf);
  4. } catch (error) {
  5. console.error('Error loading model:', error);
  6. }
  • 相机位置偏差,调整下相机(defaultMap)的位置

控制模型

这一步完成之后,模型就可以通过鼠标移动,旋转了

  1. <html>
  2. <head>
  3. <meta charset="utf-8">
  4. <title>My first three.js app</title>
  5. </head>
  6. <body>
  7. <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
  8. <script type="importmap">
  9. {
  10. "imports": {
  11. "three": "./three.module.js"
  12. }
  13. }
  14. </script>
  15. <script type="module">
  16. import { Scene, WebGLRenderer, PerspectiveCamera, DirectionalLight, HemisphereLight, DirectionalLightHelper, HemisphereLightHelper } from 'three'
  17. import { GLTFLoader } from './jsm/loaders/GLTFLoader.js'
  18. import { OrbitControls } from './jsm/controls/OrbitControls.js'
  19. let scene, renderer, camera, directionalLight, hemisphereLight, dhelper, hHelper, controls
  20. let isLoading = true
  21. let loadingWidth = 0
  22. //创建场景
  23. const setScene = () => {
  24. scene = new Scene()
  25. renderer = new WebGLRenderer()
  26. renderer.setSize(window.innerWidth, window.innerHeight)
  27. document.body.appendChild(renderer.domElement)
  28. }
  29. //相机的默认坐标
  30. const defaultMap = {
  31. x: 0,
  32. y: 10,
  33. z: 20,
  34. }
  35. //创建相机
  36. const setCamera = () => {
  37. const { x, y, z } = defaultMap
  38. camera = new PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000)
  39. camera.position.set(x, y, z)
  40. }
  41. // 设置灯光
  42. const setLight = () => {
  43. directionalLight = new DirectionalLight(0xffffff, 0.5)
  44. directionalLight.position.set(-4, 8, 4)
  45. dhelper = new DirectionalLightHelper(directionalLight, 5, 0xff0000)
  46. hemisphereLight = new HemisphereLight(0xffffff, 0xffffff, 0.4)
  47. hemisphereLight.position.set(0, 8, 0)
  48. hHelper = new HemisphereLightHelper(hemisphereLight, 5)
  49. scene.add(directionalLight)
  50. scene.add(hemisphereLight)
  51. }
  52. //使场景、照相机、模型不停调用
  53. const loop = () => {
  54. requestAnimationFrame(loop)
  55. renderer.render(scene, camera)
  56. controls.update()
  57. }
  58. // 设置模型控制
  59. const setControls = () => {
  60. // 创建一个新的 OrbitControls 对象,并将它绑定到相机 camera 和渲染器的 DOM 元素 renderer.domElement 上
  61. controls = new OrbitControls(camera, renderer.domElement)
  62. // 设置相机的最大仰角(上下旋转角度),这里将其限制为 0.9 * π / 2
  63. controls.maxPolarAngle = 0.9 * Math.PI / 2
  64. //启用相机的缩放功能,允许用户通过鼠标滚轮或触摸手势进行缩放操作
  65. controls.enableZoom = true
  66. //监听控制器的变化事件,当用户操作控制器导致相机位置发生改变时,触发渲染函数 render
  67. controls.addEventListener('change', render)
  68. }
  69. //在相机位置发生变化时,将新的相机位置保存到 defaultMap 对象中
  70. const render = () => {
  71. defaultMap.x = Number.parseInt(camera.position.x)
  72. defaultMap.y = Number.parseInt(camera.position.y)
  73. defaultMap.z = Number.parseInt(camera.position.z)
  74. }
  75. //通过Promise处理一下loadfile函数
  76. const loader = new GLTFLoader() //引入模型的loader实例
  77. const loadFile = (url) => {
  78. return new Promise(((resolve, reject) => {
  79. // loader.load 用来加载和解析 glTF 文件
  80. loader.load(url,
  81. (gltf) => {
  82. resolve(gltf)
  83. }, ({ loaded, total }) => {
  84. let load = Math.abs(loaded / total * 100)
  85. loadingWidth = load
  86. if (load >= 100) {
  87. setTimeout(() => {
  88. isLoading = false
  89. }, 1000)
  90. }
  91. console.log((loaded / total * 100) + '% loaded')
  92. },
  93. (err) => {
  94. reject(err)
  95. }
  96. )
  97. }))
  98. }
  99. (async function () {
  100. setScene()
  101. setCamera()
  102. setLight()
  103. setControls()
  104. const gltf = await loadFile('./assets/scene.gltf')
  105. scene.add(gltf.scene)
  106. loop()
  107. })()
  108. </script>
  109. </body>
  110. </html>

ps:这段代码没问题,可正常运行,前两三个可能会有些引入缺失或者声明变量的缺失,大家参考这个补齐,我就不去查漏补缺了

改变车身颜色

scene 有一个traverse函数,它回调了所有模型的子模型信息,只要我们找到对应name属性,就可以更改颜色,和增加贴图等等

  1. //设置车身颜色
  2. const setCarColor = (index) => {
  3. //Color 是 Three.js 中的一个类,用于表示颜色。它的作用是创建和管理三维场景中物体的颜色
  4. const currentColor = new Color(colorAry[index])
  5. // 使用 Three.js 中的 traverse 方法遍历场景中的每个子对象
  6. scene.traverse(child => {
  7. if (child.isMesh) {
  8. console.log(child)
  9. if (child.name) {
  10. //将当前子对象的材质颜色设置为 currentColor,实现改变颜色的效果
  11. child.material.color.set(currentColor)
  12. }
  13. }
  14. })
  15. }

整个的完整代码:

  1. <html>
  2. <head>
  3. <meta charset="utf-8">
  4. <title>My first three.js app</title>
  5. <style>
  6. body {
  7. margin: 0;
  8. }
  9. .maskLoading {
  10. background: #000;
  11. position: fixed;
  12. display: flex;
  13. justify-content: center;
  14. align-items: center;
  15. top: 0;
  16. left: 0;
  17. bottom: 0;
  18. right: 0;
  19. z-index: 1111111;
  20. color: #fff;
  21. }
  22. .maskLoading .loading {
  23. width: 400px;
  24. height: 20px;
  25. border: 1px solid #fff;
  26. background: #000;
  27. overflow: hidden;
  28. border-radius: 10px;
  29. }
  30. .maskLoading .loading div {
  31. background: #fff;
  32. height: 20px;
  33. width: 0;
  34. transition-duration: 500ms;
  35. transition-timing-function: ease-in;
  36. }
  37. canvas {
  38. width: 100%;
  39. height: 100%;
  40. margin: auto;
  41. }
  42. .mask {
  43. color: #fff;
  44. position: absolute;
  45. bottom: 0;
  46. left: 0;
  47. width: 100%;
  48. }
  49. .flex {
  50. display: flex;
  51. flex-wrap: wrap;
  52. padding: 20px;
  53. }
  54. .flex div {
  55. width: 10px;
  56. height: 10px;
  57. margin: 5px;
  58. cursor: pointer;
  59. }
  60. </style>
  61. </head>
  62. <body>
  63. <div class="boxs">
  64. <div class="maskLoading">
  65. <div class="loading">
  66. <div class="oneDiv"></div>
  67. </div>
  68. <div style="padding-left: 10px;" class="twoDiv"></div>
  69. </div>
  70. <div class="mask">
  71. <p class="realTimeDate"></p>
  72. <button class="rotatingCar">转动车</button>
  73. <button class="stop">停止</button>
  74. <div class="flex" id="colorContainer">
  75. </div>
  76. </div>
  77. </div>
  78. <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
  79. <script type="importmap">
  80. {
  81. "imports": {
  82. "three": "./three.module.js"
  83. }
  84. }
  85. </script>
  86. <script type="module">
  87. import {
  88. Color,
  89. DirectionalLight,
  90. DirectionalLightHelper,
  91. HemisphereLight,
  92. HemisphereLightHelper,
  93. PerspectiveCamera,
  94. Scene,
  95. WebGLRenderer
  96. } from 'three'
  97. import { GLTFLoader } from './jsm/loaders/GLTFLoader.js'
  98. import { OrbitControls } from './jsm/controls/OrbitControls.js'
  99. //车身颜色数组
  100. const colorAry = [
  101. "rgb(216, 27, 67)", "rgb(142, 36, 170)", "rgb(81, 45, 168)", "rgb(48, 63, 159)", "rgb(30, 136, 229)", "rgb(0, 137, 123)",
  102. "rgb(67, 160, 71)", "rgb(251, 192, 45)", "rgb(245, 124, 0)", "rgb(230, 74, 25)", "rgb(233, 30, 78)", "rgb(156, 39, 176)",
  103. "rgb(0, 0, 0)"]
  104. let scene, camera, renderer, controls, floor, dhelper, hHelper, directionalLight, hemisphereLight
  105. let gltf
  106. let isLoading = true
  107. let loadingWidth = 0
  108. //相机的默认坐标
  109. const defaultMap = {
  110. x: 0,
  111. y: 10,
  112. z: 20,
  113. }
  114. //遮罩层
  115. const maskLayer = () => {
  116. if (isLoading) {
  117. $('.maskLoading').hide();
  118. } else {
  119. $('.maskLoading').show()
  120. }
  121. }
  122. maskLayer()
  123. // 进度
  124. const schedule = () => {
  125. let timer = setInterval(function () {
  126. $('oneDiv').css('width', `${loadingWidth}%`);
  127. $('twoDiv').text(`${loadingWidth}%`);
  128. if (loadingWidth == 100) {
  129. clearInterval(timer);
  130. }
  131. }, 10);
  132. }
  133. schedule()
  134. //实时更新x,y,z
  135. const realTime = () => {
  136. let timer = setInterval(function () {
  137. $('realTimeDate').text(`x:${defaultMap.x} y:${defaultMap.y} z:${defaultMap.z}`);
  138. }, 10);
  139. }
  140. // 生成颜色旋转块
  141. $.each(colorAry, function (index, item) {
  142. $('<div>').appendTo('#colorContainer') // 在 #colorContainer 中创建一个 <div> 元素
  143. .css('background-color', item) // 设置背景颜色
  144. .click(function () {
  145. setCarColor(index); // 调用 setCarColor 函数并传递索引参数
  146. });
  147. });
  148. //创建场景
  149. const setScene = () => {
  150. scene = new Scene()
  151. renderer = new WebGLRenderer()
  152. renderer.setSize(window.innerWidth, window.innerHeight)
  153. document.body.appendChild(renderer.domElement)
  154. }
  155. //创建相机
  156. const setCamera = () => {
  157. const { x, y, z } = defaultMap
  158. camera = new PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000)
  159. camera.position.set(x, y, z)
  160. }
  161. //引入模型的loader实例
  162. const loader = new GLTFLoader()
  163. //通过Promise处理一下loadfile函数
  164. const loadFile = (url) => {
  165. return new Promise(((resolve, reject) => {
  166. loader.load(url,
  167. (gltf) => {
  168. resolve(gltf)
  169. }, ({ loaded, total }) => {
  170. let load = Math.abs(loaded / total * 100)
  171. loadingWidth = load
  172. if (load >= 100) {
  173. setTimeout(() => {
  174. isLoading = false
  175. }, 1000)
  176. }
  177. console.log((loaded / total * 100) + '% loaded')
  178. },
  179. (err) => {
  180. reject(err)
  181. }
  182. )
  183. }))
  184. }
  185. // 设置灯光
  186. const setLight = () => {
  187. directionalLight = new DirectionalLight(0xffffff, 0.8)
  188. directionalLight.position.set(-4, 8, 4)
  189. dhelper = new DirectionalLightHelper(directionalLight, 5, 0xff0000)
  190. hemisphereLight = new HemisphereLight(0xffffff, 0xffffff, 0.4)
  191. hemisphereLight.position.set(0, 8, 0)
  192. hHelper = new HemisphereLightHelper(hemisphereLight, 5)
  193. scene.add(directionalLight)
  194. scene.add(hemisphereLight)
  195. }
  196. // 设置模型控制
  197. const setControls = () => {
  198. controls = new OrbitControls(camera, renderer.domElement)
  199. controls.maxPolarAngle = 0.9 * Math.PI / 2
  200. controls.enableZoom = true
  201. controls.addEventListener('change', render)
  202. }
  203. //返回坐标信息
  204. const render = () => {
  205. defaultMap.x = Number.parseInt(camera.position.x)
  206. defaultMap.y = Number.parseInt(camera.position.y)
  207. defaultMap.z = Number.parseInt(camera.position.z)
  208. }
  209. (async function () {
  210. setScene()
  211. setCamera()
  212. setLight()
  213. setControls()
  214. try {
  215. gltf = await loadFile('./assets/scene.gltf');
  216. console.log('Model loading completed:', gltf);
  217. } catch (error) {
  218. console.error('Error loading model:', error);
  219. }
  220. scene.add(gltf.scene)
  221. loop()
  222. })()
  223. //使场景、照相机、模型不停调用
  224. const loop = () => {
  225. requestAnimationFrame(loop)
  226. renderer.render(scene, camera)
  227. controls.update()
  228. }
  229. //是否自动转动
  230. $('.rotatingCar').click(function () {
  231. console.log("旋转")
  232. controls.autoRotate = true
  233. })
  234. //停止转动
  235. $('.stop').click(function () {
  236. console.log("停止")
  237. controls.autoRotate = false
  238. })
  239. //设置车身颜色
  240. const setCarColor = (index) => {
  241. const currentColor = new Color(colorAry[index])
  242. scene.traverse(child => {
  243. if (child.isMesh) {
  244. console.log(child)
  245. if (child.name) {
  246. child.material.color.set(currentColor)
  247. }
  248. }
  249. })
  250. }
  251. </script>
  252. </body>
  253. </html>

完结撒花*★,°*:.☆( ̄▽ ̄)/$:*.°★* 。 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/133646
推荐阅读
相关标签
  

闽ICP备14008679号