threeJs学习笔记(二)

 官网:three.js docsicon-default.png?t=N7T8https://threejs.org/docs/index.html#manual/zh/introduction/Creating-a-scene

1.使用three.js绘制多个立方体

(1)定义基本的数组对象,描绘每个立方体的信息

如:绘制5个立方体,随机设置不同颜色(color),大小(宽w高h深d),位置(x,y,z轴坐标)

 const cubeInfoArr = []
  for (let i = 0; i < 5; i++) {
    cubeInfoArr.push({
      color: `rgb(${Math.floor(Math.random() * (255 - 0 + 1) + 0)}, ${Math.floor(Math.random() * (255 - 0 + 1) + 0)}, ${Math.floor(Math.random() * (255 - 0 + 1) + 0)})`,
      w: Math.floor(Math.random() * (3 - 1 + 1) + 1),
      h: Math.floor(Math.random() * (3 - 1 + 1) + 1),
      d: Math.floor(Math.random() * (3 - 1 + 1) + 1),
      x: Math.floor(Math.random() * (5 - -5 + 1) + -5),
      y: Math.floor(Math.random() * (5 - -5 + 1) + -5),
      z: Math.floor(Math.random() * (5 - -5 + 1) + -5),
    })
  }

(2) 针对每个数据对象,创建物体

cubeInfoArr.map(cubeObj => {
    const {color, w, h, d, x, y, z} = cubeObj
    const geometry = new THREE.BoxGeometry(w, h, d)
    const material = new THREE.MeshBasicMaterial({ color })
    const cube = new THREE.Mesh(geometry, material)
    cube.position.set(x, y, z)
    scene.add(cube)
  })

 2.three.js 性能监视器

 目的:可以查看网页每秒传输帧数,每帧刷新用时,内存占用,帮我们更好的调试 3D 项目

步骤:

(1)单独引入 Stats 附加组件

import Stats from 'three/examples/jsm/libs/stats.module.js'

(2)创建性能监视器

let stats
stats = new Stats()

(3)设置监视器面板类型(0:fps-每秒传输帧数,1:ms-每帧刷新用时,2:mb-内存占用)

stats.setMode(0)

 (4)设置监视器的位置并添加 DOM

stats.domElement.style.position = 'fixed'
stats.domElement.style.left = '0'
stats.domElement.style.top = '0'
document.querySelector('#threeID').appendChild(stats.domElement)

(5)性能监视器数值不断更新

function renderLoop() {
  //。。。其他代码
  // 性能监视器数值不断更新
  stats.update()
   //。。。其他代码
}

3.three.js 删除物体

目的:双击一次屏幕,删除一个立方体

(1)在创建立方体的时候,给物体定义名字(方便后续获取和甄别)

function createCube() {
  const cubeInfoArr = []
  for (let i = 0; i < 5; i++) {
  //...其他代码
  }
  cubeInfoArr.map(cubeObj => {
   //...其他代码
    cube.name = 'cu' // 给物体定义名字(方便后续获取和甄别)
   //...其他代码
  })
}

(2)给 window 绑定事件

(3)调用 three.js 相关废置函数

(4)再从场景中移除物体

function removeCube() {
  // 1. 给 window 绑定事件
  window.addEventListener('dblclick', () => {
    // 2. 调用 three.js 相关废置函数,名字与定义的名字对应匹配
    const arr = scene.children.filter(obj => obj.name === 'cu')
    const cube = arr[0]
    if (cube) {
      cube.geometry.dispose() // 移除图形数据
      cube.material.dispose() // 移除材质数据
      // 3. 再从场景中移除物体
      scene.remove(cube)
    }
  })
}

4.three.js 物体分组管理

目的:把所有 three.js 物体放入到一个大的组物体中,可以同时位移,旋转,缩放,或删除等等

(1)新建分组

let group = new THREE.Group()

(2)创建物体的同时把物体加入add分组,再将分组加入add场景

function createCube() {
  const cubeInfoArr = []
  for (let i = 0; i < 2500; i++) {
    //...其他代码
  }
  cubeInfoArr.map(cubeObj => {
    //...其他代码
    // 分组中加入物体
    group.add(cube)
  })
  // 把分组加入到场景中
  scene.add(group)
}

