Three.js 划线的知识点

Three.js 线条绘制指南

1. 基础概念

1.1 线条类型

  • THREE.Line: 基础线条,连接点之间形成直线段
  • THREE.LineLoop: 闭合线条,自动连接首尾点
  • THREE.LineSegments: 线段,每两个点之间形成独立线段

1.2 线条材质

  1. 基础线条材质 (LineBasicMaterial)
javascript 复制代码
const material = new THREE.LineBasicMaterial({
  color: 0x00ff00,     // 线条颜色
  linewidth: 1,        // 线宽(WebGL限制为1)
  linecap: 'round',    // 线条端点样式
  linejoin: 'round'    // 线条连接处样式
})
  1. 虚线材质 (LineDashedMaterial)
javascript 复制代码
const material = new THREE.LineDashedMaterial({
  color: 0xff0000,     // 线条颜色
  dashSize: 0.5,       // 虚线段长度
  gapSize: 0.2,        // 间隙长度
  scale: 1             // 虚线模式的整体缩放
})

2. 创建线条的方法

2.1 使用点数组创建

javascript 复制代码
const points = []
points.push(new THREE.Vector3(-1, 0, 0))
points.push(new THREE.Vector3(1, 0, 0))

const geometry = new THREE.BufferGeometry().setFromPoints(points)
const line = new THREE.Line(geometry, material)

2.2 使用 BufferGeometry

javascript 复制代码
const geometry = new THREE.BufferGeometry()
const positions = new Float32Array([
  -1, 0, 0,   // 第一个点
  1, 0, 0     // 第二个点
])
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3))

3. 常见线条效果

3.1 正弦波形线条

javascript 复制代码
const points = []
for (let i = 0; i <= 100; i++) {
  const x = i * 0.1 - 5
  const y = Math.sin(x)
  points.push(new THREE.Vector3(x, y, 0))
}

3.2 圆形轨迹

javascript 复制代码
const points = []
for (let i = 0; i <= 360; i++) {
  const angle = (i * Math.PI) / 180
  const x = Math.cos(angle) * radius
  const y = Math.sin(angle) * radius
  points.push(new THREE.Vector3(x, y, 0))
}

3.3 动态波浪线条

javascript 复制代码
const updateLine = (time) => {
  const positions = line.geometry.attributes.position.array
  for (let i = 0; i < points.length; i++) {
    positions[i * 3 + 1] = Math.sin(i * 0.2 + time)
  }
  line.geometry.attributes.position.needsUpdate = true
}

4. 性能优化

4.1 几何体优化

  • 使用 BufferGeometry 而不是普通 Geometry
  • 适当控制点的数量
  • 及时清理不需要的几何体

4.2 更新优化

  • 仅在必要时更新顶点位置
  • 使用 needsUpdate 标记更新
  • 避免频繁创建新的几何体

4.3 内存管理

