目标
实现图中的运动。
代码准备
一个画布
结构:
<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>