(3)比如在移除时可以直接移除分组对象

function removeCube() {
  window.addEventListener('dblclick', () => {
    group.children.map(obj => {
      obj.geometry.dispose()
      obj.material.dispose()
      group.remove(obj)
    })
    // 把组对象移除掉
    scene.remove(group)
  })
}

以上效果所有代码:

<template>
  <div id="threeID">

  </div>
</template>

<script setup>
//  引入 three 库
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import Stats from 'three/examples/jsm/libs/stats.module.js'
//引入dat.gui 库
import * as dat from 'dat.gui'
// 创建场景,摄像机,渲染器
let scene, camera, renderer
// 创建轨道控制器
let controls
//创建立方体
let cube
// 性能监视器
let stats
// 新建分组
let group = new THREE.Group()

/**
 * 创建基础场景
 */
function init() {
  // 1. 创建场景对象
  scene = new THREE.Scene()
  // 2. 创建摄像机对象
  camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
  //移动摄像机向z轴5个单位(默认摄像机和物体在原点)
  camera.position.z = 5
  // 3. 创建渲染器,并设置画布大小,添加到 DOM 显示
  renderer = new THREE.WebGLRenderer()
  // 设置画布大小
  renderer.setSize(window.innerWidth, window.innerHeight)
  // 把画布 canvas 标签添加到id为threeID上
  document.querySelector('#threeID').append(renderer.domElement)
}

/**
 * 创建多个立方体
 */
function createCube() {
  // 1. 定义数据对象,描绘每个立方体的信息
  const cubeInfoArr = []
  for (let i = 0; i < 5; i++) {
    cubeInfoArr.push({
      color: `rgb(${Math.floor(Math.random() * (255 - 0 + 1) + 0)}, ${Math.floor(Math.random() * (255 - 0 + 1) + 0)}, ${Math.floor(Math.random() * (255 - 0 + 1) + 0)})`,
      w: Math.floor(Math.random() * (3 - 1 + 1) + 1),
      h: Math.floor(Math.random() * (3 - 1 + 1) + 1),
      d: Math.floor(Math.random() * (3 - 1 + 1) + 1),
      x: Math.floor(Math.random() * (5 - -5 + 1) + -5),
      y: Math.floor(Math.random() * (5 - -5 + 1) + -5),
      z: Math.floor(Math.random() * (5 - -5 + 1) + -5),
    })
  }
  cubeInfoArr.map(cubeObj => {
    const { color, w, h, d, x, y, z } = cubeObj
    const geometry = new THREE.BoxGeometry(w, h, d)
    const material = new THREE.MeshBasicMaterial({ color })
    const cube = new THREE.Mesh(geometry, material)
    cube.position.set(x, y, z)
    cube.name = 'cu' // 给物体定义名字(方便后续获取和甄别)
    // 分组中加入物体
    group.add(cube)
    // scene.add(cube)  //单个加入场景
    scene.add(group) //分组加入场景
  })
}

/**
 * 轨道控制器
 */
function controlsCreate() {
  //  创建轨道控制器
  controls = new OrbitControls(camera, renderer.domElement)
  // 1. 阻尼效果
  controls.enableDamping = true
  // 2. 开启自动旋转轨道控制器效果->带动摄像机一起旋转(摄像机顺时针水平旋转)
  // controls.autoRotate = true
  // 3. 垂直角度范围控制(0 上面,Math.PI 下面)
  // controls.maxPolarAngle = Math.PI
  // controls.minPolarAngle = 0
  // 水平角度范围控制
  // controls.maxAzimuthAngle = 1.5 * Math.PI
  // controls.minAzimuthAngle = 0.5 * Math.PI
  // 4. 摄像机移动范围控制
  // controls.minDistance = 2
  // controls.maxDistance = 10
}