javascript 复制代码
// 清理资源
geometry.dispose()
material.dispose()
```line.md

## 5. 注意事项

1. WebGL 限制
   - 线宽在 WebGL 中始终为 1 像素
   - 某些图形卡可能不支持虚线效果

2. 虚线渲染
   - 使用虚线时需要调用 `computeLineDistances()`
   - 虚线模式可能影响性能

3. 动画性能
   - 频繁更新顶点位置时注意性能开销
   - 考虑使用 GPU 计算来优化大量点的更新

## 6. 最佳实践

1. 初始化
   - 预先创建足够的顶点缓冲
   - 合理设置相机位置和视角

2. 更新
   - 使用 requestAnimationFrame 进行动画更新
   - 在组件卸载时清理资源

3. 响应式
   - 监听窗口大小变化
   - 更新渲染器和相机参数

4. 错误处理
   - 检查 WebGL 支持
   - 提供降级方案

vue3 + three 画线

代码如下

html 复制代码
<template>
  <div class="three-container" ref="container">
    <!-- WebGL不支持时显示的错误信息 -->
    <div v-if="!webglSupported" class="webgl-error">
      <h2>WebGL不可用</h2>
      <p>{{ webglError }}</p>
    </div>
  </div>
</template>

<script setup>
import * as THREE from 'three'
import { onMounted, onBeforeUnmount, ref } from 'vue'
import { checkWebGL2Support } from '@/utils/webglCheck'

// 创建DOM引用
const container = ref(null)
// 声明Three.js全局变量
let scene, camera, renderer
let animationFrameId

// WebGL支持状态
const webglSupported = ref(true)
const webglError = ref('')

/**
 * 创建基础线条
 * @returns {THREE.Line} 线条对象
 */
const createBasicLine = () => {
  // 创建线条的点位数组
  const points = []
  // 创建一个正弦波形的线条
  for (let i = 0; i <= 100; i++) {
    const x = i * 0.1 - 5 // x轴范围 -5 到 5
    const y = Math.sin(x) // y = sin(x)
    const z = 0
    points.push(new THREE.Vector3(x, y, z))
  }

  // 创建线条几何体
  const geometry = new THREE.BufferGeometry().setFromPoints(points)
  
  // 创建线条材质
  const material = new THREE.LineBasicMaterial({
    color: 0x00ff00, // 绿色
    linewidth: 1 // 线宽(注意:在WebGL中线宽始终为1)
  })

  // 创建线条对象
  return new THREE.Line(geometry, material)
}

/**
 * 创建虚线
 * @returns {THREE.Line} 虚线对象
 */
const createDashedLine = () => {
  const points = []
  // 创建一个圆形轨迹的点
  for (let i = 0; i <= 360; i++) {
    const angle = (i * Math.PI) / 180
    const x = Math.cos(angle) * 2 // 半径为2的圆
    const y = Math.sin(angle) * 2
    const z = 0
    points.push(new THREE.Vector3(x, y - 5, z)) // 向下偏移5个单位
  }

  const geometry = new THREE.BufferGeometry().setFromPoints(points)
  
  // 使用虚线材质
  const material = new THREE.LineDashedMaterial({
    color: 0xff0000, // 红色
    dashSize: 0.5, // 虚线段长度
    gapSize: 0.2 // 间隙长度
  })

  // 创建虚线
  const line = new THREE.Line(geometry, material)
  // 计算虚线段
  line.computeLineDistances()
  
  return line
}

/**
 * 创建动态线条
 * @returns {Object} 包含线条对象和更新函数
 */
const createAnimatedLine = () => {
  const points = []
  // 初始点
  for (let i = 0; i <= 50; i++) {
    points.push(new THREE.Vector3(i * 0.2 - 5, 0, 0))
  }

  const geometry = new THREE.BufferGeometry()
  
  // 创建顶点位置属性数组
  const positions = new Float32Array(points.length * 3)
  geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3))

  const material = new THREE.LineBasicMaterial({
    color: 0x0000ff // 蓝色
  })

  const line = new THREE.Line(geometry, material)

  // 更新函数 - 创建波浪动画
  const updateLine = (time) => {
    const positions = line.geometry.attributes.position.array

    for (let i = 0; i < points.length; i++) {
      const x = i * 0.2 - 5
      // 使用时间创建动态波浪效果
      const y = Math.sin(x + time) * Math.cos(time * 0.5)
      positions[i * 3] = x
      positions[i * 3 + 1] = y + 3 // 向上偏移3个单位
      positions[i * 3 + 2] = 0
    }

    line.geometry.attributes.position.needsUpdate = true
  }

  return { line, updateLine }
}

/**
 * 初始化3D场景
 */
const initScene = (containerEl) => {
  // 检查WebGL2支持
  const webglStatus = checkWebGL2Support()
  if (!webglStatus.supported) {
    webglSupported.value = false
    webglError.value = webglStatus.reason
    return
  }

  try {
    // 创建场景
    scene = new THREE.Scene()
    scene.background = new THREE.Color(0x111111) // 深色背景

    // 创建相机
    camera = new THREE.PerspectiveCamera(
      75,
      containerEl.clientWidth / containerEl.clientHeight,
      0.1,
      1000
    )
    camera.position.z = 10

    // 创建渲染器
    renderer = new THREE.WebGLRenderer({
      antialias: true,
      alpha: true
    })
    renderer.setSize(containerEl.clientWidth, containerEl.clientHeight)
    renderer.setPixelRatio(window.devicePixelRatio)
    containerEl.appendChild(renderer.domElement)

    // 添加各种线条
    const basicLine = createBasicLine()
    scene.add(basicLine)

    const dashedLine = createDashedLine()
    scene.add(dashedLine)

    const { line: animatedLine, updateLine } = createAnimatedLine()
    scene.add(animatedLine)

    // 动画循环
    const animate = () => {
      animationFrameId = requestAnimationFrame(animate)
      
      // 更新动态线条
      updateLine(Date.now() * 0.001)
      
      renderer.render(scene, camera)
    }

    animate()
  } catch (error) {
    webglSupported.value = false
    webglError.value = `初始化3D场景失败: ${error.message}`
    console.error('场景初始化错误:', error)
  }
}

/**
 * 处理窗口大小变化
 */
const handleResize = () => {
  if (!renderer || !camera) return
  
  const containerEl = renderer.domElement.parentElement
  const width = containerEl.clientWidth
  const height = containerEl.clientHeight

  camera.aspect = width / height
  camera.updateProjectionMatrix()
  renderer.setSize(width, height)
}

// 生命周期钩子
onMounted(() => {
  initScene(container.value)
  window.addEventListener('resize', handleResize)
})

onBeforeUnmount(() => {
  window.removeEventListener('resize', handleResize)
  cancelAnimationFrame(animationFrameId)
  
  if (renderer) {
    renderer.dispose()
  }
  // 清理场景中的所有线条
  scene?.traverse((object) => {
    if (object instanceof THREE.Line) {
      object.geometry.dispose()
      object.material.dispose()
    }
  })
})

// 组件名定义
defineOptions({
  name: 'ThreeLine'
})
</script>

<style scoped>
.three-container {
  width: 100%;
  height: 100vh;
  overflow: hidden;
  position: relative;
}

.webgl-error {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  text-align: center;
  background: rgba(255, 255, 255, 0.95);
  padding: 2rem;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
</style> 
相关推荐
Fantasywt3 小时前
THREEJS 片元着色器实现更自然的呼吸灯效果
前端·javascript·着色器
IT、木易3 小时前
大白话JavaScript实现一个函数,将字符串中的每个单词首字母大写。
开发语言·前端·javascript·ecmascript
ZXT5 小时前
面试精讲 - vue3组件之间的通信
vue.js
张拭心6 小时前
2024 总结,我的停滞与觉醒
android·前端
念九_ysl6 小时前
深入解析Vue3单文件组件:原理、场景与实战
前端·javascript·vue.js
Jenna的海糖6 小时前
vue3如何配置环境和打包
前端·javascript·vue.js
星之卡比*6 小时前
前端知识点---库和包的概念
前端·harmonyos·鸿蒙
灵感__idea6 小时前
Vuejs技术内幕:数据响应式之3.x版
前端·vue.js·源码阅读
烛阴6 小时前
JavaScript 构造器进阶:掌握 “new” 的底层原理,写出更优雅的代码!
前端·javascript
Alan-Xia6 小时前
使用jest测试用例之入门篇
前端·javascript·学习·测试用例