一、构造圆角矩形
方法一:使用canvas自带绘制曲线方法
canvas中曲线的绘制可以通过三种自带的方法:
- 圆弧:
arc
- 二次贝塞尔曲线:
quadraticCurveTo(x2, y2, x3, y3) // 操作点与终点
- 贝塞尔曲线:
bezierCurveTo(x1,y1,x2,y2,x,y) // 操作点1、操作点2与终点
// 例:绘制圆角矩形
const shape = new THREE.Shape()
shape.moveTo(-50, -40)
shape.quadraticCurveTo(-50, -50, -40, -50)
shape.lineTo(40, -50)
shape.quadraticCurveTo(50, -50, 50, -40)
shape.lineTo(50, 40)
shape.quadraticCurveTo(50, 50, 40, 50)
shape.lineTo(-40, 50)
shape.quadraticCurveTo(-50, 50, -50, 40)
shape.lineTo(-50, -40)
💡 该方法的缺点在于无法控制曲线上的点数,因此也无法控制矩形的面片数。在圆角较多的情况下会导致面片数激增。
方法二:使用计算出来的点位绘制曲线
由于计算机绘制曲线,实际上是通过获取曲线上的点进行连接获得的,点越多曲线越平滑,效果越好。因此我们通过该原理自行计算曲线上的点位。
// height 矩形长 width 矩形宽 radius 圆角半径 length 圆角段数
function drawShape(height, width, radius, length) {
const shape = new THREE.Shape()
shape.moveTo(0, width - radius)
shape.lineTo(0, radius)
for (let i = 1; i <= length; i++) {
let a = Math.sin((Math.PI / length / 2) * i) * radius
let b = Math.cos((Math.PI / length / 2) * i) * radius
shape.lineTo(radius - b, radius - a)
}
shape.lineTo(height - radius, 0)
for (let i = 1; i <= length; i++) {
let a = Math.sin((Math.PI / length / 2) * i) * radius
let b = Math.cos((Math.PI / length / 2) * i) * radius
shape.lineTo(height - radius + a, radius - b)
}
shape.lineTo(height, width - radius)
for (let i = 1; i <= length; i++) {
let a = Math.sin((Math.PI / length / 2) * i) * radius
let b = Math.cos((Math.PI / length / 2) * i) * radius
shape.lineTo(height - radius + b, width - radius + a)
}
shape.lineTo(radius, width)
for (let i = 1; i <= length; i++) {
let a = Math.sin((Math.PI / length / 2) * i) * radius
let b = Math.cos((Math.PI / length / 2) * i) * radius
shape.lineTo(radius - a, width - radius + b)
}
return shape
}
通过这种方法我们可以自由的增加或减少面片数,以更少的面片数达到理想的效果。
const geometry = new THREE.ShapeGeometry(shape)
const material = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true })
// 可以通过 wireframe 属性观察面片数
let mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)
二、挤压生成立方体
使用Three.js库中的挤压缓冲几何体(ExtrudeGeometry)生成立方体。
const curve = new THREE.CatmullRomCurve3([new THREE.Vector3(0,0,0),new THREE.Vector3(0,0,1)])
const extrudeSettings = {
steps: 1, // 细分的点的数量(值越小面片数越少)
bevelEnabled: false, // 是否斜角
extrudePath: curve // 挤压路径
}
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings)
const material = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true })
const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)
💡 此处的curve可用任意曲线方法构造,如果不能使用CatmullRomCurve3方法,也可使用完整代码中使用的LineCurve3方法。
三、完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Title</title>
<style type="text/css">
html,
body {
margin: 0;
height: 100%;
}
canvas {
display: block;
}
</style>
</head>
<body onload="draw();"></body>
<script src="http://www.yanhuangxueyuan.com/3D/example/three.js"></script>
<script src="http://www.yanhuangxueyuan.com/versions/threejsR92/build/three.js"></script>
<script>
var renderer
function initRender() {
renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
}
var camera
function initCamera() {
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000)
camera.position.set(70, 100, 100)
camera.lookAt(new THREE.Vector3(0, 0, 0))
}
var scene
function initScene() {
scene = new THREE.Scene()
}
var light
function initLight() {
scene.add(new THREE.AmbientLight(0x444444))
light = new THREE.SpotLight(0xffffff)
light.position.set(60, 30, 0)
scene.add(light)
}
function initModel() {
//辅助工具
var helper = new THREE.AxesHelper(100)
scene.add(helper)
const shape = drawShape(50, 30, 10, 5)
const curve = new THREE.LineCurve3(new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 4, 0))
const extrudeSettings = {
bevelEnabled: false,
extrudePath: curve
}
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings)
const material = new THREE.MeshPhongMaterial({ color: 0xff0000, wireframe: false })
const mesh = new THREE.Mesh(geometry, material)
scene.add(mesh)
}
function drawShape(height, width, radius, length) {
const shape = new THREE.Shape()
shape.moveTo(0, width - radius)
shape.lineTo(0, radius)
for (let i = 1; i <= length; i++) {
let a = Math.sin((Math.PI / length / 2) * i) * radius
let b = Math.cos((Math.PI / length / 2) * i) * radius
shape.lineTo(radius - b, radius - a)
}
shape.lineTo(height - radius, 0)
for (let i = 1; i <= length; i++) {
let a = Math.sin((Math.PI / length / 2) * i) * radius
let b = Math.cos((Math.PI / length / 2) * i) * radius
shape.lineTo(height - radius + a, radius - b)
}
shape.lineTo(height, width - radius)
for (let i = 1; i <= length; i++) {
let a = Math.sin((Math.PI / length / 2) * i) * radius
let b = Math.cos((Math.PI / length / 2) * i) * radius
shape.lineTo(height - radius + b, width - radius + a)
}
shape.lineTo(radius, width)
for (let i = 1; i <= length; i++) {
let a = Math.sin((Math.PI / length / 2) * i) * radius
let b = Math.cos((Math.PI / length / 2) * i) * radius
shape.lineTo(radius - a, width - radius + b)
}
return shape
}
function render() {
renderer.render(scene, camera)
}
function animate() {
//更新控制器
render()
requestAnimationFrame(animate)
}
function draw() {
initRender()
initScene()
initCamera()
initLight()
initModel()
animate()
}
</script>
</html>