function renderLoop() {
  //  在渲染循环中更新场景渲染
  renderer.render(scene, camera)
  // 手动 JS 代码更新过摄像机信息,必须调用轨道控制器 update 方法
  controls.update()
  // 性能监视器数值不断更新
  stats.update()
  // 根据当前计算机浏览器刷新帧率(默认 60 次/秒),不断递归调用此函数渲染最新的画面状态
  // 好处:当前页面切换到后台,暂停递归
  requestAnimationFrame(renderLoop)
}


/**
 * 坐标轴
 */
function createHelper() {
  // 1. 创建坐标轴对象,设置长度
  const axesHelper = new THREE.AxesHelper(5)
  // 2. 添加到场景中
  scene.add(axesHelper)
}

/**
 * 适配场景大小
 */
function renderResize() {
  // 1. 创建适配函数,监听浏览器 resize 事件
  document.querySelector('#threeID').addEventListener('resize', () => {
    // 2. 调整渲染器画布大小,摄像机宽高比和更新视椎体空间
    renderer.setSize(window.innerWidth, window.innerHeight)
    camera.aspect = window.innerWidth / window.innerHeight
    camera.updateProjectionMatrix()
  })
}

/**
 * 性能监视器
 */
function createStats() {
  // 2. 创建性能监视器
  stats = new Stats()
  // 3. 设置监视器面板类型(0:fps-每秒传输帧数,1:ms-每帧刷新用时,2:mb-内存占用)
  stats.setMode(0)
  // 4. 设置监视器位置并添加 DOM
  stats.domElement.style.position = 'fixed'
  stats.domElement.style.left = '0'
  stats.domElement.style.top = '0'
  document.querySelector('#threeID').appendChild(stats.domElement)
}

/**
 * 双击删除物体
 */
function removeCube() {
  // 1. 给 window 绑定事件
  window.addEventListener('dblclick', () => {
    /**
     * 单个移除
     */
    // 2. 调用 three.js 相关废置函数
    // const arr = scene.children.filter(obj => obj.name === 'cu')
    // console.log(arr, 'arr')
    // const cube = arr[0]
    // if (cube) {
    //   cube.geometry.dispose() // 移除图形数据
    //   cube.material.dispose() // 移除材质数据
    //   // 3. 再从场景中移除物体
    //   scene.remove(cube)
    // }
    /**
     * 移除分组
     */
    group.children.map(obj => {
      obj.geometry.dispose()
      obj.material.dispose()
      group.remove(obj)
    })
    // 把组对象移除掉
    scene.remove(group)
  })
}




onMounted(() => {
  init() //初始化
  createCube()//创建多个立方体
  controlsCreate()//轨道控制器
  createStats()
  renderLoop()//循环渲染轨道控制器
  createHelper()//坐标轴
  renderResize()//适配场景大小
  removeCube() //移除物体
  renderer.render(scene, camera)
})
</script>

<style  scoped></style>

5.three.js 点物体和材质

步骤如创建立方体类似,对应的材质不一样,点物体使用对应的点材质,几何图形不限制

function createSphere() {
  // 1. 创建几何图形
  const geometry = new THREE.SphereGeometry(1, 32, 16)
  // 2. 创建点材质
  const material = new THREE.PointsMaterial({ color: 0x6600ff, size: 0.05 })
  // 3. 创建点物体
  const points = new THREE.Points(geometry, material)
  scene.add(points)
}

6.three.js 线物体和材质

 Line: 一条连续的线

LineLoop: 一条从头链接到尾的闭合线

 LineSegments:按顺序一对点链接一条线

function createLine() {
  // 1. 创建几何图形
  const geometry = new THREE.SphereGeometry(1, 32, 16)
  // 2. 创建线材质
  const material = new THREE.LineBasicMaterial({
    color: 0x6600ff
  })
  // 3. 创建线物体
  const line = new THREE.Line(geometry, material)
  scene.add(line)
}

以上代码:

<template>
  <div id="threeID">

  </div>
</template>

<script setup>
//  引入 three 库
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import Stats from 'three/examples/jsm/libs/stats.module.js'
// 创建场景,摄像机,渲染器
let scene, camera, renderer
// 创建轨道控制器
let controls

/**
 * 创建基础场景
 */
