数学——贝塞尔曲线


在这里插入图片描述

目标

实现图中的运动。

代码准备

一个画布

结构:

<div id="app">
	<div id="rectangle"></div>
</div>

表现:

#app {
    
    
	width: 600px;
	height: 600px;
	margin: 0 auto;
	outline: solid pink 2px;
	position: relative;
}

#rectangle {
    
    
	position: absolute;
	width: 100px;
	height: 100px;
	background-color: red;
}

效果:

在这里插入图片描述

一些JS

代表坐标的类:

function Point2D(x = 0, y = 0) {
    
    
	this.x = x
	this.y = y
}

动画的状态:

//目标元素
let element = document.getElementById('rectangle')
//状态
let index = 0
let totalTime = 3000
let direction = true

element:动画的目标元素。

index:当前动画的百分比,每次加减0.01。范围为零到一。
totalTime:动画的总毫秒数。
direction:当前的运动方向。

一阶贝塞尔

这个网站可以观察贝塞尔的运动:https://www.jasondavies.com/animated-bezier/

一阶是匀速运动,每帧的坐标:
起点坐标加上当前偏移。

function BezierOne(from, to, step) {
    
    
	let x = from.x + (to.x - from.x) * step
	let y = from.y + (to.y - from.y) * step
	return new Point2D(x, y)
}

实现一阶的运动:

let fromP = new Point2D(0, 0)
let toP = new Point2D(250, 0)
setInterval(() => {
    
    
	if (index > 1 || index < 0) {
    
    
		direction = !direction
	}
	if (direction) {
    
    
		index += 0.01
	} else {
    
    
		index -= 0.01
	}
	let point = BezierOne(fromP, toP, index)
	element.style.left = point.x + "px"
	element.style.top = point.y + "px"
}, 0.01 * totalTime)

先判断边界,更改运动方向。
由运动方向确定下一帧的index。
计算贝塞尔坐标。赋值。

效果:

在这里插入图片描述

二阶贝塞尔

起点A,控制点B,终点C。

算出AB一阶,BC一阶。
再由这两个一阶计算一阶。(套娃)

function BezierTwo(from, to, control, step) {
    
    
	let a = BezierOne(from, control, step)
	let b = BezierOne(control, to, step)
	return BezierOne(a, b, step)
}

多了一个点。

let fromP = new Point2D(0, 0)
let controlP = new Point2D(250, 0)
let toP = new Point2D(250, 500)

把调用改为二阶:

BezierTwo(fromP, toP, controlP, index)

效果:

在这里插入图片描述

三阶贝塞尔

起点A,控制点B,控制点C,终点D。

算出AB一阶,BC一阶,CD一阶。
再由这三个一阶计算二阶。(套娃)

function BezierThree(from, to, controlA, controlB, step) {
    
    
	let a = BezierOne(from, controlA, step)
	let b = BezierOne(controlA, controlB, step)
	let c = BezierOne(controlB, to, step)
	return BezierTwo(a, c, b, step)
}

又多了一个点:

let fromP = new Point2D(0, 0)
let controlA = new Point2D(250, 0)
let controlB = new Point2D(250, 500)
let toP = new Point2D(0, 0)

调用改为三阶:

BezierThree(fromP, toP, controlA, controlB, index)

效果:

在这里插入图片描述

四阶贝塞尔

起点A,控制点B,控制点C,控制点D,终点E。

算出AB一阶,BC一阶,CD一阶,DE一阶。
再由这四个一阶计算三阶。(套娃)

function BezierFour(from, to, controlA, controlB, controlC, step) {
    
    
	let a = BezierOne(from, controlA, step)
	let b = BezierOne(controlA, controlB, step)
	let c = BezierOne(controlB, controlC, step)
	let d = BezierOne(controlC, to, step)
	return BezierThree(a, d, b, c, step)
}

又多了一个点:

let fromP = new Point2D(0, 0)
let controlA = new Point2D(250, 0)
let controlB = new Point2D(250, 500)
let controlC = new Point2D(0, 0)
let toP = new Point2D(0, 500)

调用改为四阶:

BezierFour(fromP, toP, controlA, controlB, controlC, index)

效果:

在这里插入图片描述

更多阶贝塞尔

不会真的有人用吧。

function BezierN(arr, step) {
    
    
	if (arr.length === 2) {
    
    
		return BezierOne(arr[0], arr[1], step)
	} else {
    
    
		let arr2 = []
		for (let i = 0; i < arr.length - 1; i++) {
    
    
			arr2.push(BezierOne(arr[i], arr[i + 1], step))
		}
		return BezierN(arr2, step)
	}
}

看看四阶的,对不对。

let point = BezierN([fromP, controlA, controlB, controlC, toP], index)

效果:

在这里插入图片描述

全部代码

<!doctype html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport"
		  content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title>Document</title>
	<style>
		#app {
      
      
			width: 600px;
			height: 600px;
			margin: 0 auto;
			outline: solid pink 2px;
			position: relative;
		}

		#rectangle {
      
      
			position: absolute;
			width: 100px;
			height: 100px;
			background-color: red;
		}
	</style>
</head>
<body>
<div id="app">
	<div id="rectangle"></div>
</div>
<script>
	//目标元素
	let element = document.getElementById('rectangle')
	//状态
	let index = 0
	let totalTime = 3000
	let direction = true
	//起点,终点
	let fromP = new Point2D(0, 0)
	let controlA = new Point2D(250, 0)
	let controlB = new Point2D(250, 500)
	let controlC = new Point2D(0, 0)
	let toP = new Point2D(0, 500)
	setInterval(() => {
      
      
		if (index > 1 || index < 0) {
      
      
			direction = !direction
		}
		if (direction) {
      
      
			index += 0.01
		} else {
      
      
			index -= 0.01
		}
		let point = BezierN([fromP, controlA, controlB, controlC, toP], index)
		element.style.left = point.x + "px"
		element.style.top = point.y + "px"
	}, 0.01 * totalTime)

	function Point2D(x = 0, y = 0) {
      
      
		this.x = x
		this.y = y
	}

	function BezierOne(from, to, step) {
      
      
		let x = from.x + (to.x - from.x) * step
		let y = from.y + (to.y - from.y) * step
		return new Point2D(x, y)
	}

	function BezierTwo(from, to, control, step) {
      
      
		let a = BezierOne(from, control, step)
		let b = BezierOne(control, to, step)
		return BezierOne(a, b, step)
	}

	function BezierThree(from, to, controlA, controlB, step) {
      
      
		let a = BezierOne(from, controlA, step)
		let b = BezierOne(controlA, controlB, step)
		let c = BezierOne(controlB, to, step)
		return BezierTwo(a, c, b, step)
	}

	function BezierFour(from, to, controlA, controlB, controlC, step) {
      
      
		let a = BezierOne(from, controlA, step)
		let b = BezierOne(controlA, controlB, step)
		let c = BezierOne(controlB, controlC, step)
		let d = BezierOne(controlC, to, step)
		return BezierThree(a, d, b, c, step)
	}

	function BezierN(arr, step) {
      
      
		if (arr.length === 2) {
      
      
			return BezierOne(arr[0], arr[1], step)
		} else {
      
      
			let arr2 = []
			for (let i = 0; i < arr.length - 1; i++) {
      
      
				arr2.push(BezierOne(arr[i], arr[i + 1], step))
			}
			return BezierN(arr2, step)
		}
	}
</script>
</body>
</html>

猜你喜欢

转载自blog.csdn.net/qq_37284843/article/details/124343428