function init() {
  // 1. 创建场景对象
  scene = new THREE.Scene()
  // 2. 创建摄像机对象
  camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
  //移动摄像机向z轴5个单位(默认摄像机和物体在原点)
  camera.position.z = 5
  // 3. 创建渲染器,并设置画布大小,添加到 DOM 显示
  renderer = new THREE.WebGLRenderer()
  // 设置画布大小
  renderer.setSize(window.innerWidth, window.innerHeight)
  // 把画布 canvas 标签添加到id为threeID上
  document.querySelector('#threeID').append(renderer.domElement)
}

/**
 * 创建球形点物体
 */
function createSphere() {
  // 1. 创建几何图形
  const geometry = new THREE.SphereGeometry(1, 32, 16)
  // 2. 创建点材质
  const material = new THREE.PointsMaterial({ color: 0x6600ff, size: 0.05 })
  // 3. 创建点物体
  const points = new THREE.Points(geometry, material)
  scene.add(points)
}

/**
 * 球形点与点之间加上线段
 */
function createLine() {
  // 1. 创建几何图形
  const geometry = new THREE.SphereGeometry(1, 32, 16)
  // 2. 创建线材质
  const material = new THREE.LineBasicMaterial({
    color: 0x6600ff
  })
  // 3. 创建线物体
  const line = new THREE.LineSegments(geometry, material)
  scene.add(line)
}

/**
 * 轨道控制器
 */
function controlsCreate() {
  //  创建轨道控制器
  controls = new OrbitControls(camera, renderer.domElement)
  // 1. 阻尼效果
  controls.enableDamping = true
  // 2. 开启自动旋转轨道控制器效果->带动摄像机一起旋转(摄像机顺时针水平旋转)
  // controls.autoRotate = true
  // 3. 垂直角度范围控制(0 上面,Math.PI 下面)
  // controls.maxPolarAngle = Math.PI
  // controls.minPolarAngle = 0
  // 水平角度范围控制
  // controls.maxAzimuthAngle = 1.5 * Math.PI
  // controls.minAzimuthAngle = 0.5 * Math.PI
  // 4. 摄像机移动范围控制
  // controls.minDistance = 2
  // controls.maxDistance = 10
}

function renderLoop() {
  //  在渲染循环中更新场景渲染
  renderer.render(scene, camera)
  // 手动 JS 代码更新过摄像机信息,必须调用轨道控制器 update 方法
  controls.update()
  // 根据当前计算机浏览器刷新帧率(默认 60 次/秒),不断递归调用此函数渲染最新的画面状态
  // 好处:当前页面切换到后台,暂停递归
  requestAnimationFrame(renderLoop)
}


/**
 * 坐标轴
 */
function createHelper() {
  // 1. 创建坐标轴对象,设置长度
  const axesHelper = new THREE.AxesHelper(5)
  // 2. 添加到场景中
  scene.add(axesHelper)
}

/**
 * 适配场景大小
 */
function renderResize() {
  // 1. 创建适配函数,监听浏览器 resize 事件
  document.querySelector('#threeID').addEventListener('resize', () => {
    // 2. 调整渲染器画布大小,摄像机宽高比和更新视椎体空间
    renderer.setSize(window.innerWidth, window.innerHeight)
    camera.aspect = window.innerWidth / window.innerHeight
    camera.updateProjectionMatrix()
  })
}



onMounted(() => {
  init() //初始化
  createSphere()//创建球形点物体
  createLine()//连线
  controlsCreate()//轨道控制器
  renderLoop()//循环渲染轨道控制器
  createHelper()//坐标轴
  renderResize()//适配场景大小
  renderer.render(scene, camera)
})
</script>

<style  scoped></style>

7.three.js 全景图贴图

(1)引入图片,(vue3使用import本地引入图片,vue2可使用require)

 import earthImg from '@/assets/earth.png'

 (2)创建球体几何图形,使用纹理加载器并创建网格材质对象,创建网格物体

function createMap() {
  // 1. 创建球体几何图形
  const geometry = new THREE.SphereGeometry(1, 32, 16)
  // 2. 使用纹理加载器并创建网格材质对象
  const texture = new THREE.TextureLoader().load(earthImg);
  // 立即使用纹理进行材质创建(map: 颜色贴图)
  const material = new THREE.MeshBasicMaterial({ map: texture });
  // 3. 创建网格物体
  const sphere = new THREE.Mesh(geometry, material)
  scene.add(sphere)
}

效果如图所示:

全部代码:

<template>
  <div id="threeID">

  </div>
</template>

<script setup>
//  引入 three 库
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import earthImg from '@/assets/earth.png'
// 创建场景,摄像机,渲染器
let scene, camera, renderer
// 创建轨道控制器
let controls

/**
 * 创建基础场景
 */
function init() {
  // 1. 创建场景对象
  scene = new THREE.Scene()
  // 2. 创建摄像机对象
  camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
  //移动摄像机向z轴5个单位(默认摄像机和物体在原点)
  camera.position.z = 5
  // 3. 创建渲染器,并设置画布大小,添加到 DOM 显示
  renderer = new THREE.WebGLRenderer()
  // 设置画布大小
  renderer.setSize(window.innerWidth, window.innerHeight)
  // 把画布 canvas 标签添加到id为threeID上
  document.querySelector('#threeID').append(renderer.domElement)
}

/**
 * 创建球状全景贴图
 */
function createMap() {
  // 1. 创建球体几何图形
  const geometry = new THREE.SphereGeometry(1, 32, 16)
  // 2. 使用纹理加载器并创建网格材质对象
  const texture = new THREE.TextureLoader().load(earthImg);
  // 立即使用纹理进行材质创建(map: 颜色贴图)
  const material = new THREE.MeshBasicMaterial({ map: texture });
  // 3. 创建网格物体
  const sphere = new THREE.Mesh(geometry, material)
  scene.add(sphere)
}

/**
 * 轨道控制器
 */
function controlsCreate() {
  //  创建轨道控制器
  controls = new OrbitControls(camera, renderer.domElement)
  // 1. 阻尼效果
  controls.enableDamping = true
}

function renderLoop() {
  //  在渲染循环中更新场景渲染
  renderer.render(scene, camera)
  // 手动 JS 代码更新过摄像机信息,必须调用轨道控制器 update 方法
  controls.update()
  // 根据当前计算机浏览器刷新帧率(默认 60 次/秒),不断递归调用此函数渲染最新的画面状态
  // 好处:当前页面切换到后台,暂停递归
  requestAnimationFrame(renderLoop)
}


/**
 * 坐标轴
 */
function createHelper() {
  // 1. 创建坐标轴对象,设置长度
  const axesHelper = new THREE.AxesHelper(5)
  // 2. 添加到场景中
  scene.add(axesHelper)
}

/**
 * 适配场景大小
 */
function renderResize() {
  // 1. 创建适配函数,监听浏览器 resize 事件
  document.querySelector('#threeID').addEventListener('resize', () => {
    // 2. 调整渲染器画布大小,摄像机宽高比和更新视椎体空间
    renderer.setSize(window.innerWidth, window.innerHeight)
    camera.aspect = window.innerWidth / window.innerHeight
    camera.updateProjectionMatrix()
  })
}



onMounted(() => {
  init() //初始化
  createMap()
  controlsCreate()//轨道控制器
  renderLoop()//循环渲染轨道控制器
  createHelper()//坐标轴
  renderResize()//适配场景大小
  renderer.render(scene, camera)
})
</script>

<style  scoped></style>

8.three.js 立方体贴图

 

function createCubeMap() {
  // 1. 创建立方缓冲几何体
  const geometry = new THREE.BoxGeometry(1, 1, 1)
  // 2. 加载不同纹理图片并创建材质对象 6 个
  const imgUrlArr = ['posx.jpg', 'negx.jpg', 'posy.jpg', 'negy.jpg', 'posz.jpg', 'negz.jpg']
  // 纹理加载器
  const textureLoader = new THREE.TextureLoader()
  // 设置当前纹理加载器公共的基础路径
  textureLoader.setPath('/src/assets/park/')
  // 遍历图片地址,映射成纹理材质对象
  const materialArr = imgUrlArr.map(imgUrl => {
    // 创建纹理图片对象
    const texture = textureLoader.load(imgUrl)
    // three.js 颜色通道为 rgb 颜色(为了防止图片太浅)
    texture.colorSpace = THREE.SRGBColorSpace
    return new THREE.MeshBasicMaterial({
      map: texture,
      side: THREE.DoubleSide
    })
  })
  // 3. 创建网格物体并加入场景
  const cube = new THREE.Mesh(geometry, materialArr)
  scene.add(cube)
}

 9.three.js 视频纹理

目的:把 video 原生视频标签,转成视频物体加入到 3D 空间中


function createPlaneMap() {
  // 1. 创建平面几何物体
  // 2. 创建并设置视频纹理贴图
  const geometry = new THREE.PlaneGeometry(1, 0.5)
  // 视频纹理
  // 准备视频标签
  const video = document.createElement('video')
  video.src = '/src/assets/video/mouse_cat.mp4'
  video.muted = true // 静音
  video.addEventListener('loadedmetadata', () => { // 加载视频完成
    video.play() // 开始播放视频
  })
  // 创建视频纹理对象
  const texture = new THREE.VideoTexture(video)
  // 把视频纹理->贴到材质上
  const material = new THREE.MeshBasicMaterial({ map: texture })
  // 创建物体
  const plane = new THREE.Mesh(geometry, material)
  scene.add(plane)

  // 点击按钮->播放声音
  const button = document.createElement('button')
  button.innerHTML = '播放'
  button.style.position = 'fixed'
  button.style.left = '0'
  button.style.bottom = '0'
  document.body.appendChild(button)
  button.addEventListener('click', () => {
    video.muted = false // 关闭静音
  })
}

10.three.js 点击事件

目的:与 3D 物体进行鼠标交互

常见的场景交互:比如点击一个标记,让场景交换,切换场景

目标:与 3D 物体进行鼠标交互

类型1:原生 DOM 支持原生事件(设置 pointerEvents = ‘all’)

类型2:three.js 物体使用光射投影 Raycaster

公式:

x 点坐标:(浏览器 x 轴坐标点 / 画布宽度) * 2 - 1

y 点坐标:- (浏览器 y 轴坐标点 / 画布高度) * 2 + 1

import { CSS3DObject, CSS3DRenderer } from 'three/addons/renderers/CSS3DRenderer.js'
let labelRenderer
function domTo3D() {
  const tag = document.createElement('span')
  tag.innerHTML = '立方体'
  tag.style.color = 'white'
  // 类型1:原生 DOM 使用原生的事件绑定(设置 pointerEvents='all')
  tag.style.pointerEvents = 'all'
  tag.addEventListener('click', e => {
    alert('dom 被点击了')
    e.stopPropagation()
  })

  const tag3d = new CSS3DObject(tag)
  tag3d.scale.set(1 / 32, 1 / 32, 1 / 32)
  tag3d.position.set(0, 1, 0)
  scene.add(tag3d)

  labelRenderer = new CSS3DRenderer()
  labelRenderer.setSize(window.innerWidth, window.innerHeight)
  labelRenderer.domElement.style.pointerEvents = 'none'
  labelRenderer.domElement.style.position = 'fixed'
  labelRenderer.domElement.style.left = '0'
  labelRenderer.domElement.style.top = '0'
  document.querySelector('#threeID').appendChild(labelRenderer.domElement)
}


function bindClick() {
  window.addEventListener('click', e => {
    // 定义光线投射对象
    const raycaster = new THREE.Raycaster()
    // 定义二维向量对象(保存转换后的平面 x,y 坐标值)
    const pointer = new THREE.Vector2()

    // 把屏幕坐标 => WebGL设备坐标
    // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
    pointer.x = (e.clientX / window.innerWidth) * 2 - 1
    pointer.y = - (e.clientY / window.innerHeight) * 2 + 1

    // 更新摄像机和鼠标之间的连线(位置)
    raycaster.setFromCamera(pointer, camera)
    // 获取这条线穿过了哪些物体,收集成一个数组
    const list = raycaster.intersectObjects(scene.children)

    console.log(list)
  })
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值